1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use super::*;
use std::borrow::Borrow;

#[cfg(feature = "serde_support")]
use serde::{Serialize, Deserialize};


mod binning_int_fast;
pub use binning_int_fast::*;
mod binning_int_multi;
pub use binning_int_multi::*;

/// # Definition of a Bin
/// * Note: Most (currently all) implementations use more efficient representations of the bins underneath,
/// but are capable of returning the bins in this representation on request
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum Bin<T>
{
    /// The bin consists of a single value. A value is inside the bin if it equals this value
    SingleValued(T),
    /// The bin is defined by two inclusive borders (left, right).
    /// a value is inside the bin, if left <= value <= right
    InclusiveInclusive(T, T),
    /// The bin is defined by an inclusive and an exclusive border (left, right).
    /// a value is inside the bin, if left <= value < right
    InclusiveExclusive(T, T),
    /// The bin is defined by an exclusive and an inclusive border (left, right).
    /// a value is inside the bin, if left < value <= right
    ExclusiveInclusive(T, T),
    /// The bin is defined by two exclusive borders (left, right).
    /// a value is inside the bin, if left < value < right
    ExclusiveExclusive(T, T)
}


/// # Implements Binning
/// * Part of a histogram, but without the capability of counting stuff
/// 
/// # Note
/// * Currently binning is not used in this lib. But I plan to Refactor the histograms 
/// in the next breaking release such that one only has to implement Binning 
/// and can create a histogram from that
pub trait Binning<T>{
    /// convert val to the respective binning index
    fn get_bin_index<V: Borrow<T>>(&self, val: V) -> Option<usize>;

    /// # Get the number of underlying bins
    /// * note: if more than usize::MAX bins are there, usize::MAX is returned
    fn get_bin_len(&self) -> usize;

    /// # Iterates over all bins
    /// * Note: Most (currently all) implementations use more efficient representations of the bins underneath,
    /// but are capable of returning the bins in this representation on request
    /// * Also look if the Binning you use implements another method for the bin borders, e.g., `single_valued_bin_iter`,
    /// as that is more efficient
    fn bin_iter(&self) -> Box<dyn Iterator<Item=Bin<T>>>;

    /// Does a value correspond to a valid bin?
    fn is_inside<V: Borrow<T>>(&self, val: V) -> bool;

    /// Opposite of `is_inside`
    fn not_inside<V: Borrow<T>>(&self, val: V) -> bool;

    /// get the left most border (inclusive)
    fn first_border(&self) -> T;

    /// * get last border from the right
    /// * Note: this border might be inclusive or exclusive
    fn last_border(&self) -> T;

    /// # True if last border is inclusive, false otherwise
    /// * For most usecases this will return a constant value,
    /// as this is likely only dependent on the underlying type and not 
    /// on something that changes dynamically
    fn last_border_is_inclusive(&self) -> bool;

    /// # calculates some sort of absolute distance to the nearest valid bin
    /// * any invalid numbers (like NAN or INFINITY) should have the highest distance possible
    /// * if a value corresponds to a valid bin, the distance should be zero
    fn distance<V: Borrow<T>>(&self, val: V) -> f64;
}



#[derive(Clone, Copy, Debug)]
pub enum BinType{
    /// The bin can be defined via a single value
    SingleValued,
    /// the bin is defined by a left inclusive and a right exclusive border
    InclusiveExclusive,
    /// The bin is defined by a left exclusive and a left inclusive border
    ExclusiveInclusive
}


/// # Trait used to display bins
/// * This is, e.g., used by the glue writers to write the bins of the merged results
pub trait BinDisplay {
    type BinEntry;

    /// # Iterator over all the bins
    /// * you might require to use this if you are working with generics
    /// * if you are working with a specific type there is usually a more efficient implementation
    /// that did not require the usage of dynamic traits (`dyn`) and that are thus more efficient,
    /// consider using those instead
    fn display_bin_iter(&'_ self) -> Box<dyn Iterator<Item=Self::BinEntry> + '_>;

    /// # For writing a bin
    /// * How to write a bin to a file? If a bin consists, e.g., of an exclusive and inclusive border this 
    /// might require the writing of two values. It could also be a single value instead.
    /// It should be something the user expects from your binning, see write header
    fn write_bin<W: std::io::Write>(entry: &Self::BinEntry, writer: W) -> std::io::Result<()>;

    /// # Writing the header of the bin
    /// * This is intended to name, e.g., a column in a file. Output could be "SingleBin" or "BinBorderExclusive BinBorderInclusive"
    /// and so on
    fn write_header<W: std::io::Write>(&self, writer: W) -> std::io::Result<()>;
}