use rand::Rng;
use rand::distributions::Distribution;
use statrs::distribution::{Poisson, Binomial};
use crate::person::Person;
use crate::people::People;
use crate::floor::Floor;
use crate::floors::Floors;
use crate::elevator::Elevator;
use crate::elevators::Elevators;
const P_OUT: f64 = 0.05_f64;
const P_TIP: f64 = 0.5_f64;
const DST_TIP_TRIALS: u64 = 100_u64;
const DST_TIP_SUCCESS: f64 = 0.5_f64;
#[derive(Clone)]
pub struct Building {
pub elevators: Vec<Elevator>,
pub floors: Vec<Floor>,
pub avg_energy: f64,
pub avg_wait_time: f64,
pub tot_tips: f64,
wait_time_denom: usize,
p_in: f64,
dst_in: Poisson,
dst_tip: Binomial
}
impl Building {
pub fn from(num_floors: usize, num_elevators: usize, p_in: f64,
floor_capacity: usize, elevator_capacity: usize,
energy_up: f64, energy_down: f64, energy_coef: f64) -> Building {
let floors: Vec<Floor> = {
let mut tmp_floors: Vec<Floor> = Vec::new();
for _ in 0_usize..num_floors {
let tmp_floor: Floor = Floor::new(floor_capacity);
tmp_floors.push(tmp_floor);
}
tmp_floors
};
let elevators: Vec<Elevator> = {
let mut tmp_elevators: Vec<Elevator> = Vec::new();
for _ in 0_usize..num_elevators {
let tmp_elevator: Elevator = Elevator::from(
elevator_capacity, energy_up, energy_down, energy_coef
);
tmp_elevators.push(tmp_elevator);
}
tmp_elevators
};
let dst_in = Poisson::new(p_in).unwrap();
Building {
floors: floors,
elevators: elevators,
avg_energy: 0_f64,
avg_wait_time: 0_f64,
wait_time_denom: 0_usize,
tot_tips: 0_f64,
p_in: p_in,
dst_in: dst_in,
dst_tip: Binomial::new(DST_TIP_SUCCESS, DST_TIP_TRIALS).unwrap()
}
}
pub fn update_dest_probabilities(&mut self) {
let num_floors: usize = self.floors.len() as usize;
let dest_floors: Vec<usize> = self.elevators.get_dest_floors();
for (i, floor) in self.floors.iter_mut().enumerate() {
let dest_probability: f64 = if i == 0 {
let people_waiting: f64 = {
let waiting: f64 = if floor.are_people_waiting() { 1_f64 } else { 0_f64 };
let going: f64 = if dest_floors.contains(&i) { 1_f64 } else { 0_f64 };
if waiting > going { waiting } else { going }
};
let p_in: f64 = self.p_in * ((num_floors as f64 - 1_f64)/(num_floors as f64));
if people_waiting > p_in { people_waiting } else { p_in }
} else {
let people_waiting: f64 = {
let waiting: f64 = if floor.are_people_waiting() { 1_f64 } else { 0_f64 };
let going: f64 = if dest_floors.contains(&i) { 1_f64 } else { 0_f64 };
if waiting > going { waiting } else { going }
};
let p_out: f64 = floor.get_p_out();
if people_waiting > p_out { people_waiting } else { p_out }
};
floor.dest_prob = dest_probability;
}
}
pub fn gen_people_arriving(&mut self, mut rng: &mut impl Rng) {
let mut arrivals: Vec<Person> = Vec::new();
for _ in 0_i32..self.dst_in.sample(&mut rng) as i32 {
let new_person: Person = Person::from(P_OUT, P_TIP, self.floors.len(), &mut rng);
arrivals.push(new_person);
}
self.floors[0].extend(arrivals);
}
pub fn gen_tip_value(&self, num_tips: usize, rng: &mut impl Rng) -> f64 {
let mut tip_value: f64 = 0.0_f64;
for _ in 0..num_tips {
tip_value += self.dst_tip.sample(rng) / 100_f64;
}
tip_value
}
pub fn exchange_people_on_elevator(&mut self) {
for elevator in self.elevators.iter_mut() {
if !elevator.stopped {
continue;
}
let floor_index: usize = elevator.floor_on;
let floor_free_capacity: usize = self.floors[floor_index].get_free_capacity();
let floor_wait_capacity: usize = self.floors[floor_index].get_num_people_waiting();
let floor_exchange_capacity: usize = floor_free_capacity + floor_wait_capacity;
let elevator_free_capacity: usize = elevator.get_free_capacity();
let elevator_leav_capacity: usize = elevator.get_num_people_going_to_floor(floor_index);
let elevator_exchange_capacity: usize = elevator_free_capacity + elevator_leav_capacity;
let exchange_capacity: usize = if floor_exchange_capacity > elevator_exchange_capacity {
elevator_exchange_capacity
} else {
floor_exchange_capacity
};
let people_leaving_floor: Vec<Person> = self.floors[floor_index].flush_people_entering_elevator(exchange_capacity);
let mut people_leaving_elevator: Vec<Person> = elevator.flush_people_leaving_elevator(exchange_capacity);
let wait_times: usize = people_leaving_elevator.get_aggregate_wait_time();
let num_people: usize = people_leaving_elevator.get_num_people();
self.avg_wait_time = {
let tmp_num: f64 = wait_times as f64 + (self.avg_wait_time * self.wait_time_denom as f64);
let tmp_denom: f64 = num_people as f64 + self.wait_time_denom as f64;
if tmp_denom == 0_f64 {
0_f64 } else {
tmp_num / tmp_denom
}
};
self.wait_time_denom += num_people;
people_leaving_elevator.reset_wait_times();
elevator.extend(people_leaving_floor);
self.floors[floor_index].extend(people_leaving_elevator);
}
}
pub fn flush_and_update_tips(&mut self, rng: &mut impl Rng) {
let people_leaving_floor: Vec<Person> = self.floors.flush_first_floor();
let num_tips: usize = people_leaving_floor.gen_num_tips(rng);
let tip_value: f64 = self.gen_tip_value(num_tips, rng);
self.tot_tips += tip_value;
}
pub fn collect_tips(&mut self) -> f64 {
let tips: f64 = self.tot_tips;
self.tot_tips = 0.0_f64;
tips
}
pub fn update_average_energy(&mut self, time_step: i32, energy_spent: f64) {
self.avg_energy = {
let tmp_num: f64 = (self.avg_energy * time_step as f64) + energy_spent;
let tmp_denom: f64 = (time_step + 1_i32) as f64;
tmp_num / tmp_denom
};
}
pub fn append_elevator(&mut self, capacity: usize, energy_up: f64, energy_down: f64, energy_coef: f64) {
self.elevators.push(Elevator::from(capacity, energy_up, energy_down, energy_coef));
}
}
impl std::fmt::Display for Building {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut building_status: String = String::new();
let elevator_space: String = String::from(" \t ");
for (i, floor) in self.floors.iter().enumerate() {
let mut floor_roof: String = String::from("----\t||---\t||");
let mut floor_body: String = format!("{:.2}\t||{}/{}\t||", floor.dest_prob, floor.get_num_people(), floor.capacity);
let mut last_elevator_on_floor: usize = 0_usize;
for (j, elevator) in self.elevators.iter().enumerate() {
if elevator.floor_on != i as usize {
continue;
}
let elevator_roof: String = format!("{}{}", str::repeat(&elevator_space, j - last_elevator_on_floor as usize), String::from("|-\t|"));
let elevator_body: String = format!("{}|{}/{}\t|", str::repeat(&elevator_space, j - last_elevator_on_floor as usize), elevator.get_num_people(), elevator.capacity);
floor_roof.push_str(&elevator_roof);
floor_body.push_str(&elevator_body);
last_elevator_on_floor = j + 1_usize;
}
building_status = [floor_roof, floor_body, building_status].join("\n");
}
let wait_time_str: String = format!("Average wait time:\t{:.2}", self.avg_wait_time);
let energy_str: String = format!("Average energy spent:\t{:.2}", self.avg_energy);
let tip_str: String = format!("Total tips collected:\t${:.2}", self.tot_tips);
building_status = [building_status, wait_time_str, energy_str, tip_str].join("\n");
f.write_str(&building_status)
}
}
impl Floors for Building {
fn are_people_waiting_on_floor(&self, floor_index: usize) -> bool {
self.floors.are_people_waiting_on_floor(floor_index)
}
fn get_nearest_wait_floor(&self, floor_on: usize) -> (usize, usize) {
self.floors.get_nearest_wait_floor(floor_on)
}
fn get_dest_probabilities(&self) -> Vec<f64> {
self.floors.get_dest_probabilities()
}
fn gen_people_leaving(&mut self, rng: &mut impl Rng) {
self.floors.gen_people_leaving(rng)
}
fn flush_first_floor(&mut self) -> Vec<Person> {
self.floors.flush_first_floor()
}
fn increment_wait_times(&mut self) {
self.elevators.increment_wait_times();
self.floors.increment_wait_times();
}
fn append_floor(&mut self, capacity: usize) {
self.floors.append_floor(capacity);
}
fn update_capacities(&mut self, capacity: usize) {
self.floors.update_capacities(capacity);
}
}