1use crate::colors;
2use crate::common_options::{get_optimized_reader, setup_automatic_optimization_config};
3use clap::ArgMatches;
4use lawkit_core::{
5 common::{
6 filtering::{apply_number_filter, NumberFilter},
7 input::{parse_input_auto, parse_text_input},
8 memory::{streaming_poisson_analysis, MemoryConfig},
9 streaming_io::OptimizedFileReader,
10 },
11 error::{BenfError, Result},
12 laws::poisson::{
13 analyze_poisson_distribution, analyze_rare_events, predict_event_probabilities,
14 test_poisson_fit, EventProbabilityResult, PoissonResult, PoissonTest, PoissonTestResult,
15 RareEventAnalysis,
16 },
17};
18
19pub fn run(matches: &ArgMatches) -> Result<()> {
20 if matches.get_flag("predict") {
22 return run_prediction_mode(matches);
23 }
24
25 if matches.get_flag("rare-events") {
26 return run_rare_events_mode(matches);
27 }
28
29 if let Some(test_type) = matches.get_one::<String>("test") {
31 if test_type != "all" {
32 return run_poisson_test_mode(matches, test_type);
34 }
35 }
36
37 let (_parallel_config, _memory_config) = setup_automatic_optimization_config();
39
40 if matches.get_flag("verbose") {
42 eprintln!(
43 "Debug: input argument = {:?}",
44 matches.get_one::<String>("input")
45 );
46 }
47
48 let numbers = if let Some(input) = matches.get_one::<String>("input") {
50 match parse_input_auto(input) {
52 Ok(numbers) => {
53 if numbers.is_empty() {
54 eprintln!("Error: No valid numbers found in input");
55 std::process::exit(1);
56 }
57 numbers
58 }
59 Err(e) => {
60 eprintln!("Error processing input '{input}': {e}");
61 std::process::exit(1);
62 }
63 }
64 } else {
65 if matches.get_flag("verbose") {
67 eprintln!("Debug: Reading from stdin, using automatic optimization");
68 }
69
70 let mut reader = OptimizedFileReader::from_stdin();
71
72 if matches.get_flag("verbose") {
73 eprintln!(
74 "Debug: Using automatic optimization (streaming + incremental + memory efficiency)"
75 );
76 }
77
78 let numbers = match reader
79 .read_lines_streaming(|line: String| parse_text_input(&line).map(Some).or(Ok(None)))
80 {
81 Ok(nested_numbers) => {
82 let flattened: Vec<f64> = nested_numbers.into_iter().flatten().collect();
83 if matches.get_flag("verbose") {
84 eprintln!("Debug: Collected {} numbers from stream", flattened.len());
85 }
86 flattened
87 }
88 Err(e) => {
89 eprintln!("Analysis error: {e}");
90 std::process::exit(1);
91 }
92 };
93
94 if numbers.is_empty() {
95 eprintln!("Error: No valid numbers found in input");
96 std::process::exit(1);
97 }
98
99 let event_counts: Vec<usize> = numbers
101 .iter()
102 .filter_map(|&x| if x >= 0.0 { Some(x as usize) } else { None })
103 .collect();
104
105 if event_counts.len() > 10000 {
107 let memory_config = MemoryConfig::default();
108 let chunk_result =
109 match streaming_poisson_analysis(event_counts.into_iter(), &memory_config) {
110 Ok(result) => {
111 if matches.get_flag("verbose") {
112 eprintln!(
113 "Debug: Streaming analysis successful - {} items processed",
114 result.total_items
115 );
116 }
117 result
118 }
119 Err(e) => {
120 eprintln!("Streaming analysis error: {e}");
121 std::process::exit(1);
122 }
123 };
124
125 if matches.get_flag("verbose") {
126 eprintln!(
127 "Debug: Processed {} numbers in {} chunks",
128 chunk_result.total_items, chunk_result.chunks_processed
129 );
130 eprintln!("Debug: Memory used: {:.2} MB", chunk_result.memory_used_mb);
131 }
132
133 chunk_result
135 .result
136 .event_counts()
137 .iter()
138 .map(|&x| x as f64)
139 .collect()
140 } else {
141 if matches.get_flag("verbose") {
143 eprintln!("Debug: Memory used: 0.00 MB");
144 }
145 event_counts.iter().map(|&x| x as f64).collect()
146 }
147 };
148
149 let dataset_name = matches
150 .get_one::<String>("input")
151 .map(|s| s.to_string())
152 .unwrap_or_else(|| "stdin".to_string());
153
154 let result = match analyze_numbers_with_options(matches, dataset_name, &numbers) {
155 Ok(result) => result,
156 Err(e) => {
157 eprintln!("Analysis error: {e}");
158 std::process::exit(1);
159 }
160 };
161
162 output_results(matches, &result);
163 std::process::exit(result.risk_level.exit_code())
164}
165
166fn get_numbers_from_input(matches: &ArgMatches) -> Result<Vec<f64>> {
167 let (_parallel_config, _memory_config) = setup_automatic_optimization_config();
168
169 let buffer = if let Some(input) = matches.get_one::<String>("input") {
170 if input == "-" {
171 get_optimized_reader(None)
172 } else {
173 get_optimized_reader(Some(input))
174 }
175 } else {
176 get_optimized_reader(None)
177 };
178
179 let data = buffer.map_err(|e| BenfError::ParseError(e.to_string()))?;
180 parse_text_input(&data)
181}
182
183fn run_poisson_test_mode(matches: &ArgMatches, test_type: &str) -> Result<()> {
184 let numbers = get_numbers_from_input(matches)?;
185
186 let test = match test_type {
187 "chi-square" => PoissonTest::ChiSquare,
188 "ks" => PoissonTest::KolmogorovSmirnov,
189 "variance" => PoissonTest::VarianceTest,
190 "all" => PoissonTest::All,
191 _ => {
192 eprintln!(
193 "Error: Unknown test type '{test_type}'. Available: chi-square, ks, variance, all"
194 );
195 std::process::exit(2);
196 }
197 };
198
199 let test_result = test_poisson_fit(&numbers, test)?;
200 output_poisson_test_result(matches, &test_result);
201
202 let exit_code = if test_result.is_poisson { 0 } else { 1 };
203 std::process::exit(exit_code);
204}
205
206fn run_prediction_mode(matches: &ArgMatches) -> Result<()> {
207 let numbers = get_numbers_from_input(matches)?;
208 let result = analyze_poisson_distribution(&numbers, "prediction")?;
209
210 let max_events = matches
211 .get_one::<String>("max-events")
212 .and_then(|s| s.parse::<u32>().ok())
213 .unwrap_or(10);
214
215 let prediction_result = predict_event_probabilities(result.lambda, max_events);
216 output_prediction_result(matches, &prediction_result);
217
218 std::process::exit(0);
219}
220
221fn run_rare_events_mode(matches: &ArgMatches) -> Result<()> {
222 let numbers = get_numbers_from_input(matches)?;
223 let result = analyze_poisson_distribution(&numbers, "rare_events")?;
224
225 let rare_analysis = analyze_rare_events(&numbers, result.lambda);
226 output_rare_events_result(matches, &rare_analysis);
227
228 let exit_code = if rare_analysis.clustering_detected {
229 2
230 } else {
231 0
232 };
233 std::process::exit(exit_code);
234}
235
236fn output_results(matches: &clap::ArgMatches, result: &PoissonResult) {
237 let format = matches.get_one::<String>("format").unwrap();
238 let quiet = matches.get_flag("quiet");
239 let verbose = matches.get_flag("verbose");
240 let no_color = matches.get_flag("no-color");
241
242 match format.as_str() {
243 "text" => print_text_output(result, quiet, verbose, no_color),
244 "json" => print_json_output(result),
245 "csv" => print_csv_output(result),
246 "yaml" => print_yaml_output(result),
247 "toml" => print_toml_output(result),
248 "xml" => print_xml_output(result),
249 _ => {
250 eprintln!("Error: Unsupported output format: {format}");
251 std::process::exit(2);
252 }
253 }
254}
255
256fn output_poisson_test_result(matches: &clap::ArgMatches, result: &PoissonTestResult) {
257 let format_str = matches
258 .get_one::<String>("format")
259 .map(|s| s.as_str())
260 .unwrap_or("text");
261
262 match format_str {
263 "text" => {
264 println!("Poisson Test Result: {}", result.test_name);
265 println!("Test statistic: {:.6}", result.statistic);
266 println!("P-value: {:.6}", result.p_value);
267 println!("λ: {:.3}", result.parameter_lambda);
268 println!(
269 "Is Poisson: {}",
270 if result.is_poisson { "Yes" } else { "No" }
271 );
272 }
273 "json" => {
274 use serde_json::json;
275 let output = json!({
276 "test_name": result.test_name,
277 "statistic": result.statistic,
278 "p_value": result.p_value,
279 "critical_value": result.critical_value,
280 "lambda": result.parameter_lambda,
281 "is_poisson": result.is_poisson
282 });
283 println!("{}", serde_json::to_string_pretty(&output).unwrap());
284 }
285 _ => println!("Unsupported format for Poisson test"),
286 }
287}
288
289fn output_prediction_result(matches: &clap::ArgMatches, result: &EventProbabilityResult) {
290 let format_str = matches
291 .get_one::<String>("format")
292 .map(|s| s.as_str())
293 .unwrap_or("text");
294
295 match format_str {
296 "text" => {
297 println!("Event Probability Prediction (λ = {:.3})", result.lambda);
298 println!("Most likely count: {}", result.most_likely_count);
299 println!();
300
301 for prob in &result.probabilities {
302 println!(
303 "P(X = {}) = {:.6} (cumulative: {:.6})",
304 prob.event_count, prob.probability, prob.cumulative_probability
305 );
306 }
307
308 if result.tail_probability > 0.001 {
309 println!(
310 "P(X > {}) = {:.6}",
311 result.max_events, result.tail_probability
312 );
313 }
314 }
315 "json" => {
316 use serde_json::json;
317 let output = json!({
318 "lambda": result.lambda,
319 "max_events": result.max_events,
320 "most_likely_count": result.most_likely_count,
321 "expected_value": result.expected_value,
322 "variance": result.variance,
323 "tail_probability": result.tail_probability,
324 "probabilities": result.probabilities.iter().map(|p| json!({
325 "event_count": p.event_count,
326 "probability": p.probability,
327 "cumulative_probability": p.cumulative_probability
328 })).collect::<Vec<_>>()
329 });
330 println!("{}", serde_json::to_string_pretty(&output).unwrap());
331 }
332 _ => println!("Unsupported format for prediction"),
333 }
334}
335
336fn output_rare_events_result(matches: &clap::ArgMatches, result: &RareEventAnalysis) {
337 let format_str = matches
338 .get_one::<String>("format")
339 .map(|s| s.as_str())
340 .unwrap_or("text");
341
342 match format_str {
343 "text" => {
344 println!("Rare Events Analysis (λ = {:.3})", result.lambda);
345 println!("Total observations: {}", result.total_observations);
346 println!();
347
348 println!("Rare Event Thresholds:");
349 println!(
350 " 95%: {} ({} events)",
351 result.threshold_95, result.rare_events_95
352 );
353 println!(
354 " 99%: {} ({} events)",
355 result.threshold_99, result.rare_events_99
356 );
357 println!(
358 " 99.9%: {} ({} events)",
359 result.threshold_999, result.rare_events_999
360 );
361
362 if !result.extreme_events.is_empty() {
363 println!();
364 println!("Extreme Events:");
365 for event in &result.extreme_events {
366 println!(
367 " Index: {} {} (P = {:.6})",
368 event.index, event.event_count, event.probability
369 );
370 }
371 }
372
373 if result.clustering_detected {
374 println!();
375 println!("ALERT: Event clustering detected");
376 }
377 }
378 "json" => {
379 use serde_json::json;
380 let output = json!({
381 "lambda": result.lambda,
382 "total_observations": result.total_observations,
383 "thresholds": {
384 "95_percent": result.threshold_95,
385 "99_percent": result.threshold_99,
386 "99_9_percent": result.threshold_999
387 },
388 "rare_event_counts": {
389 "95_percent": result.rare_events_95,
390 "99_percent": result.rare_events_99,
391 "99_9_percent": result.rare_events_999
392 },
393 "extreme_events": result.extreme_events.iter().map(|e| json!({
394 "index": e.index,
395 "event_count": e.event_count,
396 "probability": e.probability,
397 "rarity_level": format!("{:?}", e.rarity_level)
398 })).collect::<Vec<_>>(),
399 "clustering_detected": result.clustering_detected
400 });
401 println!("{}", serde_json::to_string_pretty(&output).unwrap());
402 }
403 _ => println!("Unsupported format for rare events analysis"),
404 }
405}
406
407fn print_text_output(result: &PoissonResult, quiet: bool, verbose: bool, no_color: bool) {
408 if quiet {
409 println!("lambda: {:.3}", result.lambda);
410 println!("variance_ratio: {:.3}", result.variance_ratio);
411 println!("goodness_of_fit: {:.3}", result.goodness_of_fit_score);
412 return;
413 }
414
415 println!("Poisson Distribution Analysis Results");
416 println!();
417 println!("Dataset: {}", result.dataset_name);
418 println!("Numbers analyzed: {}", result.numbers_analyzed);
419 println!("Quality Level: {:?}", result.risk_level);
420
421 println!();
422 println!("Probability Distribution:");
423 println!("{}", format_poisson_probability_chart(result));
424
425 println!();
426 println!("Poisson Parameters:");
427 println!(" λ (rate parameter): {:.3}", result.lambda);
428 println!(" Sample mean: {:.3}", result.sample_mean);
429 println!(" Sample variance: {:.3}", result.sample_variance);
430 println!(" Variance/Mean ratio: {:.3}", result.variance_ratio);
431
432 if verbose {
433 println!();
434 println!("Goodness of Fit Tests:");
435 println!(
436 " Chi-Square: χ²={:.3}, p={:.3}",
437 result.chi_square_statistic, result.chi_square_p_value
438 );
439 println!(
440 " Kolmogorov-Smirnov: D={:.3}, p={:.3}",
441 result.kolmogorov_smirnov_statistic, result.kolmogorov_smirnov_p_value
442 );
443
444 println!();
445 println!("Fit Assessment:");
446 println!(
447 " Goodness of fit score: {:.3}",
448 result.goodness_of_fit_score
449 );
450 println!(" Poisson quality: {:.3}", result.poisson_quality);
451 println!(
452 " Distribution assessment: {:?}",
453 result.distribution_assessment
454 );
455
456 println!();
457 println!("Event Probabilities:");
458 println!(" P(X = 0) = {:.3}", result.probability_zero);
459 println!(" P(X = 1) = {:.3}", result.probability_one);
460 println!(" P(X ≥ 2) = {:.3}", result.probability_two_or_more);
461
462 if result.rare_events_count > 0 {
463 println!();
464 println!(
465 "Rare events: {} (events ≥ {})",
466 result.rare_events_count, result.rare_events_threshold
467 );
468 }
469
470 println!();
471 println!("Interpretation:");
472 print_poisson_interpretation(result, no_color);
473 }
474}
475
476fn print_poisson_interpretation(result: &PoissonResult, no_color: bool) {
477 use lawkit_core::laws::poisson::result::PoissonAssessment;
478
479 match result.distribution_assessment {
480 PoissonAssessment::Excellent => {
481 println!(
482 "{}",
483 colors::level_pass("Excellent Poisson distribution fit", no_color)
484 );
485 println!(" Data closely follows Poisson distribution");
486 }
487 PoissonAssessment::Good => {
488 println!(
489 "{}",
490 colors::level_pass("Good Poisson distribution fit", no_color)
491 );
492 println!(" Acceptable fit to Poisson distribution");
493 }
494 PoissonAssessment::Moderate => {
495 println!(
496 "{}",
497 colors::level_warn("Moderate Poisson distribution fit", no_color)
498 );
499 println!(" Some deviations from Poisson distribution");
500 }
501 PoissonAssessment::Poor => {
502 println!(
503 "{}",
504 colors::level_fail("Poor Poisson distribution fit", no_color)
505 );
506 println!(" Significant deviations from Poisson distribution");
507 }
508 PoissonAssessment::NonPoisson => {
509 println!(
510 "{}",
511 colors::level_critical("Non-Poisson distribution", no_color)
512 );
513 println!(" Data does not follow Poisson distribution");
514 }
515 }
516
517 if result.variance_ratio > 1.5 {
519 println!(" INFO: Distribution is overdispersed");
520 } else if result.variance_ratio < 0.7 {
521 println!(" INFO: Distribution is underdispersed");
522 }
523
524 if result.rare_events_count > 0 {
526 println!(
527 " ALERT: Rare events detected: {}",
528 result.rare_events_count
529 );
530 }
531}
532
533fn print_json_output(result: &PoissonResult) {
534 use serde_json::json;
535
536 let output = json!({
537 "dataset": result.dataset_name,
538 "numbers_analyzed": result.numbers_analyzed,
539 "risk_level": format!("{:?}", result.risk_level),
540 "lambda": result.lambda,
541 "sample_mean": result.sample_mean,
542 "sample_variance": result.sample_variance,
543 "variance_ratio": result.variance_ratio,
544 "chi_square_test": {
545 "statistic": result.chi_square_statistic,
546 "p_value": result.chi_square_p_value
547 },
548 "kolmogorov_smirnov_test": {
549 "statistic": result.kolmogorov_smirnov_statistic,
550 "p_value": result.kolmogorov_smirnov_p_value
551 },
552 "goodness_of_fit_score": result.goodness_of_fit_score,
553 "poisson_quality": result.poisson_quality,
554 "distribution_assessment": format!("{:?}", result.distribution_assessment),
555 "event_probabilities": {
556 "zero": result.probability_zero,
557 "one": result.probability_one,
558 "two_or_more": result.probability_two_or_more
559 },
560 "rare_events": {
561 "threshold": result.rare_events_threshold,
562 "count": result.rare_events_count
563 },
564 "confidence_interval_lambda": result.confidence_interval_lambda
565 });
566
567 println!("{}", serde_json::to_string_pretty(&output).unwrap());
568}
569
570fn print_csv_output(result: &PoissonResult) {
571 println!("dataset,numbers_analyzed,risk_level,lambda,sample_mean,sample_variance,variance_ratio,goodness_of_fit_score");
572 println!(
573 "{},{},{:?},{:.3},{:.3},{:.3},{:.3},{:.3}",
574 result.dataset_name,
575 result.numbers_analyzed,
576 result.risk_level,
577 result.lambda,
578 result.sample_mean,
579 result.sample_variance,
580 result.variance_ratio,
581 result.goodness_of_fit_score
582 );
583}
584
585fn print_yaml_output(result: &PoissonResult) {
586 println!("dataset: \"{}\"", result.dataset_name);
587 println!("numbers_analyzed: {}", result.numbers_analyzed);
588 println!("risk_level: \"{:?}\"", result.risk_level);
589 println!("lambda: {:.3}", result.lambda);
590 println!("sample_mean: {:.3}", result.sample_mean);
591 println!("sample_variance: {:.3}", result.sample_variance);
592 println!("variance_ratio: {:.3}", result.variance_ratio);
593 println!("goodness_of_fit_score: {:.3}", result.goodness_of_fit_score);
594}
595
596fn print_toml_output(result: &PoissonResult) {
597 println!("dataset = \"{}\"", result.dataset_name);
598 println!("numbers_analyzed = {}", result.numbers_analyzed);
599 println!("risk_level = \"{:?}\"", result.risk_level);
600 println!("lambda = {:.3}", result.lambda);
601 println!("sample_mean = {:.3}", result.sample_mean);
602 println!("sample_variance = {:.3}", result.sample_variance);
603 println!("variance_ratio = {:.3}", result.variance_ratio);
604 println!(
605 "goodness_of_fit_score = {:.3}",
606 result.goodness_of_fit_score
607 );
608}
609
610fn print_xml_output(result: &PoissonResult) {
611 println!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
612 println!("<poisson_analysis>");
613 println!(" <dataset>{}</dataset>", result.dataset_name);
614 println!(
615 " <numbers_analyzed>{}</numbers_analyzed>",
616 result.numbers_analyzed
617 );
618 println!(" <risk_level>{:?}</risk_level>", result.risk_level);
619 println!(" <lambda>{:.3}</lambda>", result.lambda);
620 println!(" <sample_mean>{:.3}</sample_mean>", result.sample_mean);
621 println!(
622 " <sample_variance>{:.3}</sample_variance>",
623 result.sample_variance
624 );
625 println!(
626 " <variance_ratio>{:.3}</variance_ratio>",
627 result.variance_ratio
628 );
629 println!(
630 " <goodness_of_fit_score>{:.3}</goodness_of_fit_score>",
631 result.goodness_of_fit_score
632 );
633 println!("</poisson_analysis>");
634}
635
636fn analyze_numbers_with_options(
638 matches: &clap::ArgMatches,
639 dataset_name: String,
640 numbers: &[f64],
641) -> Result<PoissonResult> {
642 let filtered_numbers = if let Some(filter_str) = matches.get_one::<String>("filter") {
644 let filter = NumberFilter::parse(filter_str)
645 .map_err(|e| BenfError::ParseError(format!("無効なフィルタ: {e}")))?;
646
647 let filtered = apply_number_filter(numbers, &filter);
648
649 if filtered.len() != numbers.len() {
651 eprintln!(
652 "フィルタリング結果: {} 個の数値が {} 個に絞り込まれました ({})",
653 numbers.len(),
654 filtered.len(),
655 filter.description()
656 );
657 }
658
659 filtered
660 } else {
661 numbers.to_vec()
662 };
663
664 let min_count = if let Some(min_count_str) = matches.get_one::<String>("min-count") {
666 min_count_str
667 .parse::<usize>()
668 .map_err(|_| BenfError::ParseError("無効な最小数値数".to_string()))?
669 } else {
670 10 };
672
673 if filtered_numbers.len() < min_count {
675 return Err(BenfError::InsufficientData(filtered_numbers.len()));
676 }
677
678 let confidence = if let Some(confidence_str) = matches.get_one::<String>("confidence") {
680 let conf = confidence_str
681 .parse::<f64>()
682 .map_err(|_| BenfError::ParseError("無効な信頼度レベル".to_string()))?;
683 if !(0.01..=0.99).contains(&conf) {
684 return Err(BenfError::ParseError(
685 "信頼度レベルは0.01から0.99の間である必要があります".to_string(),
686 ));
687 }
688 conf
689 } else {
690 0.95
691 };
692
693 let mut result = analyze_poisson_distribution(&filtered_numbers, &dataset_name)?;
696
697 if confidence != 0.95 {
699 result.dataset_name = format!("{} (confidence: {:.2})", result.dataset_name, confidence);
700 }
701
702 Ok(result)
703}
704
705fn format_poisson_probability_chart(result: &PoissonResult) -> String {
706 let mut output = String::new();
707 const CHART_WIDTH: usize = 50;
708
709 let lambda = result.lambda;
710
711 let max_k = ((lambda + 3.0 * lambda.sqrt()).ceil() as u32).clamp(10, 20);
713
714 let mut probabilities = Vec::new();
716 let mut max_prob: f64 = 0.0;
717
718 for k in 0..=max_k {
719 let prob = poisson_pmf(k, lambda);
721 probabilities.push((k, prob));
722 max_prob = max_prob.max(prob);
723 }
724
725 for (k, prob) in &probabilities {
727 if max_prob > 0.0 {
728 let normalized_prob = prob / max_prob;
729 let bar_length = (normalized_prob * CHART_WIDTH as f64).round() as usize;
730 let bar_length = bar_length.min(CHART_WIDTH);
731
732 let expected_line_pos = (normalized_prob * CHART_WIDTH as f64).round() as usize;
734 let expected_line_pos = expected_line_pos.min(CHART_WIDTH - 1);
735
736 let mut bar_chars = Vec::new();
738 for pos in 0..CHART_WIDTH {
739 if pos == expected_line_pos {
740 bar_chars.push('┃'); } else if pos < bar_length {
742 bar_chars.push('█'); } else {
744 bar_chars.push('░'); }
746 }
747 let full_bar: String = bar_chars.iter().collect();
748
749 output.push_str(&format!("P(X={k:2}): {full_bar} {prob:>6.3}\n"));
750 }
751 }
752
753 output.push_str(&format!(
755 "\nKey Probabilities: P(X=0)={:.3}, P(X=1)={:.3}, P(X≥2)={:.3}",
756 result.probability_zero, result.probability_one, result.probability_two_or_more
757 ));
758
759 output.push_str(&format!(
761 "\nλ={:.2}, Variance/Mean={:.3} (ideal: 1.0), Fit Score={:.3}",
762 lambda, result.variance_ratio, result.goodness_of_fit_score
763 ));
764
765 output
766}
767
768fn poisson_pmf(k: u32, lambda: f64) -> f64 {
770 if lambda <= 0.0 {
771 return 0.0;
772 }
773
774 let log_prob = k as f64 * lambda.ln() - lambda - log_factorial(k);
777 log_prob.exp()
778}
779
780fn log_factorial(n: u32) -> f64 {
782 if n <= 1 {
783 0.0
784 } else {
785 (2..=n).map(|i| (i as f64).ln()).sum()
786 }
787}