sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn smith_waterman_score(
    seq1: &[u8],
    seq2: &[u8],
    match_score: i32,
    mismatch: i32,
    gap: i32,
) -> i32 {
    let m = seq1.len();
    let n = seq2.len();
    let mut matrix = vec![vec![0i32; n + 1]; m + 1];
    let mut max_score = 0;
    for i in 1..=m {
        for j in 1..=n {
            let s = if seq1[i - 1] == seq2[j - 1] {
                match_score
            } else {
                mismatch
            };
            let diag = matrix[i - 1][j - 1] + s;
            let up = matrix[i - 1][j] + gap;
            let left = matrix[i][j - 1] + gap;
            matrix[i][j] = 0_i32.max(diag).max(up).max(left);
            if matrix[i][j] > max_score {
                max_score = matrix[i][j];
            }
        }
    }
    max_score
}

pub fn needleman_wunsch_score(
    seq1: &[u8],
    seq2: &[u8],
    match_score: i32,
    mismatch: i32,
    gap: i32,
) -> i32 {
    let m = seq1.len();
    let n = seq2.len();
    let mut matrix = vec![vec![0i32; n + 1]; m + 1];
    for (i, row) in matrix.iter_mut().enumerate() {
        row[0] = i as i32 * gap;
    }
    for (j, val) in matrix[0].iter_mut().enumerate() {
        *val = j as i32 * gap;
    }
    for i in 1..=m {
        for j in 1..=n {
            let s = if seq1[i - 1] == seq2[j - 1] {
                match_score
            } else {
                mismatch
            };
            let diag = matrix[i - 1][j - 1] + s;
            let up = matrix[i - 1][j] + gap;
            let left = matrix[i][j - 1] + gap;
            matrix[i][j] = diag.max(up).max(left);
        }
    }
    matrix[m][n]
}

pub fn edit_distance(seq1: &[u8], seq2: &[u8]) -> usize {
    let m = seq1.len();
    let n = seq2.len();
    let mut dp = vec![vec![0usize; n + 1]; m + 1];
    for (i, row) in dp.iter_mut().enumerate() {
        row[0] = i;
    }
    for (j, val) in dp[0].iter_mut().enumerate() {
        *val = j;
    }
    for i in 1..=m {
        for j in 1..=n {
            if seq1[i - 1] == seq2[j - 1] {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + dp[i - 1][j - 1].min(dp[i - 1][j]).min(dp[i][j - 1]);
            }
        }
    }
    dp[m][n]
}

pub fn hamming_distance(seq1: &[u8], seq2: &[u8]) -> usize {
    let n = seq1.len().min(seq2.len());
    (0..n).filter(|&i| seq1[i] != seq2[i]).count()
}

pub fn alignment_gc_content(seq: &[u8]) -> f64 {
    if seq.is_empty() {
        return 0.0;
    }
    let gc = seq
        .iter()
        .filter(|&&b| b == b'G' || b == b'C' || b == b'g' || b == b'c')
        .count();
    gc as f64 / seq.len() as f64
}

pub fn sequence_identity(seq1: &[u8], seq2: &[u8]) -> f64 {
    let n = seq1.len().min(seq2.len());
    if n == 0 {
        return 0.0;
    }
    let matches = (0..n).filter(|&i| seq1[i] == seq2[i]).count();
    matches as f64 / n as f64
}

pub fn codon_frequency(seq: &[u8]) -> Vec<(u32, usize)> {
    let mut counts = Vec::new();
    let mut i = 0;
    while i + 2 < seq.len() {
        let codon = (seq[i] as u32) << 16 | (seq[i + 1] as u32) << 8 | seq[i + 2] as u32;
        let mut found = false;
        for entry in counts.iter_mut() {
            let (c, count): &mut (u32, usize) = entry;
            if *c == codon {
                *count += 1;
                found = true;
                break;
            }
        }
        if !found {
            counts.push((codon, 1));
        }
        i += 3;
    }
    counts
}

pub fn reverse_complement(seq: &[u8]) -> Vec<u8> {
    seq.iter()
        .rev()
        .map(|&b| match b {
            b'A' | b'a' => b'T',
            b'T' | b't' => b'A',
            b'G' | b'g' => b'C',
            b'C' | b'c' => b'G',
            other => other,
        })
        .collect()
}

pub fn melting_temperature_basic(
    a_count: usize,
    t_count: usize,
    g_count: usize,
    c_count: usize,
) -> f64 {
    let len = a_count + t_count + g_count + c_count;
    if len < 14 {
        2.0 * (a_count + t_count) as f64 + 4.0 * (g_count + c_count) as f64
    } else {
        64.9 + 41.0 * (g_count + c_count) as f64 / len as f64 - 500.0 / len as f64
    }
}