use anyhow::Result;
use clap::Parser;
use rayon::prelude::*;
use rand::seq::{IndexedRandom};
use rand::rng;
use std::{
path::PathBuf,
};
use crate::{load_gof, write_gff_output};
#[derive(Parser, Debug)]
#[command(
about = "Sample feature groups per chromosome",
long_about = "Sample feature groups per chromosome."
)]
pub struct SampleArgs {
#[arg(short = 'i', long = "input", value_name = "FILE")]
pub input: PathBuf,
#[arg(short = 'r', long = "ratio")]
pub ratio: f32,
#[arg(short = 'o', long = "output", value_name = "FILE")]
pub output: Option<PathBuf>,
#[arg(short = 't', long = "threads", default_value_t = 12, value_name = "NUM")]
pub threads: usize,
#[arg(short = 'v', long = "verbose", default_value_t = false, value_name = "BOOL")]
pub verbose: bool,
}
pub fn run(args: &SampleArgs) -> Result<()> {
let verbose = args.verbose;
let threads = if args.threads == 0 {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
} else {
args.threads
};
let _ = rayon::ThreadPoolBuilder::new().num_threads(threads).build_global();
let gff_path = &args.input;
let gof = load_gof(&gff_path)?;
let blocks: Vec<(u32, u64, u64)> = gof.seqid_index
.par_iter()
.flat_map(|(_seqid_num, indices)| {
let mut rng = rng();
let fids: Vec<u32> = indices
.iter()
.map(|&i| gof.entries[i].feature_id)
.collect();
if fids.is_empty() {
return Vec::new();
}
let sample_size = (fids.len() as f32 * args.ratio).ceil() as usize;
let sampled: Vec<u32> = fids.choose_multiple(&mut rng, sample_size).cloned().collect();
let idx = gof.index_cached();
sampled
.into_iter()
.filter_map(|fid| idx.get(&fid).map(|&(s, e)| (fid, s, e)))
.collect::<Vec<_>>()
})
.collect();
write_gff_output(gff_path, &blocks, &args.output, verbose)?;
Ok(())
}