easy_ml/matrices/
slices.rs

1/*!
2 * Ways to transform and access matrices
3 *
4 * Slicing can only be used to downsize matrices with [retain](super::Matrix::retain()) and
5 * [retain_mut](super::Matrix::retain_mut()). In the future it may be available
6 * for further use with the views APIs, which can already apply many more transformations.
7 *
8 * See also
9 * - [views](crate::matrices::views)
10 */
11
12use std::ops::Range;
13
14use crate::matrices::{Column, Row};
15
16/**
17 * A slice defines across one dimension what values are accepted,
18 * it can act like a filter. Slices can also be constructed via
19 * boolean logic operations in the same way as in predicate logic expressions.
20 */
21#[non_exhaustive]
22pub enum Slice {
23    /** A slice that accepts all indexes */
24    All(),
25    /** A slice that accepts no indexes */
26    None(),
27    /** A slice that accepts only the provided index */
28    Single(usize),
29    /** A slice that accepts only indexes within the range */
30    Range(Range<usize>),
31    /**
32     * A slice which rejects all indexes accepted by the argument, and accepts all indexes
33     * rejected by the argument.
34     */
35    Not(Box<Slice>),
36    /**
37     * A slice which accepts only indexes accepted by both arguments, and rejects all others.
38     */
39    And(Box<Slice>, Box<Slice>),
40    /**
41     * A slice which accepts indexes accepted by either arguments, and rejects only
42     * indexes accepted by neither. This is an inclusive or.
43     *
44     * You could construct an exclusive or by using combinations of AND, OR and NOT as
45     * (a AND (NOT b)) OR ((NOT a) AND b) = a XOR b.
46     */
47    Or(Box<Slice>, Box<Slice>),
48}
49
50/**
51 * A kind of slice that can be taken on a matrix, over its rows and columns.
52 */
53pub struct Slice2D {
54    pub(crate) rows: Slice,
55    pub(crate) columns: Slice,
56}
57
58impl Slice {
59    /**
60     * Checks if this slice accepts some index.
61     */
62    pub fn accepts(&self, index: usize) -> bool {
63        match self {
64            Slice::All() => true,
65            Slice::None() => false,
66            Slice::Single(i) => i == &index,
67            Slice::Range(range) => range.contains(&index),
68            Slice::Not(slice) => !slice.accepts(index),
69            Slice::And(slice1, slice2) => slice1.accepts(index) && slice2.accepts(index),
70            Slice::Or(slice1, slice2) => slice1.accepts(index) || slice2.accepts(index),
71        }
72    }
73
74    /**
75     * Returns the negation of this slice
76     */
77    #[allow(clippy::should_implement_trait)]
78    pub fn not(self) -> Slice {
79        Slice::Not(Box::new(self))
80    }
81
82    /**
83     * Returns the and of this slice and the other one
84     */
85    pub fn and(self, other: Slice) -> Slice {
86        Slice::And(Box::new(self), Box::new(other))
87    }
88
89    /**
90     * Returns the or of this slice and the other one
91     */
92    pub fn or(self, other: Slice) -> Slice {
93        Slice::Or(Box::new(self), Box::new(other))
94    }
95}
96
97/**
98 * A builder object to create a slice. This exists to make forgetting to specify rows
99 * *and* columns a compilation error rather than a runtime one.
100 */
101pub struct EmptySlice2DBuilder {}
102/**
103 * A builder object to create a slice. This exists to make forgetting to specify rows
104 * *and* columns a compilation error rather than a runtime one.
105 */
106pub struct RowSlice2DBuilder {
107    rows: Slice,
108}
109/**
110 * A builder object to create a slice. This exists to make forgetting to specify rows
111 * *and* columns a compilation error rather than a runtime one.
112 */
113pub struct ColumnSlice2DBuilder {
114    columns: Slice,
115}
116
117/**
118 * Constructs a builder object to create a 2d slice
119 *
120 * The full syntax to create a `Slice2D` is like so:
121 *
122 * ```
123 * use easy_ml::matrices::slices;
124 * use easy_ml::matrices::slices::Slice;
125 * slices::new()
126 *      .rows(Slice::All())
127 *      .columns(Slice::Single(1));
128 * ```
129 *
130 * Rows and Column slices can be specified in either order but both must be given.
131 */
132pub fn new() -> EmptySlice2DBuilder {
133    Slice2D::new()
134}
135
136impl Slice2D {
137    /**
138     * Constructs a builder object to create a 2d slice
139     *
140     * The full syntax to create a `Slice2D` is like so:
141     *
142     * ```
143     * use easy_ml::matrices::slices::{Slice2D, Slice};
144     * Slice2D::new()
145     *      .rows(Slice::All())
146     *      .columns(Slice::Single(1));
147     * ```
148     *
149     * Rows and Column slices can be specified in either order but both must be given.
150     */
151    #[allow(clippy::new_ret_no_self)]
152    pub fn new() -> EmptySlice2DBuilder {
153        EmptySlice2DBuilder {}
154    }
155
156    /**
157     * Checks if this 2 dimensional slice accepts some index. The row and column
158     * slices it is composed from must accept the row and column respectively.
159     */
160    pub fn accepts(&self, row: Row, column: Column) -> bool {
161        self.rows.accepts(row) && self.columns.accepts(column)
162    }
163}
164
165impl EmptySlice2DBuilder {
166    /**
167     * Constructs a new builder object with the rows defined first.
168     */
169    pub fn rows(self, rows: Slice) -> RowSlice2DBuilder {
170        RowSlice2DBuilder { rows }
171    }
172
173    /**
174     * Constructs a new builder object with the columns defined first.
175     */
176    pub fn columns(self, columns: Slice) -> ColumnSlice2DBuilder {
177        ColumnSlice2DBuilder { columns }
178    }
179}
180
181impl RowSlice2DBuilder {
182    /**
183     * Constructs a 2d slice with rows and columns defined.
184     */
185    pub fn columns(self, columns: Slice) -> Slice2D {
186        Slice2D {
187            rows: self.rows,
188            columns,
189        }
190    }
191}
192
193impl ColumnSlice2DBuilder {
194    /**
195     * Constructs a 2d slice with rows and columns defined.
196     */
197    pub fn rows(self, rows: Slice) -> Slice2D {
198        Slice2D {
199            rows,
200            columns: self.columns,
201        }
202    }
203}