1use bytes::Bytes;
26
27use crate::bbi::block::{DataInterval, DataIntervals};
28use crate::bbi::rtree::LeafWalk;
29use crate::error::{Error, Result};
30use crate::genomic::{BinMode, BinStats, IndexedLoc, IndexedLocs, LocBatch, ValueStats};
31use crate::progress::ProgressTracker;
32use crate::source::ByteSource;
33
34pub(crate) struct Extraction<'a> {
36 pub source: &'a dyn ByteSource,
37 pub locs: &'a IndexedLocs,
38 pub batches: &'a [LocBatch],
39 pub tree_root: u64,
41 pub zoom: bool,
43 pub uncompress_buffer_size: u32,
44 pub tracker: &'a ProgressTracker,
45}
46
47impl Extraction<'_> {
48 fn read_leaf(&self, offset: u64, size: u64) -> Result<Bytes> {
49 let raw = self.source.read_exact_at(offset, size as usize)?;
50 crate::bbi::block::decompress(raw, self.uncompress_buffer_size, self.source.path())
51 }
52
53 fn walk_batch(
60 &self,
61 batch: LocBatch,
62 mut visit: impl FnMut(&DataInterval, usize, &IndexedLoc, i64, i64) -> Result<()>,
63 ) -> Result<()> {
64 let locs = &self.locs.locs;
65 let leaves = LeafWalk::new(self.source, self.tree_root, locs, batch, self.tracker)?;
66 for leaf in leaves {
67 let (leaf, loc_range) = leaf?;
68 let block = self.read_leaf(leaf.offset, leaf.size)?;
69 let intervals = DataIntervals::new(
70 block,
71 self.zoom,
72 locs,
73 loc_range.clone(),
74 self.source.path(),
75 )?;
76 let mut cursor = loc_range.start;
77 #[allow(clippy::needless_range_loop)]
82 for interval in intervals {
83 let interval = interval?;
84 cursor = advance_cursor(
85 locs,
86 cursor,
87 loc_range.end,
88 interval.chr_index,
89 interval.start,
90 );
91 for index in cursor..loc_range.end {
92 let loc = &locs[index];
93 if interval.chr_index != loc.chr_index as u32 {
98 break;
99 }
100 if interval.end <= loc.binned_start {
101 break;
102 }
103 if loc.binned_end <= loc.binned_start {
106 continue;
107 }
108 if interval.start >= loc.binned_end {
109 continue;
110 }
111 let overlap_start = interval.start.max(loc.binned_start);
112 let overlap_end = interval.end.min(loc.binned_end);
113 visit(&interval, index, loc, overlap_start, overlap_end)?;
114 }
115 }
116 }
117 Ok(())
118 }
119}
120
121#[inline]
124fn advance_cursor(
125 locs: &[IndexedLoc],
126 mut cursor: usize,
127 end: usize,
128 chr: u32,
129 start: i64,
130) -> usize {
131 while cursor < end {
132 let loc = &locs[cursor];
133 let loc_chr = loc.chr_index as u32;
134 if loc_chr > chr {
135 break;
136 }
137 if loc_chr == chr && loc.binned_end > start {
138 break;
139 }
140 cursor += 1;
141 }
142 cursor
143}
144
145pub(crate) fn values(
147 ex: &Extraction<'_>,
148 executor: &crate::parallel::Executor,
149 bin_mode: BinMode,
150 def_value: f32,
151) -> Result<Vec<f32>> {
152 let bin_count = ex.locs.bin_count;
153 let per_batch = executor.map_batches(ex.batches, |_, batch| {
154 let mut stats = vec![BinStats::default(); batch.len() * bin_count];
155 ex.walk_batch(*batch, |interval, index, loc, from, to| {
156 let base = (index - batch.start) * bin_count;
157 let bin_start = loc.bin_at(from);
158 let bin_end = loc.bin_after(to);
159 for b in bin_start..bin_end {
165 if b as usize >= bin_count {
166 break;
167 }
168 let covered = loc.bin_coverage(b, from, to);
169 if covered <= 0.0 {
170 continue;
171 }
172 stats[base + b as usize].add(interval.value, covered);
173 }
174 Ok(())
175 })?;
176 Ok(stats)
177 })?;
178
179 let mut output = vec![def_value; ex.locs.output_len];
180 for (batch, stats) in ex.batches.iter().zip(&per_batch) {
181 for (offset, index) in (batch.start..batch.end).enumerate() {
182 let loc = &ex.locs.locs[index];
183 for b in 0..bin_count {
184 let s = &stats[offset * bin_count + b];
185 if s.count <= 0.0 {
186 continue;
187 }
188 output[loc.output_start + b] = s.apply(bin_mode);
189 }
190 }
191 }
192 ex.locs.reverse_output_rows(&mut output);
193 Ok(output)
194}
195
196pub(crate) fn values_stats(
198 ex: &Extraction<'_>,
199 executor: &crate::parallel::Executor,
200) -> Result<Vec<ValueStats>> {
201 let bin_count = ex.locs.bin_count;
202 let per_batch = executor.map_batches(ex.batches, |_, batch| {
203 let mut stats = vec![ValueStats::default(); batch.len()];
204 ex.walk_batch(*batch, |interval, index, _loc, from, to| {
205 let overlap = to - from;
206 let span = interval.end - interval.start;
211 let covered = if span > 0 && interval.valid_count != span {
212 (interval.valid_count as f64 * overlap as f64 / span as f64).round() as i64
213 } else {
214 overlap
215 };
216 let fraction = if interval.valid_count > 0 {
221 covered as f64 / interval.valid_count as f64
222 } else {
223 0.0
224 };
225 stats[index - batch.start].add_aggregate(
226 interval.min_value,
227 interval.max_value,
228 interval.value as f64 * covered as f64,
229 interval.sum_squared * fraction,
230 covered,
231 );
232 Ok(())
233 })?;
234 Ok(stats)
235 })?;
236
237 let mut output = vec![ValueStats::default(); ex.locs.locs.len()];
238 for (batch, stats) in ex.batches.iter().zip(&per_batch) {
239 for (offset, index) in (batch.start..batch.end).enumerate() {
240 output[ex.locs.locs[index].row(bin_count)] = stats[offset];
241 }
242 }
243 Ok(output)
244}
245
246#[derive(Debug, Clone, Copy)]
248struct OpenBin {
249 bin: i64,
251 stats: BinStats,
252 reverse: bool,
256}
257
258pub(crate) fn values_profile(
270 ex: &Extraction<'_>,
271 executor: &crate::parallel::Executor,
272 bin_mode: BinMode,
273) -> Result<Vec<ValueStats>> {
274 let bin_count = ex.locs.bin_count;
275 let per_batch = executor.map_batches(ex.batches, |_, batch| {
276 let mut column = vec![ValueStats::default(); bin_count];
277 let mut open: Vec<OpenBin> = (batch.start..batch.end)
278 .map(|i| OpenBin {
279 bin: -1,
280 stats: BinStats::default(),
281 reverse: ex.locs.locs[i].reverse,
282 })
283 .collect();
284
285 let close = |open: &mut OpenBin, column: &mut Vec<ValueStats>| {
286 if open.stats.count <= 0.0 {
287 return;
288 }
289 let value = open.stats.apply(bin_mode);
290 let col = if open.reverse {
294 bin_count as i64 - 1 - open.bin
295 } else {
296 open.bin
297 };
298 if col >= 0 && (col as usize) < column.len() {
299 column[col as usize].add(value);
305 }
306 open.stats = BinStats::default();
307 };
308
309 ex.walk_batch(*batch, |interval, index, loc, from, to| {
310 let bin_start = loc.bin_at(from);
311 let bin_end = loc.bin_after(to);
312 let slot = &mut open[index - batch.start];
313 for b in bin_start..bin_end {
316 if b as usize >= bin_count {
317 break;
318 }
319 let covered = loc.bin_coverage(b, from, to);
320 if covered <= 0.0 {
321 continue;
322 }
323 if b != slot.bin {
324 close(slot, &mut column);
325 slot.bin = b;
326 }
327 slot.stats.add(interval.value, covered);
328 }
329 Ok(())
330 })?;
331 for slot in &mut open {
332 close(slot, &mut column);
333 }
334 Ok(column)
335 })?;
336
337 let mut output = vec![ValueStats::default(); bin_count];
338 for column in &per_batch {
339 for (col, batch_stats) in column.iter().enumerate() {
340 if batch_stats.count == 0 {
341 continue;
342 }
343 output[col].merge(batch_stats);
344 }
345 }
346 Ok(output)
347}
348
349impl Extraction<'_> {
356 pub(crate) fn walk_bed_batch(
357 &self,
358 batch: LocBatch,
359 auto_sql: &indexmap::IndexMap<String, String>,
360 col_count: usize,
361 mut visit: impl FnMut(&mut BedRecord, &[usize]) -> Result<()>,
362 ) -> Result<()> {
363 let locs = &self.locs.locs;
364 let leaves = LeafWalk::new(self.source, self.tree_root, locs, batch, self.tracker)?;
365 let mut matched: Vec<usize> = Vec::new();
366 for leaf in leaves {
367 let (leaf, loc_range) = leaf?;
368 let block = self.read_leaf(leaf.offset, leaf.size)?;
369 let records = super::block::BedRecords::new(
370 block,
371 auto_sql,
372 col_count,
373 locs,
374 loc_range.clone(),
375 self.source.path(),
376 )?;
377 let mut cursor = loc_range.start;
378 for record in records {
379 let (chr_index, start, end, fields) = record?;
380 cursor = advance_cursor(locs, cursor, loc_range.end, chr_index, start);
381 matched.clear();
382 let reach = end.max(start + 1);
385 #[allow(clippy::needless_range_loop)]
389 for index in cursor..loc_range.end {
390 let loc = &locs[index];
391 if chr_index != loc.chr_index as u32 {
392 break;
393 }
394 if reach <= loc.binned_start {
395 break;
396 }
397 if start >= loc.binned_end {
398 continue;
399 }
400 matched.push(index);
401 }
402 if matched.is_empty() {
403 continue;
404 }
405 let mut entry = BedRecord {
406 chr_index,
407 start,
408 end,
409 fields,
410 };
411 visit(&mut entry, &matched)?;
412 }
413 }
414 Ok(())
415 }
416}
417
418pub(crate) struct BedRecord {
421 pub chr_index: u32,
422 pub start: i64,
423 pub end: i64,
424 pub fields: Vec<(String, String)>,
425}
426
427pub(crate) fn entries(
434 ex: &Extraction<'_>,
435 executor: &crate::parallel::Executor,
436 auto_sql: &indexmap::IndexMap<String, String>,
437 chr_names: &[String],
438 col_count: usize,
439) -> Result<Vec<Vec<super::BedEntry>>> {
440 let bin_count = ex.locs.bin_count;
441 let per_batch = executor.map_batches(ex.batches, |_, batch| {
442 let mut out: Vec<Vec<super::BedEntry>> = vec![Vec::new(); batch.len()];
443 ex.walk_bed_batch(*batch, auto_sql, col_count, |entry, matched| {
444 let chr = chr_names
445 .get(entry.chr_index as usize)
446 .cloned()
447 .unwrap_or_default();
448 for (n, index) in matched.iter().enumerate() {
449 let fields = if n + 1 == matched.len() {
450 std::mem::take(&mut entry.fields)
451 } else {
452 entry.fields.clone()
453 };
454 out[index - batch.start].push(super::BedEntry {
455 chr: chr.clone(),
456 start: entry.start,
457 end: entry.end,
458 fields,
459 });
460 }
461 Ok(())
462 })?;
463 Ok(out)
464 })?;
465
466 let mut output: Vec<Vec<super::BedEntry>> = vec![Vec::new(); ex.locs.locs.len()];
467 for (batch, lists) in ex.batches.iter().zip(per_batch) {
468 for (offset, list) in lists.into_iter().enumerate() {
469 output[ex.locs.locs[batch.start + offset].row(bin_count)] = list;
470 }
471 }
472 for entries in &mut output {
476 entries.sort_by(|a, b| (&a.chr, a.start, a.end).cmp(&(&b.chr, b.start, b.end)));
477 }
478 Ok(output)
479}
480
481pub(crate) fn entries_pileup(
489 ex: &Extraction<'_>,
490 executor: &crate::parallel::Executor,
491 auto_sql: &indexmap::IndexMap<String, String>,
492 def_value: f32,
493) -> Result<Vec<f32>> {
494 let bin_count = ex.locs.bin_count;
495 let per_batch = executor.map_batches(ex.batches, |_, batch| {
496 let mut depth = vec![0.0f32; batch.len() * bin_count];
497 ex.walk_bed_batch(*batch, auto_sql, 3, |entry, matched| {
500 for index in matched {
501 let loc = &ex.locs.locs[*index];
502 if loc.binned_end <= loc.binned_start {
503 continue;
504 }
505 let from = entry.start.max(loc.binned_start);
506 let to = entry.end.min(loc.binned_end);
507 let base = (index - batch.start) * bin_count;
508 for b in loc.bin_at(from)..loc.bin_after(to) {
509 if b as usize >= bin_count {
510 break;
511 }
512 let fraction = loc.bin_fraction(b, from, to);
513 if fraction <= 0.0 {
514 continue;
515 }
516 depth[base + b as usize] += fraction as f32;
517 }
518 }
519 Ok(())
520 })?;
521 Ok(depth)
522 })?;
523
524 let mut output = vec![0.0f32; ex.locs.output_len];
525 for (batch, depth) in ex.batches.iter().zip(&per_batch) {
526 for (offset, index) in (batch.start..batch.end).enumerate() {
527 let loc = &ex.locs.locs[index];
528 output[loc.output_start..loc.output_end]
529 .copy_from_slice(&depth[offset * bin_count..(offset + 1) * bin_count]);
530 }
531 }
532 if def_value != 0.0 {
535 for value in &mut output {
536 if *value == 0.0 {
537 *value = def_value;
538 }
539 }
540 }
541 ex.locs.reverse_output_rows(&mut output);
545 Ok(output)
546}
547
548pub(crate) fn pileup_stats(locs: &IndexedLocs, pileup: &[f32]) -> Vec<ValueStats> {
560 let mut output = vec![ValueStats::default(); locs.locs.len()];
561 for loc in &locs.locs {
562 let stats = &mut output[loc.row(locs.bin_count)];
563 for value in &pileup[loc.output_start..loc.output_end] {
564 if value.is_nan() {
565 continue;
566 }
567 stats.add(*value);
568 }
569 }
570 output
571}
572
573pub(crate) fn pileup_profile(locs: &IndexedLocs, pileup: &[f32]) -> Vec<ValueStats> {
580 let mut output = vec![ValueStats::default(); locs.bin_count];
581 for (col, stats) in output.iter_mut().enumerate() {
582 for loc in &locs.locs {
583 let value = pileup[loc.output_start + col];
584 if value.is_nan() {
585 continue;
586 }
587 stats.add(value);
588 }
589 }
590 output
591}
592
593pub type WindowLoc = (String, i64, i64);
604
605const MIN_PIECE_DATA_SIZE: f64 = 16384.0;
612
613#[derive(Debug)]
622struct Walk {
623 locs: std::sync::Arc<Vec<WindowLoc>>,
624 next: usize,
625 parallel: usize,
626 bytes_per_bp: f64,
629 total_coverage: u64,
630 done_coverage: u64,
631}
632
633impl Walk {
634 fn new(locs: Vec<WindowLoc>, parallel: usize, data_size: u64, genome_size: i64) -> Self {
635 let total_coverage = locs.iter().map(|(_, s, e)| (e - s).max(0) as u64).sum();
636 let bytes_per_bp = if genome_size < 1 || data_size < 1 {
637 0.0
638 } else {
639 data_size as f64 / genome_size as f64
640 };
641 Self {
642 locs: std::sync::Arc::new(locs),
643 next: 0,
644 parallel: parallel.max(1),
645 bytes_per_bp,
646 total_coverage,
647 done_coverage: 0,
648 }
649 }
650
651 fn restarted(&self) -> Self {
658 Self {
659 locs: self.locs.clone(),
660 next: 0,
661 parallel: self.parallel,
662 bytes_per_bp: self.bytes_per_bp,
663 total_coverage: self.total_coverage,
664 done_coverage: 0,
665 }
666 }
667
668 fn split(&self, units: i64, coverage: i64) -> (i64, i64) {
679 let window_data = self.bytes_per_bp * coverage as f64;
680 let worth = (window_data / MIN_PIECE_DATA_SIZE) as i64;
681 let pieces = worth.min(self.parallel as i64).max(1);
682 let piece_units = ((units + pieces - 1) / pieces).max(1);
683 (piece_units, (units + piece_units - 1) / piece_units)
684 }
685
686 fn take(&mut self, progress: Option<&crate::progress::ProgressFn>) -> usize {
691 let index = self.next;
692 self.next += 1;
693 let (_, start, end) = &self.locs[index];
694 self.done_coverage += (end - start).max(0) as u64;
695 if let Some(report) = progress {
696 report(self.done_coverage, self.total_coverage);
697 }
698 index
699 }
700
701 fn finish(&mut self, progress: Option<&crate::progress::ProgressFn>) {
704 if let Some(report) = progress {
705 if self.done_coverage < self.total_coverage {
706 self.done_coverage = self.total_coverage;
707 report(self.total_coverage, self.total_coverage);
708 }
709 }
710 }
711}
712
713pub struct ValuesWalk {
719 walk: Walk,
720 bin_size: i64,
721 bins: std::sync::Arc<Vec<i64>>,
725 bin_mode: BinMode,
726 def_value: f32,
727 zoom: super::Zoom,
728 progress: Option<crate::progress::ProgressFn>,
729}
730
731impl std::fmt::Debug for ValuesWalk {
733 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
734 f.debug_struct("ValuesWalk")
735 .field("windows", &self.walk.locs.len())
736 .field("next", &self.walk.next)
737 .field("bin_size", &self.bin_size)
738 .finish()
739 }
740}
741
742impl ValuesWalk {
743 pub fn restarted(&self) -> Self {
751 Self {
752 walk: self.walk.restarted(),
753 bin_size: self.bin_size,
754 bins: self.bins.clone(),
755 bin_mode: self.bin_mode,
756 def_value: self.def_value,
757 zoom: self.zoom,
758 progress: self.progress.clone(),
759 }
760 }
761
762 pub fn plan(reader: &super::BbiReader, req: &super::ValuesRequest, span: i64) -> Result<Self> {
768 if span < 1 {
769 return Err(Error::invalid(format!(
770 "span must be positive (got {span})"
771 )));
772 }
773 let bin_size =
777 crate::genomic::BinPlan::new(req.common.bin_size, None, req.common.full_bin)?
778 .whole_bin_size();
779 if reader.kind().is_bigbed() && !matches!(req.common.zoom, super::Zoom::Full) {
780 return Err(Error::invalid("zoom is only supported for bigwig files"));
781 }
782 let level = reader.select_zoom(req.common.bin_size, req.common.zoom)?;
785
786 let bins_per_window = ((span + bin_size - 1) / bin_size).max(1);
789 let mut locs = Vec::new();
790 let mut bins = Vec::new();
791 for chr in reader.chr_sizes().select(&req.common.locs.chr_ids)? {
792 let chr_bins = if req.common.full_bin {
796 (chr.size + bin_size - 1) / bin_size
797 } else {
798 chr.size / bin_size
799 };
800 let mut bin = 0;
801 while bin < chr_bins {
802 let start = bin * bin_size;
803 let window_bins = bins_per_window.min(chr_bins - bin);
804 locs.push((
805 chr.id.clone(),
806 start,
807 (start + window_bins * bin_size).min(chr.size),
808 ));
809 bins.push(window_bins);
810 bin += bins_per_window;
811 }
812 }
813
814 let walk = Walk::new(
815 locs,
816 reader.parallel(),
817 reader.data_size(level),
818 reader.genome_size(),
819 );
820 Ok(Self {
821 walk,
822 bin_size,
823 bins: std::sync::Arc::new(bins),
824 bin_mode: req.bin_mode,
825 def_value: req.common.def_value,
826 zoom: req.common.zoom,
827 progress: req.common.progress.clone(),
828 })
829 }
830
831 pub fn len(&self) -> usize {
833 self.walk.locs.len()
834 }
835
836 pub fn bin_size(&self) -> i64 {
842 self.bin_size
843 }
844
845 pub fn is_empty(&self) -> bool {
846 self.walk.locs.is_empty()
847 }
848
849 pub fn locs(&self) -> &[WindowLoc] {
851 &self.walk.locs
852 }
853
854 fn read(&self, reader: &super::BbiReader, index: usize) -> Result<Vec<f32>> {
856 let (chr, start, end) = &self.walk.locs[index];
857 let (piece_bins, piece_count) = self.walk.split(self.bins[index], end - start);
858 let piece_span = piece_bins * self.bin_size;
859
860 let chr_ids = vec![chr.clone(); piece_count as usize];
861 let starts: Vec<i64> = (0..piece_count).map(|i| start + i * piece_span).collect();
862 let ends: Vec<i64> = starts.iter().map(|s| s + piece_span).collect();
863
864 let request =
873 super::ValuesRequest::new(crate::genomic::Locs::spans(&chr_ids, &starts, &ends)?)
874 .bin_size(self.bin_size as f64)
875 .bin_count(piece_bins as usize)
876 .bin_mode(self.bin_mode)
877 .def_value(self.def_value)
878 .zoom(self.zoom);
879 let values = reader.read_values(&request)?;
880 Ok(values.into_raw_vec_and_offset().0)
881 }
882
883 pub fn next_window(
888 &mut self,
889 reader: &super::BbiReader,
890 ) -> Option<Result<ndarray::Array1<f32>>> {
891 if self.walk.next >= self.walk.locs.len() {
892 self.walk.finish(self.progress.as_ref());
893 return None;
894 }
895 let index = self.walk.next;
897 let mut values = match self.read(reader, index) {
898 Ok(v) => v,
899 Err(e) => return Some(Err(e)),
906 };
907 self.walk.take(self.progress.as_ref());
908 values.truncate(self.bins[index] as usize);
912 Some(Ok(ndarray::Array1::from_vec(values)))
913 }
914}
915
916#[derive(Debug)]
920pub struct ValuesWindows<'a> {
921 reader: &'a super::BbiReader,
922 walk: ValuesWalk,
923}
924
925impl<'a> ValuesWindows<'a> {
926 pub(crate) fn plan(
927 reader: &'a super::BbiReader,
928 req: &super::ValuesRequest,
929 span: i64,
930 ) -> Result<Self> {
931 Ok(Self {
932 reader,
933 walk: ValuesWalk::plan(reader, req, span)?,
934 })
935 }
936
937 pub fn len(&self) -> usize {
938 self.walk.len()
939 }
940 pub fn is_empty(&self) -> bool {
941 self.walk.is_empty()
942 }
943 pub fn locs(&self) -> &[WindowLoc] {
945 self.walk.locs()
946 }
947 pub fn bin_size(&self) -> i64 {
950 self.walk.bin_size()
951 }
952}
953
954impl Iterator for ValuesWindows<'_> {
955 type Item = Result<ndarray::Array1<f32>>;
956 fn next(&mut self) -> Option<Self::Item> {
957 self.walk.next_window(self.reader)
958 }
959}
960
961pub struct EntryWalk {
963 walk: Walk,
964 col_count: usize,
965 progress: Option<crate::progress::ProgressFn>,
966}
967
968impl std::fmt::Debug for EntryWalk {
970 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
971 f.debug_struct("EntryWalk")
972 .field("windows", &self.walk.locs.len())
973 .field("next", &self.walk.next)
974 .field("col_count", &self.col_count)
975 .finish()
976 }
977}
978
979impl EntryWalk {
980 pub fn restarted(&self) -> Self {
983 Self {
984 walk: self.walk.restarted(),
985 col_count: self.col_count,
986 progress: self.progress.clone(),
987 }
988 }
989
990 pub fn plan(reader: &super::BbiReader, req: &super::EntriesRequest, span: i64) -> Result<Self> {
991 if !reader.kind().is_bigbed() {
992 return Err(Error::invalid("iter_all_entries only for bigbed"));
993 }
994 if span < 1 {
995 return Err(Error::invalid(format!(
996 "span must be positive (got {span})"
997 )));
998 }
999 reader.check_col_count(req.col_count, 3)?;
1000
1001 let mut locs = Vec::new();
1002 for chr in reader.chr_sizes().select(&req.common.locs.chr_ids)? {
1003 let mut start = 0;
1004 while start < chr.size {
1005 locs.push((chr.id.clone(), start, (start + span).min(chr.size)));
1006 start += span;
1007 }
1008 }
1009 let walk = Walk::new(
1012 locs,
1013 reader.parallel(),
1014 reader.data_size(None),
1015 reader.genome_size(),
1016 );
1017 Ok(Self {
1018 walk,
1019 col_count: req.col_count,
1020 progress: req.common.progress.clone(),
1021 })
1022 }
1023
1024 pub fn len(&self) -> usize {
1025 self.walk.locs.len()
1026 }
1027
1028 pub fn is_empty(&self) -> bool {
1029 self.walk.locs.is_empty()
1030 }
1031
1032 pub fn locs(&self) -> &[WindowLoc] {
1033 &self.walk.locs
1034 }
1035
1036 fn read(&self, reader: &super::BbiReader, index: usize) -> Result<Vec<super::BedEntry>> {
1037 let (chr, start, end) = &self.walk.locs[index];
1038 let coverage = end - start;
1039 let (piece_span, piece_count) = self.walk.split(coverage, coverage);
1040
1041 let chr_ids = vec![chr.clone(); piece_count as usize];
1042 let mut starts = Vec::with_capacity(piece_count as usize);
1043 let mut ends = Vec::with_capacity(piece_count as usize);
1044 let mut piece_min_starts = Vec::with_capacity(piece_count as usize);
1045 for i in 0..piece_count {
1046 let piece_start = start + i * piece_span;
1047 piece_min_starts.push(piece_start);
1048 starts.push((piece_start - 1).max(0));
1054 ends.push((piece_start + piece_span).min(*end));
1055 }
1056
1057 let request =
1058 super::EntriesRequest::new(crate::genomic::Locs::spans(&chr_ids, &starts, &ends)?)
1059 .col_count(self.col_count);
1060 let pieces = reader.read_entries(&request)?;
1061
1062 let mut out = Vec::new();
1063 for (piece, min_start) in pieces.into_iter().zip(piece_min_starts) {
1064 out.extend(piece.into_iter().filter(|e| e.start >= min_start));
1065 }
1066 Ok(out)
1067 }
1068
1069 pub fn next_window(
1071 &mut self,
1072 reader: &super::BbiReader,
1073 ) -> Option<Result<Vec<super::BedEntry>>> {
1074 if self.walk.next >= self.walk.locs.len() {
1075 self.walk.finish(self.progress.as_ref());
1076 return None;
1077 }
1078 let index = self.walk.next;
1079 let entries = match self.read(reader, index) {
1080 Ok(e) => e,
1081 Err(e) => return Some(Err(e)),
1083 };
1084 self.walk.take(self.progress.as_ref());
1085 Some(Ok(entries))
1086 }
1087}
1088
1089#[derive(Debug)]
1091pub struct EntryWindows<'a> {
1092 reader: &'a super::BbiReader,
1093 walk: EntryWalk,
1094}
1095
1096impl<'a> EntryWindows<'a> {
1097 pub(crate) fn plan(
1098 reader: &'a super::BbiReader,
1099 req: &super::EntriesRequest,
1100 span: i64,
1101 ) -> Result<Self> {
1102 Ok(Self {
1103 reader,
1104 walk: EntryWalk::plan(reader, req, span)?,
1105 })
1106 }
1107
1108 pub fn len(&self) -> usize {
1109 self.walk.len()
1110 }
1111 pub fn is_empty(&self) -> bool {
1112 self.walk.is_empty()
1113 }
1114 pub fn locs(&self) -> &[WindowLoc] {
1115 self.walk.locs()
1116 }
1117}
1118
1119impl Iterator for EntryWindows<'_> {
1120 type Item = Result<Vec<super::BedEntry>>;
1121 fn next(&mut self) -> Option<Self::Item> {
1122 self.walk.next_window(self.reader)
1123 }
1124}
1125
1126#[cfg(test)]
1127mod tests {
1128 use super::*;
1129
1130 fn loc(chr: usize, start: i64, end: i64) -> IndexedLoc {
1131 IndexedLoc {
1132 chr_index: chr,
1133 start,
1134 end,
1135 binned_start: start,
1136 binned_end: end,
1137 bin_size: 1.0,
1138 reverse: false,
1139 output_start: 0,
1140 output_end: 1,
1141 }
1142 }
1143
1144 #[test]
1145 fn the_cursor_skips_loci_the_block_has_passed() {
1146 let locs = [loc(0, 0, 10), loc(0, 20, 30), loc(0, 40, 50)];
1147 assert_eq!(advance_cursor(&locs, 0, 3, 0, 25), 1);
1149 assert_eq!(advance_cursor(&locs, 0, 3, 0, 45), 2);
1151 assert_eq!(advance_cursor(&locs, 0, 3, 0, 5), 0);
1153 }
1154
1155 #[test]
1156 fn the_cursor_stops_at_a_higher_chromosome() {
1157 let locs = [loc(0, 0, 10), loc(1, 0, 10), loc(2, 0, 10)];
1158 assert_eq!(advance_cursor(&locs, 0, 3, 1, 5), 1);
1160 assert_eq!(advance_cursor(&locs, 0, 3, 0, 5), 0);
1162 }
1163
1164 #[test]
1165 fn the_cursor_never_goes_backwards() {
1166 let locs = [loc(0, 0, 10), loc(0, 20, 30)];
1167 assert_eq!(advance_cursor(&locs, 1, 2, 0, 0), 1);
1168 }
1169}