#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use crate::{dfa, analyze, DfaResult, StructuralLaw};
#[derive(Debug)]
pub struct GenomeStructure {
pub total_bases: usize,
pub gc_windows: usize,
pub window_size: usize,
pub overall_dfa: DfaResult,
pub overall_law: StructuralLaw,
pub gc_content: f64,
pub profile: Vec<RegionAlpha>,
}
#[derive(Debug, Clone)]
pub struct RegionAlpha {
pub start_bp: usize,
pub end_bp: usize,
pub alpha: f64,
pub r_squared: f64,
}
pub fn gc_windows(sequence: &[u8], window: usize) -> Vec<f64> {
let mut result = Vec::new();
let mut gc = 0usize;
let mut at = 0usize;
for (i, &b) in sequence.iter().enumerate() {
match b.to_ascii_uppercase() {
b'G' | b'C' => gc += 1,
b'A' | b'T' => at += 1,
_ => {}
}
if (i + 1) % window == 0 && (gc + at) > 0 {
result.push(gc as f64 / (gc + at) as f64);
gc = 0;
at = 0;
}
}
result
}
pub fn parse_fasta(content: &str) -> Vec<u8> {
let mut seq = Vec::new();
for line in content.lines() {
if line.starts_with('>') { continue; }
seq.extend_from_slice(line.trim().as_bytes());
}
seq
}
pub fn genome_structure(fasta_content: &str, window: usize, profile_block: usize) -> GenomeStructure {
let seq = parse_fasta(fasta_content);
let total_bases = seq.len();
let gc_vals = gc_windows(&seq, window);
let gc_count = gc_vals.len();
let gc_content = if gc_count > 0 {
gc_vals.iter().sum::<f64>() / gc_count as f64
} else { 0.0 };
let overall_dfa = if gc_count >= 64 { dfa(&gc_vals) } else {
DfaResult { alpha: 0.0, r_squared: 0.0 }
};
let overall_law = if gc_count >= 64 { analyze(&gc_vals) } else {
analyze(&[0.0; 64])
};
let mut profile = Vec::new();
if profile_block > 0 && gc_count > profile_block {
let mut i = 0;
while i + profile_block <= gc_count {
let block = &gc_vals[i..i + profile_block];
let d = dfa(block);
profile.push(RegionAlpha {
start_bp: i * window,
end_bp: (i + profile_block) * window,
alpha: d.alpha,
r_squared: d.r_squared,
});
i += profile_block / 2;
}
}
GenomeStructure {
total_bases,
gc_windows: gc_count,
window_size: window,
overall_dfa,
overall_law,
gc_content,
profile,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gc_windows_basic() {
let seq = b"GCGCGCGCGCATATATATAT";
let wins = gc_windows(seq, 10);
assert_eq!(wins.len(), 2);
assert!((wins[0] - 1.0).abs() < 0.01); assert!((wins[1] - 0.0).abs() < 0.01); }
#[test]
fn parse_fasta_skips_headers() {
let fasta = ">chr1\nGCATGCAT\nATATGCGC\n";
let seq = parse_fasta(fasta);
assert_eq!(seq.len(), 16);
}
}