Skip to main content

u_nesting_core/
ga.rs

1//! Genetic Algorithm framework for optimization.
2//!
3//! This module provides the GA abstraction layer for u-nesting. It defines
4//! domain-specific traits ([`Individual`], [`GaProblem`]) that support
5//! mutable evaluation — the key difference from u-metaheur's immutable pattern.
6//!
7//! # Architecture
8//!
9//! u-nesting uses a **mutable evaluation** pattern: `evaluate(&mut Individual)`
10//! sets both fitness and auxiliary state (e.g., `placed_count`, `total_count`).
11//! This differs from u-metaheur's `evaluate(&Individual) -> Fitness` pattern,
12//! which only returns a fitness value without modifying the individual.
13//!
14//! Because of this fundamental difference, u-nesting maintains its own
15//! evolutionary loop while sharing the rand/rayon ecosystem with u-metaheur.
16//! Crossover and mutation operators are defined on [`Individual`] (u-nesting
17//! convention), not on [`GaProblem`] (u-metaheur convention).
18
19use rand::prelude::*;
20#[cfg(feature = "parallel")]
21use rayon::prelude::*;
22use std::sync::atomic::{AtomicBool, Ordering};
23use std::sync::Arc;
24use std::time::Duration;
25
26use crate::error::{check_at_least, check_range, Result};
27use crate::result::StopReason;
28use crate::timing::{evaluate_within, expired, Timer};
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32
33/// Configuration for the genetic algorithm.
34#[derive(Debug, Clone)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub struct GaConfig {
37    /// Population size.
38    pub population_size: usize,
39    /// Maximum number of generations.
40    pub max_generations: u32,
41    /// Crossover rate (0.0 - 1.0).
42    pub crossover_rate: f64,
43    /// Mutation rate (0.0 - 1.0).
44    pub mutation_rate: f64,
45    /// Number of elite individuals to preserve each generation.
46    pub elite_count: usize,
47    /// Tournament size for selection.
48    pub tournament_size: usize,
49    /// Maximum time limit (None = unlimited).
50    pub time_limit: Option<Duration>,
51    /// Target fitness to stop early (None = run all generations).
52    pub target_fitness: Option<f64>,
53    /// Stagnation generations before early stop.
54    pub stagnation_limit: Option<u32>,
55}
56
57impl Default for GaConfig {
58    fn default() -> Self {
59        Self {
60            population_size: 100,
61            max_generations: 500,
62            crossover_rate: 0.85,
63            mutation_rate: 0.05,
64            elite_count: 5,
65            tournament_size: 3,
66            time_limit: None,
67            target_fitness: None,
68            stagnation_limit: Some(50),
69        }
70    }
71}
72
73impl GaConfig {
74    /// Creates a new configuration with default values.
75    pub fn new() -> Self {
76        Self::default()
77    }
78
79    /// Sets the population size.
80    pub fn with_population_size(mut self, size: usize) -> Self {
81        self.population_size = size;
82        self
83    }
84
85    /// Sets the maximum generations.
86    pub fn with_max_generations(mut self, gen: u32) -> Self {
87        self.max_generations = gen;
88        self
89    }
90
91    /// Sets the crossover rate.
92    pub fn with_crossover_rate(mut self, rate: f64) -> Self {
93        self.crossover_rate = rate;
94        self
95    }
96
97    /// Sets the mutation rate.
98    pub fn with_mutation_rate(mut self, rate: f64) -> Self {
99        self.mutation_rate = rate;
100        self
101    }
102
103    /// Sets the elite count.
104    pub fn with_elite_count(mut self, count: usize) -> Self {
105        self.elite_count = count;
106        self
107    }
108
109    /// Sets the time limit.
110    pub fn with_time_limit(mut self, duration: Duration) -> Self {
111        self.time_limit = Some(duration);
112        self
113    }
114
115    /// Sets the target fitness.
116    pub fn with_target_fitness(mut self, fitness: f64) -> Self {
117        self.target_fitness = Some(fitness);
118        self
119    }
120
121    /// Checks every field against its range: the builders store what they are
122    /// given, and this is the one place a value is judged.
123    ///
124    /// # Errors
125    /// `ConfigError` naming the first field out of range: a crossover or
126    /// mutation rate outside `[0, 1]`, a population below 2 or a tournament of 0.
127    pub fn validate(&self) -> Result<()> {
128        check_range("crossover_rate", self.crossover_rate, 0.0, 1.0)?;
129        check_range("mutation_rate", self.mutation_rate, 0.0, 1.0)?;
130        check_at_least("population_size", self.population_size as f64, 2.0)?;
131        check_at_least("tournament_size", self.tournament_size as f64, 1.0)?;
132        Ok(())
133    }
134}
135
136/// Trait for individuals in the genetic algorithm.
137///
138/// In u-nesting's convention, crossover and mutation are defined on the
139/// individual itself (unlike u-metaheur which places them on GaProblem).
140pub trait Individual: Clone + Send + Sync {
141    /// The fitness type (usually f64).
142    type Fitness: PartialOrd + Copy + Send;
143
144    /// Returns the fitness of this individual.
145    fn fitness(&self) -> Self::Fitness;
146
147    /// Creates a random individual.
148    fn random<R: Rng>(rng: &mut R) -> Self;
149
150    /// Performs crossover with another individual.
151    fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self;
152
153    /// Mutates this individual in place.
154    fn mutate<R: Rng>(&mut self, rng: &mut R);
155}
156
157/// Trait for problem-specific GA operations.
158///
159/// Uses mutable evaluation: `evaluate(&mut Individual)` can set both
160/// fitness and auxiliary state on the individual.
161pub trait GaProblem: Send + Sync {
162    /// The individual type for this problem.
163    type Individual: Individual;
164
165    /// Evaluates the fitness of an individual (mutable — can set auxiliary state).
166    fn evaluate(&self, individual: &mut Self::Individual);
167
168    /// Evaluates multiple individuals in parallel.
169    /// Default implementation uses rayon when the `parallel` feature is enabled.
170    fn evaluate_parallel(&self, individuals: &mut [Self::Individual]) {
171        #[cfg(feature = "parallel")]
172        individuals.par_iter_mut().for_each(|ind| {
173            self.evaluate(ind);
174        });
175        #[cfg(not(feature = "parallel"))]
176        for ind in individuals.iter_mut() {
177            self.evaluate(ind);
178        }
179    }
180
181    /// Creates an initial population.
182    fn initialize_population<R: Rng>(&self, size: usize, rng: &mut R) -> Vec<Self::Individual> {
183        (0..size).map(|_| Self::Individual::random(rng)).collect()
184    }
185
186    /// Called after each generation (for progress reporting).
187    fn on_generation(
188        &self,
189        _generation: u32,
190        _best: &Self::Individual,
191        _population: &[Self::Individual],
192    ) {
193        // Default: do nothing
194    }
195}
196
197/// Progress information during GA execution.
198#[derive(Debug, Clone)]
199pub struct GaProgress<F> {
200    /// Current generation number.
201    pub generation: u32,
202    /// Maximum generations configured.
203    pub max_generations: u32,
204    /// Best fitness so far.
205    pub best_fitness: F,
206    /// Average fitness of current population.
207    pub avg_fitness: f64,
208    /// Elapsed time since start.
209    pub elapsed: Duration,
210    /// Whether the algorithm is still running.
211    pub running: bool,
212}
213
214/// Result of a GA run.
215#[derive(Debug, Clone)]
216pub struct GaResult<I: Individual> {
217    /// The best individual found.
218    pub best: I,
219    /// Final generation reached.
220    pub generations: u32,
221    /// Total elapsed time.
222    pub elapsed: Duration,
223    /// Why the run stopped.
224    pub stop_reason: StopReason,
225    /// Fitness history (best fitness per generation).
226    pub history: Vec<f64>,
227}
228
229/// Genetic algorithm runner.
230///
231/// Runs the evolutionary loop with mutable evaluation, tournament selection,
232/// elitism, and configurable stopping conditions.
233pub struct GaRunner<P: GaProblem> {
234    config: GaConfig,
235    problem: P,
236    cancelled: Arc<AtomicBool>,
237}
238
239impl<P: GaProblem> GaRunner<P>
240where
241    <P::Individual as Individual>::Fitness: Into<f64>,
242{
243    /// Creates a new GA runner.
244    ///
245    /// # Errors
246    /// As [`GaConfig::validate`].
247    pub fn new(config: GaConfig, problem: P) -> Result<Self> {
248        Self::with_cancellation(config, problem, Arc::new(AtomicBool::new(false)))
249    }
250
251    /// Creates a runner that stops when `cancelled` is set — the caller's own
252    /// flag, observed directly, so no thread is needed to forward it.
253    ///
254    /// # Errors
255    /// As [`GaConfig::validate`].
256    pub fn with_cancellation(
257        config: GaConfig,
258        problem: P,
259        cancelled: Arc<AtomicBool>,
260    ) -> Result<Self> {
261        config.validate()?;
262        Ok(Self {
263            config,
264            problem,
265            cancelled,
266        })
267    }
268
269    /// Returns a handle to cancel the algorithm.
270    pub fn cancel_handle(&self) -> Arc<AtomicBool> {
271        self.cancelled.clone()
272    }
273
274    /// Runs the genetic algorithm.
275    pub fn run(&self) -> GaResult<P::Individual> {
276        self.run_with_rng(&mut rand::rng())
277    }
278
279    /// Runs the genetic algorithm with a progress callback.
280    pub fn run_with_progress<F>(&self, progress_callback: F) -> GaResult<P::Individual>
281    where
282        F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
283    {
284        self.run_with_rng_and_progress(&mut rand::rng(), Some(progress_callback))
285    }
286
287    /// Runs the genetic algorithm with a specific RNG.
288    pub fn run_with_rng<R: Rng>(&self, rng: &mut R) -> GaResult<P::Individual> {
289        self.run_with_rng_and_progress::<R, fn(GaProgress<<P::Individual as Individual>::Fitness>)>(
290            rng, None,
291        )
292    }
293
294    /// Runs the genetic algorithm with a specific RNG and optional progress callback.
295    pub fn run_with_rng_and_progress<R: Rng, F>(
296        &self,
297        rng: &mut R,
298        progress_callback: Option<F>,
299    ) -> GaResult<P::Individual>
300    where
301        F: Fn(GaProgress<<P::Individual as Individual>::Fitness>),
302    {
303        let start = Timer::now();
304        let mut history = Vec::new();
305
306        // A report after every evaluated batch, not only every generation: one
307        // generation of a large problem can take seconds, and a caller that
308        // cancels from its callback has to be heard within it. Returns the
309        // best fitness seen, counting the batch.
310        let heartbeat = |generation: u32,
311                         best: Option<<P::Individual as Individual>::Fitness>,
312                         batch: &[P::Individual]| {
313            let best = batch
314                .iter()
315                .map(Individual::fitness)
316                .fold(best, |b, f| match b {
317                    Some(b) if b >= f => Some(b),
318                    _ => Some(f),
319                });
320            if let (Some(callback), Some(best_fitness)) = (&progress_callback, best) {
321                let avg_fitness = batch.iter().map(|i| i.fitness().into()).sum::<f64>()
322                    / batch.len().max(1) as f64;
323                callback(GaProgress {
324                    generation,
325                    max_generations: self.config.max_generations,
326                    best_fitness,
327                    avg_fitness,
328                    elapsed: start.elapsed(),
329                    running: true,
330                });
331            }
332            best
333        };
334
335        // Initialize population
336        let mut population = self
337            .problem
338            .initialize_population(self.config.population_size, rng);
339
340        // Evaluate the initial population — up to the time limit
341        let mut best_so_far = None;
342        evaluate_within(&mut population, &start, self.config.time_limit, |batch| {
343            self.problem.evaluate_parallel(batch);
344            best_so_far = heartbeat(0, best_so_far, batch);
345        });
346
347        // Sort by fitness (descending - higher is better in u-nesting convention)
348        population.sort_by(|a, b| {
349            b.fitness()
350                .partial_cmp(&a.fitness())
351                .unwrap_or(std::cmp::Ordering::Equal)
352        });
353
354        let mut best = population[0].clone();
355        let mut best_fitness: f64 = best.fitness().into();
356        let mut stagnation_count = 0u32;
357        let mut generation = 0u32;
358        let reached = |fitness: f64| self.config.target_fitness.is_some_and(|t| fitness >= t);
359
360        // Each exit names its reason. The target is judged before the
361        // generation budget, so a target met by the last generation is
362        // reported as met.
363        let stop_reason = loop {
364            if reached(best_fitness) {
365                break StopReason::TargetReached;
366            }
367            if generation >= self.config.max_generations {
368                break StopReason::IterationLimit;
369            }
370            if self.cancelled.load(Ordering::Relaxed) {
371                break StopReason::Cancelled;
372            }
373            if expired(&start, self.config.time_limit) {
374                break StopReason::TimeLimit;
375            }
376
377            // Record history
378            history.push(best_fitness);
379
380            // Create new generation
381            let mut new_population = Vec::with_capacity(self.config.population_size);
382
383            // Elitism: keep the best individuals
384            for individual in population
385                .iter()
386                .take(self.config.elite_count.min(population.len()))
387            {
388                new_population.push(individual.clone());
389            }
390
391            // Fill the rest with crossover and mutation
392            let mut children: Vec<P::Individual> =
393                Vec::with_capacity(self.config.population_size - new_population.len());
394
395            while children.len() < self.config.population_size - new_population.len() {
396                // Tournament selection
397                let parent1 = self.tournament_select(&population, rng);
398                let parent2 = self.tournament_select(&population, rng);
399
400                // Crossover (on Individual, u-nesting convention)
401                let mut child = if rng.random::<f64>() < self.config.crossover_rate {
402                    parent1.crossover(parent2, rng)
403                } else {
404                    parent1.clone()
405                };
406
407                // Mutation (on Individual, u-nesting convention)
408                if rng.random::<f64>() < self.config.mutation_rate {
409                    child.mutate(rng);
410                }
411
412                children.push(child);
413            }
414
415            // Evaluate the children — up to the time limit; the loop ends on the
416            // next check if it was reached
417            let best_now = best.fitness();
418            evaluate_within(&mut children, &start, self.config.time_limit, |batch| {
419                self.problem.evaluate_parallel(batch);
420                heartbeat(generation, Some(best_now), batch);
421            });
422
423            // Add evaluated children to new population
424            new_population.extend(children);
425
426            // Sort new population
427            new_population.sort_by(|a, b| {
428                b.fitness()
429                    .partial_cmp(&a.fitness())
430                    .unwrap_or(std::cmp::Ordering::Equal)
431            });
432
433            // Update best
434            let new_best_fitness: f64 = new_population[0].fitness().into();
435            if new_best_fitness > best_fitness {
436                best = new_population[0].clone();
437                best_fitness = new_best_fitness;
438                stagnation_count = 0;
439            } else {
440                stagnation_count += 1;
441            }
442
443            // Check stagnation
444            if let Some(limit) = self.config.stagnation_limit {
445                if stagnation_count >= limit {
446                    break StopReason::NoImprovement;
447                }
448            }
449
450            // Callback to GaProblem
451            self.problem
452                .on_generation(generation, &best, &new_population);
453
454            // Progress callback
455            if let Some(ref callback) = progress_callback {
456                let avg_fitness = new_population
457                    .iter()
458                    .map(|ind| ind.fitness().into())
459                    .sum::<f64>()
460                    / new_population.len() as f64;
461
462                callback(GaProgress {
463                    generation,
464                    max_generations: self.config.max_generations,
465                    best_fitness: best.fitness(),
466                    avg_fitness,
467                    elapsed: start.elapsed(),
468                    running: true,
469                });
470            }
471
472            population = new_population;
473            generation += 1;
474        };
475
476        // Final history entry
477        history.push(best_fitness);
478
479        // Final progress callback indicating completion
480        if let Some(ref callback) = progress_callback {
481            let avg_fitness = population
482                .iter()
483                .map(|ind| ind.fitness().into())
484                .sum::<f64>()
485                / population.len().max(1) as f64;
486
487            callback(GaProgress {
488                generation,
489                max_generations: self.config.max_generations,
490                best_fitness: best.fitness(),
491                avg_fitness,
492                elapsed: start.elapsed(),
493                running: false,
494            });
495        }
496
497        GaResult {
498            best,
499            generations: generation,
500            elapsed: start.elapsed(),
501            stop_reason,
502            history,
503        }
504    }
505
506    /// Tournament selection (maximization — higher fitness wins).
507    fn tournament_select<'a, R: Rng>(
508        &self,
509        population: &'a [P::Individual],
510        rng: &mut R,
511    ) -> &'a P::Individual {
512        let mut best_idx = rng.random_range(0..population.len());
513
514        for _ in 1..self.config.tournament_size {
515            let idx = rng.random_range(0..population.len());
516            if population[idx].fitness() > population[best_idx].fitness() {
517                best_idx = idx;
518            }
519        }
520
521        &population[best_idx]
522    }
523}
524
525/// Chromosome representation for permutation-based problems.
526#[derive(Debug, Clone)]
527pub struct PermutationChromosome {
528    /// The permutation (indices).
529    pub genes: Vec<usize>,
530    /// Additional rotation/orientation genes.
531    pub rotations: Vec<usize>,
532    /// Cached fitness value.
533    fitness: f64,
534}
535
536impl PermutationChromosome {
537    /// Creates a new chromosome with the given size.
538    pub fn new(size: usize, _rotation_options: usize) -> Self {
539        Self {
540            genes: (0..size).collect(),
541            rotations: vec![0; size],
542            fitness: f64::NEG_INFINITY,
543        }
544    }
545
546    /// Creates a random chromosome.
547    pub fn random_with_options<R: Rng>(size: usize, rotation_options: usize, rng: &mut R) -> Self {
548        let mut genes: Vec<usize> = (0..size).collect();
549        genes.shuffle(rng);
550
551        let rotations: Vec<usize> = (0..size)
552            .map(|_| rng.random_range(0..rotation_options.max(1)))
553            .collect();
554
555        Self {
556            genes,
557            rotations,
558            fitness: f64::NEG_INFINITY,
559        }
560    }
561
562    /// Sets the fitness value.
563    pub fn set_fitness(&mut self, fitness: f64) {
564        self.fitness = fitness;
565    }
566
567    /// Returns the number of genes.
568    pub fn len(&self) -> usize {
569        self.genes.len()
570    }
571
572    /// Returns true if empty.
573    pub fn is_empty(&self) -> bool {
574        self.genes.is_empty()
575    }
576
577    /// Order crossover (OX).
578    pub fn order_crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
579        let n = self.genes.len();
580        if n < 2 {
581            return self.clone();
582        }
583
584        // Select two crossover points
585        let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
586        if p1 > p2 {
587            std::mem::swap(&mut p1, &mut p2);
588        }
589
590        // Copy segment from parent1
591        let mut child_genes = vec![usize::MAX; n];
592        let mut used = vec![false; n];
593
594        for i in p1..=p2 {
595            child_genes[i] = self.genes[i];
596            used[self.genes[i]] = true;
597        }
598
599        // Fill remaining from parent2
600        let mut j = (p2 + 1) % n;
601        for i in 0..n {
602            let idx = (p2 + 1 + i) % n;
603            if child_genes[idx] == usize::MAX {
604                while used[other.genes[j]] {
605                    j = (j + 1) % n;
606                }
607                child_genes[idx] = other.genes[j];
608                used[other.genes[j]] = true;
609                j = (j + 1) % n;
610            }
611        }
612
613        // Crossover rotations (uniform)
614        let rotations: Vec<usize> = self
615            .rotations
616            .iter()
617            .zip(&other.rotations)
618            .map(|(a, b)| if rng.random() { *a } else { *b })
619            .collect();
620
621        Self {
622            genes: child_genes,
623            rotations,
624            fitness: f64::NEG_INFINITY,
625        }
626    }
627
628    /// Swap mutation.
629    pub fn swap_mutate<R: Rng>(&mut self, rng: &mut R) {
630        if self.genes.len() < 2 {
631            return;
632        }
633
634        let i = rng.random_range(0..self.genes.len());
635        let j = rng.random_range(0..self.genes.len());
636        self.genes.swap(i, j);
637        self.fitness = f64::NEG_INFINITY;
638    }
639
640    /// Rotation mutation.
641    pub fn rotation_mutate<R: Rng>(&mut self, rotation_options: usize, rng: &mut R) {
642        if self.rotations.is_empty() || rotation_options <= 1 {
643            return;
644        }
645
646        let idx = rng.random_range(0..self.rotations.len());
647        self.rotations[idx] = rng.random_range(0..rotation_options);
648        self.fitness = f64::NEG_INFINITY;
649    }
650
651    /// Inversion mutation (reverses a segment).
652    pub fn inversion_mutate<R: Rng>(&mut self, rng: &mut R) {
653        let n = self.genes.len();
654        if n < 2 {
655            return;
656        }
657
658        let (mut p1, mut p2) = (rng.random_range(0..n), rng.random_range(0..n));
659        if p1 > p2 {
660            std::mem::swap(&mut p1, &mut p2);
661        }
662
663        self.genes[p1..=p2].reverse();
664        self.fitness = f64::NEG_INFINITY;
665    }
666}
667
668impl Individual for PermutationChromosome {
669    type Fitness = f64;
670
671    fn fitness(&self) -> f64 {
672        self.fitness
673    }
674
675    fn random<R: Rng>(rng: &mut R) -> Self {
676        // Default: empty, should be overridden by problem
677        Self::random_with_options(0, 1, rng)
678    }
679
680    fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
681        self.order_crossover(other, rng)
682    }
683
684    fn mutate<R: Rng>(&mut self, rng: &mut R) {
685        // 70% swap, 30% inversion
686        if rng.random::<f64>() < 0.7 {
687            self.swap_mutate(rng);
688        } else {
689            self.inversion_mutate(rng);
690        }
691    }
692}
693
694#[cfg(test)]
695mod tests {
696    use super::*;
697
698    #[derive(Clone)]
699    struct SimpleIndividual {
700        value: f64,
701    }
702
703    impl Individual for SimpleIndividual {
704        type Fitness = f64;
705
706        fn fitness(&self) -> f64 {
707            // Maximize: -(x^2), optimal at x=0
708            -self.value * self.value
709        }
710
711        fn random<R: Rng>(rng: &mut R) -> Self {
712            Self {
713                value: rng.random_range(-100.0..100.0),
714            }
715        }
716
717        fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
718            Self {
719                value: if rng.random() {
720                    self.value
721                } else {
722                    other.value
723                },
724            }
725        }
726
727        fn mutate<R: Rng>(&mut self, rng: &mut R) {
728            self.value += rng.random_range(-10.0..10.0);
729        }
730    }
731
732    struct SimpleProblem;
733
734    impl GaProblem for SimpleProblem {
735        type Individual = SimpleIndividual;
736
737        fn evaluate(&self, _individual: &mut Self::Individual) {
738            // Fitness is computed in Individual::fitness()
739        }
740    }
741
742    #[test]
743    fn test_ga_basic() {
744        let config = GaConfig::default()
745            .with_population_size(50)
746            .with_max_generations(100)
747            .with_target_fitness(-0.01);
748
749        let runner = GaRunner::new(config, SimpleProblem).expect("valid config");
750        let result = runner.run();
751
752        // Should find something close to 0
753        assert!(result.best.value.abs() < 5.0);
754    }
755
756    #[test]
757    fn test_permutation_crossover() {
758        let mut rng = rand::rng();
759        let parent1 = PermutationChromosome::random_with_options(10, 4, &mut rng);
760        let parent2 = PermutationChromosome::random_with_options(10, 4, &mut rng);
761
762        let child = parent1.order_crossover(&parent2, &mut rng);
763
764        // Child should be a valid permutation
765        assert_eq!(child.genes.len(), 10);
766        let mut sorted = child.genes.clone();
767        sorted.sort();
768        assert_eq!(sorted, (0..10).collect::<Vec<_>>());
769    }
770
771    #[test]
772    fn test_permutation_mutation() {
773        let mut rng = rand::rng();
774        let mut chromosome = PermutationChromosome::random_with_options(10, 4, &mut rng);
775
776        chromosome.swap_mutate(&mut rng);
777
778        // Should still be a valid permutation
779        let mut sorted = chromosome.genes.clone();
780        sorted.sort();
781        assert_eq!(sorted, (0..10).collect::<Vec<_>>());
782    }
783}