//! # bimoh
//!
//! Fractal plasticity regulator — entropy, information gain, and local
//! complexity (`d_i`) update for artificial memory systems.
//!
//! `bimoh` implements a plasticity regulator where each processing unit
//! (memcon) has a parameter `d_i ∈ [0, 3]` representing its accumulated
//! activation history and neighborhood influence.
//!
//! ## d_i Zones
//!
//! | Range | State | Behavior |
//! |---|---|---|
//! | 0.0 – 0.4 | Inactive | Pruning candidate (no recent activation) |
//! | 0.4 – 1.5 | Specialized | Consolidated memory, specific concept |
//! | 1.5 – 3.0 | Integrator | High connectivity, multi-concept mixing |
//!
//! ## Update formula
//!
//! ```text
//! d_i(t+1) = d_i(t) - λ · (1 - a_i(t)) + β · (a_left + a_right)
//! ```
//!
//! Where:
//! - `λ` = decay rate (inactivity decay, typical: 0.015)
//! - `β` = regeneration rate (neighbor excitation, typical: 0.07)
//! - `a_i` = normalized activity of unit i ∈ [0, 1]
//! - `a_left`, `a_right` = activity of adjacent units in the mesh

#![allow(dead_code)]

/// A consolidated concept/memory unit (Memcon/Pectron).
#[derive(Debug, Clone)]
pub struct Memcon {
    pub id: u32,
    pub complexity: f32,
}

/// Fractal plasticity regulator for artificial memory systems.
///
/// Controls which processing units (memcons) get consolidated, maintained,
/// or pruned based on activation history and neighbor influence.
pub struct BimoH {
    /// Minimum information gain threshold for consolidation.
    /// Higher values = more selective (fewer consolidations).
    /// Typical: 0.15
    pub threshold_ig: f32,
    /// Decay rate λ: d_i reduction per tick without activation.
    /// Typical: 0.015
    pub lambda: f32,
    /// Regeneration rate β: d_i increase per active neighbor.
    /// Typical: 0.07
    pub beta: f32,
}

impl BimoH {
    /// Creates a new plasticity regulator.
    ///
    /// # Parameters
    /// - `threshold_ig`: minimum information gain to consolidate (e.g. 0.15)
    /// - `lambda`: decay rate (e.g. 0.015)
    /// - `beta`: regeneration rate (e.g. 0.07)
    pub fn new(threshold_ig: f32, lambda: f32, beta: f32) -> Self {
        Self { threshold_ig, lambda, beta }
    }

    /// Shannon entropy of an activation set.
    ///
    /// Measures uncertainty/disorder of activations.
    /// Maximum entropy = log₂(n) for uniform distribution.
    /// Returns 0.0 if all activities are zero.
    pub fn entropy(activities: &[f32]) -> f32 {
        let sum: f32 = activities.iter().sum();
        if sum <= 0.0 { return 0.0; }
        let mut h = 0.0;
        for &a in activities {
            let p = a / sum;
            if p > 0.0 { h -= p * p.log2(); }
        }
        h
    }

    /// Information gain: entropy reduction from before to after.
    pub fn information_gain(&self, before: f32, after: f32) -> f32 {
        before - after
    }

    /// Returns true if information gain exceeds consolidation threshold.
    pub fn should_consolidate(&self, ig: f32) -> bool {
        ig > self.threshold_ig
    }

    /// Updates fractal plasticity parameter `d_i`.
    ///
    /// Formula:
    /// ```text
    /// d_i' = d_i - λ(1 - a_i) + β(left + right)
    /// ```
    /// Result clamped to [0.0, 3.0].
    pub fn update_d_i(&self, current: f32, activity: f32, neighbors: (f32, f32)) -> f32 {
        let (left, right) = neighbors;
        let new_d = current - self.lambda * (1.0 - activity) + self.beta * (left + right);
        new_d.max(0.0).min(3.0)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn entropy_uniform() {
        let h = BimoH::entropy(&[1.0, 1.0, 1.0, 1.0]);
        assert!((h - 2.0).abs() < 0.001);
    }

    #[test]
    fn entropy_single() {
        let h = BimoH::entropy(&[5.0]);
        assert!((h - 0.0).abs() < 0.001);
    }

    #[test]
    fn entropy_all_zero() {
        let h = BimoH::entropy(&[0.0, 0.0, 0.0]);
        assert!((h - 0.0).abs() < 0.001);
    }

    #[test]
    fn information_gain_positive() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!((b.information_gain(2.0, 1.0) - 1.0).abs() < 0.001);
    }

    #[test]
    fn information_gain_zero() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!((b.information_gain(1.0, 1.0) - 0.0).abs() < 0.001);
    }

    #[test]
    fn should_consolidate_above_threshold() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!(b.should_consolidate(0.2));
        assert!(!b.should_consolidate(0.05));
    }

    #[test]
    fn update_d_i_increases_with_activity() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!(b.update_d_i(0.5, 1.0, (0.1, 0.1)) > 0.5);
    }

    #[test]
    fn update_d_i_decreases_without_activity() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!(b.update_d_i(0.5, 0.0, (0.0, 0.0)) < 0.5);
    }

    #[test]
    fn update_d_i_clamps() {
        let b = BimoH::new(0.1, 0.01, 0.05);
        assert!((b.update_d_i(10.0, 1.0, (5.0, 5.0)) - 3.0).abs() < 0.001);
        assert!((b.update_d_i(-1.0, 0.0, (0.0, 0.0)) - 0.0).abs() < 0.001);
    }
}
