11use num:: { Float , FromPrimitive } ;
2- use ordered_float:: NotNan ;
32use serde:: { Deserialize , Serialize } ;
43use std:: {
54 collections:: VecDeque ,
@@ -9,7 +8,7 @@ use std::{
98#[ doc( hidden) ]
109#[ derive( Serialize , Deserialize ) ]
1110pub struct SortedWindow < F : Float + FromPrimitive + AddAssign + SubAssign > {
12- pub ( crate ) sorted_window : VecDeque < NotNan < F > > ,
11+ pub ( crate ) sorted_window : VecDeque < F > ,
1312 pub ( crate ) unsorted_window : VecDeque < F > ,
1413 window_size : usize ,
1514}
@@ -30,42 +29,244 @@ impl<F: Float + FromPrimitive + AddAssign + SubAssign> SortedWindow<F> {
3029 }
3130
3231 pub fn front ( & self ) -> F {
33- self . sorted_window
34- . front ( )
35- . expect ( "The value is Nan" )
36- . into_inner ( )
32+ * self . sorted_window . front ( ) . expect ( "Window is empty" )
3733 }
34+
3835 pub fn back ( & self ) -> F {
39- self . sorted_window
40- . back ( )
41- . expect ( "The value is NaN" )
42- . into_inner ( )
36+ * self . sorted_window . back ( ) . expect ( "Window is empty" )
4337 }
38+
4439 pub fn push_back ( & mut self , value : F ) {
40+ // This will panic if `value` is NaN, which is the desired behavior
41+ // to maintain a sorted list of non-NaN floats.
42+ if value. is_nan ( ) {
43+ panic ! ( "Cannot push a NaN value into SortedWindow" ) ;
44+ }
45+
4546 // Before add the newest value to the sorted window
4647 // we should remove the oldest value
4748 if self . sorted_window . len ( ) == self . window_size {
48- let last_unsorted = self . unsorted_window . pop_front ( ) . unwrap ( ) ;
49+ let oldest_unsorted = self
50+ . unsorted_window
51+ . pop_front ( )
52+ . expect ( "Unsorted window should not be empty when sorted window is full" ) ;
4953
50- let last_unsorted_pos = self
54+ // Find the position of the value to remove using a custom comparison.
55+ // `partial_cmp` returns None for NaN comparisons, so `expect` will panic,
56+ // which is consistent with the behavior of NotNan.
57+ let pos_to_remove = self
5158 . sorted_window
52- . binary_search ( & NotNan :: new ( last_unsorted) . expect ( "Value is NaN" ) )
53- . expect ( "The value is Not in the sorted window" ) ;
54- self . sorted_window . remove ( last_unsorted_pos) ;
59+ . binary_search_by ( |probe| {
60+ probe
61+ . partial_cmp ( & oldest_unsorted)
62+ . expect ( "Stored values should not be NaN" )
63+ } )
64+ . expect ( "The value to remove was not found in the sorted window" ) ;
65+
66+ self . sorted_window . remove ( pos_to_remove) ;
5567 }
68+
5669 self . unsorted_window . push_back ( value) ;
5770
5871 let sorted_pos = self
5972 . sorted_window
60- . binary_search ( & NotNan :: new ( value) . expect ( "Value is NaN" ) )
73+ . binary_search_by ( |probe| {
74+ probe
75+ . partial_cmp ( & value)
76+ . expect ( "Stored values should not be NaN" )
77+ } )
6178 . unwrap_or_else ( |e| e) ;
62- self . sorted_window
63- . insert ( sorted_pos, NotNan :: new ( value) . expect ( "Value is NaN" ) ) ;
79+ self . sorted_window . insert ( sorted_pos, value) ;
6480 }
6581}
6682impl < F : Float + FromPrimitive + AddAssign + SubAssign > Index < usize > for SortedWindow < F > {
83+ type Output = F ;
84+
6785 fn index ( & self , index : usize ) -> & Self :: Output {
6886 & self . sorted_window [ index]
6987 }
70- type Output = F ;
7188}
89+
90+ #[ cfg( test) ]
91+ mod tests {
92+ use super :: * ;
93+ use std:: f64;
94+
95+ #[ test]
96+ fn test_new_and_empty ( ) {
97+ let window: SortedWindow < f64 > = SortedWindow :: new ( 5 ) ;
98+ assert ! ( window. is_empty( ) ) ;
99+ assert_eq ! ( window. len( ) , 0 ) ;
100+ assert_eq ! ( window. window_size, 5 ) ;
101+ }
102+
103+ #[ test]
104+ fn test_push_and_sort ( ) {
105+ let mut window = SortedWindow :: new ( 5 ) ;
106+ window. push_back ( 10.0 ) ;
107+ window. push_back ( 5.0 ) ;
108+ window. push_back ( 15.0 ) ;
109+
110+ assert ! ( !window. is_empty( ) ) ;
111+ assert_eq ! ( window. len( ) , 3 ) ;
112+
113+ // Check sorted order
114+ assert_eq ! ( window[ 0 ] , 5.0 ) ;
115+ assert_eq ! ( window[ 1 ] , 10.0 ) ;
116+ assert_eq ! ( window[ 2 ] , 15.0 ) ;
117+
118+ // Check accessors
119+ assert_eq ! ( window. front( ) , 5.0 ) ;
120+ assert_eq ! ( window. back( ) , 15.0 ) ;
121+
122+ // Check unsorted (insertion) order
123+ assert_eq ! (
124+ window. unsorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
125+ vec![ 10.0 , 5.0 , 15.0 ]
126+ ) ;
127+ }
128+
129+ #[ test]
130+ fn test_window_full_cycle ( ) {
131+ let mut window = SortedWindow :: new ( 3 ) ;
132+
133+ // 1. Fill the window
134+ window. push_back ( 10.0 ) ; // unsorted: [10], sorted: [10]
135+ window. push_back ( 20.0 ) ; // unsorted: [10, 20], sorted: [10, 20]
136+ window. push_back ( 5.0 ) ; // unsorted: [10, 20, 5], sorted: [5, 10, 20]
137+
138+ assert_eq ! ( window. len( ) , 3 ) ;
139+ assert_eq ! ( window. front( ) , 5.0 ) ;
140+ assert_eq ! ( window. back( ) , 20.0 ) ;
141+ assert_eq ! (
142+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
143+ vec![ 5.0 , 10.0 , 20.0 ]
144+ ) ;
145+
146+ // 2. Push a new element, should remove the oldest (10.0)
147+ window. push_back ( 15.0 ) ; // oldest '10.0' is removed
148+ // unsorted: [20, 5, 15], sorted: [5, 15, 20]
149+
150+ assert_eq ! ( window. len( ) , 3 ) ;
151+ assert_eq ! ( window. front( ) , 5.0 ) ;
152+ assert_eq ! ( window. back( ) , 20.0 ) ;
153+ assert_eq ! (
154+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
155+ vec![ 5.0 , 15.0 , 20.0 ]
156+ ) ;
157+ assert_eq ! (
158+ window. unsorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
159+ vec![ 20.0 , 5.0 , 15.0 ]
160+ ) ;
161+
162+ // 3. Push another new element, should remove the oldest (20.0)
163+ window. push_back ( 2.0 ) ; // oldest '20.0' is removed
164+ // unsorted: [5, 15, 2], sorted: [2, 5, 15]
165+
166+ assert_eq ! ( window. len( ) , 3 ) ;
167+ assert_eq ! ( window. front( ) , 2.0 ) ;
168+ assert_eq ! ( window. back( ) , 15.0 ) ;
169+ assert_eq ! (
170+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
171+ vec![ 2.0 , 5.0 , 15.0 ]
172+ ) ;
173+ assert_eq ! (
174+ window. unsorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
175+ vec![ 5.0 , 15.0 , 2.0 ]
176+ ) ;
177+ }
178+
179+ #[ test]
180+ fn test_with_duplicate_values ( ) {
181+ let mut window = SortedWindow :: new ( 4 ) ;
182+ window. push_back ( 10.0 ) ;
183+ window. push_back ( 5.0 ) ;
184+ window. push_back ( 10.0 ) ; // Duplicate value
185+
186+ assert_eq ! ( window. len( ) , 3 ) ;
187+ assert_eq ! ( window. front( ) , 5.0 ) ;
188+ assert_eq ! ( window. back( ) , 10.0 ) ;
189+ assert_eq ! (
190+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
191+ vec![ 5.0 , 10.0 , 10.0 ]
192+ ) ;
193+
194+ // Fill window
195+ window. push_back ( 20.0 ) ;
196+ assert_eq ! (
197+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
198+ vec![ 5.0 , 10.0 , 10.0 , 20.0 ]
199+ ) ;
200+
201+ // Push another value, oldest (the first 10.0) should be removed
202+ window. push_back ( 1.0 ) ;
203+ assert_eq ! (
204+ window. sorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
205+ vec![ 1.0 , 5.0 , 10.0 , 20.0 ]
206+ ) ;
207+ assert_eq ! (
208+ window. unsorted_window. iter( ) . copied( ) . collect:: <Vec <_>>( ) ,
209+ vec![ 5.0 , 10.0 , 20.0 , 1.0 ]
210+ ) ;
211+ }
212+
213+ #[ test]
214+ fn test_window_size_one ( ) {
215+ let mut window = SortedWindow :: new ( 1 ) ;
216+
217+ window. push_back ( 10.0 ) ;
218+ assert_eq ! ( window. len( ) , 1 ) ;
219+ assert_eq ! ( window. front( ) , 10.0 ) ;
220+ assert_eq ! ( window[ 0 ] , 10.0 ) ;
221+
222+ window. push_back ( 5.0 ) ;
223+ assert_eq ! ( window. len( ) , 1 ) ;
224+ assert_eq ! ( window. front( ) , 5.0 ) ;
225+ assert_eq ! ( window[ 0 ] , 5.0 ) ;
226+
227+ window. push_back ( 20.0 ) ;
228+ assert_eq ! ( window. len( ) , 1 ) ;
229+ assert_eq ! ( window. front( ) , 20.0 ) ;
230+ assert_eq ! ( window[ 0 ] , 20.0 ) ;
231+ }
232+
233+ #[ test]
234+ #[ should_panic]
235+ fn test_window_size_zero ( ) {
236+ let mut window = SortedWindow :: new ( 0 ) ;
237+ window. push_back ( 10.0 ) ;
238+
239+ assert_eq ! ( window. len( ) , 0 ) ;
240+ assert ! ( window. is_empty( ) ) ;
241+ assert ! ( window. unsorted_window. is_empty( ) ) ;
242+ }
243+
244+ #[ test]
245+ #[ should_panic( expected = "Cannot push a NaN value into SortedWindow" ) ]
246+ fn test_panic_on_nan_push ( ) {
247+ let mut window = SortedWindow :: new ( 3 ) ;
248+ window. push_back ( f64:: NAN ) ;
249+ }
250+
251+ #[ test]
252+ #[ should_panic( expected = "Window is empty" ) ]
253+ fn test_panic_on_front_empty ( ) {
254+ let window: SortedWindow < f64 > = SortedWindow :: new ( 3 ) ;
255+ window. front ( ) ;
256+ }
257+
258+ #[ test]
259+ #[ should_panic( expected = "Window is empty" ) ]
260+ fn test_panic_on_back_empty ( ) {
261+ let window: SortedWindow < f64 > = SortedWindow :: new ( 3 ) ;
262+ window. back ( ) ;
263+ }
264+
265+ #[ test]
266+ #[ should_panic]
267+ fn test_panic_on_index_out_of_bounds ( ) {
268+ let mut window = SortedWindow :: new ( 3 ) ;
269+ window. push_back ( 1.0 ) ;
270+ let _ = window[ 1 ] ; // Should panic
271+ }
272+ }
0 commit comments