swarmkit 0.1.0

Composable particle swarm optimization with nested searches
Documentation
//! [`Floats<N>`] — element-wise `[f64; N]` newtype usable as a particle
//! [`crate::Unit`] without further boilerplate.

use crate::FieldwiseClamp;
use std::array::from_fn;
use std::ops::{Add, Index, IndexMut, Mul, Sub};

/// Element-wise newtype over `[f64; N]`.
///
/// Implements [`Add`], [`Sub`], [`Mul<f64>`], and [`FieldwiseClamp`] —
/// the minimum the standard PSO mover needs — plus [`Index`] /
/// [`IndexMut`] for `floats[i]` access. Inherits [`Copy`] / [`Default`]
/// / [`Debug`] from the underlying array, so it slots straight in as a
/// particle [`crate::Unit`].
///
/// The inner field is `pub`; bulk operations go through `.0`. Users who
/// want different element-wise semantics (saturating, modular, integer,
/// scalar-generic) define their own newtype around `[T; N]`.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Floats<const N: usize>(pub [f64; N]);

impl<const N: usize> Default for Floats<N> {
    fn default() -> Self {
        Self([0.0; N])
    }
}

impl<const N: usize> Index<usize> for Floats<N> {
    type Output = f64;
    fn index(&self, i: usize) -> &f64 {
        &self.0[i]
    }
}

impl<const N: usize> IndexMut<usize> for Floats<N> {
    fn index_mut(&mut self, i: usize) -> &mut f64 {
        &mut self.0[i]
    }
}

impl<const N: usize> FieldwiseClamp for Floats<N> {
    fn clamp(&self, min: Self, max: Self) -> Self {
        Self(from_fn(|i| self.0[i].clamp(min.0[i], max.0[i])))
    }
}

impl<const N: usize> Add for Floats<N> {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        Self(from_fn(|i| self.0[i] + rhs.0[i]))
    }
}

impl<const N: usize> Sub for Floats<N> {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self {
        Self(from_fn(|i| self.0[i] - rhs.0[i]))
    }
}

impl<const N: usize> Mul<f64> for Floats<N> {
    type Output = Self;
    fn mul(self, rhs: f64) -> Self {
        Self(from_fn(|i| self.0[i] * rhs))
    }
}