1use{
2 crate::histogram::*,
3 num_traits::{
4 Bounded,
5 int::*,
6 cast::*,
7 identities::*,
8 ops::{
9 checked::*,
10 wrapping::*
11 }
12 },
13 std::{
14 borrow::*,
15 ops::*,
16 num::*
17 }
18};
19use super::binning::BinDisplay;
20
21#[cfg(feature = "serde_support")]
22use serde::{Serialize, Deserialize};
23
24#[derive(Debug, PartialEq, Eq, Clone, Copy)]
26pub enum Outcome{
27 Success,
29 Failure
31}
32
33impl Outcome{
34 pub fn is_success(self) -> bool
36 {
37 self == Outcome::Success
38 }
39
40 pub fn is_failure(self) -> bool
42 {
43 self == Outcome::Failure
44 }
45}
46
47#[derive(Debug, Clone)]
51#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
52pub struct HistogramFast<T> {
53 left: T,
54 right: T,
55 hist: Vec<usize>,
56}
57
58impl<T> BinDisplay for HistogramFast<T>
59where
60T: PrimInt + HasUnsignedVersion + Copy + std::fmt::Display + WrappingAdd,
61T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes>
62 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned>
63{
64 type BinEntry = T;
65
66 fn display_bin_iter(&'_ self) -> Box<dyn Iterator<Item=Self::BinEntry> + '_>{
67 Box::new(
68 self.bin_iter()
69 )
70 }
71
72 fn write_bin<W: std::io::Write>(entry: &Self::BinEntry, mut writer: W) -> std::io::Result<()> {
73 write!(writer, "{entry}")
74 }
75
76 fn write_header<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
77 write!(writer, "Bin")
78 }
79}
80
81impl<T> HistogramFast<T>
82where T: Copy
83{
84 pub fn left(&self) -> T
86 {
87 self.left
88 }
89
90 pub fn right(&self) -> T
92 {
93 self.right
94 }
95
96 pub fn range_inclusive(&self) -> RangeInclusive<T>
98 {
99 self.left..=self.right
100 }
101}
102
103impl<T> HistogramFast<T>
104 where
105 T: PrimInt + HasUnsignedVersion + WrappingAdd,
106 T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes>
107 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned>
108{
109 pub fn new(left: T, right: T) -> Result<Self, HistErrors>
113 {
114 let right = match right.checked_sub(&T::one()){
115 Some(res) => res,
116 None => return Err(HistErrors::Underflow),
117 };
118 Self::new_inclusive(left, right)
119 }
120
121 pub fn new_inclusive(left: T, right: T) -> Result<Self, HistErrors>
126 {
127 if left > right {
128 Err(HistErrors::OutsideHist)
129 } else {
130 let left_u = to_u(left);
131 let right_u = to_u(right);
132 let size = match (right_u - left_u).to_usize(){
133 Some(val) => match val.checked_add(1){
134 Some(val) => val,
135 None => return Err(HistErrors::Overflow),
136 },
137 None => return Err(HistErrors::UsizeCastError),
138 };
139
140 Ok(
141 Self{
142 left,
143 right,
144 hist: vec![0; size],
145 }
146 )
147 }
148 }
149
150 pub fn bin_iter(&self) -> impl Iterator<Item=T>
165 {
166 HistFastIterHelper{
167 current: self.left,
168 right: self.right,
169 invalid: false
170 }
171
172 }
173
174 pub fn bin_hits_iter(&'_ self) -> impl Iterator<Item=(T, usize)> + '_
193 {
194 self.bin_iter()
195 .zip(
196 self.hist
197 .iter()
198 .copied()
199 )
200 }
201
202 pub fn equal_range(&self, other: &Self) -> bool
205 where T: Eq
206 {
207 self.left.eq(&other.left) && self.right.eq(&other.right)
208 }
209
210 pub fn try_add(&mut self, other: &Self) -> Outcome
217 where T: Eq
218 {
219 if self.equal_range(other) {
220 self.hist
221 .iter_mut()
222 .zip(other.hist().iter())
223 .for_each(|(s, o)| *s += o);
224 Outcome::Success
225 } else {
226 Outcome::Failure
227 }
228 }
229
230 #[inline]
231 pub fn increment<V: Borrow<T>>(&mut self, val: V) -> Result<usize, HistErrors> {
238 self.count_val(val)
239 }
240
241 #[inline]
242 pub fn increment_quiet<V: Borrow<T>>(&mut self, val: V)
246 {
247 let _ = self.increment(val);
248 }
249}
250
251pub(crate) struct HistFastIterHelper<T>
252{
253 pub(crate) current: T,
254 pub(crate) right: T,
255 pub(crate) invalid: bool,
256}
257
258impl<T> Iterator for HistFastIterHelper<T>
259where
260 T: PrimInt + WrappingAdd,
261{
262 type Item = T;
263
264 #[inline]
265 fn next(&mut self) -> Option<T>
266 {
267 if self.invalid {
268 return None;
269 }
270
271 let next = self.current.wrapping_add(&T::one());
272 let current = std::mem::replace(&mut self.current, next);
273 self.invalid = current == self.right;
274 Some(
275 current
276 )
277
278 }
279}
280
281pub(crate) struct BinModIterHelper<T>
282{
283 pub(crate) current: T,
284 pub(crate) right: T,
285 pub(crate) step_by: T,
286 pub(crate) invalid: bool,
287}
288
289impl<T> BinModIterHelper<T>
290{
291 pub(crate) fn new_unchecked(left: T, right: T, step_by: T) -> Self
292 {
293 Self{
294 current: left,
295 right,
296 step_by,
297 invalid: false
298 }
299 }
300}
301
302impl<T> Iterator for BinModIterHelper<T>
303where
304 T: Add::<T, Output=T>
305 + Ord + Copy + WrappingAdd
306 + WrappingSub
307 + One,
308{
309 type Item = RangeInclusive<T>;
310
311 #[inline]
312 fn next(&mut self) -> Option<RangeInclusive<T>>
313 {
314 if self.invalid {
315 return None;
316 }
317
318 let next = self.current.wrapping_add(&self.step_by);
319 let right = next.wrapping_sub(&T::one());
320 self.invalid = right == self.right;
321 let left = std::mem::replace(&mut self.current, next);
322 Some(
323 left..=right
324 )
325
326 }
327}
328
329impl<T> HistogramPartition for HistogramFast<T>
330where T: PrimInt + CheckedSub + ToPrimitive + CheckedAdd + One + FromPrimitive
331 + HasUnsignedVersion + Bounded + WrappingAdd,
332T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes, Unsigned=T::Unsigned>
333 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned> + FromPrimitive + WrappingSub,
334{
335 fn overlapping_partition(&self, n: NonZeroUsize, overlap: usize) -> Result<Vec<Self>, HistErrors>
336 {
337 let mut result = Vec::with_capacity(n.get());
338 let size = self.bin_count() - 1;
339 let denominator = n.get() + overlap;
340 for c in 0..n.get() {
341 let left_distance = c.checked_mul(size)
342 .ok_or(HistErrors::Overflow)?
343 / denominator;
344
345 let left = to_u(self.left) + T::Unsigned::from_usize(left_distance)
346 .ok_or(HistErrors::CastError)?;
347
348 let right_distance = (c + overlap + 1).checked_mul(size)
349 .ok_or(HistErrors::Overflow)?
350 / denominator;
351
352 let right = to_u(self.left) + T::Unsigned::from_usize(right_distance)
353 .ok_or(HistErrors::CastError)?;
354
355 let left = from_u(left);
356 let right = from_u(right);
357
358 result.push(Self::new_inclusive(left, right)?);
359 if result.last()
360 .unwrap()
361 .hist
362 .is_empty()
363 {
364 return Err(HistErrors::IntervalWidthZero);
365 }
366
367 }
368 Ok(result)
369 }
370}
371
372pub type HistUsizeFast = HistogramFast<usize>;
374pub type HistU128Fast = HistogramFast<u128>;
376pub type HistU64Fast = HistogramFast<u64>;
378pub type HistU32Fast = HistogramFast<u32>;
380pub type HistU16Fast = HistogramFast<u16>;
382pub type HistU8Fast = HistogramFast<u8>;
384
385pub type HistIsizeFast = HistogramFast<isize>;
387pub type HistI128Fast = HistogramFast<i128>;
389pub type HistI64Fast = HistogramFast<i64>;
391pub type HistI32Fast = HistogramFast<i32>;
393pub type HistI16Fast = HistogramFast<i16>;
395pub type HistI8Fast = HistogramFast<i8>;
397
398
399impl<T> Histogram for HistogramFast<T>
400{
401
402 #[inline]
403 fn increment_index_by(&mut self, index: usize, count: usize) -> Result<(), HistErrors> {
404 match self.hist.get_mut(index) {
405 None => Err(HistErrors::OutsideHist),
406 Some(val) => {
407 *val += count;
408 Ok(())
409 },
410 }
411 }
412
413 #[inline]
414 fn hist(&self) -> &Vec<usize> {
415 &self.hist
416 }
417
418 #[inline]
419 fn bin_count(&self) -> usize {
420 self.hist.len()
421 }
422
423 #[inline]
424 fn reset(&mut self) {
425 self.hist
427 .iter_mut()
428 .for_each(|h| *h = 0);
429 }
430}
431
432impl<T> HistogramVal<T> for HistogramFast<T>
433where T: PrimInt + HasUnsignedVersion + WrappingAdd,
434 T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes>
435 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned>
436{
437 #[inline]
438 fn first_border(&self) -> T {
439 self.left
440 }
441
442 fn last_border(&self) -> T {
443 self.right
444 }
445
446 #[inline(always)]
447 fn last_border_is_inclusive(&self) -> bool {
448 true
449 }
450
451 fn distance<V: Borrow<T>>(&self, val: V) -> f64 {
452 let val = val.borrow();
453 if self.not_inside(val) {
454 let dist = if *val < self.first_border() {
455 self.first_border() - *val
456 } else {
457 val.saturating_sub(self.right)
458 };
459 dist.to_f64()
460 .unwrap_or(f64::INFINITY)
461 } else {
462 0.0
463 }
464 }
465
466 #[inline(always)]
467 fn get_bin_index<V: Borrow<T>>(&self, val: V) -> Result<usize, HistErrors> {
468 let val = *val.borrow();
469 if val <= self.right{
470 match val.checked_sub(&self.left) {
471 None => {
472 let left = self.left.to_isize()
473 .ok_or(HistErrors::CastError)?;
474 let val = val.to_isize()
475 .ok_or(HistErrors::CastError)?;
476 match val.checked_sub(left){
477 None => Err(HistErrors::OutsideHist),
478 Some(index) => {
479 index.to_usize()
480 .ok_or(HistErrors::OutsideHist)
481 }
482 }
483 },
484 Some(index) => index.to_usize()
485 .ok_or(HistErrors::OutsideHist)
486 }
487 } else {
488 Err(HistErrors::OutsideHist)
489 }
490 }
491
492 fn bin_enum_iter(&self) -> Box<dyn Iterator<Item=Bin<T>> + '_>{
496
497 let iter = self
498 .bin_iter()
499 .map(|bin| Bin::SingleValued(bin));
500 Box::new(iter)
501 }
502
503 #[inline]
504 fn is_inside<V: Borrow<T>>(&self, val: V) -> bool {
505 let val = *val.borrow();
506 val >= self.left && val <= self.right
507 }
508
509 #[inline]
510 fn not_inside<V: Borrow<T>>(&self, val: V) -> bool {
511 let val = *val.borrow();
512 val > self.right || val < self.left
513 }
514
515 #[inline]
516 fn count_val<V: Borrow<T>>(&mut self, val: V) -> Result<usize, HistErrors> {
517 let index = self.get_bin_index(val)?;
518 self.hist[index] += 1;
519 Ok(index)
520 }
521}
522
523impl<T> HistogramIntervalDistance<T> for HistogramFast<T>
524where Self: HistogramVal<T>,
525 T: PartialOrd + std::ops::Sub<Output=T> + NumCast + Copy
526{
527 fn interval_distance_overlap<V: Borrow<T>>(&self, val: V, overlap: NonZeroUsize) -> usize {
528 let val = val.borrow();
529 if self.not_inside(val) {
530 let num_bins_overlap = 1usize.max(self.bin_count() / overlap.get());
531 let dist =
532 if *val < self.left {
533 self.left - *val
534 } else {
535 *val - self.right
536 };
537 1 + dist.to_usize().unwrap() / num_bins_overlap
538 } else {
539 0
540 }
541 }
542}
543
544impl<T> HistogramCombine for HistogramFast<T>
545 where Self: HistogramVal<T>,
546 T: PrimInt + HasUnsignedVersion + WrappingAdd,
547 T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes>
548 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned>
549{
550 fn encapsulating_hist<S>(hists: &[S]) -> Result<Self, HistErrors>
551 where S: Borrow<Self> {
552 if hists.is_empty() {
553 Err(HistErrors::EmptySlice)
554 } else if hists.len() == 1 {
555 let h = hists[0].borrow();
556 Ok(Self{
557 left: h.left,
558 right: h.right,
559 hist: vec![0; h.hist.len()]
560 })
561 } else {
562 let mut min = hists[0].borrow().left;
563 let mut max = hists[0].borrow().right;
564 hists[1..].iter()
565 .for_each(
566 |h|
567 {
568 let h = h.borrow();
569 if h.left < min {
570 min = h.left;
571 }
572 if h.right > max {
573 max = h.right;
574 }
575 }
576 );
577 Self::new_inclusive(min, max)
578 }
579 }
580
581 fn align<S>(&self, right: S) -> Result<usize, HistErrors>
582 where S: Borrow<Self> {
583 let right = right.borrow();
584
585 if self.is_inside(right.left) {
586 (to_u(right.left) - to_u(self.left))
587 .to_usize()
588 .ok_or(HistErrors::UsizeCastError)
589 } else {
590 Err(HistErrors::OutsideHist)
591 }
592 }
593}
594
595impl<T> IntervalOrder for HistogramFast<T>
596where T: PrimInt
597{
598 fn left_compare(&self, other: &Self) -> std::cmp::Ordering {
599 let order = self.left.cmp(&other.left);
600 if order.is_eq() {
601 return self.right.cmp(&other.right)
602 }
603 order
604 }
605}
606
607
608
609#[cfg(test)]
610mod tests{
611 use super::*;
612 use rand::{distr::*, SeedableRng};
613 use rand_pcg::Pcg64Mcg;
614
615 fn hist_test_fast<T>(left: T, right: T)
616 where T: PrimInt + num_traits::Bounded + PartialOrd + CheckedSub + One
617 + NumCast + Copy + FromPrimitive + HasUnsignedVersion + WrappingAdd,
618 std::ops::RangeInclusive<T>: Iterator<Item=T>,
619 T::Unsigned: Bounded + HasUnsignedVersion<LeBytes=T::LeBytes>
620 + WrappingAdd + ToPrimitive + Sub<Output=T::Unsigned>
621 {
622 let mut hist = HistogramFast::<T>::new_inclusive(left, right).unwrap();
623 assert!(hist.not_inside(T::max_value()));
624 assert!(hist.not_inside(T::min_value()));
625 let two = unsafe{NonZeroUsize::new_unchecked(2)};
626 for (id, i) in (left..=right).enumerate() {
627 assert!(hist.is_inside(i));
628 assert_eq!(hist.is_inside(i), !hist.not_inside(i));
629 assert!(hist.get_bin_index(i).unwrap() == id);
630 assert_eq!(hist.distance(i), 0.0);
631 assert_eq!(hist.interval_distance_overlap(i, two), 0);
632 hist.count_val(i).unwrap();
633 }
634 let lm1 = left - T::one();
635 let rp1 = right + T::one();
636 assert!(hist.not_inside(lm1));
637 assert!(hist.not_inside(rp1));
638 assert_eq!(hist.is_inside(lm1), !hist.not_inside(lm1));
639 assert_eq!(hist.is_inside(rp1), !hist.not_inside(rp1));
640 assert_eq!(hist.distance(lm1), 1.0);
641 assert_eq!(hist.distance(rp1), 1.0);
642 let one = unsafe{NonZeroUsize::new_unchecked(1)};
643 assert_eq!(hist.interval_distance_overlap(rp1, one), 1);
644 assert_eq!(hist.interval_distance_overlap(lm1, one), 1);
645 assert_eq!(hist.bin_enum_iter().count(), hist.bin_count());
646 }
647
648 #[test]
649 fn hist_fast()
650 {
651 hist_test_fast(20usize, 31usize);
652 hist_test_fast(-23isize, 31isize);
653 hist_test_fast(-23i16, 31);
654 hist_test_fast(1u8, 3u8);
655 hist_test_fast(123u128, 300u128);
656 hist_test_fast(-123i128, 300i128);
657
658 hist_test_fast(-100i8, 100i8);
659 }
660
661
662
663 #[test]
664 fn hist_creation()
665 {
666 let _ = HistU8Fast::new_inclusive(0, u8::MAX).unwrap();
667 let _ = HistI8Fast::new_inclusive(i8::MIN, i8::MAX).unwrap();
668 }
669
670 #[test]
671 fn partion_test()
672 {
673 let n = NonZeroUsize::new(2).unwrap();
674 let h = HistU8Fast::new_inclusive(0, u8::MAX).unwrap();
675 let h_part = h.overlapping_partition(n, 0).unwrap();
676 assert_eq!(h.left, h_part[0].left);
677 assert_eq!(h.right, h_part.last().unwrap().right);
678
679
680 let h = HistI8Fast::new_inclusive(i8::MIN, i8::MAX).unwrap();
681 let h_part = h.overlapping_partition(n, 0).unwrap();
682 assert_eq!(h.left, h_part[0].left);
683 assert_eq!(h.right, h_part.last().unwrap().right);
684
685 let h = HistI16Fast::new_inclusive(i16::MIN, i16::MAX).unwrap();
686 let h_part = h.overlapping_partition(n, 2).unwrap();
687 assert_eq!(h.left, h_part[0].left);
688 assert_eq!(h.right, h_part.last().unwrap().right);
689
690
691 let _ = h.overlapping_partition(NonZeroUsize::new(2000).unwrap(), 0).unwrap();
692 }
693
694 #[test]
695 fn overlapping_partition_test2()
696 {
697 let mut rng = Pcg64Mcg::seed_from_u64(2314668);
698 let uni = Uniform::new_inclusive(-100, 100)
699 .unwrap();
700 for overlap in 0..=3 {
701 for _ in 0..100 {
702 let (left, right) = loop {
703 let mut num_1 = uni.sample(&mut rng);
704 let mut num_2 = uni.sample(&mut rng);
705
706 if num_1 != num_2 {
707 if num_2 < num_1 {
708 std::mem::swap(&mut num_1, &mut num_2);
709 }
710 if (num_2 as isize - num_1 as isize) < (overlap as isize + 1) {
711 continue;
712 }
713 break (num_1, num_2)
714 }
715 };
716 let hist_fast = HistI8Fast::new_inclusive(left, right).unwrap();
717 let overlapping = hist_fast.overlapping_partition(NonZeroUsize::new(3).unwrap(), overlap).unwrap();
718
719 assert_eq!(
720 overlapping.last().unwrap().last_border(),
721 hist_fast.last_border()
722 );
723
724 assert_eq!(
725 overlapping.first().unwrap().first_border(),
726 hist_fast.first_border()
727 );
728 }
729 }
730 }
731
732 #[test]
733 fn hist_combine()
734 {
735 let left = HistI8Fast::new_inclusive(-5,0).unwrap();
736 let right = HistI8Fast::new_inclusive(-1, 2).unwrap();
737
738 let en = HistI8Fast::encapsulating_hist(&[&left, &right]).unwrap();
739
740 assert_eq!(en.left, left.left);
741 assert_eq!(en.right, right.right);
742 assert_eq!(en.bin_count(), 8);
743
744 let align = left.align(right).unwrap();
745
746 assert_eq!(align, 4);
747
748 let left = HistI8Fast::new_inclusive(i8::MIN, 0).unwrap();
749 let right = HistI8Fast::new_inclusive(0, i8::MAX).unwrap();
750
751 let en = HistI8Fast::encapsulating_hist(&[&left, &right]).unwrap();
752
753 assert_eq!(en.bin_count(), 256);
754
755 let align = left.align(right).unwrap();
756
757 assert_eq!(128, align);
758
759 let left = HistI8Fast::new_inclusive(i8::MIN, i8::MAX).unwrap();
760 let small = HistI8Fast::new_inclusive(127, 127).unwrap();
761
762 let align = left.align(&small).unwrap();
763
764 assert_eq!(255, align);
765
766 let en = HistI8Fast::encapsulating_hist(&[&left]).unwrap();
767 assert_eq!(en.bin_count(), 256);
768 let slice = [&left];
769 let en = HistI8Fast::encapsulating_hist(&slice[1..]);
770 assert_eq!(en.err(), Some(HistErrors::EmptySlice));
771 let en = HistI8Fast::encapsulating_hist(&[small, left]).unwrap();
772
773 assert_eq!(en.bin_count(), 256);
774 }
775
776 #[test]
777 fn hist_try_add()
778 {
779 let mut first = HistU8Fast::new_inclusive(0, 23)
780 .unwrap();
781 let mut second = HistU8Fast::new_inclusive(0, 23)
782 .unwrap();
783
784 for i in 0..=23{
785 first.increment(i)
786 .unwrap();
787 }
788 for i in 0..=11{
789 second.increment(i)
790 .unwrap();
791 }
792
793 let outcome = first.try_add(&second);
794 assert!(outcome.is_success());
795
796 let hist = first.hist();
797
798 #[allow(clippy::needless_range_loop)]
799 for i in 0..=11{
800 assert_eq!(hist[i], 2);
801 }
802 #[allow(clippy::needless_range_loop)]
803 for i in 12..=23{
804 assert_eq!(hist[i], 1);
805 }
806
807 let third = HistU8Fast::new(0,23)
808 .unwrap();
809
810 let outcome = first.try_add(&third);
811 assert!(
812 outcome.is_failure(),
813 "Needs to be Err because ranges do not match"
814 )
815 }
816
817}