Rust API Reference
Accurate reference for the current Rust workspace API.
For Python bindings, see Python SDK.
For day-to-day usage, start with:
Use this page for complete type and function coverage.
openentropy-core
Section titled “openentropy-core”Crate: openentropy-core
Path: crates/openentropy-core/
Public re-exports (openentropy_core)
Section titled “Public re-exports (openentropy_core)”pub use conditioning::{ ConditioningMode, MinEntropyReport, QualityReport, condition, grade_min_entropy, min_entropy_estimate, quick_autocorrelation_lag1, quick_min_entropy, quick_quality, quick_shannon,};pub use platform::{detect_available_sources, platform_info};pub use pool::{EntropyPool, HealthReport, SourceHealth, SourceInfoSnapshot};pub use source_resolution::{SourceMatchMode, SourceResolution, resolve_source_names};pub use comparison::{ AggregateDelta, ComparisonResult, DigramAnalysis, MarkovAnalysis, MultiLagAnalysis, RunLengthComparison, TemporalAnalysis, TwoSampleTests, WindowAnomaly, aggregate_delta, cliffs_delta, compare, compare_with_analysis, digram_analysis, markov_analysis, multi_lag_analysis, run_length_comparison, temporal_analysis, two_sample_tests,};pub use trials::{ CalibrationResult, StoufferResult, TrialAnalysis, TrialConfig, calibration_check, stouffer_combine, trial_analysis,};pub use chaos::{ BiEntropyResult, BootstrapHurstResult, ChaosAnalysis, CorrelationDimResult, DfaResult, EpiplexityResult, HurstResult, LyapunovResult, RollingHurstResult, RqaResult, SampleEntropyResult, bientropy, bootstrap_hurst, bootstrap_hurst_default, chaos_analysis, correlation_dimension, dfa, dfa_default, epiplexity, hurst_exponent, lyapunov_exponent, rolling_hurst, rolling_hurst_default, rqa, rqa_default, sample_entropy, sample_entropy_default,};pub use analysis::{ AndersonDarlingResult, ApproxEntropyResult, PermutationEntropyResult, anderson_darling, approximate_entropy, approximate_entropy_default, permutation_entropy, permutation_entropy_default,};pub use statistics::{ StatisticsAnalysis, CramerVonMisesResult, LjungBoxResult, GapTestResult, AnovaResult, KruskalWallisResult, LeveneResult, PowerResult, MultipleCorrectionResult, statistics_analysis, cramer_von_mises, ljung_box, gap_test, anova, kruskal_wallis, levene_test, power_analysis, bonferroni_correction, holm_bonferroni_correction,};pub use temporal::{ TemporalAnalysisSuite, ChangePointResult, AnomalyDetectionResult, BurstResult, ShiftResult, DriftResult, StabilityResult, temporal_analysis_suite, change_point_detection, anomaly_detection, burst_detection, shift_detection, temporal_drift, inter_session_stability,};pub use synchrony::{ SynchronyAnalysis, MutualInfoResult, PhaseCoherenceResult, CrossSyncResult, GlobalEventResult, synchrony_analysis, mutual_information, phase_coherence, cross_sync, global_event_detection,};pub use dispatcher::{ AnalysisConfig, AnalysisProfile, AnalysisReport, SourceReport, VerdictSummary, analyze,};pub use session::{ MachineInfo, SessionConfig, SessionMeta, SessionSourceAnalysis, SessionWriter, detect_machine_info,};pub use source::{EntropySource, Platform, Requirement, SourceCategory, SourceInfo};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");Single-source sampling (common pattern)
Section titled “Single-source sampling (common pattern)”use openentropy_core::{ConditioningMode, EntropyPool};
let pool = EntropyPool::auto();let source = pool.source_names()[0].clone();
let raw = pool.get_source_raw_bytes(&source, 4096).unwrap();let conditioned = pool .get_source_bytes(&source, 256, ConditioningMode::Sha256) .unwrap();EntropyPool (openentropy_core::pool)
Section titled “EntropyPool (openentropy_core::pool)”pub fn new(seed: Option<&[u8]>) -> Selfpub fn auto() -> Selfpub fn add_source(&mut self, source: Box<dyn EntropySource>)pub fn source_count(&self) -> usize
pub fn collect_all(&self) -> usizepub fn collect_all_parallel(&self, timeout_secs: f64) -> usizepub fn collect_enabled(&self, enabled_names: &[String]) -> usizepub fn collect_enabled_n(&self, enabled_names: &[String], n_samples: usize) -> usize
pub fn get_raw_bytes(&self, n_bytes: usize) -> Vec<u8>pub fn get_random_bytes(&self, n_bytes: usize) -> Vec<u8>pub fn get_bytes(&self, n_bytes: usize, mode: ConditioningMode) -> Vec<u8>pub fn get_source_bytes( &self, source_name: &str, n_bytes: usize, mode: ConditioningMode,) -> Option<Vec<u8>>pub fn get_source_raw_bytes(&self, source_name: &str, n_samples: usize) -> Option<Vec<u8>>
pub fn health_report(&self) -> HealthReportpub fn print_health(&self)pub fn source_names(&self) -> Vec<String>pub fn source_infos(&self) -> Vec<SourceInfoSnapshot>Pool report types
Section titled “Pool report types”pub struct HealthReport { pub healthy: usize, pub total: usize, pub raw_bytes: u64, pub output_bytes: u64, pub buffer_size: usize, pub sources: Vec<SourceHealth>,}
pub struct SourceHealth { pub name: String, pub healthy: bool, pub bytes: u64, pub entropy: f64, pub min_entropy: f64, pub autocorrelation: f64, pub time: f64, pub failures: u64,}
pub struct SourceInfoSnapshot { pub name: String, pub description: String, pub physics: String, pub category: String, pub platform: String, pub requirements: Vec<String>, pub entropy_rate_estimate: f64, pub composite: bool, pub config: Vec<(&'static str, String)>,}EntropySource and metadata (openentropy_core::source)
Section titled “EntropySource and metadata (openentropy_core::source)”pub trait EntropySource: Send + Sync { fn info(&self) -> &SourceInfo; fn is_available(&self) -> bool; fn collect(&self, n_samples: usize) -> Vec<u8>; fn name(&self) -> &'static str { self.info().name }}pub struct SourceInfo { pub name: &'static str, pub description: &'static str, pub physics: &'static str, pub category: SourceCategory, pub platform: Platform, pub requirements: &'static [Requirement], pub entropy_rate_estimate: f64, pub composite: bool, pub is_fast: bool,}pub enum Platform { Any, MacOS, Linux }pub enum Requirement { Metal, AudioUnit, Wifi, Usb, Camera, AppleSilicon, Bluetooth, IOKit, IOSurface, SecurityFramework, RawBlockDevice,}pub enum SourceCategory { Thermal, Timing, Scheduling, IO, IPC, Microarch, GPU, Network, System, Quantum, Signal, Sensor,}Source discovery and registry
Section titled “Source discovery and registry”pub fn detect_available_sources() -> Vec<Box<dyn EntropySource>>pub fn platform_info() -> PlatformInfopub fn resolve_source_names( available: &[String], requested: &[String], mode: SourceMatchMode,) -> SourceResolutionpub fn all_sources() -> Vec<Box<dyn EntropySource>> // registry size varies by releaseresolve_source_names() is the shared name-matching helper used across surfaces.
Use SourceMatchMode::ExactOnly for strict SDK/API behavior and
SourceMatchMode::ExactThenSubstringInsensitive for CLI-style exact-then-partial matching.
openentropy-tests
Section titled “openentropy-tests”Crate: openentropy-tests
Path: crates/openentropy-tests/
pub struct TestResult { pub name: String, pub passed: bool, pub p_value: Option<f64>, pub statistic: f64, pub details: String, pub grade: char,}
pub fn run_all_tests(data: &[u8]) -> Vec<TestResult>pub fn calculate_quality_score(results: &[TestResult]) -> f64openentropy-server
Section titled “openentropy-server”Crate: openentropy-server
Path: crates/openentropy-server/
pub async fn run_server(pool: EntropyPool, host: &str, port: u16, allow_raw: bool) -> std::io::Result<()>HTTP endpoints:
GET /api/v1/random?length=N&type=T[&raw=true|&conditioning=...]lengthis output bytes, not array-item count. Responses reportlengthas returned bytes andvalue_countas the number of encoded items indata.type=hex16andtype=uint16require an evenlength.GET /healthGET /sourcesGET /pool/status
Invalid query parameters return JSON 400 Bad Request responses. /health
and /pool/status use sources_healthy for the aggregate healthy-source
count; per-source entries use healthy as a boolean.
/sources and /pool/status source rows expose:
name, healthy, bytes, entropy, min_entropy, autocorrelation,
time, and failures.
openentropy-cli
Section titled “openentropy-cli”Crate: openentropy-cli
Binary: openentropy
Path: crates/openentropy-cli/
Subcommands:
scanbenchanalyzestreamservermonitorrecordsessions
Benchmark Module (openentropy_core::benchmark)
Section titled “Benchmark Module (openentropy_core::benchmark)”benchmark_sources(pool: &EntropyPool, config: &BenchConfig) -> Result<BenchReport, BenchError>
Section titled “benchmark_sources(pool: &EntropyPool, config: &BenchConfig) -> Result<BenchReport, BenchError>”Run a multi-round benchmark across all sources in a pool.
use openentropy_core::{EntropyPool, benchmark::{benchmark_sources, BenchConfig}};
let pool = EntropyPool::auto();let config = BenchConfig::default();let report = benchmark_sources(&pool, &config)?;for src in &report.sources { println!("{}: grade={} score={:.3}", src.name, src.grade, src.score);}BenchConfig fields (all public):
samples_per_round: usize— default 2048rounds: usize— default 3warmup_rounds: usize— default 1timeout_sec: f64— default 2.0rank_by: RankBy—Balanced|MinEntropy|Throughput, defaultBalancedinclude_pool_quality: bool— default truepool_quality_bytes: usize— default 65536conditioning: ConditioningMode— defaultSha256
BenchReport fields: generated_unix, config, sources: Vec<BenchSourceReport>, pool: Option<PoolQualityReport>
BenchSourceReport fields: name, composite, healthy, success_rounds, failures, avg_shannon, avg_min_entropy, avg_throughput_bps, avg_autocorrelation, p99_latency_ms, stability, grade: char, score: f64
Session Utilities (openentropy_core::session)
Section titled “Session Utilities (openentropy_core::session)”SessionWriter session configs must declare a non-empty, duplicate-free source list.
write_sample() only accepts source names declared in that session config.
list_sessions(dir: &Path) -> Result<Vec<(PathBuf, SessionMeta)>, std::io::Error>
Section titled “list_sessions(dir: &Path) -> Result<Vec<(PathBuf, SessionMeta)>, std::io::Error>”List all recorded sessions in a directory, sorted newest-first. Returns empty Vec for nonexistent directory.
load_session_raw_data(session_dir: &Path) -> Result<HashMap<String, Vec<u8>>, std::io::Error>
Section titled “load_session_raw_data(session_dir: &Path) -> Result<HashMap<String, Vec<u8>>, std::io::Error>”Load raw entropy data from a session directory. Returns a map of source name → raw bytes.
use openentropy_core::{list_sessions, load_session_raw_data, full_analysis};use std::path::Path;
let sessions = list_sessions(Path::new("sessions"))?;for (path, meta) in &sessions { println!("{}: {} samples", meta.id, meta.total_samples); let raw = load_session_raw_data(path)?; for (source, data) in &raw { let analysis = full_analysis(source, data); println!(" {}: H∞={:.4}", source, analysis.min_entropy); }}Forensic Analysis (openentropy_core::analysis)
Section titled “Forensic Analysis (openentropy_core::analysis)”Core statistical analysis battery for evaluating entropy quality. Six tests that assess the fundamental properties expected of random data. See Choose an Analysis Path for interpretation guides and verdict thresholds.
full_analysis(source_name: &str, data: &[u8]) -> SourceAnalysis
Section titled “full_analysis(source_name: &str, data: &[u8]) -> SourceAnalysis”Run all six forensic tests in one call. Returns a SourceAnalysis containing
autocorrelation, spectral, bit bias, distribution, stationarity, and runs
results, plus Shannon and min-entropy estimates.
use openentropy_core::full_analysis;
let analysis = full_analysis("clock_jitter", &data);println!("Shannon: {:.4} bits/byte", analysis.shannon_entropy);println!("Min-entropy: {:.4}", analysis.min_entropy);println!("Spectral flatness: {:.4}", analysis.spectral.flatness);println!("Stationary: {}", analysis.stationarity.is_stationary);Individual functions
Section titled “Individual functions”| Function | Returns | Description |
|---|---|---|
autocorrelation_profile(data, max_lag) | AutocorrResult | Serial dependence at multiple lags |
spectral_analysis(data) | SpectralResult | FFT-based power spectral density |
bit_bias(data) | BitBiasResult | Per-bit deviation from 50/50 |
distribution_stats(data) | DistributionResult | Byte-value distribution vs uniform |
stationarity_test(data) | StationarityResult | ANOVA stability over 10 windows |
runs_analysis(data) | RunsResult | Consecutive identical value patterns |
cross_correlation_matrix(sources) | CrossCorrMatrix | Pairwise Pearson correlation, flags |r| > 0.3 |
pearson_correlation(a, b) | f64 | Pearson correlation coefficient |
Chaos Theory Analysis (openentropy_core::chaos)
Section titled “Chaos Theory Analysis (openentropy_core::chaos)”Distinguish true randomness from deterministic chaos with core and extended metrics. See Chaos Theory Analysis for interpretation guides and verdict thresholds.
chaos_analysis(data: &[u8]) -> ChaosAnalysis
Section titled “chaos_analysis(data: &[u8]) -> ChaosAnalysis”Run the core chaos battery on a byte stream. Returns a ChaosAnalysis containing
Hurst exponent, Lyapunov exponent, correlation dimension, BiEntropy, and epiplexity.
use openentropy_core::chaos::chaos_analysis;
let result = chaos_analysis(&data);println!("Hurst H={:.4} (valid={})", result.hurst.hurst_exponent, result.hurst.is_valid);println!("Lyapunov λ={:.4}", result.lyapunov.lyapunov_exponent);println!("Correlation dim D₂={:.4}", result.correlation_dimension.dimension);println!("BiEntropy={:.4}, TBiEn={:.4}", result.bientropy.bien, result.bientropy.tbien);println!("Compression ratio={:.4}", result.epiplexity.compression_ratio);Individual functions
Section titled “Individual functions”| Function | Returns | Description |
|---|---|---|
hurst_exponent(data) | HurstResult | Rescaled range (R/S) analysis — H≈0.5 = random |
lyapunov_exponent(data) | LyapunovResult | Largest Lyapunov exponent — λ>0 = chaotic |
correlation_dimension(data) | CorrelationDimensionResult | Grassberger–Procaccia D₂ estimate |
bientropy(data) | BiEntropyResult | Binary entropy derivative (BiEn, TBiEn) |
epiplexity(data) | EpiplexityResult | Compression-ratio complexity metric |
Extended chaos functions:
| Function | Returns | Description |
|---|---|---|
sample_entropy(data, m, r) | SampleEntropyResult | Sample entropy |
dfa(data, order) | DfaResult | Detrended fluctuation analysis |
rqa(data, dim, delay, threshold) | RqaResult | Recurrence quantification |
rolling_hurst(data, window, step) | RollingHurstResult | Sliding Hurst estimate |
bootstrap_hurst(data, n_bootstrap) | BootstrapHurstResult | Hurst uncertainty/p-value |
Interpreting results
Section titled “Interpreting results”For true random data, expect: Hurst H ≈ 0.5, Lyapunov λ > 0 (sensitive dependence), high correlation dimension, BiEntropy near maximum, compression ratio near 1.0. See the Verdict System for automated pass/fail classification of each metric.
Statistics Analysis (openentropy_core::statistics)
Section titled “Statistics Analysis (openentropy_core::statistics)”Core one-call entry:
use openentropy_core::statistics_analysis;let stats = statistics_analysis(&data);println!("CvM p={:.4}, Ljung-Box p={:.4}", stats.cramer_von_mises.p_value, stats.ljung_box.p_value);Additional group-level helpers are exported: anova, kruskal_wallis, levene_test,
power_analysis, bonferroni_correction, holm_bonferroni_correction.
Temporal Analysis (openentropy_core::temporal)
Section titled “Temporal Analysis (openentropy_core::temporal)”Core one-call entry:
use openentropy_core::temporal_analysis_suite;let temporal = temporal_analysis_suite(&data);println!("drift slope={:.4}", temporal.drift.drift_slope);Individual functions include change_point_detection, anomaly_detection, burst_detection,
shift_detection, temporal_drift, and inter_session_stability.
Synchrony Analysis (openentropy_core::synchrony)
Section titled “Synchrony Analysis (openentropy_core::synchrony)”Pairwise and multi-stream entries:
use openentropy_core::{synchrony_analysis, global_event_detection};let pair = synchrony_analysis(&data_a, &data_b);let events = global_event_detection(&[&data_a, &data_b, &data_c]);println!("NMI={:.4}", pair.mutual_info.normalized_mi);Unified Analysis Dispatcher (openentropy_core::dispatcher)
Section titled “Unified Analysis Dispatcher (openentropy_core::dispatcher)”Run multiple analysis modules through a single entry point with configurable profiles.
analyze(sources: &[(&str, &[u8])], config: &AnalysisConfig) -> AnalysisReport
Section titled “analyze(sources: &[(&str, &[u8])], config: &AnalysisConfig) -> AnalysisReport”Dispatch analysis across one or more labeled byte streams. The config controls which modules run.
use openentropy_core::dispatcher::{analyze, AnalysisConfig, AnalysisProfile};
// Use a preset profilelet config = AnalysisProfile::Deep.to_config();let report = analyze(&[("clock_jitter", &data)], &config);
for source in &report.sources { println!("{}: forensic={} chaos={} trials={}", source.label, source.forensic.is_some(), source.chaos.is_some(), source.trials.is_some(), ); println!( " Verdicts: bias={:?} hurst={:?}", source.verdicts.bias, source.verdicts.hurst );}AnalysisConfig fields
Section titled “AnalysisConfig fields”| Field | Type | Description |
|---|---|---|
forensic | bool | Run full_analysis (autocorrelation, spectral, bias, distribution, stationarity, runs) |
entropy | bool | Run min_entropy_estimate (detailed entropy breakdown) |
chaos | bool | Run chaos_analysis (Hurst, Lyapunov, correlation dimension, BiEntropy, epiplexity) |
chaos_extended | bool | Run extended chaos metrics (SampEn/ApEn/DFA/RQA/Hurst variants/PermEn/AD) |
temporal | bool | Run temporal suite (change-point/anomaly/burst/shift/drift) |
statistics | bool | Run statistics suite (CvM/Ljung-Box/gap) |
synchrony | bool | Run synchrony suite (pairwise + global-event checks) |
trials | Option<TrialConfig> | Run trial_analysis with given config; None = skip |
cross_correlation | bool | Run cross_correlation_matrix when 2+ sources present |
AnalysisProfile presets
Section titled “AnalysisProfile presets”| Profile | Forensic | Entropy | Chaos | Chaos Extended | Temporal | Statistics | Synchrony | Trials | Cross-Correlation |
|---|---|---|---|---|---|---|---|---|---|
Quick | ✓ | — | — | — | — | — | — | — | — |
Standard | ✓ | — | — | — | — | — | — | — | — |
Deep | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | —* | ✓ | ✓ |
Security | ✓ | ✓ | — | — | — | — | — | — | — |
* Synchrony is explicit in CLI because it requires 2+ streams.
Custom config
Section titled “Custom config”use openentropy_core::dispatcher::{analyze, AnalysisConfig};use openentropy_core::trials::TrialConfig;
let config = AnalysisConfig { forensic: true, entropy: false, chaos: true, chaos_extended: false, temporal: false, statistics: false, synchrony: false, trials: Some(TrialConfig::default()), cross_correlation: false,};
let report = analyze(&[ ("source_a", &data_a), ("source_b", &data_b),], &config);Verdict System (openentropy_core::verdict)
Section titled “Verdict System (openentropy_core::verdict)”Automated pass/fail classification for every forensic and chaos metric.
Each source report includes a VerdictSummary with up to 11 verdict fields.
See Verdict System for
all thresholds and interpretation guidance.
use openentropy_core::dispatcher::{analyze, AnalysisProfile, VerdictSummary};
let config = AnalysisProfile::Deep.to_config();let report = analyze(&[("src", &data)], &config);
for source in &report.sources { let v = &source.verdicts; println!("Autocorrelation: {:?}", v.autocorrelation); println!("Spectral: {:?}", v.spectral); println!("Bias: {:?}", v.bias); println!("Distribution: {:?}", v.distribution); println!("Stationarity: {:?}", v.stationarity); println!("Runs: {:?}", v.runs); println!("Hurst: {:?}", v.hurst); println!("Lyapunov: {:?}", v.lyapunov); println!("Correlation dim: {:?}", v.correlation_dimension); println!("BiEntropy: {:?}", v.bientropy); println!("Compression: {:?}", v.compression);}Verdict values: Pass, Warn, Fail, Na. Serializes as "PASS", "WARN", "FAIL", "N/A".
Trial Analysis (openentropy_core::trials)
Section titled “Trial Analysis (openentropy_core::trials)”PEAR-style 200-bit trial analysis. Slices byte data into fixed-length trials and computes Z-scores, cumulative deviation, and effect sizes. See Trial Analysis Methodology for the statistical model.
trial_analysis(data: &[u8], config: &TrialConfig) -> TrialAnalysis
Section titled “trial_analysis(data: &[u8], config: &TrialConfig) -> TrialAnalysis”use openentropy_core::trials::{trial_analysis, TrialConfig};
let config = TrialConfig::default(); // 200 bits per triallet result = trial_analysis(&data, &config);println!("Trials: {}, Terminal Z: {:.4}, Effect: {:.6}, p={:.4}", result.num_trials, result.terminal_z, result.effect_size, result.terminal_p_value);stouffer_combine(analyses: &[&TrialAnalysis]) -> StoufferResult
Section titled “stouffer_combine(analyses: &[&TrialAnalysis]) -> StoufferResult”Weighted Stouffer composition across multiple sessions (weights = √num_trials).
use openentropy_core::trials::{trial_analysis, stouffer_combine, TrialConfig};
let config = TrialConfig::default();let t1 = trial_analysis(&data_a, &config);let t2 = trial_analysis(&data_b, &config);let combined = stouffer_combine(&[&t1, &t2]);println!("Combined Z: {:.4}, p={:.4}", combined.stouffer_z, combined.p_value);calibration_check(data: &[u8], config: &TrialConfig) -> CalibrationResult
Section titled “calibration_check(data: &[u8], config: &TrialConfig) -> CalibrationResult”Pre-recording suitability check. Thresholds: |Z| < 2.0, bit bias < 0.005, Shannon > 7.9, Z-score std in [0.85, 1.15].
use openentropy_core::trials::{calibration_check, TrialConfig};
let result = calibration_check(&data, &TrialConfig::default());println!("Suitable: {}, Warnings: {:?}", result.is_suitable, result.warnings);Comparison (openentropy_core::comparison)
Section titled “Comparison (openentropy_core::comparison)”Differential statistical analysis between two byte streams. See Choose an Analysis Path for context on how comparison fits into the analysis pipeline.
compare(label_a, data_a, label_b, data_b) -> ComparisonResult
Section titled “compare(label_a, data_a, label_b, data_b) -> ComparisonResult”Full differential report including aggregate deltas, two-sample tests, temporal analysis, digram analysis, Markov transitions, multi-lag autocorrelation, and run-length distributions.
use openentropy_core::compare;
let result = compare("session_a", &data_a, "session_b", &data_b);println!("KS p-value: {:.4}", result.two_sample.ks_p_value);println!("Cliff's d: {:.4}", result.two_sample.cliffs_delta);Individual comparison functions
Section titled “Individual comparison functions”| Function | Returns | Description |
|---|---|---|
aggregate_delta(a, b) | AggregateDelta | Shannon/min-entropy/mean/variance deltas, Cohen’s d |
two_sample_tests(a, b) | TwoSampleTests | KS, chi-squared, Mann-Whitney, Cliff’s delta |
cliffs_delta(a, b) | f64 | Non-parametric effect size [-1, 1] |
temporal_analysis(a, b, window, z) | TemporalAnalysis | Sliding-window anomaly detection |
digram_analysis(a, b) | DigramAnalysis | Digram chi-squared uniformity |
markov_analysis(a, b) | MarkovAnalysis | Per-bit transition probabilities |
multi_lag_analysis(a, b) | MultiLagAnalysis | Autocorrelation at multiple lags |
run_length_comparison(a, b) | RunLengthComparison | Byte run-length distributions |