From 0ab40c5007311f83a422ac3c915ecc6a64f52a68 Mon Sep 17 00:00:00 2001 From: andy_golden_myth <16195005+andygoldenmyth@user.noreply.gitee.com> Date: Thu, 20 Nov 2025 16:05:35 +0800 Subject: [PATCH 1/5] save the parameters into rmi_data --- rmi_lib/Cargo.toml | 2 + rmi_lib/src/codegen.rs | 88 ++++++++++++++- rmi_lib/src/lib.rs | 2 + rmi_lib/src/manifest.rs | 156 +++++++++++++++++++++++++++ rmi_lib/src/models/balanced_radix.rs | 4 + rmi_lib/src/models/cubic_spline.rs | 4 + rmi_lib/src/models/histogram.rs | 4 + rmi_lib/src/models/linear.rs | 14 ++- rmi_lib/src/models/linear_spline.rs | 4 + rmi_lib/src/models/mod.rs | 10 ++ rmi_lib/src/models/normal.rs | 8 ++ rmi_lib/src/models/radix.rs | 15 +++ src/load.rs | 121 +++++++++++---------- src/main.rs | 144 +++++++++++++------------ tests/cache_fix_osm/main.cpp | 9 +- tests/cache_fix_wiki/main.cpp | 9 +- tests/common/rmi_learned_index.h | 38 +++++++ tests/max_size_wiki/main.cpp | 9 +- tests/radix_model_wiki/main.cpp | 9 +- tests/simple_model_osm/main.cpp | 9 +- tests/simple_model_wiki/main.cpp | 9 +- 21 files changed, 514 insertions(+), 154 deletions(-) create mode 100644 rmi_lib/src/manifest.rs create mode 100644 tests/common/rmi_learned_index.h diff --git a/rmi_lib/Cargo.toml b/rmi_lib/Cargo.toml index df21e0c..36be380 100644 --- a/rmi_lib/Cargo.toml +++ b/rmi_lib/Cargo.toml @@ -21,3 +21,5 @@ superslice = "1.0.0" json = "0.12.0" indicatif = "0.13.0" tabular = "0.1.4" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/rmi_lib/src/codegen.rs b/rmi_lib/src/codegen.rs index 6193b39..54e332b 100644 --- a/rmi_lib/src/codegen.rs +++ b/rmi_lib/src/codegen.rs @@ -9,13 +9,14 @@ use crate::models::Model; use crate::models::*; +use crate::manifest::{self, CacheFixMetadata, LayerMetadata, LayerStorage, ParameterDescriptor, ParamValue}; use bytesize::ByteSize; use log::*; use std::collections::HashSet; use std::io::Write; use std::str; use crate::train::TrainedRMI; -use std::fs::File; +use std::fs::{self, File}; use std::io::BufWriter; use std::path::Path; use std::fmt; @@ -208,6 +209,39 @@ impl LayerParams { }; } + fn num_models(&self) -> usize { + match self { + LayerParams::Constant(_, _) => 1, + LayerParams::Array(_, ppm, params) | + LayerParams::MixedArray(_, ppm, params) => { + assert_eq!(params.len() % ppm, 0); + params.len() / ppm + } + } + } + + fn sample_params(&self) -> &[ModelParam] { + match self { + LayerParams::Constant(_, params) => params.as_slice(), + LayerParams::Array(_, ppm, params) | + LayerParams::MixedArray(_, ppm, params) => ¶ms[0..*ppm], + } + } + + fn storage_descriptor(&self, namespace: &str) -> LayerStorage { + match self { + LayerParams::Constant(_, params) => LayerStorage::Constant { + values: params.iter().map(ParamValue::from).collect(), + }, + LayerParams::Array(idx, _, _) => LayerStorage::Array { + file: format!("{}_{}", namespace, array_name!(*idx)), + }, + LayerParams::MixedArray(idx, _, _) => LayerStorage::MixedArray { + file: format!("{}_{}", namespace, array_name!(*idx)), + }, + } + } + fn size(&self) -> usize { return self.params().iter().map(|p| p.size()).sum(); } @@ -462,9 +496,15 @@ fn generate_code( .enumerate() .map(|(layer_idx, models)| params_for_layer(layer_idx, models)) .collect(); - + let report_last_layer_errors = !rmi.last_layer_max_l1s.is_empty(); + let namespace_dir = Path::new(data_dir).join(namespace); + if !namespace_dir.exists() { + fs::create_dir_all(&namespace_dir) + .expect("Unable to create namespace-specific RMI data directory"); + } + let mut report_lle: Vec = Vec::new(); if report_last_layer_errors { let lle = &rmi.last_layer_max_l1s; @@ -512,7 +552,7 @@ fn generate_code( LayerParams::Array(idx, _, _) | LayerParams::MixedArray(idx, _, _) => { - let data_path = Path::new(&data_dir) + let data_path = namespace_dir .join(format!("{}_{}", namespace, array_name!(idx))); let f = File::create(data_path) .expect("Could not write data file to RMI directory"); @@ -522,8 +562,13 @@ fn generate_code( lp.to_decl(data_output)?; // write to source code read_code.push(" {".to_string()); - read_code.push(format!(" std::ifstream infile(std::filesystem::path(dataPath) / \"{ns}_{fn}\", std::ios::in | std::ios::binary);", + read_code.push(format!(" auto primary = std::filesystem::path(dataPath) / \"{ns}\" / \"{ns}_{fn}\";", + ns=namespace, fn=array_name!(idx))); + read_code.push(format!(" std::ifstream infile(primary, std::ios::in | std::ios::binary);")); + read_code.push(" if (!infile.good()) {".to_string()); + read_code.push(format!(" infile.open(std::filesystem::path(dataPath) / \"{ns}_{fn}\", std::ios::in | std::ios::binary);", ns=namespace, fn=array_name!(idx))); + read_code.push(" }".to_string()); read_code.push(" if (!infile.good()) return false;".to_string()); if lp.requires_malloc() { read_code.push(format!(" {} = ({}*) malloc({});", @@ -538,6 +583,41 @@ fn generate_code( } } } + let manifest_layers: Vec = layer_params + .iter() + .take(rmi.rmi.len()) + .zip(rmi.rmi.iter()) + .map(|(lp, models)| LayerMetadata { + index: lp.index(), + model_type: models[0].model_name().to_string(), + num_models: lp.num_models(), + params_per_model: lp.params_per_model(), + parameters: lp.sample_params().iter().map(ParameterDescriptor::from).collect(), + storage: lp.storage_descriptor(namespace), + }) + .collect(); + + let cache_fix_meta = if let Some((line_size, points)) = &rmi.cache_fix { + let idx = layer_params.len() - 1; + Some(CacheFixMetadata { + file: format!("{}_{}", namespace, array_name!(idx)), + line_size: *line_size, + points: points.len(), + }) + } else { + None + }; + + manifest::write_metadata( + namespace, + data_dir, + key_type, + &rmi, + report_last_layer_errors, + manifest_layers, + cache_fix_meta, + ).expect("Unable to write RMI manifest"); + read_code.push(" return true;".to_string()); read_code.push("}".to_string()); diff --git a/rmi_lib/src/lib.rs b/rmi_lib/src/lib.rs index 0dcc925..cb90d3c 100644 --- a/rmi_lib/src/lib.rs +++ b/rmi_lib/src/lib.rs @@ -2,6 +2,7 @@ mod codegen; mod models; mod train; mod cache_fix; +mod manifest; pub mod optimizer; pub use models::{RMITrainingData, RMITrainingDataIteratorProvider, ModelInput}; @@ -10,3 +11,4 @@ pub use optimizer::find_pareto_efficient_configs; pub use train::{train, train_for_size, train_bounded}; pub use codegen::rmi_size; pub use codegen::output_rmi; +pub use manifest::{CacheFixMetadata, LayerMetadata, ParameterDescriptor, ParamKind, ParamValue, RmiMetadata}; diff --git a/rmi_lib/src/manifest.rs b/rmi_lib/src/manifest.rs new file mode 100644 index 0000000..1938d55 --- /dev/null +++ b/rmi_lib/src/manifest.rs @@ -0,0 +1,156 @@ +use crate::models::KeyType; +use crate::models::ModelParam; +use crate::train::TrainedRMI; +use serde::{Deserialize, Serialize}; +use std::fs::{self, File}; +use std::io::Write; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum ParamKind { + Int, + Float, + ShortArray, + IntArray, + Int32Array, + FloatArray, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", content = "value")] +pub enum ParamValue { + Int(u64), + Float(f64), + ShortArray(Vec), + IntArray(Vec), + Int32Array(Vec), + FloatArray(Vec), +} + +impl From<&ModelParam> for ParamValue { + fn from(param: &ModelParam) -> Self { + match param { + ModelParam::Int(v) => ParamValue::Int(*v), + ModelParam::Float(v) => ParamValue::Float(*v), + ModelParam::ShortArray(v) => ParamValue::ShortArray(v.clone()), + ModelParam::IntArray(v) => ParamValue::IntArray(v.clone()), + ModelParam::Int32Array(v) => ParamValue::Int32Array(v.clone()), + ModelParam::FloatArray(v) => ParamValue::FloatArray(v.clone()), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ParameterDescriptor { + pub kind: ParamKind, + pub len: usize, +} + +impl From<&ModelParam> for ParameterDescriptor { + fn from(param: &ModelParam) -> Self { + let kind = match param { + ModelParam::Int(_) => ParamKind::Int, + ModelParam::Float(_) => ParamKind::Float, + ModelParam::ShortArray(_) => ParamKind::ShortArray, + ModelParam::IntArray(_) => ParamKind::IntArray, + ModelParam::Int32Array(_) => ParamKind::Int32Array, + ModelParam::FloatArray(_) => ParamKind::FloatArray, + }; + + ParameterDescriptor { + kind, + len: param.len(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum LayerStorage { + Constant { values: Vec }, + Array { file: String }, + MixedArray { file: String }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LayerMetadata { + pub index: usize, + pub model_type: String, + pub num_models: usize, + pub params_per_model: usize, + pub parameters: Vec, + pub storage: LayerStorage, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CacheFixMetadata { + pub file: String, + pub line_size: usize, + pub points: usize, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RmiMetadata { + pub namespace: String, + pub key_type: String, + pub models: String, + pub branching_factor: u64, + pub build_time_ns: u128, + pub num_rmi_rows: usize, + pub num_data_rows: usize, + pub model_avg_error: f64, + pub model_avg_l2_error: f64, + pub model_avg_log2_error: f64, + pub model_max_error: u64, + pub model_max_error_idx: usize, + pub model_max_log2_error: f64, + pub last_layer_reports_error: bool, + pub layers: Vec, + pub cache_fix: Option, +} + +impl RmiMetadata { + pub fn manifest_path>(data_dir: P, namespace: &str) -> PathBuf { + data_dir.as_ref().join(namespace).join("manifest.json") + } +} + +pub fn write_metadata>(namespace: &str, + data_dir: P, + key_type: KeyType, + rmi: &TrainedRMI, + last_layer_reports_error: bool, + layers: Vec, + cache_fix: Option) + -> std::io::Result<()> { + + let metadata = RmiMetadata { + namespace: namespace.to_string(), + key_type: key_type.as_str().to_string(), + models: rmi.models.clone(), + branching_factor: rmi.branching_factor, + build_time_ns: rmi.build_time, + num_rmi_rows: rmi.num_rmi_rows, + num_data_rows: rmi.num_data_rows, + model_avg_error: rmi.model_avg_error, + model_avg_l2_error: rmi.model_avg_l2_error, + model_avg_log2_error: rmi.model_avg_log2_error, + model_max_error: rmi.model_max_error, + model_max_error_idx: rmi.model_max_error_idx, + model_max_log2_error: rmi.model_max_log2_error, + last_layer_reports_error, + layers, + cache_fix, + }; + + let manifest_path = RmiMetadata::manifest_path(data_dir, namespace); + if let Some(parent) = manifest_path.parent() { + fs::create_dir_all(parent)?; + } + + let mut file = File::create(manifest_path)?; + let serialized = serde_json::to_vec_pretty(&metadata) + .expect("serialization to JSON should not fail"); + file.write_all(&serialized)?; + Ok(()) +} + diff --git a/rmi_lib/src/models/balanced_radix.rs b/rmi_lib/src/models/balanced_radix.rs index a8e6633..f473037 100644 --- a/rmi_lib/src/models/balanced_radix.rs +++ b/rmi_lib/src/models/balanced_radix.rs @@ -161,6 +161,10 @@ inline uint64_t bradix_clamp_low(uint64_t prefix_length, }; } + fn model_name(&self) -> &'static str { + "bradix" + } + fn needs_bounds_check(&self) -> bool { return false; } diff --git a/rmi_lib/src/models/cubic_spline.rs b/rmi_lib/src/models/cubic_spline.rs index 55cef04..c801964 100644 --- a/rmi_lib/src/models/cubic_spline.rs +++ b/rmi_lib/src/models/cubic_spline.rs @@ -181,6 +181,10 @@ inline double cubic(double a, double b, double c, double d, double x) { fn function_name(&self) -> String { return String::from("cubic"); } + + fn model_name(&self) -> &'static str { + "cubic" + } fn needs_bounds_check(&self) -> bool { return false; } diff --git a/rmi_lib/src/models/histogram.rs b/rmi_lib/src/models/histogram.rs index bc73a48..7d3924d 100644 --- a/rmi_lib/src/models/histogram.rs +++ b/rmi_lib/src/models/histogram.rs @@ -99,6 +99,10 @@ inline uint64_t ed_histogram(const uint64_t length, } fn function_name(&self) -> String { return String::from("ed_histogram"); } + + fn model_name(&self) -> &'static str { + "histogram" + } fn restriction(&self) -> ModelRestriction { return ModelRestriction::MustBeTop; } fn needs_bounds_check(&self) -> bool { return false; } } diff --git a/rmi_lib/src/models/linear.rs b/rmi_lib/src/models/linear.rs index a564d13..5065673 100644 --- a/rmi_lib/src/models/linear.rs +++ b/rmi_lib/src/models/linear.rs @@ -113,6 +113,10 @@ inline double linear(double alpha, double beta, double inp) { return String::from("linear"); } + fn model_name(&self) -> &'static str { + "linear" + } + fn set_to_constant_model(&mut self, constant: u64) -> bool { self.params = (constant as f64, 0.0); return true; @@ -202,6 +206,10 @@ inline double loglinear(double alpha, double beta, double inp) { fn function_name(&self) -> String { return String::from("loglinear"); } + + fn model_name(&self) -> &'static str { + "loglinear" + } fn standard_functions(&self) -> HashSet { let mut to_r = HashSet::new(); to_r.insert(StdFunctions::EXP1); @@ -285,11 +293,15 @@ inline double linear(double alpha, double beta, double inp) { }", ); } - + fn function_name(&self) -> String { return String::from("linear"); } + fn model_name(&self) -> &'static str { + "robust_linear" + } + fn set_to_constant_model(&mut self, constant: u64) -> bool { self.params = (constant as f64, 0.0); return true; diff --git a/rmi_lib/src/models/linear_spline.rs b/rmi_lib/src/models/linear_spline.rs index 8a20f1f..377becd 100644 --- a/rmi_lib/src/models/linear_spline.rs +++ b/rmi_lib/src/models/linear_spline.rs @@ -76,6 +76,10 @@ inline double linear(double alpha, double beta, double inp) { return String::from("linear"); } + fn model_name(&self) -> &'static str { + "linear_spline" + } + fn set_to_constant_model(&mut self, constant: u64) -> bool { self.params = (constant as f64, 0.0); return true; diff --git a/rmi_lib/src/models/mod.rs b/rmi_lib/src/models/mod.rs index 1b086d4..49ee563 100644 --- a/rmi_lib/src/models/mod.rs +++ b/rmi_lib/src/models/mod.rs @@ -52,6 +52,15 @@ impl KeyType { } } + pub fn as_str(&self) -> &'static str { + match self { + KeyType::U32 => "u32", + KeyType::U64 => "u64", + KeyType::F64 => "f64", + KeyType::U128 => "u128", + } + } + pub fn to_model_data_type(self) -> ModelDataType { match self { KeyType::U32 => ModelDataType::Int, @@ -743,6 +752,7 @@ pub trait Model: Sync + Send { fn code(&self) -> String; fn function_name(&self) -> String; + fn model_name(&self) -> &'static str; fn standard_functions(&self) -> HashSet { return HashSet::new(); diff --git a/rmi_lib/src/models/normal.rs b/rmi_lib/src/models/normal.rs index 1940e6d..eb4571f 100644 --- a/rmi_lib/src/models/normal.rs +++ b/rmi_lib/src/models/normal.rs @@ -118,6 +118,10 @@ inline double ncdf(double mean, double stdev, double scale, double inp) { fn function_name(&self) -> String { return String::from("ncdf"); } + + fn model_name(&self) -> &'static str { + "normal" + } fn standard_functions(&self) -> HashSet { let mut to_r = HashSet::new(); to_r.insert(StdFunctions::EXP1); @@ -193,6 +197,10 @@ inline double lncdf(double mean, double stdev, double scale, double inp) { fn function_name(&self) -> String { return String::from("lncdf"); } + + fn model_name(&self) -> &'static str { + "lognormal" + } fn standard_functions(&self) -> HashSet { let mut to_r = HashSet::new(); to_r.insert(StdFunctions::EXP1); diff --git a/rmi_lib/src/models/radix.rs b/rmi_lib/src/models/radix.rs index 558c419..3cdbaaa 100644 --- a/rmi_lib/src/models/radix.rs +++ b/rmi_lib/src/models/radix.rs @@ -72,6 +72,10 @@ inline uint64_t radix(uint64_t prefix_length, uint64_t bits, uint64_t inp) { fn function_name(&self) -> String { return String::from("radix"); } + + fn model_name(&self) -> &'static str { + "radix" + } fn needs_bounds_check(&self) -> bool { return false; } @@ -161,6 +165,17 @@ inline uint64_t radix_table(const uint32_t* table, const uint64_t inp) {{ fn function_name(&self) -> String { return String::from("radix_table"); } + + fn model_name(&self) -> &'static str { + match self.table_bits { + 8 => "radix8", + 18 => "radix18", + 22 => "radix22", + 26 => "radix26", + 28 => "radix28", + _ => "radix_table", + } + } fn needs_bounds_check(&self) -> bool { return false; } diff --git a/src/load.rs b/src/load.rs index 9d9bbee..db91a5e 100644 --- a/src/load.rs +++ b/src/load.rs @@ -1,26 +1,25 @@ -// < begin copyright > +// < begin copyright > // Copyright Ryan Marcus 2020 -// +// // See root directory of this project for license terms. -// -// < end copyright > - - -use memmap::MmapOptions; -use rmi_lib::{RMITrainingData, RMITrainingDataIteratorProvider, KeyType}; +// +// < end copyright > + use byteorder::{LittleEndian, ReadBytesExt}; -use std::fs::File; +use memmap::MmapOptions; +use rmi_lib::{KeyType, RMITrainingData, RMITrainingDataIteratorProvider}; use std::convert::TryInto; +use std::fs::File; pub enum DataType { UINT64, UINT32, - FLOAT64 + FLOAT64, } struct SliceAdapterU64 { data: memmap::Mmap, - length: usize + length: usize, } impl RMITrainingDataIteratorProvider for SliceAdapterU64 { @@ -28,25 +27,31 @@ impl RMITrainingDataIteratorProvider for SliceAdapterU64 { fn cdf_iter(&self) -> Box + '_> { Box::new((0..self.length).map(move |i| self.get(i).unwrap())) } - + fn get(&self, idx: usize) -> Option<(Self::InpType, usize)> { - if idx >= self.length { return None; }; - let mi = u64::from_le_bytes((&self.data[8 + idx * 8..8 + (idx + 1) * 8]) - .try_into().unwrap()); + if idx >= self.length { + return None; + }; + let mi = u64::from_le_bytes( + (&self.data[8 + idx * 8..8 + (idx + 1) * 8]) + .try_into() + .unwrap(), + ); return Some((mi.into(), idx)); } - + fn key_type(&self) -> KeyType { KeyType::U64 } - - fn len(&self) -> usize { self.length } -} + fn len(&self) -> usize { + self.length + } +} struct SliceAdapterU32 { data: memmap::Mmap, - length: usize + length: usize, } impl RMITrainingDataIteratorProvider for SliceAdapterU32 { @@ -54,24 +59,30 @@ impl RMITrainingDataIteratorProvider for SliceAdapterU32 { fn cdf_iter(&self) -> Box + '_> { Box::new((0..self.length).map(move |i| self.get(i).unwrap())) } - + fn get(&self, idx: usize) -> Option<(Self::InpType, usize)> { - if idx >= self.length { return None; }; + if idx >= self.length { + return None; + }; let mi = (&self.data[8 + idx * 4..8 + (idx + 1) * 4]) - .read_u32::().unwrap().into(); + .read_u32::() + .unwrap() + .into(); return Some((mi, idx)); } - + fn key_type(&self) -> KeyType { KeyType::U32 } - - fn len(&self) -> usize { self.length } + + fn len(&self) -> usize { + self.length + } } struct SliceAdapterF64 { data: memmap::Mmap, - length: usize + length: usize, } impl RMITrainingDataIteratorProvider for SliceAdapterF64 { @@ -79,25 +90,31 @@ impl RMITrainingDataIteratorProvider for SliceAdapterF64 { fn cdf_iter(&self) -> Box + '_> { Box::new((0..self.length).map(move |i| self.get(i).unwrap())) } - + fn get(&self, idx: usize) -> Option<(Self::InpType, usize)> { - if idx >= self.length { return None; }; + if idx >= self.length { + return None; + }; let mi = (&self.data[8 + idx * 8..8 + (idx + 1) * 8]) - .read_f64::().unwrap().into(); + .read_f64::() + .unwrap() + .into(); return Some((mi, idx)); } - + fn key_type(&self) -> KeyType { KeyType::F64 } - - fn len(&self) -> usize { self.length } + + fn len(&self) -> usize { + self.length + } } pub enum RMIMMap { UINT64(RMITrainingData), UINT32(RMITrainingData), - FLOAT64(RMITrainingData) + FLOAT64(RMITrainingData), } macro_rules! dynamic { @@ -110,7 +127,6 @@ macro_rules! dynamic { } } - impl RMIMMap { pub fn soft_copy(&self) -> RMIMMap { match self { @@ -123,34 +139,31 @@ impl RMIMMap { pub fn into_u64(self) -> Option> { match self { RMIMMap::UINT64(x) => Some(x), - _ => None + _ => None, } } } - -pub fn load_data(filepath: &str, - dt: DataType) -> (usize, RMIMMap) { - let fd = File::open(filepath).unwrap_or_else(|_| { - panic!("Unable to open data file at {}", filepath) - }); +pub fn load_data(filepath: &str, dt: DataType) -> (usize, RMIMMap) { + let fd = + File::open(filepath).unwrap_or_else(|_| panic!("Unable to open data file at {}", filepath)); let mmap = unsafe { MmapOptions::new().map(&fd).unwrap() }; let num_items = (&mmap[0..8]).read_u64::().unwrap() as usize; let rtd = match dt { - DataType::UINT64 => - RMIMMap::UINT64(RMITrainingData::new(Box::new( - SliceAdapterU64 { data: mmap, length: num_items } - ))), - DataType::UINT32 => - RMIMMap::UINT32(RMITrainingData::new(Box::new( - SliceAdapterU32 { data: mmap, length: num_items } - ))), - DataType::FLOAT64 => - RMIMMap::FLOAT64(RMITrainingData::new(Box::new( - SliceAdapterF64 { data: mmap, length: num_items } - ))) + DataType::UINT64 => RMIMMap::UINT64(RMITrainingData::new(Box::new(SliceAdapterU64 { + data: mmap, + length: num_items, + }))), + DataType::UINT32 => RMIMMap::UINT32(RMITrainingData::new(Box::new(SliceAdapterU32 { + data: mmap, + length: num_items, + }))), + DataType::FLOAT64 => RMIMMap::FLOAT64(RMITrainingData::new(Box::new(SliceAdapterF64 { + data: mmap, + length: num_items, + }))), }; return (num_items, rtd); diff --git a/src/main.rs b/src/main.rs index b6e32ff..61b30fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ -// < begin copyright > +// < begin copyright > // Copyright Ryan Marcus 2020 -// +// // See root directory of this project for license terms. -// -// < end copyright > - - +// +// < end copyright > #![allow(clippy::needless_return)] @@ -13,22 +11,21 @@ mod load; use load::{load_data, DataType}; -use rmi_lib::{train, train_bounded}; -use rmi_lib::KeyType; use rmi_lib::optimizer; +use rmi_lib::KeyType; +use rmi_lib::{train, train_bounded}; use json::*; use log::*; +use rayon::prelude::*; use std::f64; +use std::fs; use std::fs::File; use std::io::BufWriter; -use std::fs; use std::path::Path; -use rayon::prelude::*; -use indicatif::{ProgressBar, ProgressStyle}; use clap::{App, Arg}; - +use indicatif::{ProgressBar, ProgressStyle}; fn main() { env_logger::init(); @@ -103,20 +100,23 @@ fn main() { // set the max number of threads to 4 by default, otherwise Rayon goes // crazy on larger machines and allocates too many workers for folds / reduces - let num_threads = matches.value_of("threads") + let num_threads = matches + .value_of("threads") .map(|x| x.parse::().unwrap()) .unwrap_or(4); - rayon::ThreadPoolBuilder::new().num_threads(num_threads).build_global().unwrap(); - - let fp = matches.value_of("input").unwrap(); + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() + .unwrap(); + let fp = matches.value_of("input").unwrap(); let data_dir = matches.value_of("data-path").unwrap_or("rmi_data"); - + if matches.value_of("namespace").is_some() && matches.value_of("param-grid").is_some() { panic!("Can only specify one of namespace or param-grid"); } - + info!("Reading {}...", fp); let mut key_type = KeyType::U64; @@ -132,8 +132,7 @@ fn main() { }; if matches.is_present("optimize") { - let results = dynamic!(optimizer::find_pareto_efficient_configs, - data, 10); + let results = dynamic!(optimizer::find_pareto_efficient_configs, data, 10); optimizer::RMIStatistics::display_table(&results); @@ -141,20 +140,24 @@ fn main() { matches.value_of("namespace").unwrap() } else { let path = Path::new(fp); - path.file_name().map(|s| s.to_str()).unwrap_or(Some("rmi")).unwrap() + path.file_name() + .map(|s| s.to_str()) + .unwrap_or(Some("rmi")) + .unwrap() }; - - let grid_specs: Vec = results.into_iter() + + let grid_specs: Vec = results + .into_iter() .enumerate() .map(|(idx, v)| { let nmspc = format!("{}_{}", nmspc_prefix, idx); v.to_grid_spec(&nmspc) - }).collect(); + }) + .collect(); let grid_specs_json = object!("configs" => grid_specs); let fp = matches.value_of("optimize").unwrap(); - let f = File::create(fp) - .expect("Could not write optimization results file"); + let f = File::create(fp).expect("Could not write optimization results file"); let mut bw = BufWriter::new(f); grid_specs_json.write(&mut bw).unwrap(); return; @@ -162,12 +165,14 @@ fn main() { // if we aren't optimizing, we should make sure the RMI data directory exists. if !Path::new(data_dir).exists() { - info!("The RMI data directory specified {} does not exist. Creating it.", - data_dir); + info!( + "The RMI data directory specified {} does not exist. Creating it.", + data_dir + ); std::fs::create_dir_all(data_dir) .expect("The RMI data directory did not exist, and it could not be created."); } - + if let Some(param_grid) = matches.value_of("param-grid").map(|x| x.to_string()) { let pg = { let raw_json = fs::read_to_string(param_grid.clone()).unwrap(); @@ -182,7 +187,7 @@ fn main() { let branching = el["branching factor"].as_u64().unwrap(); let namespace = match el["namespace"].as_str() { Some(s) => Some(String::from(s)), - None => None + None => None, }; to_test.push((layers, branching, namespace)); @@ -191,19 +196,23 @@ fn main() { trace!("# RMIs to train: {}", to_test.len()); let pbar = ProgressBar::new(to_test.len() as u64); - pbar.set_style(ProgressStyle::default_bar() - .template("{pos} / {len} ({msg}) {wide_bar} {eta}")); + pbar.set_style( + ProgressStyle::default_bar().template("{pos} / {len} ({msg}) {wide_bar} {eta}"), + ); let train_func = |(models, branch_factor, namespace): &(String, u64, Option)| { - trace!("Training RMI {} with branching factor {}", - models, *branch_factor); - + trace!( + "Training RMI {} with branching factor {}", + models, + *branch_factor + ); + let loc_data = data.soft_copy(); let mut trained_model = dynamic!(train, loc_data, models, *branch_factor); - + let size_bs = rmi_lib::rmi_size(&trained_model); - + let result_obj = object! { "layers" => models.clone(), "branching factor" => *branch_factor, @@ -223,61 +232,56 @@ fn main() { if matches.is_present("zero-build-time") { trained_model.build_time = 0; } - + if let Some(nmspc) = namespace { - rmi_lib::output_rmi( - &nmspc, - trained_model, - data_dir, - key_type, - true).unwrap(); - + rmi_lib::output_rmi(&nmspc, trained_model, data_dir, key_type, true) + .unwrap(); } - + pbar.inc(1); return result_obj; }; - let results: Vec = - if matches.is_present("disable-parallel-training") { - trace!("Training models sequentially"); - to_test.iter().map(train_func).collect() - } else { - trace!("Training models in parallel"); - to_test.par_iter().map(train_func).collect() - }; - + let results: Vec = if matches.is_present("disable-parallel-training") { + trace!("Training models sequentially"); + to_test.iter().map(train_func).collect() + } else { + trace!("Training models in parallel"); + to_test.par_iter().map(train_func).collect() + }; + //let results: Vec = to_test //.par_iter().map( pbar.finish(); - let f = File::create(format!("{}_results", param_grid)).expect("Could not write results file"); + let f = File::create(format!("{}_results", param_grid)) + .expect("Could not write results file"); let mut bw = BufWriter::new(f); let json_results = object! { "results" => results }; json_results.write(&mut bw).unwrap(); - } else { panic!("Configs must have an array as its value"); } - } else if matches.value_of("namespace").is_some() { let namespace = matches.value_of("namespace").unwrap().to_string(); let mut trained_model = match matches.value_of("max-size") { None => { - // assume they gave a model spec + // assume they gave a model spec let models = matches.value_of("models").unwrap(); let branch_factor = matches .value_of("branching factor") .unwrap() .parse::() .unwrap(); - + let trained_model = match matches.value_of("bounded") { None => dynamic!(train, data, models, branch_factor), Some(s) => { - let line_size = s.parse::() + let line_size = s + .parse::() .expect("Line size must be a positive integer."); - let d_u64 = data.into_u64() + let d_u64 = data + .into_u64() .expect("Can only construct a bounded RMI on u64 data."); train_bounded(&d_u64, models, branch_factor, line_size) } @@ -292,9 +296,12 @@ fn main() { trained_model } }; - + let no_errors = matches.is_present("no-errors"); - info!("Model build time: {} ms", trained_model.build_time / 1_000_000); + info!( + "Model build time: {} ms", + trained_model.build_time / 1_000_000 + ); info!( "Average model error: {} ({}%)", @@ -319,18 +326,13 @@ fn main() { trained_model.model_max_error, trained_model.model_max_error as f64 / num_rows as f64 * 100.0 ); - + if !matches.is_present("no-code") { if matches.is_present("zero-build-time") { trained_model.build_time = 0; } - rmi_lib::output_rmi( - &namespace, - trained_model, - data_dir, - key_type, - !no_errors).unwrap(); + rmi_lib::output_rmi(&namespace, trained_model, data_dir, key_type, !no_errors).unwrap(); } else { trace!("Skipping code generation due to CLI flag"); } diff --git a/tests/cache_fix_osm/main.cpp b/tests/cache_fix_osm/main.cpp index d8d1773..e809068 100644 --- a/tests/cache_fix_osm/main.cpp +++ b/tests/cache_fix_osm/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; size_t err; @@ -29,7 +30,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); @@ -42,6 +43,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } diff --git a/tests/cache_fix_wiki/main.cpp b/tests/cache_fix_wiki/main.cpp index 35afc50..3f150ac 100644 --- a/tests/cache_fix_wiki/main.cpp +++ b/tests/cache_fix_wiki/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; size_t err; @@ -29,7 +30,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); @@ -42,6 +43,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } diff --git a/tests/common/rmi_learned_index.h b/tests/common/rmi_learned_index.h new file mode 100644 index 0000000..eb9cd00 --- /dev/null +++ b/tests/common/rmi_learned_index.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + +#include "rmi.h" + +class RMILearnedIndex { + public: + bool Load(const std::string& root, + const std::string& dataset_namespace = rmi::NAME) { + std::filesystem::path full_path = std::filesystem::path(root) / dataset_namespace; + if (!std::filesystem::exists(full_path)) { + full_path = std::filesystem::path(root); + } + data_path_ = full_path.string(); + loaded_ = rmi::load(data_path_.c_str()); + return loaded_; + } + + uint64_t Lookup(uint64_t key, size_t* err) const { + return rmi::lookup(key, err); + } + + void Cleanup() { + rmi::cleanup(); + loaded_ = false; + } + + size_t SizeBytes() const { return rmi::RMI_SIZE; } + uint64_t BuildTimeNs() const { return rmi::BUILD_TIME_NS; } + + private: + std::string data_path_; + bool loaded_{false}; +}; diff --git a/tests/max_size_wiki/main.cpp b/tests/max_size_wiki/main.cpp index 959da88..025e181 100644 --- a/tests/max_size_wiki/main.cpp +++ b/tests/max_size_wiki/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; if (rmi::RMI_SIZE > 50000000) { std::cout << "RMI was larger than 50MB" << std::endl; @@ -34,7 +35,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); if (diff > err) { @@ -46,6 +47,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } diff --git a/tests/radix_model_wiki/main.cpp b/tests/radix_model_wiki/main.cpp index 264f549..eeda706 100644 --- a/tests/radix_model_wiki/main.cpp +++ b/tests/radix_model_wiki/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; size_t err; @@ -29,7 +30,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); if (diff > err) { @@ -41,6 +42,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } diff --git a/tests/simple_model_osm/main.cpp b/tests/simple_model_osm/main.cpp index 06768c3..095694c 100644 --- a/tests/simple_model_osm/main.cpp +++ b/tests/simple_model_osm/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; size_t err; @@ -29,7 +30,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); if (diff > err) { @@ -41,6 +42,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } diff --git a/tests/simple_model_wiki/main.cpp b/tests/simple_model_wiki/main.cpp index 264f549..eeda706 100644 --- a/tests/simple_model_wiki/main.cpp +++ b/tests/simple_model_wiki/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "rmi.h" +#include "../common/rmi_learned_index.h" int main() { // load the data @@ -19,7 +19,8 @@ int main() { std::cout << "Data loaded." << std::endl; - std::cout << "RMI status: " << rmi::load("rmi_data") << std::endl; + RMILearnedIndex learned_index; + std::cout << "RMI status: " << learned_index.Load("rmi_data") << std::endl; size_t err; @@ -29,7 +30,7 @@ int main() { std::distance(data.begin(), std::lower_bound(data.begin(), data.end(), lookup)); - uint64_t rmi_guess = rmi::lookup(lookup, &err); + uint64_t rmi_guess = learned_index.Lookup(lookup, &err); uint64_t diff = (rmi_guess > true_index ? rmi_guess - true_index : true_index - rmi_guess); if (diff > err) { @@ -41,6 +42,6 @@ int main() { } } - rmi::cleanup(); + learned_index.Cleanup(); exit(0); } From cb3469eb1a040c1d8496f31670d51f4e0b762bf1 Mon Sep 17 00:00:00 2001 From: andy_golden_myth <16195005+andygoldenmyth@user.noreply.gitee.com> Date: Fri, 28 Nov 2025 11:07:38 +0800 Subject: [PATCH 2/5] add pla hybrid --- README.md | 1 + rmi_lib/src/models/mod.rs | 2 + rmi_lib/src/models/optimal_pla.rs | 218 ++++++++++++++++++++++++++++++ rmi_lib/src/train/mod.rs | 1 + 4 files changed, 222 insertions(+) create mode 100644 rmi_lib/src/models/optimal_pla.rs diff --git a/README.md b/README.md index de1c6ec..c96a996 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Currently, the following types of RMI layers are supported: * `radix`, eliminates common prefixes and returns a fixed number of significant bits based on the branching factor * `bradix`, same as radix, but attempts to choose the number of bits based on balancing the dataset * `histogram`, partitions the data into several even-sized blocks (based on the branching factor) +* `optimal_pla`, a piecewise linear approximation leaf that stitches together short linear segments with a bounded per-segment error Tuning an RMI is critical to getting good performance. A good place to start is a `cubic` layer followed by a large linear layer, for example: `cubic,linear 262144`. For automatic tuning, try the RMI optimizer using the `--optimize` flag: diff --git a/rmi_lib/src/models/mod.rs b/rmi_lib/src/models/mod.rs index 49ee563..09cb244 100644 --- a/rmi_lib/src/models/mod.rs +++ b/rmi_lib/src/models/mod.rs @@ -16,6 +16,7 @@ mod normal; mod radix; mod stdlib; mod utils; +mod optimal_pla; pub use balanced_radix::BalancedRadixModel; pub use cubic_spline::CubicSplineModel; @@ -29,6 +30,7 @@ pub use normal::NormalModel; pub use radix::RadixModel; pub use radix::RadixTable; pub use stdlib::StdFunctions; +pub use optimal_pla::OptimalPLAModel; use std::cmp::Ordering; use std::collections::HashSet; diff --git a/rmi_lib/src/models/optimal_pla.rs b/rmi_lib/src/models/optimal_pla.rs new file mode 100644 index 0000000..4a72f2a --- /dev/null +++ b/rmi_lib/src/models/optimal_pla.rs @@ -0,0 +1,218 @@ +// < begin copyright > +// Copyright Ryan Marcus 2020 +// +// See root directory of this project for license terms. +// +// < end copyright > + +use crate::models::*; +use log::trace; + +const MAX_SEGMENT_ABS_ERROR: f64 = 1.0; + +fn simple_lr>(loc_data: T) -> (f64, f64) { + let mut mean_x = 0.0; + let mut mean_y = 0.0; + let mut c = 0.0; + let mut n: u64 = 0; + let mut m2 = 0.0; + let mut data_size = 0; + + for (x, y) in loc_data { + n += 1; + let dx = x - mean_x; + mean_x += dx / (n as f64); + mean_y += (y - mean_y) / (n as f64); + c += dx * (y - mean_y); + + let dx2 = x - mean_x; + m2 += dx * dx2; + data_size += 1; + } + + if data_size == 0 { + return (0.0, 0.0); + } + + if data_size == 1 { + return (mean_y, 0.0); + } + + let cov = c / ((n - 1) as f64); + let var = m2 / ((n - 1) as f64); + + if var == 0.0 { + return (mean_y, 0.0); + } + + let beta: f64 = cov / var; + let alpha = mean_y - beta * mean_x; + + return (alpha, beta); +} + +fn segment_error(data: &RMITrainingData, + start: usize, + end: usize, + params: (f64, f64)) -> f64 { + let (alpha, beta) = params; + data.iter() + .skip(start) + .take(end - start) + .map(|(key, pos)| { + let prediction = beta.mul_add(key.as_float(), alpha); + (prediction - pos as f64).abs() + }) + .fold(0.0, f64::max) +} + +fn fit_segment(data: &RMITrainingData, start: usize, end: usize) -> (f64, f64) { + simple_lr(data + .iter() + .skip(start) + .take(end - start) + .map(|(inp, offset)| (inp.as_float(), offset as f64))) +} + +fn build_segments(data: &RMITrainingData) -> (Vec, Vec, Vec) { + if data.len() == 0 { + return (vec![0.0], vec![0.0], vec![u64::MAX]); + } + + let mut intercepts = Vec::new(); + let mut slopes = Vec::new(); + let mut boundaries = Vec::new(); + + let mut start = 0; + while start < data.len() { + let mut end = start + 1; + let mut best_params = fit_segment(data, start, end); + let mut best_err = segment_error(data, start, end, best_params); + + while end < data.len() { + let candidate_params = fit_segment(data, start, end + 1); + let candidate_err = segment_error(data, start, end + 1, candidate_params); + + if candidate_err <= MAX_SEGMENT_ABS_ERROR { + best_params = candidate_params; + best_err = candidate_err; + end += 1; + } else { + break; + } + } + + trace!("PLA segment {}:{} err {}", start, end, best_err); + intercepts.push(best_params.0); + slopes.push(best_params.1); + boundaries.push(data.get_key(end - 1).as_uint()); + start = end; + } + + return (intercepts, slopes, boundaries); +} + +pub struct OptimalPLAModel { + intercepts: Vec, + slopes: Vec, + boundaries: Vec, +} + +impl OptimalPLAModel { + pub fn new(data: &RMITrainingData) -> OptimalPLAModel { + let (intercepts, slopes, boundaries) = build_segments(data); + OptimalPLAModel { intercepts, slopes, boundaries } + } +} + +impl Model for OptimalPLAModel { + fn predict_to_float(&self, inp: &ModelInput) -> f64 { + if self.boundaries.is_empty() { + return 0.0; + } + + let key = inp.as_int(); + let idx = match self.boundaries.binary_search(&key) { + Ok(exact) => exact + 1, + Err(insert) => insert, + }; + + let bounded_idx = idx.min(self.intercepts.len() - 1); + self.slopes[bounded_idx].mul_add(inp.as_float(), self.intercepts[bounded_idx]) + } + + fn input_type(&self) -> ModelDataType { ModelDataType::Float } + fn output_type(&self) -> ModelDataType { ModelDataType::Float } + + fn params(&self) -> Vec { + vec![ + ModelParam::Int(self.intercepts.len() as u64), + ModelParam::FloatArray(self.intercepts.clone()), + ModelParam::FloatArray(self.slopes.clone()), + ModelParam::IntArray(self.boundaries.clone()), + ] + } + + fn code(&self) -> String { + String::from( + "inline double optimal_pla(uint64_t length, const double intercepts[], const double slopes[], const uint64_t boundaries[], double inp) {\n uint64_t idx = bs_upper_bound(boundaries, length, (uint64_t)inp);\n if (idx >= length) { idx = length - 1; }\n return std::fma(slopes[idx], inp, intercepts[idx]);\n}" + ) + } + + fn standard_functions(&self) -> HashSet { + let mut to_r = HashSet::new(); + to_r.insert(StdFunctions::BinarySearch); + to_r + } + + fn function_name(&self) -> String { String::from("optimal_pla") } + + fn model_name(&self) -> &'static str { "optimal_pla" } + + fn needs_bounds_check(&self) -> bool { false } + + fn set_to_constant_model(&mut self, constant: u64) -> bool { + self.intercepts = vec![constant as f64]; + self.slopes = vec![0.0]; + self.boundaries = vec![u64::MAX]; + true + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fits_linear_run() { + let mut pts = Vec::new(); + for i in 0..50u64 { pts.push((i, i * 2)); } + let md = ModelData::IntKeyToIntPos(pts); + let pla = OptimalPLAModel::new(&md); + + assert_eq!(pla.boundaries.len(), 1); + assert_eq!(pla.predict_to_int(10.into()), 20); + assert_eq!(pla.predict_to_int(49.into()), 98); + } + + #[test] + fn makes_multiple_segments_when_needed() { + let md = ModelData::IntKeyToIntPos(vec![ + (0, 0), (1, 1), (2, 2), // first segment + (100, 120), (101, 121), (102, 122), // second far apart + ]); + + let pla = OptimalPLAModel::new(&md); + assert!(pla.boundaries.len() >= 2); + + assert_eq!(pla.predict_to_int(0.into()), 0); + assert_eq!(pla.predict_to_int(101.into()), 121); + } + + #[test] + fn empty_defaults_to_zero() { + let md: ModelData = ModelData::empty(); + let pla = OptimalPLAModel::new(&md); + assert_eq!(pla.predict_to_int(5.into()), 0); + } +} diff --git a/rmi_lib/src/train/mod.rs b/rmi_lib/src/train/mod.rs index c52918f..4a3aefb 100644 --- a/rmi_lib/src/train/mod.rs +++ b/rmi_lib/src/train/mod.rs @@ -50,6 +50,7 @@ fn train_model(model_type: &str, "radix28" => Box::new(RadixTable::new(data, 28)), "bradix" => Box::new(BalancedRadixModel::new(data)), "histogram" => Box::new(EquidepthHistogramModel::new(data)), + "optimal_pla" => Box::new(OptimalPLAModel::new(data)), _ => panic!("Unknown model type: {}", model_type), }; From 59b583109e61e7c6a8a2d3e6a7fc4a17a11d4dd7 Mon Sep 17 00:00:00 2001 From: andy_golden_myth <16195005+andygoldenmyth@user.noreply.gitee.com> Date: Thu, 4 Dec 2025 09:49:21 +0800 Subject: [PATCH 3/5] add output path --- rmi_lib/src/codegen.rs | 20 +++++++++++++------- src/main.rs | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/rmi_lib/src/codegen.rs b/rmi_lib/src/codegen.rs index 54e332b..cf38bb6 100644 --- a/rmi_lib/src/codegen.rs +++ b/rmi_lib/src/codegen.rs @@ -837,17 +837,23 @@ inline size_t FCLAMP(double inp, double bound) {{ pub fn output_rmi(namespace: &str, mut trained_model: TrainedRMI, data_dir: &str, + output_dir: &str, key_type: KeyType, include_errors: bool) -> Result<(), std::io::Error> { - - let f1 = File::create(format!("{}.cpp", namespace)).expect("Could not write RMI CPP file"); + + let output_dir = Path::new(output_dir); + fs::create_dir_all(output_dir)?; + + let f1_path = output_dir.join(format!("{}.cpp", namespace)); + let f1 = File::create(&f1_path).expect("Could not write RMI CPP file"); let mut bw1 = BufWriter::new(f1); - - let f2 = - File::create(format!("{}_data.h", namespace)).expect("Could not write RMI data file"); + + let f2_path = output_dir.join(format!("{}_data.h", namespace)); + let f2 = File::create(&f2_path).expect("Could not write RMI data file"); let mut bw2 = BufWriter::new(f2); - - let f3 = File::create(format!("{}.h", namespace)).expect("Could not write RMI header file"); + + let f3_path = output_dir.join(format!("{}.h", namespace)); + let f3 = File::create(&f3_path).expect("Could not write RMI header file"); let mut bw3 = BufWriter::new(f3); if !include_errors { diff --git a/src/main.rs b/src/main.rs index 61b30fe..643ccf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,6 +70,11 @@ fn main() { .short("d") .value_name("dir") .help("exports parameters to files in this directory (default: rmi_data)")) + .arg(Arg::with_name("output-path") + .long("output-path") + .short("o") + .value_name("dir") + .help("exports generated model files to this directory (default: current directory)")) .arg(Arg::with_name("no-errors") .long("no-errors") .help("do not save last-level errors, and modify the RMI function signature")) @@ -112,6 +117,7 @@ fn main() { let fp = matches.value_of("input").unwrap(); let data_dir = matches.value_of("data-path").unwrap_or("rmi_data"); + let output_dir = matches.value_of("output-path").unwrap_or("."); if matches.value_of("namespace").is_some() && matches.value_of("param-grid").is_some() { panic!("Can only specify one of namespace or param-grid"); @@ -173,6 +179,16 @@ fn main() { .expect("The RMI data directory did not exist, and it could not be created."); } + if !Path::new(output_dir).exists() { + info!( + "The RMI output directory specified {} does not exist. Creating it.", + output_dir + ); + std::fs::create_dir_all(output_dir).expect( + "The RMI output directory did not exist, and it could not be created." + ); + } + if let Some(param_grid) = matches.value_of("param-grid").map(|x| x.to_string()) { let pg = { let raw_json = fs::read_to_string(param_grid.clone()).unwrap(); @@ -234,8 +250,15 @@ fn main() { } if let Some(nmspc) = namespace { - rmi_lib::output_rmi(&nmspc, trained_model, data_dir, key_type, true) - .unwrap(); + rmi_lib::output_rmi( + &nmspc, + trained_model, + data_dir, + output_dir, + key_type, + true + ) + .unwrap(); } pbar.inc(1); @@ -332,7 +355,15 @@ fn main() { trained_model.build_time = 0; } - rmi_lib::output_rmi(&namespace, trained_model, data_dir, key_type, !no_errors).unwrap(); + rmi_lib::output_rmi( + &namespace, + trained_model, + data_dir, + output_dir, + key_type, + !no_errors + ) + .unwrap(); } else { trace!("Skipping code generation due to CLI flag"); } From 8ed36e98f7752327b05229bdf32722fa0eb2b797 Mon Sep 17 00:00:00 2001 From: JY-Qin <64207375+bdhxxnix@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:52:55 +0800 Subject: [PATCH 4/5] Add configurable output directory for generated models --- rmi_lib/src/codegen.rs | 20 +++++++++++++------- src/main.rs | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/rmi_lib/src/codegen.rs b/rmi_lib/src/codegen.rs index 54e332b..cf38bb6 100644 --- a/rmi_lib/src/codegen.rs +++ b/rmi_lib/src/codegen.rs @@ -837,17 +837,23 @@ inline size_t FCLAMP(double inp, double bound) {{ pub fn output_rmi(namespace: &str, mut trained_model: TrainedRMI, data_dir: &str, + output_dir: &str, key_type: KeyType, include_errors: bool) -> Result<(), std::io::Error> { - - let f1 = File::create(format!("{}.cpp", namespace)).expect("Could not write RMI CPP file"); + + let output_dir = Path::new(output_dir); + fs::create_dir_all(output_dir)?; + + let f1_path = output_dir.join(format!("{}.cpp", namespace)); + let f1 = File::create(&f1_path).expect("Could not write RMI CPP file"); let mut bw1 = BufWriter::new(f1); - - let f2 = - File::create(format!("{}_data.h", namespace)).expect("Could not write RMI data file"); + + let f2_path = output_dir.join(format!("{}_data.h", namespace)); + let f2 = File::create(&f2_path).expect("Could not write RMI data file"); let mut bw2 = BufWriter::new(f2); - - let f3 = File::create(format!("{}.h", namespace)).expect("Could not write RMI header file"); + + let f3_path = output_dir.join(format!("{}.h", namespace)); + let f3 = File::create(&f3_path).expect("Could not write RMI header file"); let mut bw3 = BufWriter::new(f3); if !include_errors { diff --git a/src/main.rs b/src/main.rs index 61b30fe..643ccf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,6 +70,11 @@ fn main() { .short("d") .value_name("dir") .help("exports parameters to files in this directory (default: rmi_data)")) + .arg(Arg::with_name("output-path") + .long("output-path") + .short("o") + .value_name("dir") + .help("exports generated model files to this directory (default: current directory)")) .arg(Arg::with_name("no-errors") .long("no-errors") .help("do not save last-level errors, and modify the RMI function signature")) @@ -112,6 +117,7 @@ fn main() { let fp = matches.value_of("input").unwrap(); let data_dir = matches.value_of("data-path").unwrap_or("rmi_data"); + let output_dir = matches.value_of("output-path").unwrap_or("."); if matches.value_of("namespace").is_some() && matches.value_of("param-grid").is_some() { panic!("Can only specify one of namespace or param-grid"); @@ -173,6 +179,16 @@ fn main() { .expect("The RMI data directory did not exist, and it could not be created."); } + if !Path::new(output_dir).exists() { + info!( + "The RMI output directory specified {} does not exist. Creating it.", + output_dir + ); + std::fs::create_dir_all(output_dir).expect( + "The RMI output directory did not exist, and it could not be created." + ); + } + if let Some(param_grid) = matches.value_of("param-grid").map(|x| x.to_string()) { let pg = { let raw_json = fs::read_to_string(param_grid.clone()).unwrap(); @@ -234,8 +250,15 @@ fn main() { } if let Some(nmspc) = namespace { - rmi_lib::output_rmi(&nmspc, trained_model, data_dir, key_type, true) - .unwrap(); + rmi_lib::output_rmi( + &nmspc, + trained_model, + data_dir, + output_dir, + key_type, + true + ) + .unwrap(); } pbar.inc(1); @@ -332,7 +355,15 @@ fn main() { trained_model.build_time = 0; } - rmi_lib::output_rmi(&namespace, trained_model, data_dir, key_type, !no_errors).unwrap(); + rmi_lib::output_rmi( + &namespace, + trained_model, + data_dir, + output_dir, + key_type, + !no_errors + ) + .unwrap(); } else { trace!("Skipping code generation due to CLI flag"); } From bfdd24cdb7ec24202181c59b8e54a03564dd061d Mon Sep 17 00:00:00 2001 From: andy_golden_myth <16195005+andygoldenmyth@user.noreply.gitee.com> Date: Thu, 11 Dec 2025 22:38:06 +0800 Subject: [PATCH 5/5] add binary output rmi models --- .gitignore | 5 + .vscode/c_cpp_properties.json | 18 +++ .vscode/launch.json | 48 ++++++ .vscode/settings.json | 59 ++++++++ .vscode/tasks.json | 28 ++++ rmi_lib/src/binary.rs | 273 ++++++++++++++++++++++++++++++++++ rmi_lib/src/lib.rs | 2 + rmi_lib/src/models/mod.rs | 1 + src/main.rs | 27 +++- tests/verify_rmi | Bin 0 -> 391112 bytes 10 files changed, 456 insertions(+), 5 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 rmi_lib/src/binary.rs create mode 100755 tests/verify_rmi diff --git a/.gitignore b/.gitignore index ab653d5..16ea2a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ Cargo.lock *\# \#* rmi_data +binary_models/ +rmi_models/ + +binary_models/ + diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c2098a2 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f77b2ec --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,48 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Test the correctness of RMI loading", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/tests/verify_rmi", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": false, + "cwd": "/home/andy/Projects/RMI", + "program": "/home/andy/Projects/RMI/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3e5eb95 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..08d9005 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build active file", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/rmi_lib/src/binary.rs b/rmi_lib/src/binary.rs new file mode 100644 index 0000000..b3ec476 --- /dev/null +++ b/rmi_lib/src/binary.rs @@ -0,0 +1,273 @@ +use crate::models::{KeyType, ModelDataType, ModelParam}; +use crate::train::TrainedRMI; + +#[derive(Debug, Clone)] +pub enum BinaryParamKind { + Int, + Float, + ShortArray, + IntArray, + Int32Array, + FloatArray, +} + +#[derive(Debug, Clone)] +pub struct BinaryModelParam { + pub kind: BinaryParamKind, + pub len: usize, + pub value: ModelParam, +} + +impl From for BinaryModelParam { + fn from(param: ModelParam) -> Self { + let kind = match ¶m { + ModelParam::Int(_) => BinaryParamKind::Int, + ModelParam::Float(_) => BinaryParamKind::Float, + ModelParam::ShortArray(_) => BinaryParamKind::ShortArray, + ModelParam::IntArray(_) => BinaryParamKind::IntArray, + ModelParam::Int32Array(_) => BinaryParamKind::Int32Array, + ModelParam::FloatArray(_) => BinaryParamKind::FloatArray, + }; + + BinaryModelParam { + kind, + len: param.len(), + value: param, + } + } +} + +#[derive(Debug, Clone)] +pub struct Model { + pub model_type: String, + pub input_type: ModelDataType, + pub output_type: ModelDataType, + pub params: Vec, + pub error: Option, +} + +#[derive(Debug, Clone)] +pub struct Stage { + pub models: Vec, +} + +#[derive(Debug, Clone)] +pub struct CacheFix { + pub line_size: usize, + pub spline_points: Vec<(u64, usize)>, +} + +#[derive(Debug, Clone)] +pub struct RMIModel { + + pub branching_factor: u64, + pub models: String, + pub last_layer_reports_error: bool, + pub num_rmi_rows: usize, + pub num_data_rows: usize, + pub model_avg_error: f64, + pub model_avg_l2_error: f64, + pub model_avg_log2_error: f64, + pub model_max_error: u64, + pub model_max_error_idx: usize, + pub model_max_log2_error: f64, + pub build_time: u128, + pub last_layer_max_l1s: Vec, + pub stages: Vec, + pub cache_fix: Option, +} + +impl RMIModel { + pub fn from_trained( + rmi: &TrainedRMI, + key_type: KeyType, + last_layer_reports_error: bool, + ) -> RMIModel { + let mut stages = Vec::new(); + + for stage in &rmi.rmi { + let mut models = Vec::new(); + for m in stage { + let params = m.params().into_iter().map(Into::into).collect(); + + models.push(Model { + model_type: m.model_name().to_string(), + input_type: m.input_type(), + output_type: m.output_type(), + params, + error: m.error_bound(), + }); + } + stages.push(Stage { models }); + } + + let cache_fix = rmi.cache_fix.as_ref().map(|(line_size, points)| CacheFix { + line_size: *line_size, + spline_points: points.clone(), + }); + + RMIModel { + branching_factor: rmi.branching_factor, + models: rmi.models.clone(), + last_layer_reports_error, + num_rmi_rows: rmi.num_rmi_rows, + num_data_rows: rmi.num_data_rows, + model_avg_error: rmi.model_avg_error, + model_avg_l2_error: rmi.model_avg_l2_error, + model_avg_log2_error: rmi.model_avg_log2_error, + model_max_error: rmi.model_max_error, + model_max_error_idx: rmi.model_max_error_idx, + model_max_log2_error: rmi.model_max_log2_error, + build_time: rmi.build_time, + last_layer_max_l1s: rmi.last_layer_max_l1s.clone(), + stages, + cache_fix, + } + } +} + +use std::io::{Result, Write}; + +fn write_string(w: &mut W, s: &str) -> Result<()> { + let bytes = s.as_bytes(); + w.write_all(&(bytes.len() as u64).to_le_bytes())?; + w.write_all(bytes) +} + +fn write_model_data_type(w: &mut W, t: &ModelDataType) -> Result<()> { + let code = match t { + ModelDataType::Int => 0u8, + ModelDataType::Int128 => 1u8, + ModelDataType::Float => 2u8, + }; + + w.write_all(&[code]) +} + +fn write_param(w: &mut W, p: &BinaryModelParam) -> Result<()> { + let kind_code = match p.kind { + BinaryParamKind::Int => 0u8, + BinaryParamKind::Float => 1u8, + BinaryParamKind::ShortArray => 2u8, + BinaryParamKind::IntArray => 3u8, + BinaryParamKind::Int32Array => 4u8, + BinaryParamKind::FloatArray => 5u8, + }; + + w.write_all(&[kind_code])?; + w.write_all(&(p.len as u64).to_le_bytes())?; + + match &p.value { + ModelParam::Int(v) => w.write_all(&v.to_le_bytes()), + ModelParam::Float(v) => w.write_all(&v.to_le_bytes()), + ModelParam::ShortArray(arr) => { + for v in arr { + w.write_all(&v.to_le_bytes())?; + } + Ok(()) + } + ModelParam::IntArray(arr) => { + for v in arr { + w.write_all(&v.to_le_bytes())?; + } + Ok(()) + } + ModelParam::Int32Array(arr) => { + for v in arr { + w.write_all(&v.to_le_bytes())?; + } + Ok(()) + } + ModelParam::FloatArray(arr) => { + for v in arr { + w.write_all(&v.to_le_bytes())?; + } + Ok(()) + } + } +} + +fn write_cache_fix(w: &mut W, cf: &CacheFix) -> Result<()> { + w.write_all(&(cf.line_size as u64).to_le_bytes())?; + w.write_all(&(cf.spline_points.len() as u64).to_le_bytes())?; + for (key, offset) in &cf.spline_points { + w.write_all(&key.to_le_bytes())?; + w.write_all(&(*offset as u64).to_le_bytes())?; + } + Ok(()) +} + +fn write_key_type(w: &mut W, k: KeyType) -> Result<()> { + let code = match k { + KeyType::U32 => 0u8, + KeyType::U64 => 1u8, + KeyType::F64 => 2u8, + KeyType::U128 => 3u8, + }; + + w.write_all(&[code]) +} + +impl RMIModel { + pub fn save_binary(&self, path: &str) -> Result<()> { + let mut f = std::fs::File::create(path)?; + + f.write_all(b"RMIB")?; + f.write_all(&1u32.to_le_bytes())?; + + f.write_all(&self.branching_factor.to_le_bytes())?; + f.write_all(&(self.num_rmi_rows as u64).to_le_bytes())?; + f.write_all(&(self.num_data_rows as u64).to_le_bytes())?; + f.write_all(&self.build_time.to_le_bytes())?; + f.write_all(&self.model_avg_error.to_le_bytes())?; + f.write_all(&self.model_avg_l2_error.to_le_bytes())?; + f.write_all(&self.model_avg_log2_error.to_le_bytes())?; + f.write_all(&self.model_max_error.to_le_bytes())?; + f.write_all(&(self.model_max_error_idx as u64).to_le_bytes())?; + f.write_all(&self.model_max_log2_error.to_le_bytes())?; + f.write_all(&[self.last_layer_reports_error as u8])?; + + write_string(&mut f, &self.models)?; + + f.write_all(&(self.last_layer_max_l1s.len() as u64).to_le_bytes())?; + for v in &self.last_layer_max_l1s { + f.write_all(&v.to_le_bytes())?; + } + + if let Some(cf) = &self.cache_fix { + f.write_all(&[1u8])?; + write_cache_fix(&mut f, cf)?; + } else { + f.write_all(&[0u8])?; + } + + let stage_count = self.stages.len() as u64; + f.write_all(&stage_count.to_le_bytes())?; + + for stage in &self.stages { + let model_count = stage.models.len() as u64; + f.write_all(&model_count.to_le_bytes())?; + + for m in &stage.models { + write_string(&mut f, &m.model_type)?; + write_model_data_type(&mut f, &m.input_type)?; + write_model_data_type(&mut f, &m.output_type)?; + + match m.error { + Some(err) => { + f.write_all(&[1u8])?; + f.write_all(&err.to_le_bytes())?; + } + None => f.write_all(&[0u8])?, + }; + + f.write_all(&(m.params.len() as u64).to_le_bytes())?; + for p in &m.params { + write_param(&mut f, p)?; + } + } + } + + Ok(()) + } +} diff --git a/rmi_lib/src/lib.rs b/rmi_lib/src/lib.rs index cb90d3c..29998fd 100644 --- a/rmi_lib/src/lib.rs +++ b/rmi_lib/src/lib.rs @@ -1,4 +1,5 @@ mod codegen; +mod binary; mod models; mod train; mod cache_fix; @@ -12,3 +13,4 @@ pub use train::{train, train_for_size, train_bounded}; pub use codegen::rmi_size; pub use codegen::output_rmi; pub use manifest::{CacheFixMetadata, LayerMetadata, ParameterDescriptor, ParamKind, ParamValue, RmiMetadata}; +pub use binary::{Model, Stage, RMIModel}; diff --git a/rmi_lib/src/models/mod.rs b/rmi_lib/src/models/mod.rs index 09cb244..124d21a 100644 --- a/rmi_lib/src/models/mod.rs +++ b/rmi_lib/src/models/mod.rs @@ -501,6 +501,7 @@ impl From for ModelInput { ModelInput::Float(f) } } +#[derive(Clone, Copy, Debug)] pub enum ModelDataType { Int, Int128, diff --git a/src/main.rs b/src/main.rs index 643ccf1..63b442c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ mod load; use load::{load_data, DataType}; use rmi_lib::optimizer; use rmi_lib::KeyType; +use rmi_lib::RMIModel; use rmi_lib::{train, train_bounded}; use json::*; @@ -75,6 +76,10 @@ fn main() { .short("o") .value_name("dir") .help("exports generated model files to this directory (default: current directory)")) + .arg(Arg::with_name("binary-output") + .long("binary-output") + .value_name("PATH") + .help("Path to write binary model file")) .arg(Arg::with_name("no-errors") .long("no-errors") .help("do not save last-level errors, and modify the RMI function signature")) @@ -118,6 +123,7 @@ fn main() { let data_dir = matches.value_of("data-path").unwrap_or("rmi_data"); let output_dir = matches.value_of("output-path").unwrap_or("."); + let binary_output = matches.value_of("binary-output").map(|s| s.to_string()); if matches.value_of("namespace").is_some() && matches.value_of("param-grid").is_some() { panic!("Can only specify one of namespace or param-grid"); @@ -184,12 +190,15 @@ fn main() { "The RMI output directory specified {} does not exist. Creating it.", output_dir ); - std::fs::create_dir_all(output_dir).expect( - "The RMI output directory did not exist, and it could not be created." - ); + std::fs::create_dir_all(output_dir) + .expect("The RMI output directory did not exist, and it could not be created."); } if let Some(param_grid) = matches.value_of("param-grid").map(|x| x.to_string()) { + if binary_output.is_some() { + warn!("Binary output is ignored when training multiple RMIs from a parameter grid"); + } + let pg = { let raw_json = fs::read_to_string(param_grid.clone()).unwrap(); let mut as_json = json::parse(raw_json.as_str()).unwrap(); @@ -256,7 +265,7 @@ fn main() { data_dir, output_dir, key_type, - true + true, ) .unwrap(); } @@ -350,6 +359,14 @@ fn main() { trained_model.model_max_error as f64 / num_rows as f64 * 100.0 ); + if let Some(path) = binary_output.as_ref() { + let rmi_model = RMIModel::from_trained(&trained_model, key_type, !no_errors); + rmi_model + .save_binary(path) + .expect("Failed to save binary RMI model"); + info!("Saved binary RMI model to {}", path); + } + if !matches.is_present("no-code") { if matches.is_present("zero-build-time") { trained_model.build_time = 0; @@ -361,7 +378,7 @@ fn main() { data_dir, output_dir, key_type, - !no_errors + !no_errors, ) .unwrap(); } else { diff --git a/tests/verify_rmi b/tests/verify_rmi new file mode 100755 index 0000000000000000000000000000000000000000..fde25bdb78e3110f61dde5c00bec5c5d0dece64e GIT binary patch literal 391112 zcmeFa349dA7CzcNJws2J2}xKYn-UO^orFzx*#d)XVHFe!fdmo=B!+;%MMO4XOfuekegF4+zu$X~4yUWW@0>cd zpRVbd+?qIapi9>^^)j^H8mF!jIejGHJ?Bvrt&i4Ki^RWIY0b52$aUl0N1~zfxJv*^ zdL_?*B)z(HQH5+MvNa`@ha^ca+{=5pK$O&?MVj=u25};-g~;_+i+m-WdR*RQsTU&M zbw$3CPCaUy%;hYbC~*W4wHXP82fX>)N5gwcH0>y|FJBPxI2U;)V@D6>A4O6ZWre1r)w8Mlj?T_ZKvfTH> z@Y{sp|2s@O>=XMr)w;StHWXGG9who<1vU zcJiFul$_k;WG#8{m|@AOX*p@rGv?%`<%}8DKQn7~+L)9nnP~#68o7S>=-h-(Q&Q$+ zOij+1HYYbHEoIi=smM#1nx2xAoST!9kvnJb)WpP2S=ni`6O)Dz)-ic-)|})K$e5Lt z3iD*->g439^XDfdBydSum2CnKX~&@X=YBa}uXmS0e9!?u-5jvfVnR%}&j% zs?QQfQMZep&61;JO!Aocw3`!AnX8?{lygz<4#|n4kpF5* zpxMIChm1~`NvY`aOldHFT1IBtoCTPhv(!}Sl%0~B-Z^Iec`dWVB}T zoO#JN=cHt3r{yG$8Zx@0=ocCwNdrfX&Khmi&EZNHEFZaZ=YT(abaDm1WJ9RD|9Vf#u)#w)CylqbzcwaRJN$)qpE{3bSeWHgP1OH&jAEK$RHWs~4V!nlztBku@i3E` zF(*4~PMRF8ou+2Z&6Q1t?aiM1e__iaC$X5MU^$=-{eKyFh#{0ue>OX;?Eqt~gIvwJ zsp*!PH63Bv|GiiS8%1AQ5R=n05G~TuFjXuJ(`Kb;$zx~VoPjnPl{RPYEChBGT~3{e z2yJHaw3LiYZCXxRnwB;{BUhW1HfvVayfh7~0zxcp7B8|;z#2wN1;wPZQZi=CcAu6( zOW49R4e*@Y)Tymo(?LuZO3t#brcZ??r4lHAs>mPIzkhPacJbPvp@aMNPwvpJlXcb6 zzU+JpaGI17qo4Z1S;O*|cW;-%yI z9o$2x`=+Gk3w${B(Y`?*y-4dbiT6Rec1A{|runqr4^i7D-oUacAw)Qjw;YI)_CjzlyJtGG9oQ|B%f0%KIjjuhK{F z5_~jO6K-6;~d?M08-uNIDcHD(BncjV(1*Lrnz|Ik=TO@m`L!JU9gciDM^>-Bj{I1a30dgKzs!+;*pV+kx?uFEBfp-5 zf83E@-;rPJ$Zz1t4?6PIXEy|wI`ZX5G^|zT$iKp2N0Z~6#zG@UzSof-=g5zCzo8@|!vGyE^ilJM#ND^5s&(Xuy%*!og2+k9Xv^ za^z2T zkR!i~BY&GCUw$OV=nhAIR|kKuBfq;NztEB2(~*DJk>AUa|M%tJE%5)x7Wg6Rii?3| zXT5=Z&&e!J3#=&2b(J3tEIZ_Vg`1@;ocCGXj3+R#f5 z+G*OzOLp36+Q3V;*l7v~OV-+H+Q>_m+G*OrOLFWqZQLbO>@;oIC8O*#ZPX?G>@;oA zB^~TEZOkQ2>@;o2B{l6dZNw!mJ53vK$%Wso`o~fFl%1vxx8yxLO&e{=K|4(wY{^bL zO&e>;7CTKFYROtVO&e*+Qaeo>Xi1Kprj4^?ik+qnvt*Q=rj4?spPi-+vZRBZrj4-7X~E zJS2TZNIEtoT_Yr2EhPO%M#cL69Fjg4l0F@h{yZdoEF^s-B>h@QdVffIPe}Tiko03A z=?6m6_lBhJ3`wsHNiPmb&kIS<4oOc7Nlyw%j|)i;4@nOUN%sy(cL_mVH&4G$t{j zFyZw;L9g{#*30X>0P2s^+C?>5j!B9QvY^*V!36s#wbY!;4I7fH5)dOnGmhXnX!%`+$dIbtpuxhYRa5p2(ZTizD83S)rl?tk85^`HLg77Ktgfx6W z^n_}S`po1uCx?9^NlsDn5~8n4m0E$#hAWjD+4n`%N_&C+IWzBN4Y5V=7mTPto~P?{ zXy$toNUSKFTY)(=kRMr>06APpPkOhJv_o65cGU%XUZB`fvEgmcU*Xtq`dpKNGamK} zICmECcBl>GYx=%Ossr+h1Nmcvse#tDc@Re}Z;0%Yn)ruCe1Q8$HPrVQ)MrdPUyYxS zL-JMA9SF%UmQDHu@&kFtD-@jT3HG@tTD&V7z%E1;o*QCJ6+ST}Ull$eBwrO?zpT96 zS&2C5RPCyrdI}9n!{8A(7kq#cw;iI{e?#AiH@>141@!WeG^$fka5E@t9LTb>ae=&B zf`PobXz0Ws2Eu&bw5aoi7XneO3eVIFCE+F~GPO#IemIctaIplV$8r%$?WvgN4_tG@0(7MrR-Ll}PSfQ5}=GK5IcwZFUjTSf)sZvEhqfcxjdAD53 zn_HHjc*&PoR@s(PqE3bq-=WdMkGniBi}7WJ=+&ZNA&OP}rJ|6k`QSDIZu>AA=IT}$ z82Mw-2*=2(7fe_UqUGbz9mRoV2T>YBvuN;Mp(Y1$6d%(Gm%sl)syCKZEU1N5P~KRY zbz&q6i!-?jlugFCkl>G16!H^pXK2o%kPTc2=oJb=h2kzm#Uz&DpJ5o7F=K=1(P2nN z6P3s3yY?9U0fpFJ+KMy@rbV?+ESp!IJG&%^9w!@Rlv_G)AYEguxA(k@+lV<)oWx{m zQ&_TJGHF&sJ_qzd)Wib}1@_Un!D~q%;b_UdxKI{7`DDe{FgU6diZC`v4)dYthDmL-c@ZLRTGYCq)Gx5g@sMj9k!h`Tt7;)y)&H}oCHW-S#K!`LfG z^5~1BBq&tw7s0nK$gx{QZfa{PjS(@2^5&MZTNq98*xAIQ<3d&$E=vP>NnXc%g>zN5 z1^J5AqkvWq9=Pkpbj0u;3$KS!js3i9wJ(2!WHmId~yDA72gsI?d z-1W}2Cv3-2?X6z>f;M2<0;=e>wy^0~ga2ZmWd2#7l<@vvWuMID&`mCAkc zEOju({u8Kg6@79DyI`e0xrH34Y-*Y`H3_DI6L{dzJh9eaOcd*xkhr8O-MLlip8JkXz-yaNZRB`-34K%GfO*V0$f!zexk(;lA5F@dWTB@KR zNRw5P=!8I`?{TBAm#DZ~<6XKhJ{}09 zx0Li;!_K^ht6k$ruPC_a41oMPy*SCmZK53e=;Jv)N%7RQG#5(Ee#B#6_zWg*MIUSo z*w+iz%QKlZv{4j9u5E5r@i%cQ6U{rwih$yCx?n}N;P1BiZ!&+qFjOFLc_zc-GzFnl z)Sf~S5cK2rt21XT6~{F>%9R#&QPmic#{Q zD@nyx6v$uCx4){#xtP&(ibDb2iZao&NnF38;FAPY=&hlgL%qe+JcK%*2lCzt9<9V| z!Z|B6Hg&(3nhIk;Bv-JH`70*?B9v3MDq$gHut<5_r$?Tx?2aX|xR-Lsvz? z8q~(9b-t`Whu#~%5t#*^V^RU}_*X&2Wap`iKMH19%-uF~A2ShNuV-e3uGvc`Vpgmj zZ>cY^)&F4?#MKuCRkN5AZRSXe$(0q%Pfsi7yV}gFEGD;(VD2Plex3J6QB!dL#Dn%S zLap-PW2CT*$C{4D;i=w@lTk}oQ3KoaaWmrHmuWx-Z-SEAXXWfc6Ss!z_BLA}MhEh5 zq;nR>a5qADYTS1wp3{IBCq=>cP_Q$iDTpJ43JI9jMy>Et+3I zx$v-1oYRW{U@Uc9egW$hB(8Bn-3cmKF`2c-76uB2p$U0$p2|@}B9cYHhj>><42SD= zVuxo9B>m77C+xlKA}W;r5#ODWZBdLETq(S~2$Ox4`GWPrEDx-I6C-~GSGoY|LeKb0 z)Q?%mn4~l|E5JiM65>d;SQAaMahZWa-E0~V; zM|gY}GOHMB&1ENF0Vgdpvex*7rD~<`rQ0P?4t~W|3b{Ftv(}jz`se#B?NUWFd&T$5B4DSvn%ZH5GZ$Tq-XIMZt0C#46V|i625^ z{~3NC*UNGz>|M%N#g*(->2EchWK{6C>h&Am*%Ml=MaOZMaujzdv| z%eNeek1z+m2*#d7=CWIkYICCyO&rJbBt*?7YCfGr6b0*ZGmphNJd$({$0u62dm*bl zkIrF+Igf(5w99F}Vf?|u1uCQBYNVD9$LU)ugq#&u=N+^_%C&o(Jm2X+EG?|guC)^Nj$8t!w83IHTEz-qIo@0u@LH$PS$(i zEMsjU_SL4s$D-g!QD`aEgU2!(pNiZwT@|M-ak?qh#+;3slRpPe;ei9EW-m;NsyyM= z5{)MOzXL>v$F!m-Z=JS}+AE4$O`Y2?o&M7*P+%?50WmAlqZJSL%RUVW1>$IOR7E`=iBR)jpFcOCY%gUnW zU4s~fQhWKcB3X&IQL-5Ad*xvCeZsY6rp%&GAJ0UPDh!sQxUuxP0E*-RcJU8V5bH*7 zQl`yd>^5Y+fY!;y+V;wIhTexW*^_AUOdig-Jh%}MZscPQ@IGt zUD7NHzD-$0R3+5Oxm)mTMNWm$9<6do)r(4pj2s#lwy{7_A3A7a!?=#7Yv*Xf3`5cx z3-p0~(I~V%)6Sb4FRIML0eyv8#alG5KB&boN~0z`mcON`?i_((qk=#_rj#7TSZBm~ z>F|YmC}0iVWw-3mqLzP0#|v_VY{&8jX9_tj*518*ylEK&Do03B@G%O3dn6X-#47!D zl+zmeJrWPksc;LioNOLO{*IiEirt1wquv&UHG=c;j zlM4;CgVP0d`r`yrYEL0;G2_6*=gnd&jG)z(hoP7bPt%Cbqv8?7`(Kq^8Vu+ZE~(iO z-N;PXeI#Z^PmXRZ-jc2ThRF&g)5wHSq|;j#>c1#4p7$gMp%5Z5v+yTP7L zjy){i8lN<2={UPJs>WL_+*o!?v6edk1z`wdHRP($DK>AOsA1Th^9FR>8rI84i-dft zYVSj(j=AW_<&$+<&qJ2#Y&g3OE6hV!d^c2Hd;y_h^AiCl?xoUq;6e^r>S#h59sDyvY zo}YyAgD{{u?2z6zK=2SJ#bR6?MN1zKEDP$uj-H`zr_%5QKh}t-yE18lkRu^m;`$FO zEKzFv;%%K{7gRxbg;KC^IM1^Tz-|XcycCC&9oJ%ydEx^($YG-3 zeZFRWo%A`n=i~)^Zj6?r&$ZFX)QaruUT$VBpi{GAl8CZjC33gK;+&c)=M$RRJraxO z&@()2@O9*zaU%sS#}jJs=rZ=G>K%!0L3qBjhGr0DQp3@}L6Z-oW-oQ_?`bsrsybtd z<4&Ufq~^~XM$?=9z`7&50#6EZr#bt!q8f*4MZtO81kvmdR#0weM_;Sa0Epr-9vDP+ z?2y#Mb>R^_LG|Pg+nLSQMH1C33O1JwjqYrzHlB)iQ&yGe4j`Y4piR5m!8vqfSITXT zLc`aG3bt2EG!6G~%l9hG1-XDY_v2+}`=IQ@p-aVHj7NG!p?8kL1N1tN5L5>Rsot|# zyiCyWq7SN%%EIHIPn8InpFh1@Aui&YS7=ccVN-*?mRPRCfNWT<(;2KqAzfsJ?9^~* zzse1#)^mD9SRIF`JK{X-L~{L}h(&pD-XSEp--glV%F_tFi!FtdML8Q} z9+bK4P+Y}_4#TfHIp9}Vb?h|dGdSpA)D^DOEEN7%37>=(@25wq;m6%CyCexSG~j#|F!t$2H41%EgvB zrowT=5#fdljm?U8k2HWBle@~n8Le^=g^TB|U!+e2L=5b#WXMjj2DVFS9Q}hom!Loj zT}De7`;IljIj%#LQTI|!p5J$8LskpGN`c?SmUsZLHTvFFC0d1+2rYm-5ur79KemAd zxWZkuTI9soI4gb4%4r#X`}HQtW(qDW~MCw7B?hqL0n31oJQBAHGOW{oH>+e z-M%gUP0g4#4Jl1?{!U%@IP3RDGqPsKP0Pue6*qfsW@cQ*oVeLpxpDLGBW0=DJoucI z)4e1i2M*B2IIEc(c5GD{$9a<>mC-$Xy}7f*cO%25&ZGQ^*G( z2SC0Exg4_D+vVlkA@7822s>{=_JsTt_aciRuf;vceULjL3n7D$UqSZ3O@kYcrA>lt z3i&W@P=`WZfXsyKjDwYRkSif~LB4?_j!z-0;h>3rW4bS7LmYZfgX{@84{{>pI><$k zJ0b6bJOue7ZiVavc@T0kWHICt$Uh(-f^7H^{Dy1?Sq#|+ zaxcCvxe=dnxbZaTR>-E1dmsluz6D9Y`AwfWt%7X+S$X+3$T5(IA(ui1AumCC@$}l3 z&&$i>A)kj#g8T_G8?wO{xOao>4Y`9PJ}W*BnF(12xf-%A{MZHA74ief@sK}4&WCJ> z=KwZ9_JlkRnF?73IS;Zfo;_Rz*%dM#4?|3*G~^PpM~F$z40{7VaPF% zLCCp~UVQO756?ljfZPQcfII=24(UCQeuDJlgX!&%6CmG$TmyL)@-;|5et+Xz$TpDi z_(*&x6f1fh>nif^3E7DzYK_K(2ut z1-S!q7vxdMMn9I9mqI2$M&pBo4UqAWMUY95A46tCx_^Q`WM9Y~kXs;+LVf^Q3W@*u zr7s+%>GPxY27c4K21gN;Pe0p{bfCPvIWY#9Xe~`^igzcx$M1uEx)-exJ+MaIYomNO zdzWa})bG}+W78(cW&Mee54{BcC_}!;ZZ6&%kUyF9<%`N%jrX7;oNrNHVvXpPuKtnL z2ErjE3Ez%)7vNnWEqs=ZzlH3f!136tjSsW&v&ilToPXa-*&k=)e$<=r_D=n2Hr@vK zDB%3NVM>3jjgy+bFli{aq`A3SOH5PMp44vij)#5*r2AIXQrSpcX1Hz;c8D_+@0%2F zdUzhSu)2Fl~#r&2!U z+IUmow*!xX9ZTQv>ck-X1AzB|{Sg)Lffk+$eEi|^@>$OEU8!6^9P*X}e++oK6Q?%0 zUid(q$*}z`IMny$M!`S|p#Cdy?QA30eA`A4oXKHqP#bv> zqfGh}oPGgf;-BCF;Me~No(_EcpWv&2kNXpR8}Ko}t7@Y-_q8^)(JS#etK7x2Ed}C`FCeDRvpzu#pZ9P-kEYq)2)-ieb9Ii{#A`kO*sz~S|N zb;Yur`_C@e8io6e1gEWZ;Y%FyNHrDsNL;%oIqbLsmOnd?y%rqu$KBh>5rF}!-J%M*P9=`1-0`Cr->SpQ7IEVBX0q+f*;vEZj^|$Qb2Yd$bs`f|H ze-Zd5;NjxIPl4|MUe&%72YqVCXMnfEwYxVuTJ>$3XiF+14$IhHaQN@rDBOtV2}m~j z0N?p>dHHl)Tl&bBW8=$#*E|t^jBW+)23}P?$j3v#UBEjy?Pu8b zzXDzxc)0!B?HBe(IrUv)119@Tfp_^6{{{eW0sJbbzMSLK_YF~gJaGEO822Zb$f_OD zPDV8sI~oVx0Pq^(zIYz4-5)u4E)nFBFFR1yY;daDg9twgJQa9jTwCRgx7(r=cpC89 zi194k@Ns{Vel)_Y3Ap!e<F- zaK__4f0)w;*8nDvkGp`c03L4Ke*pZxKf!+lz7u%3d087F>mJ|(o#n$v3a6<2PQX6} z-rk8j?vus!L(fd2^n+nl%z=D7aA$3Z{bn*IUs z0Pwa>eMgM(Bk-}ntMWe%I&=<^3cSLc9z^$4F5WrG2Rfs89Gr0Dgw8H@0uL8I%m=;= zc(^)j0KN%$in9(bF_5YJy}&;OKC=QY0xiOi1HTGSxQDY}2D~2d3UzdeNlW^5ann8- zczdUPM;~_up7kg8#{*9R-rA|}hy&*X&jcRso^u26Jm6vL5Bv?_9h~+ZYu<6-p8~(e ziO&;bkh03lb^n!EVtRWLwMmeaz6!Ftr>}OsNDt@>ne5qDO}oXjrJ8oX?!klUFX^5O z9_$>K7Q(I$r9yGNbhUa!ud(-foucj5a=OO>wYMXSnOSWU@dG_hrJ-TP5 zOM6iF;0e~Cp2X;1LC9i6A5W^bRu`_;zZNphbEl!*>iN~BJ*IEa3k~fBqdQfV*zE^- zwpQ2D3iQ?0we>E~j}h8om*T06w6fvx7?Y5gnK7RO|oX zCUN<#m2YmW$No!4Nbzr%=ZK+yZ+Lz-v{iIlfR+$1BLDsRy9NGkf&Xu{fcm>8>hGAS zSx$d@g$keyzGrNcA@()ZbTWAaUHu@T1K2D1MdEeHvc4{pMG(#HCvrefB~xDd=Y?`NjLZye1*lH1C}v z6QM7^qGdsJ4o)vq2#tPnn_gw|Lgkloplkc>l?^G~w94mu1L&*z;aOv@ALyL*hfJ4B zN6Tf}&86b+Wyo~8#PKTttgq@JY5&JWQ1(lx!T30l(?`lADJM&rE#(p^*GT!0lslv> zl=7&Q#Zs0^sWsO46)k0)l<`vbkupij$x>!ZxkSn}Qa&W*4k-(zJSt_el%-OtKdKtt zMEDUWWxSMqq)d`>vXt3UE|GGLln+U{L&`!ak4jlAWvP_fm9qX)#z`43WgjV%q?{~e zwv5AWjGZ!fcJAD`gpT+-Sn+MU%;jW<#T`1ei|^D@W^43C z1pJSe80;li2_uW;)SAw5KYkfTWNY(ens1kow_T>?%+e0ZG#_yRKPA)crGCwJ;zCWa zc$vU>Z%qJo4Z+9B^h#;}(-8b(NV;yE?o_-6hj%{O*)#IjOas{RC0@S1i+U zB3sk?i;EUA-9o0-{0)Sp(`8yN6WW>(Jaj(pkhroRIzNv~T=n0#A?Zspy-oVJb%3}S zFVmM~8uv;33Z2j65=Fk6kDCUHw4CPJTQWUTWNTLp5*Nzee3`yO;;w+WQ1xjg(|r5` zXt+!dmHbUIt=5lkWm-;a?LW=`xWU5E71CaBnU>3Br9T<|zpfW6uN8);OZ(?!x~EKs zt|zUA2tf5$PnlN!g|0W(OT2@$w_2vv`gWg8k5c8!w6f2i-vd+Qt%Yn@I{u_r&*4JO z6GGc5(`x=7mT46!l*lw6SwiPOt%tvl5QbYx`z>Tgs`)ourq%pfB-4i6KbFXWHB9Qi zEz_$1-Lm2;-Cd^Dc$y^Ba+qrWX?@-*`Er}oK9K3oviu0S@zs*)hBAGdOg|vgHD!8- zOso2RCezBke~cii@iRxJReL=k(`r52CDY3Of7(x8ll&CvPm8hQVx~;rBh$*>qcW}h z@sATk>&98nU>4E%(ca0EKK-o1zyUz zU4Q?CrZrYmNiD&O+{o*x^)B|wQ8D<0|JY@;Fh)=@jlAZ>3|(8L$3@q8LXTc$YTC`n zo>k*TU3*fGq-(D>>$?|p?I}HKF;dad(DBrOtwz!AtMHw#JpB$R)!3yQH2b6P&^Mmc zw0s?ZtyR-I=)V5v@ex8)ci#$t?&yxbM@va|@;%G4v+pIAU3{;z?CP8GJ>lJa^H_HG zEoIrmcPGnUzUnM{`(jyM<7>jQkFOoee!f#I`}^KtndtkF?GE&vWI4$99m|04SC-fM zHZgCA?=hA`eLGnW^X+Fj!q=T;l5Zf(k-kwZNBJhP9OFC3a;)zdWV@INrmr?0UTPO} zz3KDfq7JpU-bt@d4L*AaI(kR-*`MGRxtcHQ^e$bibz|(s$H1sHwfaZ8rnP+u|7fwD zH{y?})|y`ZTgswmVYJvgV(@oeYt5|wJ7p17%YN}*9FTX2?36NPj#g)c(GR1FI;asA zH=QmtfebN*0@2}2H$vz_B7=;sKpOv`X`>0D3yJhHrUT)DKnPt(q^mIw2o31l3E`0) z{liO|7IUMK7SP#)hY4X11Tw+M20{hDKnQyf{Y8qd#bg_aHw&E)Z5>83jU*sMJ#Xu@ z_y9V~jQ3v?IuV9d4kNc3p93MCD+v+hoX>@C_ZlOx^pH#al#Xz;$;9nK%;UQ8+-*WB$x;%~Bf7C45NXY{wQj~Bh$+^MH%;N+N?VJePj%xMAkuo+ z((05pKU<6W#P|ar+LBu@SV{u<$ne$|Iv-j(o(}J!q&VGZd6n?*C#9t4)j*vE-cUEv z{w18OgJ_?dL*i5Nq2EzAmd+RYoe8JX^~bRmX&%Pg>qgH7LVv8K&v-NZv1~17pl)o4 z6HaFW@;5w0ua>ALaJ7@?|HQ+54>@MhUrH)}ETb>q@pq2HKrt}BlMf#>SRU1$p`y04{A zDb2GGUxtm@ryCh-h5kgPFQGlUkqd~(H!D?qGTR1PTa0UBh0@)Yl0Y6Xt_4CmPuV(i z(a|yIjEQm{zG~|*@~tr)23F6VK}Xk(1uu$%F4{^A{cfxPLQ1u<1&Jx)`2@>q zOl{p55h;{95o9-sz@v5JVhy1>MyX2ux-FqU+HF+BK_v}{ETyk`7#i+2e#8P!5N&*7 z*%Z)Vw^3JWJ*uo~o=!AJXSj{~&fuY1tg-Z zofv8>+{QmI34<{R0Hi^Nmbi_43AG`J7j_Rr^WDZH5*kDhwVmd9ttmWw$Zgb{tFwnw zY=aDKb{mlry2Uog&^>OWnS?gj25(ydgL~XY0P~Hi{G4r&p`C7{n}m+o1{r$BZCodz z65HUSPB8ek+qk@=@X#G$RhgmJ-A0sz8WAL_%+RZDqqT&(6C|p<=Q0@l(rs*#WnHff z%7$e83#h&?3S6kHO4E#g;x@*j{iueUlxb=I-+!Qwes>#%s3+n3LhLjCtJ~;)wZKn= z*k}AFw{aPEThhN6V*f4tf_%(n9;5jpf!D7t8$|lgcrA}{=R$#ZC!AVE`p3W5G1h zz9&NLGd>7-s=zOX*k`=I$5@sw@cO=r{#V-p`)M9yE2b&6Uw6VCs{rFE9wYsntUuw7 z_F?=+k8u!TBIz#-v44Ul^##+&NffKhdSzepFm%o|76YOdr7wlZR-M@lt?Kp~n`#NI zH*GD3G_Ua(AksQ*Yt5srH`Z&cxm#%I_((=}Geb4K#(jWDt06&jp?Th14G&v*jl|PJ z>uOtzp{8CV2@sJ-*;>ERlH1*D)OuKKV>wDo7RY!Pukp+lu_3Khs-o0pX*tuhm;qkn zmB)qt(@I|xP#>@HHXthXuu|1L)fb}FabDvj!Z7NjFD)$rjq)02WrX&Jt#x@_Xr+6N zX$Y4|s~%Q-=_W&|UgIW;q)}EUf<(IZ=)Y4zUl?h|q_tk-x|UEz z`Sf^@ZAAM2HXdq^dB|(5{8F@4ZQ92iBb)L2yvD!37DhS|PS&LUFWb;+PkN2WM#7UZ zA^MCz<~6p<7$-MGe`qFJ;^#=?WQiz$V+DQL989_pX(Zt&jAqq-B3aW*u0c#X7iru$ zRkYcc6-tusGHI9B`1l#&t`P-Gj_NYL!)r8rRv5XGa7T6D*$;R3dySg7SEfC?x6;=< z4DIt8S4e1rQq??hz0gSSdJQjb$f&SfTZ^H$y+$2CRL)(t)~>Hm)|Xym#4d63^rWrD z&=+1~qJ&v3SCKBn3e*|d{d9pRaxM7U2 zl&UOg9)=!@Fs1?GMo{{iXC?L39*m2vqW+s~ErxbR7;OQO)^1y?-*$L?IKn8zJq@+P zhe}JFCU_V>6k*K1PN@E*R3%=&HcCAaVT_zCe62$}CK;0VcZ?s4F!U+H;m(9}g=zk4 zp2r45|7?U&`kl~EQu-1KMi{>XB8{0!Rr5^3UK#TT+Ipn$dZn$!(8UO2DIn5%*wS*x z_VIPCrEBik0^c$GWlDGKUA|y#l52eQ;Hx|KZeIfo9(U|o-$0i4_@=O2=Uc^cy>Bba zdwmC3{?m7g?5#_ztk#;roo`PT%h=pYvUTUDzGF%h!$NZr>P| zdwf|epZDFta+Ql5lt7wgkwt6fePem%AcUqtG$ zgZQF>UW4k=F7^t&1~qoO*v5JdYUOsZP4yboyzOFJ=rx3TOTC6rZ>85D^&tZe=-Oqy zB7>J{THHWfdoN4z9Yn%+d0PNeBbSt7jQ1tvvb5-X|Bcf^+>3X`+m#7*o#DDo+X0^L zzdT0Qh5{O;V6AQqOpeRa{aQzSVH>%b7Sj6TN1-z#GV@M+4FqK@2XY6YH^Hn{n+wb_ zSG}QY4~8Q2Q=*?K&LFYT3u8`X#c~kd6TaF3`j>! zMMeHW+L4``z};15+J|ro8T9}+wJ2PGpsY5NX6Q`m33!l2VJo#Wam#<%J?5iu@diNC z6zt*!qxOBz1HRWx#4X4|Kr0liQ<2(g{f0<5u_gNSMRBX3KM2C3MB=6?&{LCuKW`3t z2fEPEU$8JOf*wnDUp6+n6nS^*T+(Te)|h&Vs9XXF{pu6(reB)E(nxR9R2(3qd31iL zxlU?tTz1tjUxx-u*ohm$h*|*Mm;F%x9az0;HhN2Ujnw@$>T6p4ynFD&56Sw4LAo}a zOx!`m+(i|sv+8~LJX&wO5eg9mHcPunbIs7rlFi~K>sC@Au25)}nX9>G>t@8gqT&x! zggrN*8m@V|d2yBqy1YrfKi#H4lgqJQ=txa~;#oxBG|;H@Z?VYy5zynR*PL za$Z2D>kHlg3(luRM}MbxOw_fjiTxtA$pQA1JNl&FI3rTmA_(M;F4N7s8;YJxCk5gP z1@7oey7@JVA!l!|2#b!^T;`Lz#ktsa!e1kG{>RbLewWz<`yS;KDU$5ySeM!LJ$#KH zYG)OsbIY^SqNAI*%;rOIZVplmDyh?jvk>m+b}nU?W#pL!o2I8%U^uC>S&iSeS)sV zq5t#`sZCC>r`*w{uEukzqY31Wjx@|vtODfCNKzoKP~eV^G0dzZ!rAPKu;}PIhS?q; z8PYIcPxu$4&i^<%I?gaVHW3}YLy=@hw=~RYXkpU$P(h-j*=fU)Y;H>!ns=9(J98%rJ@AU(P@S`ubW^V zQXj~8s-xNbtZY~HYSwB9sRap z?zvr%+FB&;XlfAI(H|P-FFnyl$O#b1b%oW@bBvZFsiP@hbmYf|Is2?2&Ly&Nn`iWA zhS@DiwB34TM;POdK4o0KK<5%fN1ry#X4OQ-%Ze^KntDug*X`^x;fcF$7vC`K2=2O9 z`_dund^QZN(OGXu_s?+;w1p4eYkYS#Mqsq7H$IskZ(bS1z-!c$>j}jQAU2Tc<0{KXeT!f%*?nj{l>Vkr-6}Q`{#>vW}0q2}}tUI4Iv zBkug#vwlU1_nD_L)VqN)*izsMkd_wNtuGYzm@i;Aj;8ekNXTG?h*4@>|8HE?*Qp;LxnVfJZ?UN z1?ofO7Ar*GNig?S#P*u4unatpj55Fm%(AkeT$g#Tu00DpTIGvQ(jwM@9M_>Qnhitd zAPP`JX7zRW7#!>XX}rj8@L-x~_P+)R0+hD`%PSzlkGqP<^OaEZ#zy zuB;*RAt|dNGZc2;dKJgw-sPL1xZmYbGJF`-B0vok$7|Yq7?snkFJ>uB7X<0`}=;1L~bMn z#(G43VxNtG_b-_J&?BTd0Pkzbf-4R_^rv5GGYgZJ2rvb}bPJ3;L#nTu1HKXHtw2`T z=uKTv)Ej0))P)S+kM|=M>K~B+-G4-8)2a~JvK6TBm{0W4nQ|DkBNjFCX{TLM#y6)zhRvmBQe`GP~er|q+x=`UwfV8mDtA7CcwR!S-JmG@o>;q(= zjnXZc`K|d4_8LMb0ZFw{S{lvsCjCAEp^Jblwb6&Z1^SEm9tI7?5u1QKWTCuxoAV?6 zXktBbV+}a*hZ#FTP+kS?sG^EqZkm#-nbmZ2^*?mB5(NB1MT*h1Ckl+w&AA!EjQ=Wp zWglwO9|##=5y1TMTio!}~^yTRpKk;Hz}A zQ7w2s2lJs1rw094&Z|!1iGC!yP$w%5OxDBv5pL1@qxBZ1z^V%%x=MW-Iv-%RcD5 zYU%Ox7oOzmX2xLQ?dQOMusF6S4)y}w3_d43@wdcJ+2I}12s*K)6LgMX7V0Ld&8>V5 z=p!r@s-TnjD#jK9S}Hdkr1=(+*AmLs47P|H`^}n|GIxXYm_@XTcABMo{Vc(L8O(Po zvo)iP9DPqWv-${P5Tr7T=x~&YAL-_hu7ViTO4AzSEgHe$C=)-?%_U1kjk<$0!Xkz` z%Iq(6v(MME{$S3p%+`!joP1!dF=I~(;@u!UY!MxfGOF?)%d&;p7{teo@fP*B*qV_M2}g0TaTF0FmERqtYb~O~Q6~O@ zqnd5P;#834SVV`TOf+0(;r+tmYLFhbh*r_E>zVC!nJ2%I^#}8f%52RzkI>7EbeWmh zBgxS(LHfZWIvizUEtlDPtFY+DV$cxpN?p&y%Uov7F9fkONY`4#P)C{F&}HUhA*b@E zf;p!$TQe@@!qKKK^ZM6>+0`K3XAvEaGO?A*q%z6kZpc?HqN7Ebh%=jO`U~RcApKww zLmg#yN0<2uwh=Px$Hv+a@6fLI%%(-Km&;teLo6#jX$K|^(KStW--4kLaMh&PMr$&C zE>eSCb*MP6b*6Utmm74g86EsZP|XsOQ#37Zz^90|5g@JZ5+*e{D(zOi76d947ct6$T81LSKb?dlv7FGZFWAYI(4 zYYkxYN^sg)JT65ANO6Fz0EwbdJ)#GgDnM$!M`)%YZzfr2MlQ+#DfXZMmjYO2fhs_1 zwppML0eQ?uWq{Cd3 zdOwgyY*YqF3(Lh}!3#hRSf~n+T2Ys%0BPiBf^r(P?-f<&URD9pL2L?SB_bZ918;5t zL9qg)+9`t57I;U+(X0UJ!d^Ux3KK&Cjj~`BAn_Qn0;EQR@Qe^R^YLD6ajgJp)~)Ee zb;y5^FdFc}6)QlhcNB!(01HD1DnQB_3c@jfCoKXEVmm`s`i7nG5_5i(?tn9c%4XZ9>K2Jso(y`nr9u^S5)mNP_ldEd2hv81$ZH9?V+Tl&rU>Frkcup# zwQM=fB0ze4i73Aq%+ktiJ3w0WmLNuS#8Y2*+x;djssJepAI6eL9YE@55gm@I0BIx+ zMTmFcxPAf2&@4o6jh zv~|9)=*EDlhqtJDsG}-CTD?`)AIyH0*>-?5Ax738q?s1c;iw9bR^$GQ99<65I*aIV zR0T+z_lcsP2I-(h40TimNZlTn^#}7zWwsq4z5al#e`ibryeoCR3Xqm22#ak&>SYlf zj;a9Zwzh&e4y2hDG1O5NAkCX7>ksC-%4|D8qF$tidK#pcETW@DRe<#2JA!x&q|+AB z;iw9bB5(^%7U}1_YGb>sy6aVd^iQl?#BK{_ugYxA3Xp#MP%IQT(hf`-Vh)7~kf>cm zfJ7d0fJC1wYY|klgt4_XEiP`g2#}t>CQN{IH*U@kp|m&1t@gCgs}rP-dAjyFa!VAV zcOqB?NNcC!lLA2WMRr!yyV>ws^JF5PfCbd10<1p*4J$zEj1#2(P#OTv2#d$1r~oMd z*b0#7gBU$xBA6;b`W*MNq`4CB)ns89`5*(NrelQJM*uuwfhs^6wL_o>fE3xN43PHY ze3%S>f%n%Issf~)XiY0X`WhcS5~X@qG&SDB2NfW_eL)y#0i>Oc$^hw>6oC!|GSo(8 zfb=~&k1VGGnPH_zgfHaD_LO|8w03J^q8kG8V^bci=*0D~IzQcjky~rL6 z<^+rFAgTbV@kX&W2C!{s}2cH9PrK-$M(d*Mu4>OA;BIFW_lR5Y&R7k?dd3n@NJ;q zZK+TNokRpkUt>?9a-RfgpGD-ggtF}b>0W$}K{@Y(6tsxssIw8cd;~~0+$`8SHsKh& zMK4=yJ3!ieL=1;kAa%Eh4o6jhRC|LUjs|I}MRYi-0;Hp>MbV2uy4xaJMa%lD0O_kS zvi@N1tIW0oq^x?f{vds65gm@I04cSGaP$&Lew-$WsyiH20n%X%0&3A_Aa%Ehp^mBm zX}w3*AIzzh*^U4S>mr$51k!4Y=x|g8NbTB*@*f3hw?%X~ssf}fR|$(pK>E@mhB~SO zq;#(^dkIWGKA)}B^&Ft!1ZmYBvi=}-wulZ#Re-eoGg1CUtF*b&xwu97vrl zVyL4kKsui)%nk>0Qf0OsAXQ%^7K+6nIc8;;0Evnd0TMMO2T1fhmKH%ZOUTAY0&yp> z^3Vy=kwBOL$<+^=FiJZ^Zl%DW6(E(}fcOu&)%!?9pGL3>ke-Uuv?~C$wBVUGyw)@^ z+Is;SRsq%z!Jri&U3~{`o}rWmPPWD4QdEGn5U>>>(E|*6#4TW|0I44CB}nseyq_iu zw~`MsK)MOHr34taI*?g5Dg&hDodmi9NWP8A0BJaGc&Rxb0rG^6 z$^a?xZB!iS0U$*-Dg&fpxT_@e3m{+Hs0@%^M$gknY`+6BFg|4E>G=qJ#I~HeLGv}Kr}3;{f@BE<@j?!a**m6V0|T*a{iq*Yks3BD8Wbr!4wBpxGH zfE0U9aQ1;$XmPCo>BzI_x?{*cNf-@y;jI-QCH@M+F9hO{*hx?U(xTg;Qy1Ws7J&w_ z9U#%~R8!;j1U`~DG${2vRg@^t3XrOm;)x5eXMnlTVw1ikssO1s4mBw(xd)^NEF!7e z0a9bH2%??`{+7kDjOh+G0;H!A-IB$x!E_H$j#gx|6HZU?5nGpiqLj;_)56l@>2C!{ zQKJN>FYs{|$F}WYBS30x2zC~jd12Vn2^ApSv0t?E7SMNEDpWxy5dqSkw*>J`kUp`9 zyq1tVc7PNY5EJT0kRlRQbC9FXMoJsB8&Hfw9_JnI;sMsE*Q(ieiO`3DzohX>C`4!e~{b*Rs9`~ zssO3iTY`8wNNp{m!%-C=op@IiJrJZDEMlmmDnPpJR$(?5%;lBYc7T+HeS_-{($f~v z;iw9bZoruw5nlu8m_>9rssf~mCh8)PSRPu>-ciTQrozQ57KNUMp(U7o>3(G1O5NAZf42`h$6EWwsq4 zovkJoiU&b*%*rqU5)~%`B=V30Bzlfni=dh%3`CskuD8rL!%uR#?|znd`0jz0yIz6s zS(a;jZ?U}FcZ%g&Um44Ld^H*qZ@sS@%MHG9EI0XPvAo}x$MON+qbwivy~grk-#09` z`n*j@|8YFNNAd|@OO{Xida!)TcLU4qzFS#7?R$jfzkK^x?(luW^2gYt?Dwy+FZ_hT zTkm4*FPwiVw$_zo_xISFSpE^)l4W`9FvxcGG(C1TWz?VI`?NO6slGN%Nv8Swv7F`` z!*aTB2FrBcZ7ef<8(7ZpJ;QRQ?^TwWzSAsc`OIcy=O*8kEOUH=S?2m?u$=45XF1Qe zo#lMrD=ZiIPOx0;`-bH*->)o}`=ao8jJy5{Un`ck`+BlmnwHqxmsfq$U6qPmT;WBS~OV`MS`9yjV4r!jtUxexqml=x(s>qdlET|4Wc-CMi zLNU~-4n6GJV6PrYpDNec{s-*6uY0KiTFzA&sM%P(Z_DR3Ew0<6x)$fZ&ewlBo@T6t zJ58^Dca3pFv5*nDF6H=NbTvMV&wo*p4}j}UY8j%4=RgIg(ADQ*JPVK9_7yoFlH0zt z{<`KrRGqZ6yY!l?ao>eQ`fC+cJ_g9o_xh*DZ}=I;*XX93o{ILr;a>n=s|V5MQQF98 z|J&wfq`TeM6DE(;IsyCLE-VG_om!MHo*eW~?XZK4L{Qe<-h{Fds+ro12IL+|>!Ct=FldLyn2I?$)_(Q9tQ^+vNlCdvh< z)I~_E(}mV|@HQCSVP1=_ZG+sNc)QM%LAnwR7MrjbH(~yVYRp4cUrP8NaSeYJV*%-O zQfPb?6xg2cb)4J!AN6xW^EE^i?k}qSA-Y2IeLMh~hij6j-tp8#bqC>u-2Z`T9MH8V zU}qQE;=;Mz>!xC0_&+wEh`?h2fZn%Yu54@8{@i?^q0Vh_2JrVp`IUOG^&`t+`%AO; z1EQrOhNCy|X8Rh4-v0E?TdBWJm)3h<_iszY`2stw46Ztyvf6lm2uB%@_~V(OUsMX~TO{;LcLr?ESH(k%O-QdY@o2 z%vIti*0iYCr-E3Zn^)irJO~2)hNOeUm80cEi+K~ft^c3+z8WHA%8pIKlW=&4u(g=# z=Rm|)J^A-K0`EN|XKN$n`Ur@Fnf240%pliQaWWJ(* zG(Lv#&-AvUn?gbUOn(_`NYjzZ^Sesrl_v00`j`gz%*VpHW?Xar7|b8k%?9z}zAKeT z>^c3TMXkICHUC*RJC$qN0$|JW76Yv&28b3#`t=s!A%{UdUYV{{=k=?)%cS4b*Uy3Rqhe_t7{ha2_hSS{;C`+)-ohXL&VbOp(q-;% z2;EiyJ6i;HSP(k7%;Fdjh5(#gfgtMcu$AaCd!qOBd7$N2LFIZ0tN7yDdpMuf9|rA( zFjTSp3jR!&S>rkI-v#%y#kYo?llrL39Jw09@e*hzPKiX7FQYCHwhp??+#VoY4zR67 zpzYOZrpRT!KLM=%V2rj{)G$ugTP}0JBxGfPvA|+cVUiV~4dzP2T!sfQ^}E2>q*!7T zHRxM5{x*i0hUwc7dd~shuQ)n)gy39ln9=J6=Of^!EDn!O!Rc?9=?H7c&Tqi0VM!F# z=T%N{#v104Ji%!Myp_e_CK8;DhS>*;2HEKke7MD-R>B`jpuKg6VctGWY^c+L&ms;l zeg-{2?|;ECUyR4e9p>RmK&zDrO~8i?bKg8MYaa#ll%?I29avF%+8H% z?ze{dO)o@X;Pk+|FL6bk=#hK>kA}J9cVX&!K$8ik4O=r9{=+bzDHJ6v0CcOZP2UUf zd)?-Qlfvl@fF7`6I<@r2xXo7RJ6gD&2lSE+(`lo>f!q9~k#Ox8piga>jvM{W+~%vV zi?V+L^qURSd85C*+kE3DvHI5@hi?etEh|O`j{Yug^TWXc?g*%x4O49E@98!lED_t$ zC_v*axca0WFh0a>8dqa@&qn@Y!Z?WGMUBUuU`=qFV-V@+>%e%h0!ypj^AgNVahu=l z2VpnB7ZgD=-W>?+Znt^iYvI8MfKCw19aBB{9UOepZN5DPJzk3Z-;}D@kOX0u+l<71 zsmEN0C*koH-B!I>5^TTVHaDiD+_uQ?XkiAOruq-L%^qldI!+x5XcWO*E`Q$apLX@( zpJ|}cHx~RKxXqhK;rTak=HtCgafKiJhZk*y({8hHr_RJDK;A_PR?}&80PDZx zHb-G^C7W;I{Si^9d|{I*H9h8wi$rnf0beAFZH3Qh@n^Hl`(F?x)xwQcJ-kH=2)Mn+ zbR~#VIsocQuo(9AEeU@Qk9pG;p*<4Nb+$IcgFI&TOrf0(Xr8To(@toQ^O%LJ#9DF} zpiN3!u5HYj>@l}Q$@LHTev2d4Hs;Lmm_N6a>mTq_7DudY%$e^opI$6DzX7i{LA8Zg zw_5!J{;ctslV;2H4|pq!Bi35xZ1I>Qi-ev2z{gkMh<%SYGlSXx@|d&yf_)R1w^?j! zf6@f;C6BrGOF`TO(xVlKlJkzo?7djHw;%Wsi(}6xVeEv*%<&0h-+*+%B63};x1*k` zSIs>93UMiz7A+ z=44ef8+8|)T;O>Yhc`@iT{IZ8msB&`9T4m-VD7Zojs|1m-PO!1dkf;5ARVhf)C}f4 zQq8=suHc*p{(tdx9q?5YUw7w~>}#on5C{+my+m3<3B40~FVcIjB1rEjpr9ZqMXAz3 zM7n^|M5Ia=@J|z@DT;y$g5SAwcV=Hg{PO#4W_QlHck0g0&e`2ZuOHd{pQras+!tc@ ze~pQ;ZL$83jfvbjwt5|yo`sli{>4|V3c#Caj+|5mHGyQ;HO(A7nB51=k!jgMZhM&K zb3j_IiP9t2we&dFG)MN~JnjbR{L94P-X~4wnP!cT+4n=xUF}q{r0<{_mF%^q z`8Tc?bY;&0X5!0i!zqaeOfxfX>WEkiq_$~^>1*P=X+EgMH8B*-$(l`j);-o7H#*$# zoK%wzXEM!hGkeT(xC5j#)`GW7TS@oy&tPI9kGc9FyFUZc|1^=@Tbzm>^VT~&W%v`Z zW_wi+G7FOEd*^jzp$Uk%!4WyY$fH<>^G~cc;9kM(fC~!cr~;rS3bt$A8nz^RqQ{)- zWOg4gr~L=pHnvh(bY(eLQu3hWwkO|!InBN~@lhUBq(r9YB900~4CH3i@Z6O!Ar+R&grtfxa#S6FI!lKg;AJ zZ{{VKn^0vP1pRw$qKQHdFOj!sq2Xt*`8pOFi2Vr6KqpmlyQS^Q3Li|XYrlETh*^e_ z8`LVAPRkM8S#{&dLnHDJIys(r&2m^=p=8>?<~40+C!_6*$O5Ba^2}@2D#9jjgT6?c z*hKkmV<6OL=J}B)cw0gGmWXnqBb!VoVE9PK4aEA)dR1`>UjyyF_DLBtoF8DK6UgH; zH-uo8jCON%#*H5~Yeqxwat4a~%zTIO3Ju^qfC{C9EBMS}$^7MjYJlnx%=7$rXjQ1D z&wORLAs3Q51L|p8le!pa=`&yVaZ$ztnyX;@=AJ86z8o0n?lU(PiApN9?K~*W!pYJp0u4STuNpS==u1dP@TS=3;7+B>qr~JtoEd)|IO$9?W-NX0y5)c;GV+W7SAQbbo(^z<2(>|%cN7hLR#T?ou_FSA))4bvxTWOUa z%o{JWSzQf$nY3 zG%cUAQC8I|lZ0drjsX6FSn>18&ns{#DR^@d(n`9W&0i9s2qw}}F6 z17@BYSgA*=4g+bbCQ@Tqrw2U?5*QdTPffC9P>|MYB9(}r1qqxAn4hd>&H>;THAg#B zcTeOIze657jM=||8G$J&pYUnfG84f$i4HZpj_0+^La<5Fc2s3nPRfLutu`>H0q`!G zWBU$bcL+7VddN8$1?JrUU~?u)ik`@PoYV2*Ei8tDdH|dJ;suE|JYS*DLhwpj?0%ZN z|6s0OiLal4_!wJQmnu9;Gg)8V#X;oXh<#v6ix*=s8Nl_c05)ks8D`6-43hZYce2 z2CiR=5&LOcPDAPhN`E6ObS+XpA+tsP10PaQdw_T)s5OIu64WSO3u=Xkt`xIb-{5^x zpt0!7qATS{a+PBCD!589qt`Q(Vzz&=F!I8rE}7AeR?KqM!n2Tw7=X>uiyYD(7f~x_ zs}ZFYvvp`H94{b)1!N%oDaA}4XvM4r6+|g!lv7XZZz=bdV)pA8VO)lv+vH0oE^A6D zX7ts`r-0nO6)X#@6tk?gxGr)4&O?;`$cvsY)>$vzRG8 zKltirKO_VeHNbw-hs4UAUrgnOWHq zN-=vi8{T=)xZIZdXz_voKi%$C2<ws;;#^>_{IwiGYmM|O7zX7|YaqWcP zY-e{$G0SrT=Rc^v{>q)E+lpDcqZop6fstRaY{jf52Hf(9uB9Lz*H}J+VpcCJ%FzyB zPfd^+W&#wmDIbF{2H@;81TMS${1Q{4n7QwRx*D`S8Bk@pmPS$}fcEo$P=m7x z#jITlis&4GaT6QYtL}EeR48U6PJ#LgXyr6@Dm4M84aMxU5lFicz>bSG{;T2$ z*$`SW+l&Qy;&=x_|HNk30#=O@(BGk$Ex|BHoJ8PdHAkLXS~07K`z7Ku0p3n?WEE+} ztP#!$;=B%gyyj3XLBzm{Gbv_^=0JSLrD;C!#l(?=pH|Ga6y$Sw2cW&mg{_$N#*COw z(+hxp(AHWp`>`flKLPZQw$_T-k=|^bc@Q2i!p3PU#VpS*RJT^lcGqT3Rq$#Pm#(r} zF)L_up_moy1c3yc{@7k4E|*CuX4N4LQF3o%n@zBEr4_St23xNK^clfytrWAoPR{hV zfKJ)4Qp{>$%%>W;3+SN@E5)oKL@mmiXE3II*evHtG3$MhOIH9;q75s>Y~e<}pwt3X z--eZ9c5fiV-2wHr;c`_`QBch0f5_L4$$+M7SSw~%+o8uVLHs7d8qReL4OCpPq&jbj{a_*Q#)>F~7QvlY~1gN2`1JBsf8wT=a4!P z6tiA4__{Ly_$1Adlgc2Ek`2XdG{$3^m@WcyeOh*qTPS9yG3$c|E{IbIc)2tjA&FWsdjo@nXaq(J#j=zNC}t-<;8G6& zG)cjBty{yEQp}DYW%eR4zxWTfZEU3i#q3N$tN+91mkjKJ+D$2DEq8EICMK`hvDu?M zC&1@~WJ58l{gm0sU^dEt9n^x-1d3Vj7M!PEppVfevIXTo?x&Is#q1T_+EVYB2j->> z*y&3E#jJD|PT&aW|N9RUtBRnQ?HI!BKf#O~smg0-Cw&Q^m^EF-c`6KgjSNieyFIO# zb$OLjX%G7F3`}G(f~ppZnZO-4mHb`M*JfZsT~N?Jt1M8=Ruvz05DofphXnyt?x?jdsE*NRy++~_s~b%>_Z5{%uK zg1W6%%qCRkWM;tT6K!WFV>h~1%x2AIlf9sy&?YufD`pXyc!Kv6NdFL#rXak&pcAka zGd~{E5mBQd2V>*9q1v()vuGETTwIn*1Ft|Fp3x}9>{mR(NN@{4ZPUR}%toQ}QEwRv zXe7Zjr81mKF?%m7FC@(YG~c#XirItJT$Ig#4lCHcxtBF-31LvoR+nP-H86kGY<=al z6|=DpCVH_3oE00_P*7DW#jG`6^P@T|2~sspq~^DhR*Kn(Htex2NPRUiC~2jb&HJ2* zlR=uVi8|?Ew@}QQF5>hzgSr1@wyl_5c%6xtLAtMrK^awwS#_hq)Nn0c)dO$OldeIBIynwUPLN-_H;i&g${C_lEBnqCs2m=*npiHRUp)Wo2SD#h%26;8h; zNIf($D5FX-E0M^=aUi{~iRm+{6tnH8tn!1o=Vi97nAK=xl^>+rni!N(rI>9&mQxD zS?xdALE|~+1d7=o(>Rs3u<4=gsLE``tU*`ij066@=GZw2VnZ>Tg+&T_k9z}{hyR1k zqlNxu7yUIYsZWvggkPgTJ?2_dK}daS(xM=Js{KTq+-JV9^{Jyck>U_p1e@a;Dp3$c z?5FBf`c&pshEW-@jnZ;%5l87$WQDn=rB6*4a3uAqw-K-Ose3R``V_@$eQFuel|GfS z1@BV<-GNO!r5s7F(x=XVtMn=QI)>7xo{q!wF)+DDX0)UAspD9mc!7wpNhspK&3_ew0FG10pakZc>`&=a(z({B9;2DGK1Dh8wC;odN}o!^Lfk0$nM%H7 z;&ocMbHA0UbgxzYO6qM3|d26A0F(E8Los#K*WOc-IvNRBDc|FFrz+xk?IrFd2ra0Nis2$luY`qUKMezih$d)rd!Q(fzE znTG)yZNob{BRkNiPUFf+WuFV^6N1UHEK)jss?a<<3kkx>v?Q`kr%ye>l#ZhBfce+U zY+IlD;}9>&MPjWxJ2p<6zCfe(sSj$R%z*e?VDeiyvZa(h^;b76{bYozH@cpn)EbwW zl`Wz4sra?f{4_52=%3c7ey6Uh^r><)gmFZZH-Kf0+%L|%(*PN)~E8!!~1{0T4Lir*|j65JQq6pd>q z^1Fjpzg7BF!__$dL0$VY-PWh>h9fUuf$_Cs+4|J#Xz1UFzO5i0*I2B9K9xHLwet@^ z^Q|-)W&-r7ONZe+2f&hP2wZk~8DJ{(si#Nbs}5-GGoZ?9Vk-2h5xGHq4YYUugBqMo z=u>Mj4vOXA?$CVQ?SiS$r#^Ba!zV!dUQ=^W6L8wlrydmq;UU0(HK7lNzhF1er`ilc zikWc*&xg%!$zT@rsi`MnSRRaennj6OETvB+ds6-UUP zqxGrWxaTF#8sJ+rNA^ywPt`uhevSjbs5$c7()tuuXQZFsfd8X8vWm1mb@eUwlX*G@ zer#NRswKl&{|wG8=u;QJ;R|Xx;FXCZ2S2S(9Z2AFxHX^-%7v{@-P_M+?QlS2w6)f! z>YQfld4LvaYpqXR#~g}I@~wcr(AG+yYQGoNt@Wv|=QHOzct2@Z>W!{0HW&I-Qd1#0 zp))YBW3$Sn^r@^>@MAM@Q4~-~g6UdlztL5$EvHZqP!ro)=~IgraHjhJ8f3#tpQ?)| zRj5qU0L`*trBC(l#m-j)+GxW{pQ;+cIX?{OxD6|P>O<5goq@LjJ+NV=PgN-o(G9Ts zZOm1$S>-NQ6-^C&D(fb;&I_ophP6I5wi0@L6~s3tOwSMa>ZB9Hvzt*2(~^IwSjZ663}{rWoOg+RO)Ct=MaBfnesTQ^{Kasa1-ENMX>d$!B`a# z|03S~j&4%*Mpua>p1R}&l$T(cuF|J=(RE1cQ=1_y6Q?e=7K+RHQ2NwH+W`91=*>(V z1@crf&{e13=o)#8eSQphOnwowVLL_?+l$tx)?Me6ZUMiqIEL1zvJAts zTZndH!sx?hHNVoQ*5i(fQpf|S5W(E-l|EG)55$o*{qA2K+gj;U#v44AbOuB}^vn6< zu}$eyzlT`kAMkfIhsQRhPaW;ToHf9=Xbz8UN}sybh{v|$z%ObJk6TKgdSfE{`3?9# zn#1`~`qVp1nUi@IIy^Qz&wSmJ7c;F-4Xw!R>R>k0Y<+#Q^{FkmE2bg5A4tQ~5N&o zCW+9e20_Row;zGDIt|f&qw5!p*~Hlg{5#DFsxavc`jo&#kcjs{daQ}uIi!38eQHmB zTyHQ4he0~dgN^G$PAY>uN;dSVQFZt#RRPTUY1u(;p-;tO$fcyagY>#4N{`&}q(|se zt0Fm%GeIIvozvIE;L4OH(5D_&Vc+{eKcChls756l`qV|M!5@P8@5^jkpZeIrIf=yz zb0W5&oTRS_=u;1~a=q08v!!O!o^_99=~Gmbwmx;g24{UVcyDVf>7M=>TA%8GYdAH_ zCm?OoL~?I&pid=ZMnRk-i2XhdM@XX9ry6~TTs;Kiv0_=e0Q9LGleyGkbI|_S(wCZB zOR6+@qpRlv&VL0k+x`bTxXGj)^r>;ybZ;nZW@ca))NaxQZ**}0f^{YF;+>n>!{0rUad#3pKes>FGofxZdSA|lEa57}fo0b8HC zHXeE14B8%|$_WppV(U}O8(~=y?RFmc73I#5pAMxry5`}MM(|@m&(gurr+k=-Q_|5N zU}XTCOu^Qt{_V*NNu>alBbe`els?sF1Q(?#pq>i0Z|=EL^&4HM8uH9`JecolHl1z3 zM7+`UtTww{1JX`S45~_{PtoVRsLoDa+;`<4t5KD>V(rOKbT!!X50GItffpG1=0*n49cj|r@lMO#1$ZI(ZryPDt&7D zNiNZGkZx#V`iv@lYS21%`wUD!t}tAF&9?Qa5-pgR7o;Rj49cj|rvz^CsOlSl)Ls*V zGOF~c=cn1@Fpy?wqE6bXQKe5!Ey8YBfVt&mwjqC#j^601yTd9!NIz&|P)3zLb>|IE z{|QJTxQe~h^je=9#UA59O47vi8CCk!>ALK;0hsMyX50GI(2G|2L7J+GK^ay0RQvp# z{$h|m)x@A0Rr*vE&UmW&Z$SD%6Vqo@=~I~&S>*>aWT7g*W*hP+>FABFy|`N-xA7nq z)5M^RDt&5uKVjUNfIA?NT5BSiT8*jR=*r!KHJrg9jn+gek*!ZH!CZz`8Rr6Dt2x@4 zwK60<;*GArAzqU_0OpPVV9QJd=LGsx4+x`_$}`x6phE2aZ|hS<&``vQ2cD!kc220u zEH=H-h2BE!24MF54>o5)>QhvW`eWu&pCakW@KQbIT2Wg_eJaoPAbsjNoF;~TrL?Er zj_$nR2v50mL+Mmt)c zdQ=!+07Aq@Y>s*4koLHUTA#XuD5X!0_!NQwjxUkHH)J6FDSb*FXnkrf6{BLz@R4a>qReQM8pm|G!%_JF$*Wg&8-^{EM6 z7##&ros;h4HU`NrxtxEj4p`ouOQB$ zet&9p81ye-v(w;$K4z=+DcPaC#L)Vb4=w!(GO`De2b8I;PbK0l*R_DJ0J=f2tbVOe zZK{t?OdGY{DAe>P2TVQ_pGTYXt{>EYzwbo}K?bby40I=4l=oODEfcR7F@>9gJTPl5O z9A0nuCnMa_r!K8Ush28ynOWHqN}pt*;)7kn|#pc5BFN5lVjdATn*cBPB^r=A|asGo^_hq`RPmSA%ymSJihho|KRMir~ z7=`Gm3gU5%#Tw{Sua`pYECRSn6J&;&0DWq4YY_GTJe`KXWtW!$rb3^3jJbrk1KRTp zsIr=v3VrIWrpR)p<+#{jv+K#O(co-CpYmY}EvkdtTJv>n1yi9~AAcWQm=WNzm42mXfU z$a72UQ*T3TAV2Q`U!*y*inKnp3b#1K*$VtC&7oQ{oL95+`%`5L@CEgI;8%$w2S2S( zwImIDDYS4vFO&;Ie{pbFD4(@iKEZMbHn!IKlnbwHkacN56$qwlf}!=PeVy651)#Rt zTIo|C>_>HLeJUw4b0&cIrgo+DsYNyy`qZ)sLUJ~M_qq1VFG%5wgI970ncOizr?o5n z{#3|QPT@YF-)w88Pqn~6LnD)KC1xeqtO`^5RQ@&$7XnnwhLt`w?0wE!9Y76jSm{%p ziZk33P=6a%`c&vT9{r~RdfSGTKDF!}hCczc&W4pfHL)RIjt&C)R>NAKTDclM{u<(c zB}`tu<)9|}oz|!3;oeDvtish5o6WNIsZLwbYy|)&DuS&~4XDI9s0FA#!LqYyed^*> zn07&Ye`P9168rtBBG*8e1aO)n*!t8QG?iG4_)j#Z-=EsklJ%**fDRHY(^dLZDY_16 zeaaaD!4{l**d8k`=R@gJ1#AQ8Q?J&-3JHjDt17E~$f{i)r!c#+RafaxQ$GJW=` z^r`2w*h^=?{nC2T`qXFta+an5pP@L0)~8OOZ&4~M0IemMPi3V~eRqsgH~{E~ZLRbv zCmyCD>l=XX*w#`r!WReUz0Tu}ScCBo8<&#DHp%IPFAm=3@g^Ve;+n%_o8;h&gUu(g zpH$$@HHXJG$-x%~eLq{{AMiIchsQ0YPxWuYocDk)(j3l@(x)b&KgsbA__t{|_7%#K zl<>vD&EIm)Zi4x{X6x&dMZ^~eFQLK7Z2ZXaZtXiKMlYkC765td~;0lwP4fLrVn7WgbkAbhz9Igz>fj+g&s`&lD zk82KJFeC^1)VRi+ncKjhX%6`bt}w}lJ~i(TW=CUzhOq}_+ZTmsxX-<^r<_T?31(UAkEc8?i^CSfj+hC0?#}*0zaTRG-`7> zxb`F)`c%waW?upGL0Wc@Tj*1LaXF);-B5=zW3$h6>mn~bLZ2FboQbc1ROe-4aAisp z=uihOJNiI+!cECP+eKsI{tC8qt#{|iSKDFd)&dzx-f6ag$)bpeX^r`1#*~E)`>YUiP&~^em z3`#cisSQ_{T?Wi18L-or0QywC%*k+`$!#*>G(KGh;8Cvy}wx3ry|g0{2u zsp8g{`vUZ^&s5QEqSmLTZsi$h0gx&Xk)|NLzMvDZ^{Kk+0X7A#z4l2bhpkU-9E?|D zFy9^qd^B-*Mx)-JDqb2-i2|MrXkj`S`qXXQ^HOiw0%#Y(^6o>wKUE6PW6?6v89?9L z)=HlmRfvo72#~p16-AesD^=@L*(WkP7nsF0TVFZt_owJvqf`~CAhpp%?bagVi-X0j z$@~zICTb!zzm>Gor;foJd0Yt6I!z2pTIo}Tt1$5(NZ)IsPCD2v^r>a&Cgk=JF+YE) z{I)*TY8Mwh4x}QQ7?e?^PYo)^9&3QqToZ#bs`RO2xVNRG`-3!56Vqo@=~Ky%t@4Ap z?q#;EPkAO;PgEU4HgEFf0shYTHp!DZ~v{Dm;GOF~cIu)4sB}ivAF?~jrKK00J zl^@LKFSBiZ>JVfPS$^E}<;TY556Y<0r)oGj{mLLU(!`*QDt&4YN<$udfiy-F(`Qua zQ|o&(dmfl8UuN6-RH1uJ{1T*-ni!N(rBBT}jI~t^p?5&~OA{$a*6E@2sn0XB;uEKx5K^$Hr&XLT1qoqNW$fVrD_!Q_?WnhHTI!cb3 z<`2JN{)yP**v=4I9PaoxbaSMdgDY|(_ksMT(dzUo8aWz#dQ8Y1cu<-!Y;2mfV@-oT zUuk&h8^R^BEfz)zvm}0WEvh;4Nk6(qZ%XSQU89$J^^dMK|40}~@YETbNTzbVhCTa7 z*XUg|FM%2V=$i396i1++q%jwCnDLLU4a80QX-y_mPEh~oS~ejJ%EHeCrBKc2A6=^h zh_d2AX&BN!x<>CJ>K|RB*YWg^uF03_a`kg!xeVPP=PtV&$k!rOH>e1o8FzzLKy)|Q z9>8rdBIaUqyi3(ZdxAK+8%V#Z8~hB}b{o*$%0PF6*Bjuc7Z7_E+a*Hh26ux^Gcd70 z>?3S{Y1HZl&r>-E;V4aJY-~#1fa<~UQgfHcAAx^gl)yO<^A7Uqsq8<2o15q@SOoL5 zHt#>c3Ls3IllfvwnUQwpsXZ9~5c`@!#0r9w>GN8iZn0I-(bgbh2H<&VDbb&k?_Q>V zIutEHTNTU;)l#?h)XFBG7Ye;y6cTG+^LI>ZiFXOxE#hy4Z$W$bk*{|_sgvz}(5&?qDhSjpC|CkEHl{s8uC94fv+W?`Q2L2M&sg*9Y*aZ$rrKb%B0d#d z(4L?l2IThlRF9X#Ks5bKpl6YpI~vPCpuA-pz=gCjLTPc+v(1c~$qtu*vyymJ3ZqDA zLT{U0GvP@k#O}p*P^0q8s3fi%-b9okS9WS8??iuFT&HVOjEHXe1HN>&-0YTu@87}B z0OHs~o*7wXe)Kl>5gV@%ey2sYm3MvA^@(@`75p-QtEHv1>IW%r3a$=~R6IA5;U#sy zz^!b^42>N044WP~sP6R0v8fuK7K8CZY~mPIPGWNup!*Wq6U5OYhxDsPj{C{D zD+c;R8R(J2KN3Hxh}bY>DH@xjC|&pEkFetcavvop*F}Mp)TlLbOk2)iDu6~Bq>+Pr zA~ko3^RMAw@!aT>b+NWWBZucJH9oX9A7;Y20MnUdUYb%?BZngdzkh((4GIzE305OV z7xcC&h&TlJR9cD}IbJ;_j2{6#RB+mnL*9I=kt2ZRXyQeGiT;nxQ4_ue?dg%DF2$&k zL(byV$Z-I)qS%^XYln@EX-|(FjmTJy9Q$#%MyXE#<2~Y2!3FL8KO;x2j<_`jWxqC% z1t2Rua^!gpA{rttVY@~=Duo_7uD{^q{sQtsqjKb+X^-LUi3-Tom|6)V$6T~hZHmz& zN9!@TJR>#*o1-~-RwKu^*D(Ggwv9qqZB!#isVjIP4E(`>N2jI8k)skGcAKW+)yP5f zt^yZYATu;_tk@Sca!|qb$T1)H4XeRDhfQ>)iu?(C_Q*kgyaZ-Ea@6}4kKF^!iUe$P zL5CTS9IJ=ood-=OQ%+Ei9Dn0xo3hZENWG|<^vF@5qLBF!1oMvl+mGvkrtC=fkz+{SBBeHuj-=m9FM8ae9q#pMREJ+SrDs5NplEyds@0Mj%`BM0|H zYVHyjyJG7399P*G-$Z0FXIbB2P-hD|%2_sFG9IuIUy<=~O4k@z^nGFEY!FjmKjL#X z^2K63DW>xfvTPD?<6w@USx3$$uMEv=7=M%DX)3?GSmP7ji11&Ff{P~gE@K}bXApz> z4>6i0`*Zz092f8SO5S2eP)QTe67kg|>1IfHUxD8`g*T#@Gv5|Qc^nya7WEWHPx>e} z9f~?L@#Jx@C-oS0)cN02EC_cV$A_*uZ)e2^0#g?-*;A)A^4oVIPGho-sN=%Crr)l! zjOLmvI``{m#P^T${cbt098Fn^AK@O*TNtNcavq!G0@Vj26Wu-Dz^Wi(A1g#$A^0^i ze5VIQGV>sw3CAYQ_z@kpUFa_HKJJA9mA7H4F?q{FC1)?I!XU>q0Hw92UAJ?v)&2K3 z=mCHxXqa*!h$EyIPVS)l@leb}L0X%ZDA`PGKy`o4y|*mpKOh}ZL>@FkseR+Gx%Z=)Z2a3YQ4lRW_cl_V(U5OaW(3-A8P`|M`Zc~n(z6(H|!kw=Si>!G7y`o_D;B1G+ ziVqd;mY7h|Q%SXfH%-f#Cz`#1W+@@u-(bwmjhYez!C(g;i1u{=mJ#lgI2UE%hAypV z7zN{_9g$CQ&z^uEq&4D;I-*C!BTosA_DC<$k=ccD#gniSQ|lNet@ zP8vI0w=kg205(tI+?bq5Q-}NFI$^8>woTzOCq}qz>=q7phzG+Eu=8p0$b8h&tsU;< zf!t0Hfj?FpBmBF)sGhD4cP=FFL#=1SChI`NQbA>Z3m-=Pt%t+C;1ZsI0HJbPQW%&< zc!y~q^+s(L#T^g`-O`eNl~q0)6RG%q4)>mR(1Jjirb&bguk@6u1w4&*xO?S*{t3cH zO(NW?{F9qN8td@A_Kq+PfN(;Sa*&hPB|gsKi)n;sAT>sxy%+MIY$E!Hi@-$miG*QT zdX6tILS^6RE(erXg}V+$tOT67iP(5(kod6Lc-W@BxPdcm*dk2IJBbjSO72y7>@&Wr zaF@rxMpf4VR#qKetVwk!+{cnIULg7{?NIi8`|SQu^zMdX_O6H&@i;XTQqcwDUx>`y zU1W2+9PUD&31b&r93nSQsqX%z3Z75K@gWZP47|^E3D~W)_yUOs9PUa!m^=e0kcM<% zEEID-L{i}n_u#{rbi$j6B~Z2;)_y=P(Ho8I&5KyWOXstv7uJ(a2^zK4JkLS+)e$Z z(C)6>&HM#q*xY{}S20)acK#ajxVyij414)U%CNWpU4(5`Vi~bHCO?50sL+JeVvpfX zIJ@I_?h>jv%;z=#K|8zdHI(y4A0h?`1Pb3 znXjXeuDliex6$8Rc`N!K$*_|DR4g4=@voyS<*n-9F2id6(=x2?|5=7L{GP&u*YxL< zVJ&|-8P@hUl3^piGnDYg{_Z%g*8@$TuT1D&Wbk9z9QpRS#A1=}V|RSgQDi#iY#o#_ zYIvXkjyy|O3nOZzC%FiQRck*bXunudJ3a~f#*pz#aj|UejIIppC1M1Q@>O$38}*j& zM6yYt0sanI*E=>EPdDnBo7djN-h zHyz)_D~Fn$jDJSNSPTN7WZx_#T+*!OPDB)?SLYtDAYoo(#{3fQ?z^-Y01L2W0bhtisgzdugdoyDG2JSj;IcFa$cflH%Yd&Yjx>*?PZYRzIsz#R`HHp=&|wXS z)9=-~b~yq)aCN_e=o_F-Fmn^Z`f2-LDy41Z0^;GtYG+e`_6~`{0{B9dyJjL>D8@H`q=P zS7ugvI^>8shm(%b+t?mxG%{}mWaF?SQ=6Okcn-X|kwHH;uBb?Qg3R@eBU2YdU1za6aD#6@9()wFR)@t1`B>~(8P%jOK(+^C#jyVF^ib4=V^dy3~ zfo@Mjy2l-Xb4!KsA)=QnNKhI08tppkD0urv92s6Z6AKL8g}X9WTW3VhQ@BjiNE1#` zuJ%rMNp#|?aPbGZaiFsb8Yg5gvt|*J+0p6#&4)WN*oR}2Od}x@lLl93XMiq*1&vR{ z;l?;FDRMW!rRf1uDJJmXGD=cjxG&(|M%006Loz>wA{+H3R#CXk;pqQ}9-ts`iEy^9 zEZp@UAxDz|&eDXdHlc=akA4M&PXKP!gdaf=zabAs_RF|QawQ4ByaE%WewqHrC*~k6 z@}U1C;wNRsC#QUr*ws_`*Dn{wT}lblWj{8VZ_XTZdI^8l>W1Xx1zt>ZWd4}bTli0o zVon|4%`}Iy$d7`#`UrmsG&!Z!5BTtOocAz&a*YxG8*|yuyTBJ}j;t*Csdm?V;U9rh zpZshA{$)m-1;YQwGUi+aep7RPk@=y=5nT&~f3210e}J2qm~+{Gw>gW1zj$HxlM8qe z&7lhiL^$&Ek?@zn1%mQZ19&6Np-#q6vAT8&p9eE+(H&r4MKGLmYQz2u;g3R}B>TyL zrrYp2OfX!#g}+Zt!3yw-e3VVhBM?Z6J1OF{8 zNB&Ht>nq{w@eOilVkQ%XjjMv(8qTOqNa&!*dJ&JDQ3VzOUV=D(Ntk{`(sf8=9d!V& zR3oM?pvE@5nY!lJ!d(@Qby3gk4QQYZ)6~&*TtuIb;d@Y;O3k$4IIN|+PKxM`IdG2u zgbM+n4K_T6W>n`y^ybTK{WYLtHhe1+;0q#p6t4WGV0{bFeH-phX>;a;#iRjTdcWG9e2T&m!&O}+eA);ru=d4u&RL6#2r>xx((O;n>l67Z5J#F|0 z%Gx~<{i~m|HXhJa8*WcoyDy@@!Ga!TZ84xvY?!2F*F)jWcA76ydjTC$u+{dG^Q-XY z|AVieH-P`FIo6p#etr{PFK!Fz>J@Sh7bk48284Ac5a*HbUUsve!oZU?NA_}OE8;v6 zzOlG_qOrFj@aBqxx&ON;&7Z=T5XW$TK(E!-qZ{s)G?1$4@WPta-dO!%^6K19}c0X?+go7G_bkMI>9#IWZ) zrjXdIKJa`7te*>CIBw<0x&WX=8hT>hws}MhPwmmYr}o1 zTAU8wW=v*j+AtZ=bQ^9()#7sa?jVZ7h9^+9xE;PPdosKq&^I=`nW`nk;p>6Z zovg0|`pJgN=7+WE@U=^3*tme!$7YrLDpiZe;kz=7;dns#Y&hQrz+Q(hQwPp@B|tT7 z*gF%j&*9toIm7J%b+zF^xR$y64qukG437ph(T3kdr*?%od`mF>pgLa&XsHd~!`+xG z+~GTulbeK|2|lD?tM4b!r9Z;q8(fQB{15ouw49+dMT>Q9bcX-@Jx+T$VcYI>-yDlk zF!Un6e}>Ir(!EZ7iS2Z{`&7rB1fokRNCXHUEvMAGoZ(9cU_yqN`oLSI8J74nQc1`_Bg|jp^Fh`4Yp4;*LmPG zz+XDUzsEI^(*6d}34&$T8QzDiQO`#O`~>Kcf(4!OhVz4MaBgZ5{5y@8DffM7JP_CV%!U(+HMM?Dv3pVVc*s3ZhrZS0r7F z9PUki9_#-m5_LyDXTm>w3wik1;cLGTBOtD)(b!}`jL46vMwdAJd&&xVCzlAQ6v14@ zyEnpenIn=e??%F1%$Qu~ao>h%c`o0L9Wbpc%xSm;is7(%6Pv!v&vE`&K0&$0=$>Ev zhOTP(DyOpDK;%k%3Y#yL9o_n;v!jIo%Wlm%wtILNc0a-9Pi03{kVdQIbVcUe>`+*XSz^?et*?NLqevDMLwp5?U+0m3Ph$K0$a89)gbHZLDn~l zP)XSOW|8G&?JC$MdM}#QRj_OHMwH1_uv_#AdE7nvtPFcZUqYB`=Md-$?EWz9z=1SZ!4gxw#a~qN?saS;UiS6jcZ$}_v z4&aZ|QF7yI7IG+R2xjc9LD>d)x1t!K5=1CBjPneWU~IgXs^(T1!PZa=f=p4`%Qd>{8$BCM5y<9KR`YU484ZP(7PhA z9Xqut%ITJ={U*$VWjMKS$mumoj-fw%Sa*ZJ7C<|tWpLu* z_-V(y`29m*<2C+}a^Rbf&Jw!7VUBKw<-~aamuiA6m|a2bf5*vCRMwE;osTlaRBMNU zA#`_O=XKQ;_C_{yp&P)(O>*-GCBfZiUu3^w!uXS7!Ip^ln_%uF2P03uA&eM6xi$R4 zh7U#3ugxR_s-j^h^=5W{%=~5_22w!XG@QPZ+%tO@$G`)lvEYbMvgS_mvpM}KL}{e< zAt5qZ?j#RG#B~+LTK0_5DsYm2F ziqhns5+TfIrLY=`L_==?%0f`)eJye56~)Mrm*U;sW}@$866SBX>&^~N3X$25gkpqQ zelRKxPz&vc8$DJ;?!1HRKcWX=bL6KaxW{D|W&|$MQxH8@L9#W1yInyMS#UX?{{vyG zCdqutZdX(sK7&PkI6VgRLRtoQ+*gG8{dBBb0sB+qMJOBG?am3a(hKA)6qOf?O=gix zXID9QJF0Wl_x4`LGW(o-)b}p9{N5i?=@cc#k@gxI>dw&;qbJR{>bJ;_i2Clx>pl#F zNMH;lQXCl&q&!%7Lw98HJ-7n|_L0U@2$!o(q0K_fQ(H0oAZ8ozeVP-7lr2t&5Yx!R zoJ+uOX-+oc$Tg49??TMWxG5CR0EQr%GeT|+%%{9G9(%^k#(Kz9{}T)cYTtpGK+Lm# zC~e5Yz%1A$(g2+zieVIgWRCs`#Xw{$a@YmFL{DDS1nkniE9SjeeZ4@Ya01l(thXlUYqz)mWbYPP*=W2r9QOxE4 z2+?J>Hba3zG7EnK@|z&9Kye~>en3Y3PtmnQ1_<-7lfuY?X-NV$$1uwCcq)o-BjGcJ z*$*d&C=0Nv%K< zaAiO>36@z~E!u{{{I-b5x1D>w=mfBbCOGAU;TK^}z+D67XB?m@1XDd5J~`d^TbP5Z zp!F9bexIPo#}e?iy{@USLOZ5nHYY(<~Lyz&Pa{{wngGv*UxE%!*jBU5Wk zA<0O%&=iGyQyL)J1E?48+^iwJxT4u}DCgIKm?9-ZBu67CZia?C} zH;m|oz6cb4n_U})p7>1FpsiP z7&A3N(xbuf*O7%1k#RsfoQmGb&O zD9I}KP26|1z;Rph5hn{Vl#tc#GpN6Th#jdAK5f`=jk_{>{Y*gf6)bX*^*Y&Jh1N!S zF`P%b>b=l7+$_C1wKL|n#YvdWco*xh zSiTcc^+Ezwz~`~3en{y1xvgPlyS$Oy{f7lT609ufS#~Jm-8k!s z_cG2pJ*2I&PLIwTF(sC9t~zRqw7H^(2UQyM)TXd4!ox=(%u=;s7Y!TTzSU*%A!lMpW_F@1`aL@OBSBm@q062eZRN9Wwa zpuY9;&fvn8@r&Zsor6yZ<1Lu;Q+Mv?eKFB`7UC9|C)1t8?lhkD&B1taZZB-))Hq@@ z2BKHGxodf$gI~dj2a~Y6m@3fiwy^)*L$w<2$&WdKZZ%M741f6_1ZCZxST-R4xq$I( zhi5|(6H;VK!=@R=yGW@SaG4UXw**i%a!;ldQ9V0uCLX%imWIRw@-QMFL$M@9KBeFp z(h>4fbAM^%*qzuDlO(_Cp0XQv0JvxTA~<8B0nPfiQhw|P7jn`Pv4x8NzWeGcT>P7q zPWSR6UGWW-*co&Ox4$%M0g074i#I5cSizrzYe6QKfT))fn?yD6zWeYYu7SoN$;8su zfDy6#ORRD{bpO5%S~d#*8u6&`vN>j;VU0Em_TlmGUvO31i;>Yc-(mK|GMAVQ<}z%K z!zEO8etHBASF+a-uwor%p7(^Y9fa>RNeb|el49BXX3?{v=7d^>+2ZwoN9{7}9ksSs_u`yRCV)$}A0=2HgvHJ{I&A}KD?Al&zq7ukK z-XSw*r=rdkB1&#aOxmF6DVdeN@mtBjVe$_)1x4cYZ8;okFKNtSB@B z3pot8IqXN4u)G_3WSTIFZ80|v=ZVN2aDTz(n1{7IBZKt4$?5e-?W3#xrpR-~!nu5$tex914rESc)%EFne8syPp8HP&C!O zcOL$25H5G`0YeQh#dxCI11%iWg!cEWHCr_+8@ANI>;qQpGrFxZxh-a`BJTVg*Y(^s_L_(O2Y zM<1j8`?OytN~Sxj2li7r-N_rk|B&-ju@&~`$4Sn03_B$j@&3UM;4jHW@f(H5)2SPv zL%HcNL_ViP=L#yV`aqPPB0Tj_nI&VnFq2VpC9`q?dZL<2X5&mBKt+^{GAWzU+6_G_V5lj5`fNSu7A$)sG_{-J#@YA-2wwrJusMJ**IWGhPht1%l(E*3*Q z!n0~1Ruf8Ok~LFt4&qDl{wYi$lS}dbD&kj&mEC0}@hfo+S0H|6-akqFD!e}!6M*FU zY&(ed8*%*4$V2k0naffh-a|_zw~eWR{ZGY^{=JRTPXBY9d7H%3;ur`+!YZK=B4R%i z0|w#HjG!rP^AwoBJ#UP|t(7CJAR;`yI`qBw@70b~dj3k0 zPLABSYTBVDDZMIe7MB;|@=K656|4fCJWj)FV!u=oAn_ho(Mq^IrgTbGAbu14+ah}4zt`FN zJ<#zNFC5)M5kYtTTSaa3d(lexpZc-z+X3T9*?fLi%rk!o9_@W4#)Z33x#Jf8+SrMy z`UZwENAti+7<(3=Y8(RtlW-AlMqdzej4`*MPn>{>SPmy*HU1eM zdYr&9xE!4{Uc!>P4>lZcnFF_Rxt|lAm`qRVH`H*vZGJGEN3uA&b+XY2Qq�+%s^k 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%0Dro`C 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 Ef7CMU(EtDd literal 0 HcmV?d00001