1use crate::value::StrykeValue;
7use statrs::distribution::{
8 Beta, Cauchy, ChiSquared, ContinuousCDF, Discrete, Exp, FisherSnedecor, Gamma, LogNormal,
9 Normal, StudentsT, Uniform, Weibull,
10};
11
12fn arg_f64(args: &[StrykeValue], idx: usize) -> Option<f64> {
13 args.get(idx).map(|v| v.to_number())
14}
15
16macro_rules! http_status {
17 ($name:ident, $code:expr) => {
18 pub fn $name(_args: &[StrykeValue]) -> StrykeValue {
19 StrykeValue::integer($code)
20 }
21 };
22}
23
24http_status!(http_status_continue, 100);
25http_status!(http_status_switching_protocols, 101);
26http_status!(http_status_ok, 200);
27http_status!(http_status_created, 201);
28http_status!(http_status_accepted, 202);
29http_status!(http_status_no_content, 204);
30http_status!(http_status_partial_content, 206);
31http_status!(http_status_multiple_choices, 300);
32http_status!(http_status_moved_permanently, 301);
33http_status!(http_status_found, 302);
34http_status!(http_status_see_other, 303);
35http_status!(http_status_not_modified, 304);
36http_status!(http_status_temporary_redirect, 307);
37http_status!(http_status_permanent_redirect, 308);
38http_status!(http_status_bad_request, 400);
39http_status!(http_status_unauthorized, 401);
40http_status!(http_status_payment_required, 402);
41http_status!(http_status_forbidden, 403);
42http_status!(http_status_not_found, 404);
43http_status!(http_status_method_not_allowed, 405);
44http_status!(http_status_not_acceptable, 406);
45http_status!(http_status_conflict, 409);
46http_status!(http_status_gone, 410);
47http_status!(http_status_length_required, 411);
48http_status!(http_status_precondition_failed, 412);
49http_status!(http_status_payload_too_large, 413);
50http_status!(http_status_uri_too_long, 414);
51http_status!(http_status_unsupported_media_type, 415);
52http_status!(http_status_range_not_satisfiable, 416);
53http_status!(http_status_expectation_failed, 417);
54http_status!(http_status_im_a_teapot, 418);
55http_status!(http_status_unprocessable_entity, 422);
56http_status!(http_status_too_many_requests, 429);
57http_status!(http_status_internal_server_error, 500);
58http_status!(http_status_not_implemented, 501);
59http_status!(http_status_bad_gateway, 502);
60http_status!(http_status_service_unavailable, 503);
61http_status!(http_status_gateway_timeout, 504);
62http_status!(http_status_http_version_not_supported, 505);
63
64macro_rules! http_method {
65 ($name:ident, $verb:expr) => {
66 pub fn $name(_args: &[StrykeValue]) -> StrykeValue {
67 StrykeValue::string($verb.to_string())
68 }
69 };
70}
71
72http_method!(http_method_get, "GET");
73http_method!(http_method_post, "POST");
74http_method!(http_method_put, "PUT");
75http_method!(http_method_delete, "DELETE");
76http_method!(http_method_patch, "PATCH");
77http_method!(http_method_head, "HEAD");
78http_method!(http_method_options, "OPTIONS");
79http_method!(http_method_trace, "TRACE");
80http_method!(http_method_connect, "CONNECT");
81
82pub fn dbeta(args: &[StrykeValue]) -> StrykeValue {
87 let x = arg_f64(args, 0).unwrap_or(0.0);
88 let a = arg_f64(args, 1).unwrap_or(1.0);
89 let b = arg_f64(args, 2).unwrap_or(1.0);
90 match Beta::new(a, b) {
91 Ok(d) => {
92 use statrs::distribution::Continuous;
93 StrykeValue::float(d.pdf(x))
94 }
95 Err(_) => StrykeValue::UNDEF,
96 }
97}
98
99pub fn qbeta(args: &[StrykeValue]) -> StrykeValue {
100 let p = arg_f64(args, 0).unwrap_or(0.5);
101 let a = arg_f64(args, 1).unwrap_or(1.0);
102 let b = arg_f64(args, 2).unwrap_or(1.0);
103 match Beta::new(a, b) {
104 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
105 Err(_) => StrykeValue::UNDEF,
106 }
107}
108
109pub fn rbeta(args: &[StrykeValue]) -> StrykeValue {
110 use rand::Rng;
111 let mut rng = rand::thread_rng();
112 let p: f64 = rng.gen();
113 qbeta(&[
114 StrykeValue::float(p),
115 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
116 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
117 ])
118}
119
120pub fn dcauchy(args: &[StrykeValue]) -> StrykeValue {
121 let x = arg_f64(args, 0).unwrap_or(0.0);
122 let loc = arg_f64(args, 1).unwrap_or(0.0);
123 let scale = arg_f64(args, 2).unwrap_or(1.0);
124 match Cauchy::new(loc, scale) {
125 Ok(d) => {
126 use statrs::distribution::Continuous;
127 StrykeValue::float(d.pdf(x))
128 }
129 Err(_) => StrykeValue::UNDEF,
130 }
131}
132
133pub fn qcauchy(args: &[StrykeValue]) -> StrykeValue {
134 let p = arg_f64(args, 0).unwrap_or(0.5);
135 let loc = arg_f64(args, 1).unwrap_or(0.0);
136 let scale = arg_f64(args, 2).unwrap_or(1.0);
137 match Cauchy::new(loc, scale) {
138 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
139 Err(_) => StrykeValue::UNDEF,
140 }
141}
142
143pub fn rcauchy(args: &[StrykeValue]) -> StrykeValue {
144 use rand::Rng;
145 let p: f64 = rand::thread_rng().gen();
146 qcauchy(&[
147 StrykeValue::float(p),
148 args.first().cloned().unwrap_or(StrykeValue::float(0.0)),
149 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
150 ])
151}
152
153pub fn dexp(args: &[StrykeValue]) -> StrykeValue {
154 let x = arg_f64(args, 0).unwrap_or(0.0);
155 let rate = arg_f64(args, 1).unwrap_or(1.0);
156 match Exp::new(rate) {
157 Ok(d) => {
158 use statrs::distribution::Continuous;
159 StrykeValue::float(d.pdf(x))
160 }
161 Err(_) => StrykeValue::UNDEF,
162 }
163}
164
165pub fn qexp(args: &[StrykeValue]) -> StrykeValue {
166 let p = arg_f64(args, 0).unwrap_or(0.5);
167 let rate = arg_f64(args, 1).unwrap_or(1.0);
168 match Exp::new(rate) {
169 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
170 Err(_) => StrykeValue::UNDEF,
171 }
172}
173
174pub fn rexp(args: &[StrykeValue]) -> StrykeValue {
175 use rand::Rng;
176 let p: f64 = rand::thread_rng().gen();
177 qexp(&[
178 StrykeValue::float(p),
179 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
180 ])
181}
182
183pub fn dgamma(args: &[StrykeValue]) -> StrykeValue {
184 let x = arg_f64(args, 0).unwrap_or(0.0);
185 let shape = arg_f64(args, 1).unwrap_or(1.0);
186 let rate = arg_f64(args, 2).unwrap_or(1.0);
187 match Gamma::new(shape, rate) {
188 Ok(d) => {
189 use statrs::distribution::Continuous;
190 StrykeValue::float(d.pdf(x))
191 }
192 Err(_) => StrykeValue::UNDEF,
193 }
194}
195
196pub fn qgamma(args: &[StrykeValue]) -> StrykeValue {
197 let p = arg_f64(args, 0).unwrap_or(0.5);
198 let shape = arg_f64(args, 1).unwrap_or(1.0);
199 let rate = arg_f64(args, 2).unwrap_or(1.0);
200 match Gamma::new(shape, rate) {
201 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
202 Err(_) => StrykeValue::UNDEF,
203 }
204}
205
206pub fn rgamma(args: &[StrykeValue]) -> StrykeValue {
207 use rand::Rng;
208 let p: f64 = rand::thread_rng().gen();
209 qgamma(&[
210 StrykeValue::float(p),
211 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
212 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
213 ])
214}
215
216pub fn dlnorm(args: &[StrykeValue]) -> StrykeValue {
217 let x = arg_f64(args, 0).unwrap_or(1.0);
218 let mu = arg_f64(args, 1).unwrap_or(0.0);
219 let sigma = arg_f64(args, 2).unwrap_or(1.0);
220 match LogNormal::new(mu, sigma) {
221 Ok(d) => {
222 use statrs::distribution::Continuous;
223 StrykeValue::float(d.pdf(x))
224 }
225 Err(_) => StrykeValue::UNDEF,
226 }
227}
228
229pub fn qlnorm(args: &[StrykeValue]) -> StrykeValue {
230 let p = arg_f64(args, 0).unwrap_or(0.5);
231 let mu = arg_f64(args, 1).unwrap_or(0.0);
232 let sigma = arg_f64(args, 2).unwrap_or(1.0);
233 match LogNormal::new(mu, sigma) {
234 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
235 Err(_) => StrykeValue::UNDEF,
236 }
237}
238
239pub fn rlnorm(args: &[StrykeValue]) -> StrykeValue {
240 use rand::Rng;
241 let p: f64 = rand::thread_rng().gen();
242 qlnorm(&[
243 StrykeValue::float(p),
244 args.first().cloned().unwrap_or(StrykeValue::float(0.0)),
245 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
246 ])
247}
248
249pub fn dlogis(args: &[StrykeValue]) -> StrykeValue {
250 let x = arg_f64(args, 0).unwrap_or(0.0);
251 let loc = arg_f64(args, 1).unwrap_or(0.0);
252 let scale = arg_f64(args, 2).unwrap_or(1.0).max(1e-12);
253 let z = (x - loc) / scale;
254 let pdf = (-z).exp() / (scale * (1.0 + (-z).exp()).powi(2));
255 StrykeValue::float(pdf)
256}
257
258pub fn qlogis(args: &[StrykeValue]) -> StrykeValue {
259 let p = arg_f64(args, 0).unwrap_or(0.5).clamp(1e-12, 1.0 - 1e-12);
260 let loc = arg_f64(args, 1).unwrap_or(0.0);
261 let scale = arg_f64(args, 2).unwrap_or(1.0);
262 StrykeValue::float(loc + scale * (p / (1.0 - p)).ln())
263}
264
265pub fn rlogis(args: &[StrykeValue]) -> StrykeValue {
266 use rand::Rng;
267 let p: f64 = rand::thread_rng().gen();
268 qlogis(&[
269 StrykeValue::float(p),
270 args.first().cloned().unwrap_or(StrykeValue::float(0.0)),
271 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
272 ])
273}
274
275pub fn dpois(args: &[StrykeValue]) -> StrykeValue {
276 let k = arg_f64(args, 0).unwrap_or(0.0).max(0.0).round() as u64;
277 let lambda = arg_f64(args, 1).unwrap_or(1.0);
278 match statrs::distribution::Poisson::new(lambda) {
279 Ok(d) => StrykeValue::float(d.pmf(k)),
280 Err(_) => StrykeValue::UNDEF,
281 }
282}
283
284pub fn qpois(args: &[StrykeValue]) -> StrykeValue {
285 let p = arg_f64(args, 0).unwrap_or(0.5);
286 let lambda = arg_f64(args, 1).unwrap_or(1.0);
287 let Ok(d) = statrs::distribution::Poisson::new(lambda) else {
288 return StrykeValue::UNDEF;
289 };
290 use statrs::distribution::DiscreteCDF;
291 let mut k: u64 = 0;
292 while d.cdf(k) < p && k < 1_000_000 {
293 k += 1;
294 }
295 StrykeValue::integer(k as i64)
296}
297
298pub fn rpois(args: &[StrykeValue]) -> StrykeValue {
299 use rand::Rng;
300 let p: f64 = rand::thread_rng().gen();
301 qpois(&[
302 StrykeValue::float(p),
303 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
304 ])
305}
306
307pub fn dweibull(args: &[StrykeValue]) -> StrykeValue {
308 let x = arg_f64(args, 0).unwrap_or(0.0);
309 let shape = arg_f64(args, 1).unwrap_or(1.0);
310 let scale = arg_f64(args, 2).unwrap_or(1.0);
311 match Weibull::new(shape, scale) {
312 Ok(d) => {
313 use statrs::distribution::Continuous;
314 StrykeValue::float(d.pdf(x))
315 }
316 Err(_) => StrykeValue::UNDEF,
317 }
318}
319
320pub fn qweibull(args: &[StrykeValue]) -> StrykeValue {
321 let p = arg_f64(args, 0).unwrap_or(0.5);
322 let shape = arg_f64(args, 1).unwrap_or(1.0);
323 let scale = arg_f64(args, 2).unwrap_or(1.0);
324 match Weibull::new(shape, scale) {
325 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
326 Err(_) => StrykeValue::UNDEF,
327 }
328}
329
330pub fn rweibull(args: &[StrykeValue]) -> StrykeValue {
331 use rand::Rng;
332 let p: f64 = rand::thread_rng().gen();
333 qweibull(&[
334 StrykeValue::float(p),
335 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
336 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
337 ])
338}
339
340pub fn qnorm(args: &[StrykeValue]) -> StrykeValue {
341 let p = arg_f64(args, 0).unwrap_or(0.5);
342 let mu = arg_f64(args, 1).unwrap_or(0.0);
343 let sigma = arg_f64(args, 2).unwrap_or(1.0);
344 match Normal::new(mu, sigma) {
345 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
346 Err(_) => StrykeValue::UNDEF,
347 }
348}
349
350pub fn rnorm(args: &[StrykeValue]) -> StrykeValue {
351 use rand::Rng;
352 let p: f64 = rand::thread_rng().gen();
353 qnorm(&[
354 StrykeValue::float(p),
355 args.first().cloned().unwrap_or(StrykeValue::float(0.0)),
356 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
357 ])
358}
359
360pub fn qunif(args: &[StrykeValue]) -> StrykeValue {
361 let p = arg_f64(args, 0).unwrap_or(0.5);
362 let lo = arg_f64(args, 1).unwrap_or(0.0);
363 let hi = arg_f64(args, 2).unwrap_or(1.0);
364 match Uniform::new(lo, hi) {
365 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
366 Err(_) => StrykeValue::UNDEF,
367 }
368}
369
370pub fn runif(args: &[StrykeValue]) -> StrykeValue {
371 use rand::Rng;
372 let lo = arg_f64(args, 0).unwrap_or(0.0);
373 let hi = arg_f64(args, 1).unwrap_or(1.0);
374 StrykeValue::float(rand::thread_rng().gen_range(lo..hi))
375}
376
377pub fn qbinom(args: &[StrykeValue]) -> StrykeValue {
378 let p = arg_f64(args, 0).unwrap_or(0.5);
379 let n = arg_f64(args, 1).unwrap_or(10.0).max(0.0).round() as u64;
380 let pr = arg_f64(args, 2).unwrap_or(0.5);
381 let Ok(d) = statrs::distribution::Binomial::new(pr, n) else {
382 return StrykeValue::UNDEF;
383 };
384 use statrs::distribution::DiscreteCDF;
385 for k in 0..=n {
386 if d.cdf(k) >= p {
387 return StrykeValue::integer(k as i64);
388 }
389 }
390 StrykeValue::integer(n as i64)
391}
392
393pub fn rbinom(args: &[StrykeValue]) -> StrykeValue {
394 use rand::Rng;
395 let p: f64 = rand::thread_rng().gen();
396 qbinom(&[
397 StrykeValue::float(p),
398 args.first().cloned().unwrap_or(StrykeValue::float(10.0)),
399 args.get(1).cloned().unwrap_or(StrykeValue::float(0.5)),
400 ])
401}
402
403pub fn qgeom(args: &[StrykeValue]) -> StrykeValue {
404 let p = arg_f64(args, 0).unwrap_or(0.5);
405 let pr = arg_f64(args, 1).unwrap_or(0.5).clamp(1e-12, 1.0);
406 let k = ((1.0 - p).ln() / (1.0 - pr).max(1e-12).ln()).ceil() as i64 - 1;
408 StrykeValue::integer(k.max(0))
409}
410
411pub fn rgeom(args: &[StrykeValue]) -> StrykeValue {
412 use rand::Rng;
413 let p: f64 = rand::thread_rng().gen();
414 qgeom(&[
415 StrykeValue::float(p),
416 args.first().cloned().unwrap_or(StrykeValue::float(0.5)),
417 ])
418}
419
420pub fn qhyper(args: &[StrykeValue]) -> StrykeValue {
421 let p = arg_f64(args, 0).unwrap_or(0.5);
422 let pop = arg_f64(args, 1).unwrap_or(10.0).max(1.0).round() as u64;
423 let succ = arg_f64(args, 2).unwrap_or(5.0).max(0.0).round() as u64;
424 let draws = arg_f64(args, 3).unwrap_or(3.0).max(0.0).round() as u64;
425 let Ok(d) = statrs::distribution::Hypergeometric::new(pop, succ, draws) else {
426 return StrykeValue::UNDEF;
427 };
428 use statrs::distribution::DiscreteCDF;
429 let max = draws.min(succ);
430 for k in 0..=max {
431 if d.cdf(k) >= p {
432 return StrykeValue::integer(k as i64);
433 }
434 }
435 StrykeValue::integer(max as i64)
436}
437
438pub fn rhyper(args: &[StrykeValue]) -> StrykeValue {
439 use rand::Rng;
440 let p: f64 = rand::thread_rng().gen();
441 qhyper(&[
442 StrykeValue::float(p),
443 args.first().cloned().unwrap_or(StrykeValue::float(10.0)),
444 args.get(1).cloned().unwrap_or(StrykeValue::float(5.0)),
445 args.get(2).cloned().unwrap_or(StrykeValue::float(3.0)),
446 ])
447}
448
449pub fn qchisq(args: &[StrykeValue]) -> StrykeValue {
450 let p = arg_f64(args, 0).unwrap_or(0.5);
451 let df = arg_f64(args, 1).unwrap_or(1.0);
452 match ChiSquared::new(df) {
453 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
454 Err(_) => StrykeValue::UNDEF,
455 }
456}
457
458pub fn rchisq(args: &[StrykeValue]) -> StrykeValue {
459 use rand::Rng;
460 let p: f64 = rand::thread_rng().gen();
461 qchisq(&[
462 StrykeValue::float(p),
463 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
464 ])
465}
466
467pub fn qf(args: &[StrykeValue]) -> StrykeValue {
468 let p = arg_f64(args, 0).unwrap_or(0.5);
469 let df1 = arg_f64(args, 1).unwrap_or(1.0);
470 let df2 = arg_f64(args, 2).unwrap_or(1.0);
471 match FisherSnedecor::new(df1, df2) {
472 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
473 Err(_) => StrykeValue::UNDEF,
474 }
475}
476
477pub fn rf(args: &[StrykeValue]) -> StrykeValue {
478 use rand::Rng;
479 let p: f64 = rand::thread_rng().gen();
480 qf(&[
481 StrykeValue::float(p),
482 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
483 args.get(1).cloned().unwrap_or(StrykeValue::float(1.0)),
484 ])
485}
486
487pub fn qt(args: &[StrykeValue]) -> StrykeValue {
488 let p = arg_f64(args, 0).unwrap_or(0.5);
489 let df = arg_f64(args, 1).unwrap_or(1.0);
490 match StudentsT::new(0.0, 1.0, df) {
491 Ok(d) => StrykeValue::float(d.inverse_cdf(p)),
492 Err(_) => StrykeValue::UNDEF,
493 }
494}
495
496pub fn rt(args: &[StrykeValue]) -> StrykeValue {
497 use rand::Rng;
498 let p: f64 = rand::thread_rng().gen();
499 qt(&[
500 StrykeValue::float(p),
501 args.first().cloned().unwrap_or(StrykeValue::float(1.0)),
502 ])
503}