src/measures/merging.rs

changeset 52
f0e8704d3f0e
parent 51
0693cc9ba9f0
equal deleted inserted replaced
31:6105b5cd8d89 52:f0e8704d3f0e
5 the [`SpikeMerging::merge_spikes`] method. The trait is implemented on 5 the [`SpikeMerging::merge_spikes`] method. The trait is implemented on
6 [`DiscreteMeasure<Loc<F, N>, F>`]s in dimensions `N=1` and `N=2`. 6 [`DiscreteMeasure<Loc<F, N>, F>`]s in dimensions `N=1` and `N=2`.
7 */ 7 */
8 8
9 use numeric_literals::replace_float_literals; 9 use numeric_literals::replace_float_literals;
10 use serde::{Deserialize, Serialize};
10 use std::cmp::Ordering; 11 use std::cmp::Ordering;
11 use serde::{Serialize, Deserialize};
12 //use clap::builder::{PossibleValuesParser, PossibleValue}; 12 //use clap::builder::{PossibleValuesParser, PossibleValue};
13 use alg_tools::nanleast::NaNLeast; 13 use alg_tools::nanleast::NaNLeast;
14 14
15 use crate::types::*;
16 use super::delta::*; 15 use super::delta::*;
17 use super::discrete::*; 16 use super::discrete::*;
17 use crate::types::*;
18 18
19 /// Spike merging heuristic selection 19 /// Spike merging heuristic selection
20 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] 20 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
21 #[allow(dead_code)] 21 #[allow(dead_code)]
22 pub enum SpikeMergingMethod<F> { 22 pub struct SpikeMergingMethod<F> {
23 /// Try to merge spikes within a given radius of eachother 23 // Merging radius
24 HeuristicRadius(F), 24 pub(crate) radius: F,
25 /// No merging 25 // Enabled
26 None, 26 pub(crate) enabled: bool,
27 } 27 // Interpolate merged points
28 28 pub(crate) interp: bool,
29 // impl<F : Float> SpikeMergingMethod<F> {
30 // /// This is for [`clap`] to display command line help.
31 // pub fn value_parser() -> PossibleValuesParser {
32 // PossibleValuesParser::new([
33 // PossibleValue::new("none").help("No merging"),
34 // PossibleValue::new("<radius>").help("Heuristic merging within indicated radius")
35 // ])
36 // }
37 // }
38
39 impl<F : ClapFloat> std::fmt::Display for SpikeMergingMethod<F> {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
41 match self {
42 Self::None => write!(f, "none"),
43 Self::HeuristicRadius(r) => std::fmt::Display::fmt(r, f),
44 }
45 }
46 }
47
48 impl<F : ClapFloat> std::str::FromStr for SpikeMergingMethod<F> {
49 type Err = F::Err;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 if s == "none" {
53 Ok(Self::None)
54 } else {
55 Ok(Self::HeuristicRadius(F::from_str(s)?))
56 }
57 }
58 } 29 }
59 30
60 #[replace_float_literals(F::cast_from(literal))] 31 #[replace_float_literals(F::cast_from(literal))]
61 impl<F : Float> Default for SpikeMergingMethod<F> { 32 impl<F: Float> Default for SpikeMergingMethod<F> {
62 fn default() -> Self { 33 fn default() -> Self {
63 SpikeMergingMethod::HeuristicRadius(0.02) 34 SpikeMergingMethod {
35 radius: 0.01,
36 enabled: false,
37 interp: true,
38 }
64 } 39 }
65 } 40 }
66 41
67 /// Trait for dimension-dependent implementation of heuristic peak merging strategies. 42 /// Trait for dimension-dependent implementation of heuristic peak merging strategies.
68 pub trait SpikeMerging<F> { 43 pub trait SpikeMerging<F> {
73 /// new candidate measure (it will generally be internally mutated `self`, although this is 48 /// new candidate measure (it will generally be internally mutated `self`, although this is
74 /// not guaranteed), and return [`None`] if the merge is accepted, and otherwise a [`Some`] of 49 /// not guaranteed), and return [`None`] if the merge is accepted, and otherwise a [`Some`] of
75 /// an arbitrary value. This method will return that value for the *last* accepted merge, or 50 /// an arbitrary value. This method will return that value for the *last* accepted merge, or
76 /// [`None`] if no merge was accepted. 51 /// [`None`] if no merge was accepted.
77 /// 52 ///
78 /// This method is stable with respect to spike locations: on merge, the weight of existing 53 /// This method is stable with respect to spike locations: on merge, the weights of existing
79 /// spikes is set to zero, and a new one inserted at the end of the spike vector. 54 /// removed spikes is set to zero, new ones inserted at the end of the spike vector.
80 fn merge_spikes<G, V>(&mut self, method : SpikeMergingMethod<F>, accept : G) -> Option<V> 55 /// They merge may also be performed by increasing the weights of the existing spikes,
81 where G : Fn(&'_ Self) -> Option<V> { 56 /// without inserting new spikes.
82 match method { 57 fn merge_spikes<G>(&mut self, method: SpikeMergingMethod<F>, accept: G) -> usize
83 SpikeMergingMethod::HeuristicRadius(ρ) => self.do_merge_spikes_radius(ρ, accept), 58 where
84 SpikeMergingMethod::None => None, 59 G: FnMut(&'_ Self) -> bool,
60 {
61 if method.enabled {
62 self.do_merge_spikes_radius(method.radius, method.interp, accept)
63 } else {
64 0
85 } 65 }
86 } 66 }
87 67
88 /// Attempt to merge spikes based on a value and a fitness function. 68 /// Attempt to merge spikes based on a value and a fitness function.
89 /// 69 ///
90 /// Calls [`SpikeMerging::merge_spikes`] with `accept` constructed from the composition of 70 /// Calls [`SpikeMerging::merge_spikes`] with `accept` constructed from the composition of
91 /// `value` and `fitness`, compared to initial fitness. Returns the last return value of `value` 71 /// `value` and `fitness`, compared to initial fitness. Returns the last return value of `value`
92 // for a merge accepted by `fitness`. If no merge was accepted, `value` applied to the initial 72 // for a merge accepted by `fitness`. If no merge was accepted, `value` applied to the initial
93 /// `self` is returned. 73 /// `self` is returned. also the number of merges is returned;
94 fn merge_spikes_fitness<G, H, V, O>( 74 fn merge_spikes_fitness<G, H, V, O>(
95 &mut self, 75 &mut self,
96 method : SpikeMergingMethod<F>, 76 method: SpikeMergingMethod<F>,
97 value : G, 77 value: G,
98 fitness : H 78 fitness: H,
99 ) -> V 79 ) -> (V, usize)
100 where G : Fn(&'_ Self) -> V, 80 where
101 H : Fn(&'_ V) -> O, 81 G: Fn(&'_ Self) -> V,
102 O : PartialOrd { 82 H: Fn(&'_ V) -> O,
103 let initial_res = value(self); 83 O: PartialOrd,
104 let initial_fitness = fitness(&initial_res); 84 {
105 self.merge_spikes(method, |μ| { 85 let mut res = value(self);
106 let res = value(μ); 86 let initial_fitness = fitness(&res);
107 (fitness(&res) <= initial_fitness).then_some(res) 87 let count = self.merge_spikes(method, |μ| {
108 }).unwrap_or(initial_res) 88 res = value(μ);
89 fitness(&res) <= initial_fitness
90 });
91 (res, count)
109 } 92 }
110 93
111 /// Attempt to merge spikes that are within radius $ρ$ of each other (unspecified norm). 94 /// Attempt to merge spikes that are within radius $ρ$ of each other (unspecified norm).
112 /// 95 ///
113 /// This method implements [`SpikeMerging::merge_spikes`] for 96 /// This method implements [`SpikeMerging::merge_spikes`].
114 /// [`SpikeMergingMethod::HeuristicRadius`]. The closure `accept` and the return value are 97 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, accept: G) -> usize
115 /// as for that method. 98 where
116 fn do_merge_spikes_radius<G, V>(&mut self, ρ : F, accept : G) -> Option<V> 99 G: FnMut(&'_ Self) -> bool;
117 where G : Fn(&'_ Self) -> Option<V>;
118 } 100 }
119 101
120 #[replace_float_literals(F::cast_from(literal))] 102 #[replace_float_literals(F::cast_from(literal))]
121 impl<F : Float, const N : usize> DiscreteMeasure<Loc<F, N>, F> { 103 impl<F: Float, const N: usize> DiscreteMeasure<Loc<F, N>, F> {
122 /// Attempts to merge spikes with indices `i` and `j`. 104 /// Attempts to merge spikes with indices `i` and `j`.
123 /// 105 ///
124 /// This assumes that the weights of the two spikes have already been checked not to be zero. 106 /// This assumes that the weights of the two spikes have already been checked not to be zero.
125 /// 107 ///
126 /// The parameter `res` points to the current “result” for [`SpikeMerging::merge_spikes`]. 108 /// The parameter `res` points to the current “result” for [`SpikeMerging::merge_spikes`].
127 /// If the merge is accepted by `accept` returning a [`Some`], `res` will be replaced by its 109 /// If the merge is accepted by `accept` returning a [`Some`], `res` will be replaced by its
128 /// return value. 110 /// return value.
129 fn attempt_merge<G, V>( 111 ///
112 /// Returns the index of `self.spikes` storing the new spike.
113 fn attempt_merge<G>(
130 &mut self, 114 &mut self,
131 res : &mut Option<V>, 115 i: usize,
132 i : usize, 116 j: usize,
133 j : usize, 117 interp: bool,
134 accept : &G 118 accept: &mut G,
135 ) -> bool 119 ) -> Option<usize>
136 where G : Fn(&'_ Self) -> Option<V> { 120 where
137 let &DeltaMeasure{ x : xi, α : αi } = &self.spikes[i]; 121 G: FnMut(&'_ Self) -> bool,
138 let &DeltaMeasure{ x : xj, α : αj } = &self.spikes[j]; 122 {
139 123 let &DeltaMeasure { x: xi, α: αi } = &self.spikes[i];
140 // Merge inplace 124 let &DeltaMeasure { x: xj, α: αj } = &self.spikes[j];
141 self.spikes[i].α = 0.0; 125
142 self.spikes[j].α = 0.0; 126 if interp {
143 //self.spikes.push(DeltaMeasure{ α : αi + αj, x : (xi + xj)/2.0 }); 127 // Merge inplace
144 self.spikes.push(DeltaMeasure{ α : αi + αj, x : (xi * αi + xj * αj) / (αi + αj) }); 128 self.spikes[i].α = 0.0;
145 match accept(self) { 129 self.spikes[j].α = 0.0;
146 some@Some(..) => { 130 let αia = αi.abs();
147 // Merge accepted, update our return value 131 let αja = αj.abs();
148 *res = some; 132 self.spikes.push(DeltaMeasure {
149 // On next iteration process the newly merged spike. 133 α: αi + αj,
150 //indices[k+1] = self.spikes.len() - 1; 134 x: (xi * αia + xj * αja) / (αia + αja),
151 true 135 });
152 }, 136 if accept(self) {
153 None => { 137 Some(self.spikes.len() - 1)
138 } else {
154 // Merge not accepted, restore modification 139 // Merge not accepted, restore modification
155 self.spikes[i].α = αi; 140 self.spikes[i].α = αi;
156 self.spikes[j].α = αj; 141 self.spikes[j].α = αj;
157 self.spikes.pop(); 142 self.spikes.pop();
158 false 143 None
159 } 144 }
160 } 145 } else {
161 } 146 // Attempt merge inplace, first combination
162 147 self.spikes[i].α = αi + αj;
163 /* 148 self.spikes[j].α = 0.0;
164 /// Attempts to merge spikes with indices i and j, acceptance through a delta. 149 if accept(self) {
165 fn attempt_merge_change<G, V>( 150 // Merge accepted
166 &mut self, 151 Some(i)
167 res : &mut Option<V>, 152 } else {
168 i : usize, 153 // Attempt merge inplace, second combination
169 j : usize,
170 accept_change : &G
171 ) -> bool
172 where G : Fn(&'_ Self) -> Option<V> {
173 let &DeltaMeasure{ x : xi, α : αi } = &self.spikes[i];
174 let &DeltaMeasure{ x : xj, α : αj } = &self.spikes[j];
175 let δ = DeltaMeasure{ α : αi + αj, x : (xi + xj)/2.0 };
176 let λ = [-self.spikes[i], -self.spikes[j], δ.clone()].into();
177
178 match accept_change(&λ) {
179 some@Some(..) => {
180 // Merge accepted, update our return value
181 *res = some;
182 self.spikes[i].α = 0.0; 154 self.spikes[i].α = 0.0;
183 self.spikes[j].α = 0.0; 155 self.spikes[j].α = αi + αj;
184 self.spikes.push(δ); 156 if accept(self) {
185 true 157 // Merge accepted
186 }, 158 Some(j)
187 None => { 159 } else {
188 false 160 // Merge not accepted, restore modification
189 } 161 self.spikes[i].α = αi;
190 } 162 self.spikes[j].α = αj;
191 }*/ 163 None
192 164 }
165 }
166 }
167 }
193 } 168 }
194 169
195 /// Sorts a vector of indices into `slice` by `compare`. 170 /// Sorts a vector of indices into `slice` by `compare`.
196 /// 171 ///
197 /// The closure `compare` operators on references to elements of `slice`. 172 /// The closure `compare` operators on references to elements of `slice`.
198 /// Returns the sorted vector of indices into `slice`. 173 /// Returns the sorted vector of indices into `slice`.
199 pub fn sort_indices_by<V, F>(slice : &[V], mut compare : F) -> Vec<usize> 174 pub fn sort_indices_by<V, F>(slice: &[V], mut compare: F) -> Vec<usize>
200 where F : FnMut(&V, &V) -> Ordering 175 where
176 F: FnMut(&V, &V) -> Ordering,
201 { 177 {
202 let mut indices = Vec::from_iter(0..slice.len()); 178 let mut indices = Vec::from_iter(0..slice.len());
203 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); 179 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j]));
204 indices 180 indices
205 } 181 }
206 182
207 #[replace_float_literals(F::cast_from(literal))] 183 #[replace_float_literals(F::cast_from(literal))]
208 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { 184 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> {
209 185 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize
210 fn do_merge_spikes_radius<G, V>( 186 where
211 &mut self, 187 G: FnMut(&'_ Self) -> bool,
212 ρ : F, 188 {
213 accept : G
214 ) -> Option<V>
215 where G : Fn(&'_ Self) -> Option<V> {
216 // Sort by coordinate into an indexing array. 189 // Sort by coordinate into an indexing array.
217 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { 190 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| {
218 let &Loc([x1]) = &δ1.x; 191 let &Loc([x1]) = &δ1.x;
219 let &Loc([x2]) = &δ2.x; 192 let &Loc([x2]) = &δ2.x;
220 // nan-ignoring ordering of floats 193 // nan-ignoring ordering of floats
221 NaNLeast(x1).cmp(&NaNLeast(x2)) 194 NaNLeast(x1).cmp(&NaNLeast(x2))
222 }); 195 });
223 196
224 // Initialise result 197 // Initialise result
225 let mut res = None; 198 let mut count = 0;
226 199
227 // Scan consecutive pairs and merge if close enough and accepted by `accept`. 200 // Scan consecutive pairs and merge if close enough and accepted by `accept`.
228 if indices.len() == 0 { 201 if indices.len() == 0 {
229 return res 202 return count;
230 } 203 }
231 for k in 0..(indices.len()-1) { 204 for k in 0..(indices.len() - 1) {
232 let i = indices[k]; 205 let i = indices[k];
233 let j = indices[k+1]; 206 let j = indices[k + 1];
234 let &DeltaMeasure{ x : Loc([xi]), α : αi } = &self.spikes[i]; 207 let &DeltaMeasure {
235 let &DeltaMeasure{ x : Loc([xj]), α : αj } = &self.spikes[j]; 208 x: Loc([xi]),
209 α: αi,
210 } = &self.spikes[i];
211 let &DeltaMeasure {
212 x: Loc([xj]),
213 α: αj,
214 } = &self.spikes[j];
236 debug_assert!(xi <= xj); 215 debug_assert!(xi <= xj);
237 // If close enough, attempt merging 216 // If close enough, attempt merging
238 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { 217 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ {
239 if self.attempt_merge(&mut res, i, j, &accept) { 218 if let Some(l) = self.attempt_merge(i, j, interp, &mut accept) {
240 indices[k+1] = self.spikes.len() - 1; 219 // For this to work (the debug_assert! to not trigger above), the new
241 } 220 // coordinate produced by attempt_merge has to be at most xj.
242 } 221 indices[k + 1] = l;
243 } 222 count += 1
244 223 }
245 res 224 }
225 }
226
227 count
246 } 228 }
247 } 229 }
248 230
249 /// Orders `δ1` and `δ1` according to the first coordinate. 231 /// Orders `δ1` and `δ1` according to the first coordinate.
250 fn compare_first_coordinate<F : Float>( 232 fn compare_first_coordinate<F: Float>(
251 δ1 : &DeltaMeasure<Loc<F, 2>, F>, 233 δ1: &DeltaMeasure<Loc<F, 2>, F>,
252 δ2 : &DeltaMeasure<Loc<F, 2>, F> 234 δ2: &DeltaMeasure<Loc<F, 2>, F>,
253 ) -> Ordering { 235 ) -> Ordering {
254 let &Loc([x11, ..]) = &δ1.x; 236 let &Loc([x11, ..]) = &δ1.x;
255 let &Loc([x21, ..]) = &δ2.x; 237 let &Loc([x21, ..]) = &δ2.x;
256 // nan-ignoring ordering of floats 238 // nan-ignoring ordering of floats
257 NaNLeast(x11).cmp(&NaNLeast(x21)) 239 NaNLeast(x11).cmp(&NaNLeast(x21))
258 } 240 }
259 241
260 #[replace_float_literals(F::cast_from(literal))] 242 #[replace_float_literals(F::cast_from(literal))]
261 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { 243 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> {
262 244 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize
263 fn do_merge_spikes_radius<G, V>(&mut self, ρ : F, accept : G) -> Option<V> 245 where
264 where G : Fn(&'_ Self) -> Option<V> { 246 G: FnMut(&'_ Self) -> bool,
247 {
265 // Sort by first coordinate into an indexing array. 248 // Sort by first coordinate into an indexing array.
266 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); 249 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate);
267 250
268 // Initialise result 251 // Initialise result
269 let mut res = None; 252 let mut count = 0;
270 let mut start_scan_2nd = 0; 253 let mut start_scan_2nd = 0;
271 254
272 // Scan in order 255 // Scan in order
273 if indices.len() == 0 { 256 if indices.len() == 0 {
274 return res 257 return count;
275 } 258 }
276 for k in 0..indices.len()-1 { 259 for k in 0..indices.len() - 1 {
277 let i = indices[k]; 260 let i = indices[k];
278 let &DeltaMeasure{ x : Loc([xi1, xi2]), α : αi } = &self[i]; 261 let &DeltaMeasure {
262 x: Loc([xi1, xi2]),
263 α: αi,
264 } = &self[i];
279 265
280 if αi == 0.0 { 266 if αi == 0.0 {
281 // Nothin to be done if the weight is already zero 267 // Nothin to be done if the weight is already zero
282 continue 268 continue;
283 } 269 }
284 270
285 let mut closest = None; 271 let mut closest = None;
286 272
287 // Scan for second spike. We start from `start_scan_2nd + 1` with `start_scan_2nd` 273 // Scan for second spike. We start from `start_scan_2nd + 1` with `start_scan_2nd`
288 // the smallest invalid merging index on the previous loop iteration, because a 274 // the smallest invalid merging index on the previous loop iteration, because a
289 // the _closest_ mergeable spike might have index less than `k` in `indices`, and a 275 // the _closest_ mergeable spike might have index less than `k` in `indices`, and a
290 // merge with it might have not been attempted with this spike if a different closer 276 // merge with it might have not been attempted with this spike if a different closer
291 // spike was discovered based on the second coordinate. 277 // spike was discovered based on the second coordinate.
292 'scan_2nd: for l in (start_scan_2nd+1)..indices.len() { 278 'scan_2nd: for l in (start_scan_2nd + 1)..indices.len() {
293 if l == k { 279 if l == k {
294 // Do not attempt to merge a spike with itself 280 // Do not attempt to merge a spike with itself
295 continue 281 continue;
296 } 282 }
297 let j = indices[l]; 283 let j = indices[l];
298 let &DeltaMeasure{ x : Loc([xj1, xj2]), α : αj } = &self[j]; 284 let &DeltaMeasure {
285 x: Loc([xj1, xj2]),
286 α: αj,
287 } = &self[j];
299 288
300 if xj1 < xi1 - ρ { 289 if xj1 < xi1 - ρ {
301 // Spike `j = indices[l]` has too low first coordinate. Update starting index 290 // Spike `j = indices[l]` has too low first coordinate. Update starting index
302 // for next iteration, and continue scanning. 291 // for next iteration, and continue scanning.
303 start_scan_2nd = l; 292 start_scan_2nd = l;
304 continue 'scan_2nd 293 continue 'scan_2nd;
305 } else if xj1 > xi1 + ρ { 294 } else if xj1 > xi1 + ρ {
306 // Break out: spike `j = indices[l]` has already too high first coordinate, no 295 // Break out: spike `j = indices[l]` has already too high first coordinate, no
307 // more close enough spikes can be found due to the sorting of `indices`. 296 // more close enough spikes can be found due to the sorting of `indices`.
308 break 'scan_2nd 297 break 'scan_2nd;
309 } 298 }
310 299
311 // If also second coordinate is close enough, attempt merging if closer than 300 // If also second coordinate is close enough, attempt merging if closer than
312 // previously discovered mergeable spikes. 301 // previously discovered mergeable spikes.
313 let d2 = (xi2-xj2).abs(); 302 let d2 = (xi2 - xj2).abs();
314 if αj != 0.0 && d2 <= ρ { 303 if αj != 0.0 && d2 <= ρ {
315 let r1 = xi1-xj1; 304 let r1 = xi1 - xj1;
316 let d = (d2*d2 + r1*r1).sqrt(); 305 let d = (d2 * d2 + r1 * r1).sqrt();
317 match closest { 306 match closest {
318 None => closest = Some((l, j, d)), 307 None => closest = Some((l, j, d)),
319 Some((_, _, r)) if r > d => closest = Some((l, j, d)), 308 Some((_, _, r)) if r > d => closest = Some((l, j, d)),
320 _ => {}, 309 _ => {}
321 } 310 }
322 } 311 }
323 } 312 }
324 313
325 // Attempt merging closest close-enough spike 314 // Attempt merging closest close-enough spike
326 if let Some((l, j, _)) = closest { 315 if let Some((l, j, _)) = closest {
327 if self.attempt_merge(&mut res, i, j, &accept) { 316 if let Some(n) = self.attempt_merge(i, j, interp, &mut accept) {
328 // If merge was succesfull, make new spike candidate for merging. 317 // If merge was succesfull, make new spike candidate for merging.
329 indices[l] = self.spikes.len() - 1; 318 indices[l] = n;
330 let compare = |i, j| compare_first_coordinate(&self.spikes[i], 319 count += 1;
331 &self.spikes[j]); 320 let compare = |i, j| compare_first_coordinate(&self.spikes[i], &self.spikes[j]);
332 // Re-sort relevant range of indices 321 // Re-sort relevant range of indices
333 if l < k { 322 if l < k {
334 indices[l..k].sort_by(|&i, &j| compare(i, j)); 323 indices[l..k].sort_by(|&i, &j| compare(i, j));
335 } else { 324 } else {
336 indices[k+1..=l].sort_by(|&i, &j| compare(i, j)); 325 indices[k + 1..=l].sort_by(|&i, &j| compare(i, j));
337 } 326 }
338 } 327 }
339 } 328 }
340 } 329 }
341 330
342 res 331 count
343 } 332 }
344 } 333 }
345

mercurial