use std::f64::consts::PI;
use std::ops::Deref;
use std::time::SystemTime;
use crate::event::EventType;
use crate::utils::get_base_url;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RateShape {
#[cfg_attr(feature = "serde", serde(rename = "square"))]
Square,
#[cfg_attr(feature = "serde", serde(rename = "sine"))]
Sine,
}
#[derive(Clone, Debug)]
pub struct NexmarkConfig {
pub active_people: usize,
pub in_flight_auctions: usize,
pub out_of_order_group_size: usize,
pub avg_person_byte_size: usize,
pub avg_auction_byte_size: usize,
pub avg_bid_byte_size: usize,
pub hot_seller_ratio: usize,
pub hot_auction_ratio: usize,
pub hot_bidder_ratio: usize,
pub hot_channel_ratio: usize,
pub first_event_id: usize,
pub first_event_number: usize,
pub base_time: u64,
pub num_categories: usize,
pub auction_id_lead: usize,
pub person_proportion: usize,
pub auction_proportion: usize,
pub bid_proportion: usize,
pub first_auction_id: usize,
pub first_person_id: usize,
pub first_category_id: usize,
pub person_id_lead: usize,
pub sine_approx_steps: usize,
pub us_states: Vec<String>,
pub us_cities: Vec<String>,
pub hot_channels: Vec<String>,
pub hot_urls: Vec<String>,
pub first_names: Vec<String>,
pub last_names: Vec<String>,
pub num_event_generators: usize,
pub rate_shape: RateShape,
pub rate_period: usize,
pub first_rate: usize,
pub next_rate: usize,
pub us_per_unit: usize,
}
impl Default for NexmarkConfig {
fn default() -> Self {
Self {
active_people: 1000,
in_flight_auctions: 100,
out_of_order_group_size: 1,
avg_person_byte_size: 200,
avg_auction_byte_size: 500,
avg_bid_byte_size: 100,
hot_seller_ratio: 4,
hot_auction_ratio: 2,
hot_bidder_ratio: 4,
hot_channel_ratio: 2,
first_event_id: 0,
first_event_number: 0,
num_categories: 5,
auction_id_lead: 10,
person_proportion: 1,
auction_proportion: 3,
bid_proportion: 46,
first_auction_id: 1000,
first_person_id: 1000,
first_category_id: 10,
person_id_lead: 10,
sine_approx_steps: 10,
base_time: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as u64,
us_states: split_str("az,ca,id,or,wa,wy"),
us_cities: split_str("phoenix,los angeles,san francisco,boise,portland,bend,redmond,seattle,kent,cheyenne"),
hot_channels: split_str("Google,Facebook,Baidu,Apple"),
hot_urls: (0..4).map(get_base_url).collect(),
first_names: split_str("peter,paul,luke,john,saul,vicky,kate,julie,sarah,deiter,walter"),
last_names: split_str("shultz,abrams,spencer,white,bartels,walton,smith,jones,noris"),
num_event_generators: 1,
rate_shape: RateShape::Sine,
rate_period: 600,
first_rate: 10_000,
next_rate: 10_000,
us_per_unit: 1_000_000,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct GeneratorConfig {
pub config: NexmarkConfig,
pub proportion_denominator: usize,
pub step_length: usize,
pub events_per_epoch: usize,
pub epoch_period: f32,
pub inter_event_delays: Vec<f32>,
}
impl Default for GeneratorConfig {
fn default() -> Self {
NexmarkConfig::default().into()
}
}
impl Deref for GeneratorConfig {
type Target = NexmarkConfig;
fn deref(&self) -> &Self::Target {
&self.config
}
}
impl From<NexmarkConfig> for GeneratorConfig {
fn from(cfg: NexmarkConfig) -> Self {
let proportion_denominator =
cfg.person_proportion + cfg.auction_proportion + cfg.bid_proportion;
let generators = cfg.num_event_generators as f32;
let mut inter_event_delays = Vec::new();
let rate_to_period = |r| cfg.us_per_unit as f32 / r as f32;
if cfg.first_rate == cfg.next_rate {
inter_event_delays.push(rate_to_period(cfg.first_rate) * generators);
} else {
match cfg.rate_shape {
RateShape::Square => {
inter_event_delays.push(rate_to_period(cfg.first_rate) * generators);
inter_event_delays.push(rate_to_period(cfg.next_rate) * generators);
}
RateShape::Sine => {
let mid = (cfg.first_rate + cfg.next_rate) as f64 / 2.0;
let amp = (cfg.first_rate - cfg.next_rate) as f64 / 2.0;
for i in 0..cfg.sine_approx_steps {
let r = (2.0 * PI * i as f64) / cfg.sine_approx_steps as f64;
let rate = mid + amp * r.cos();
inter_event_delays.push(rate_to_period(rate.round() as usize) * generators);
}
}
}
}
let n = if cfg.rate_shape == RateShape::Square {
2
} else {
cfg.sine_approx_steps
};
let step_length = (cfg.rate_period + n - 1) / n;
let mut events_per_epoch = 0;
let mut epoch_period = 0.0;
if inter_event_delays.len() > 1 {
for inter_event_delay in &inter_event_delays {
let num_events_for_this_cycle =
(step_length * 1_000_000) as f32 / inter_event_delay;
events_per_epoch += num_events_for_this_cycle.round() as usize;
epoch_period += (num_events_for_this_cycle * inter_event_delay) / 1000.0;
}
}
Self {
config: cfg,
proportion_denominator,
step_length,
events_per_epoch,
epoch_period,
inter_event_delays,
}
}
}
impl GeneratorConfig {
pub fn event_timestamp(&self, event_number: usize) -> u64 {
if self.inter_event_delays.len() == 1 {
return self.base_time
+ ((event_number as f32 * self.inter_event_delays[0]) / 1000.0).round() as u64;
}
let epoch = event_number / self.events_per_epoch;
let mut event_i = event_number % self.events_per_epoch;
let mut offset_in_epoch = 0.0;
for inter_event_delay in &self.inter_event_delays {
let num_events_for_this_cycle =
(self.step_length * 1_000_000) as f32 / inter_event_delay;
if self.out_of_order_group_size < num_events_for_this_cycle.round() as usize {
let offset_in_cycle = event_i as f32 * inter_event_delay;
return self.base_time
+ (epoch as f32 * self.epoch_period
+ offset_in_epoch
+ offset_in_cycle / 1000.0)
.round() as u64;
}
event_i -= num_events_for_this_cycle.round() as usize;
offset_in_epoch += (num_events_for_this_cycle * inter_event_delay) / 1000.0;
}
0
}
pub fn next_adjusted_event(&self, events_so_far: usize) -> usize {
let n = self.out_of_order_group_size;
let event_number = self.first_event_number + events_so_far;
(event_number / n) * n + (event_number * 953) % n
}
pub fn event_type(&self, event_number: usize) -> EventType {
let rem = event_number % self.proportion_denominator;
if rem < self.person_proportion {
EventType::Person
} else if rem < self.person_proportion + self.auction_proportion {
EventType::Auction
} else {
EventType::Bid
}
}
}
fn split_str(string: &str) -> Vec<String> {
string.split(',').map(String::from).collect()
}