wasm4pm 26.7.1

High-performance process mining algorithms in WebAssembly for JavaScript/TypeScript
Documentation
//! Statistical analysis functions for event logs stored in the handle cache.
//!
//! Functions in this module operate on handles returned by `load_eventlog_from_xes()`
//! and return JSON strings that must be `JSON.parse()`d on the JS side.
//!
//! ## Available analyses
//!
//! | Function | Output |
//! |---|---|
//! | `analyze_event_statistics` | `{ total_cases, total_events, avg_events_per_case }` |
//! | `analyze_case_duration` | `{ min_ms, max_ms, mean_ms, median_ms, p95_ms }` |
//! | `analyze_dotted_chart` | `{ events[] }` for timeline visualization |

use crate::error::{codes, wasm_err};
use crate::state::{get_or_init_state, StoredObject};
use crate::utilities::to_js_str;
use crate::{Data, Median}; // Conditional import: statrs or hand_rolled_stats
use serde_json::json;
use wasm_bindgen::prelude::*;

/// Perform dotted chart analysis on an EventLog
#[wasm_bindgen]
pub fn analyze_dotted_chart(eventlog_handle: &str) -> Result<JsValue, JsValue> {
    get_or_init_state().with_event_log(eventlog_handle, |log| {
        let mut data = Vec::new();
        let mut total_events = 0;

        for (case_idx, trace) in log.traces.iter().enumerate() {
            let case_events = trace.events.len();
            total_events += case_events;

            data.push(json!({
                "case_id": case_idx,
                "event_count": case_events,
            }));
        }

        to_js_str(&json!({
            "type": "dotted_chart",
            "case_count": log.traces.len(),
            "total_events": total_events,
            "cases": data
        }))
    })
}

/// Get event statistics from an EventLog
#[wasm_bindgen]
pub fn analyze_event_statistics(eventlog_handle: &str) -> Result<JsValue, JsValue> {
    get_or_init_state().with_event_log(eventlog_handle, |log| {
        let total_events = log.event_count();
        let total_cases = log.case_count();

        let stats = json!({
            "total_events": total_events,
            "total_cases": total_cases,
            "avg_events_per_case": if total_cases > 0 {
                total_events as f64 / total_cases as f64
            } else {
                0.0
            },
        });

        to_js_str(&stats)
    })
}

/// Get object statistics from an OCEL
#[wasm_bindgen]
pub fn analyze_ocel_statistics(ocel_handle: &str) -> Result<JsValue, JsValue> {
    get_or_init_state().with_ocel(ocel_handle, |ocel| {
        let stats = json!({
            "total_events": ocel.event_count(),
            "total_objects": ocel.object_count(),
        });

        to_js_str(&stats)
    })
}

/// Analyze case duration from an EventLog
#[wasm_bindgen]
pub fn analyze_case_duration(eventlog_handle: &str) -> Result<JsValue, JsValue> {
    get_or_init_state().with_event_log(eventlog_handle, |log| {
        let mut event_counts: Vec<usize> = log.traces.iter().map(|t| t.events.len()).collect();

        let stats = if !event_counts.is_empty() {
            event_counts.sort_unstable();
            let sum: usize = event_counts.iter().sum();
            let avg = sum as f64 / event_counts.len() as f64;

            // Use statrs for proper median calculation
            let counts_f64: Vec<f64> = event_counts.iter().map(|&x| x as f64).collect();
            let data = Data::new(counts_f64);
            let median = data.median() as usize;

            json!({
                "case_count": event_counts.len(),
                "average_events_per_case": avg,
                "median_events_per_case": median,
                "min_events_per_case": event_counts[0],
                "max_events_per_case": event_counts[event_counts.len() - 1],
            })
        } else {
            json!({
                "case_count": 0,
                "average_events_per_case": 0,
                "median_events_per_case": 0,
                "min_events_per_case": 0,
                "max_events_per_case": 0,
            })
        };

        to_js_str(&stats)
    })
}

/// Get list of available analysis functions
#[wasm_bindgen]
pub fn available_analysis_functions() -> String {
    json!({
        "functions": [
            {
                "name": "dotted_chart",
                "description": "Dotted chart analysis for EventLogs",
                "input_type": "EventLog"
            },
            {
                "name": "event_statistics",
                "description": "Event frequency and activity statistics",
                "input_type": "EventLog"
            },
            {
                "name": "ocel_statistics",
                "description": "Object-centric statistics from OCEL",
                "input_type": "OCEL"
            },
            {
                "name": "case_duration",
                "description": "Case duration analysis",
                "input_type": "EventLog"
            }
        ]
    })
    .to_string()
}