pyoe2_craftpath/calc/statistics/analyzers/
common_analyzer_utils.rs1use anyhow::Result;
2use rayon::{
3 iter::{IntoParallelIterator, ParallelIterator},
4 slice::ParallelSliceMut,
5};
6use tracing::instrument;
7
8use crate::{
9 api::{
10 calculator::{Calculator, GroupRoute},
11 currency::CraftCurrencyList,
12 provider::{item_info::ItemInfoProvider, market_prices::MarketPriceProvider},
13 types::THashMap,
14 },
15 calc::statistics::{
16 analyzers::collectors::{
17 currency_groups::group_chance_memory_efficient_collector::CurrencyGroupChanceMemoryEfficientCollector,
18 utils::{
19 statistic_analyzer_currency_grouped_collector::calculate_currency_groups,
20 statistic_analyzer_currency_grouped_memory_efficient_collector::calculate_currency_groups_memory_efficient,
21 },
22 },
23 helpers::{
24 RouteChance, RouteCustomWeight, StatisticAnalyzerCurrencyGroupCollectorTrait,
25 SubpathAmount,
26 },
27 },
28 utils::float_compare,
29};
30
31#[instrument(skip_all)]
32pub fn get_grouped_statistic_memory_efficient<T: StatisticAnalyzerCurrencyGroupCollectorTrait>(
33 lower_is_better: bool,
34 calculator: &Calculator,
35 item_provider: &ItemInfoProvider,
36 market_provider: &MarketPriceProvider,
37 max_ram_in_bytes: u64,
38) -> Result<Vec<GroupRoute>> {
39 let res: THashMap<
40 Vec<&CraftCurrencyList>,
41 (
42 RouteChance,
43 RouteCustomWeight,
44 SubpathAmount,
45 Vec<RouteChance>,
46 ),
47 > = calculate_currency_groups_memory_efficient::<CurrencyGroupChanceMemoryEfficientCollector>(
48 calculator,
49 item_provider,
50 market_provider,
51 max_ram_in_bytes,
52 )?;
53
54 let mut data: Vec<GroupRoute> = res
55 .into_par_iter()
56 .map(|(k, v)| {
57 let key_owned: Vec<CraftCurrencyList> = k.into_iter().cloned().collect();
58 let chance: RouteChance = v.0;
59 let weight: RouteCustomWeight = v.1;
60 let subpaths_amount: SubpathAmount = v.2;
61 let subpaths: Vec<RouteChance> = v.3;
62
63 GroupRoute {
64 group: key_owned,
65 weight: weight,
66 amount_subpaths: subpaths_amount,
67 unique_route_weights: vec![subpaths],
68 chance,
69 }
70 })
71 .collect();
72
73 if lower_is_better {
74 data.par_sort_unstable_by(|a, b| {
75 float_compare::cmp_f64(*a.weight.get_raw_value(), *b.weight.get_raw_value()).then(
76 float_compare::cmp_f64(*a.chance.get_raw_value(), *b.chance.get_raw_value()),
77 )
78 });
79 } else {
80 data.par_sort_unstable_by(|a, b| {
81 float_compare::cmp_f64(*b.weight.get_raw_value(), *a.weight.get_raw_value()).then(
82 float_compare::cmp_f64(*a.chance.get_raw_value(), *b.chance.get_raw_value()),
83 )
84 });
85 }
86
87 Ok(data)
88}
89
90#[instrument(skip_all)]
91pub fn get_grouped_statistic<T: StatisticAnalyzerCurrencyGroupCollectorTrait>(
92 lower_is_better: bool,
93 calculator: &Calculator,
94 item_provider: &ItemInfoProvider,
95 market_provider: &MarketPriceProvider,
96 max_ram_in_bytes: u64,
97) -> Result<Vec<GroupRoute>> {
98 let res: THashMap<Vec<&CraftCurrencyList>, Vec<Vec<RouteChance>>> =
99 calculate_currency_groups::<T>(
100 calculator,
101 item_provider,
102 market_provider,
103 max_ram_in_bytes,
104 )?;
105
106 let mut data: Vec<GroupRoute> = res
107 .into_par_iter()
108 .map(|(k, v)| {
109 let chance = T::calculate_group_chance(&v);
110 let weight = T::calculate_group_weight(&k, &v);
111
112 GroupRoute {
113 group: k.into_iter().cloned().collect(),
114 weight,
115 amount_subpaths: SubpathAmount::from(v.len() as u32),
116 unique_route_weights: v,
117 chance,
118 }
119 })
120 .collect();
121
122 if lower_is_better {
123 data.par_sort_unstable_by(|a, b| {
124 float_compare::cmp_f64(*a.weight.get_raw_value(), *b.weight.get_raw_value()).then(
125 float_compare::cmp_f64(*a.chance.get_raw_value(), *b.chance.get_raw_value()),
126 )
127 });
128 } else {
129 data.par_sort_unstable_by(|a, b| {
130 float_compare::cmp_f64(*b.weight.get_raw_value(), *a.weight.get_raw_value()).then(
131 float_compare::cmp_f64(*a.chance.get_raw_value(), *b.chance.get_raw_value()),
132 )
133 });
134 }
135
136 Ok(data)
137}
138
139#[macro_export]
140macro_rules! impl_common_group_analyzer_methods {
141 () => {
142 fn calculate_chance_for_group_step_index(
143 &self,
144 group_routes: &Vec<Vec<crate::calc::statistics::helpers::RouteChance>>,
145 subpath_amount: crate::calc::statistics::helpers::SubpathAmount,
146 index: usize,
147 ) -> crate::calc::statistics::helpers::RouteChance {
148 use crate::calc::statistics::helpers::RouteChance;
149
150 let total: f64 = group_routes
152 .iter()
153 .filter_map(|gr| gr.get(index))
154 .map(|rc| rc.get_raw_value())
155 .sum();
156
157 let denom = (*subpath_amount.get_raw_value()) as f64;
159
160 let value = if denom > 0.0 { total / denom } else { 0.0 };
161
162 RouteChance::new(value.clamp(0.0, 1.0))
163 }
164
165 fn calculate_cost_per_craft(
166 &self,
167 currency: &Vec<crate::api::currency::CraftCurrencyList>,
168 item_info: &crate::api::provider::item_info::ItemInfoProvider,
169 market_provider: &crate::api::provider::market_prices::MarketPriceProvider,
170 ) -> crate::api::provider::market_prices::PriceInDivines {
171 let pc = crate::api::provider::market_prices::PriceInDivines::new(
172 currency.iter().fold(0.0_f64, |a, b| {
173 a + b.list.iter().fold(0.0_f64, |a, b| {
174 a + market_provider
175 .try_lookup_currency_in_divines_default_if_fail(b, item_info)
176 .get_divine_value()
177 })
178 }),
179 );
180
181 pc
182 }
183
184 fn calculate_tries_needed_for_60_percent(
185 &self,
186 group_route: &crate::api::calculator::GroupRoute,
187 ) -> u64 {
188 let tries_for_60 = ((((1.0_f64 - 0.6_f64).ln()
189 / (1.0_f64 - group_route.chance.get_raw_value()).ln())
190 .ceil()) as u64)
191 .max(1);
192
193 tries_for_60
194 }
195 };
196}
197
198#[macro_export]
199macro_rules! impl_common_unique_path_analyzer_methods {
200 () => {
201 fn calculate_tries_needed_for_60_percent(
202 &self,
203 route: &crate::api::calculator::ItemRoute,
204 ) -> u64 {
205 let tries_for_60_percent = ((((1.0_f64 - 0.6_f64).ln()
206 / (1.0_f64 - route.chance.get_raw_value()).ln())
207 .ceil()) as u64)
208 .max(1);
209
210 tries_for_60_percent
211 }
212
213 fn calculate_cost_per_craft(
214 &self,
215 currency: &Vec<crate::api::currency::CraftCurrencyList>,
216 item_info: &crate::api::provider::item_info::ItemInfoProvider,
217 market_provider: &crate::api::provider::market_prices::MarketPriceProvider,
218 ) -> crate::api::provider::market_prices::PriceInDivines {
219 crate::api::provider::market_prices::PriceInDivines::new(currency.iter().fold(
220 0.0_f64,
221 |a, b| {
222 a + b.list.iter().fold(0.0_f64, |a, b| {
223 a + market_provider
224 .try_lookup_currency_in_divines_default_if_fail(b, item_info)
225 .get_divine_value()
226 })
227 },
228 ))
229 }
230 };
231}