sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn ppi_degree(adjacency_row: &[bool]) -> usize {
    adjacency_row.iter().filter(|&&x| x).count()
}

pub fn clustering_coefficient(neighbors_connected: usize, degree: usize) -> f64 {
    if degree < 2 {
        return 0.0;
    }
    2.0 * neighbors_connected as f64 / (degree as f64 * (degree as f64 - 1.0))
}

pub fn betweenness_centrality_approx(
    shortest_paths_through: f64,
    total_shortest_paths: f64,
) -> f64 {
    shortest_paths_through / total_shortest_paths.max(1e-30)
}

pub fn network_density(edges: usize, nodes: usize) -> f64 {
    if nodes < 2 {
        return 0.0;
    }
    2.0 * edges as f64 / (nodes as f64 * (nodes as f64 - 1.0))
}

pub fn scale_free_exponent(degree_distribution: &[f64]) -> f64 {
    let n = degree_distribution.len();
    if n == 0 {
        return 0.0;
    }
    let mut sum_ln = 0.0;
    let mut count = 0;
    for &d in degree_distribution {
        if d > 0.0 {
            sum_ln += d.ln();
            count += 1;
        }
    }
    if count == 0 {
        return 0.0;
    }
    1.0 + count as f64 / sum_ln
}

pub fn hub_score(degree: usize, max_degree: usize) -> f64 {
    degree as f64 / max_degree.max(1) as f64
}

pub fn edge_betweenness(flow_through_edge: f64, total_flow: f64) -> f64 {
    flow_through_edge / total_flow.max(1e-30)
}

pub fn protein_complex_stoichiometry(abundances: &[f64]) -> Vec<f64> {
    if abundances.is_empty() {
        return vec![];
    }
    let min_val = abundances
        .iter()
        .cloned()
        .fold(f64::INFINITY, f64::min)
        .max(1e-30);
    abundances.iter().map(|a| (a / min_val).round()).collect()
}

pub fn functional_enrichment_odds_ratio(
    hits_in_set: usize,
    set_size: usize,
    hits_total: usize,
    genome_size: usize,
) -> f64 {
    let a = hits_in_set as f64;
    let b = (set_size - hits_in_set) as f64;
    let c = (hits_total - hits_in_set) as f64;
    let d = (genome_size - set_size - hits_total + hits_in_set) as f64;
    (a * d) / (b * c).max(1e-30)
}

pub fn guilt_by_association_score(neighbor_annotations: &[bool]) -> f64 {
    if neighbor_annotations.is_empty() {
        return 0.0;
    }
    let positive: f64 = neighbor_annotations.iter().filter(|&&x| x).count() as f64;
    positive / neighbor_annotations.len() as f64
}