// dendritic_nanotube.sy -- Dendritic Nanotube Module
// Private conduits between specific heads that transport cargo directly,
// bypassing the public cascade channels.
//
// Based on: Chang et al. (2025), "Dendritic Nanotubes," Science.
// Johns Hopkins University. Super-resolution microscopy revealed tubular
// bridges between neuron dendrites that transport calcium, ions, and
// entire mitochondria between cells.
//
// Three cargo modes:
//
//     1. Ion transport (calcium-like): Scalar state modulation.
//        Fast, lightweight. Sender's neurochemical state pushes or
//        pulls the receiver's state through a private channel.
//        NOT the same as cross-tier gating (which is public).
//
//     2. Organelle transport (mitochondria-like): Capacity sharing.
//        Slow, heavyweight. Sender shares its output energy with the
//        receiver, amplifying the receiver's signal strength.
//        No other ML architecture models inter-head compute sharing.
//
//     3. Pathological containment (amyloid-beta-like): Health monitoring.
//        Each tube monitors cargo health and SEVERS if pathological
//        cargo is detected. More precise than apoptosis: you cut the
//        specific conduit carrying the poison, not the whole cell.
//
// Tube topology:
//
//     Lateral (within-tier): Specific head pairs form private channels.
//         Tier 2 NT pairs (antagonistic + cooperative pathways).
//         Tier 3 brain region circuits (language, motor, memory, emotion).
//
//     Skip (cross-tier): Lower tier heads form direct conduits to higher
//         tier heads, bypassing the cascade. Amino acids and precursors
//         can directly modulate brain region states without going through
//         the full Tier 0 -> 1 -> 2 -> 3 cascade.
//
//     Formation is DYNAMIC and LEARNED. Tubes form/dissolve based on
//     the combined neurochemical state of sender and receiver.
//     This is NOT hardcoded connectivity. The topology defines where
//     tubes CAN form. Whether they DO form is input-dependent.
//
// Patent Pending: U.S. Provisional Application Nos. 63/939,190,
//     63/962,385 & 63/988,485
// Copyright 2025-2026 Nexus Concordat Inc.
// Author: Aislinn McCubbins, ported from Marjorie McCubbins' Python NCA

use cascade::{ConductanceHead, CascadeTier}

// ============================================================
// CONFIGURATION
// ============================================================

struct DNTConfig {
    enabled: Bool,
    formation_threshold: Float,
    ion_gain_init: Float,
    organelle_gain_init: Float,
    health_sever_threshold: Float,
    pathological_value_limit: Float,
}

impl DNTConfig {
    fn default() -> DNTConfig {
        DNTConfig {
            enabled: true,
            formation_threshold: 0.3,
            ion_gain_init: 0.1,
            organelle_gain_init: 0.05,
            health_sever_threshold: 0.3,
            pathological_value_limit: 3.0,
        }
    }
}

// ============================================================
// TUBE TOPOLOGY DEFINITIONS
// ============================================================
// These define WHERE tubes CAN form. Whether they DO form
// depends on the dynamic formation gate (learned, input-dependent).

struct LateralDef {
    head_a: String,
    head_b: String,
    rationale: String,
}

struct SkipDef {
    sender_tier: Int,
    sender_name: String,
    receiver_tier: Int,
    receiver_name: String,
    rationale: String,
}

// Lateral tubes: within-tier private channels
// All lateral tubes are BIDIRECTIONAL (A->B and B->A created)

fn lateral_tubes_tier_2() -> Vec<LateralDef> {
    [
        LateralDef { head_a: "dopamine", head_b: "serotonin", rationale: "antagonistic_private" },
        LateralDef { head_a: "cortisol", head_b: "oxytocin", rationale: "antagonistic_private" },
        LateralDef { head_a: "norepinephrine", head_b: "gaba", rationale: "antagonistic_private" },
        LateralDef { head_a: "dopamine", head_b: "norepinephrine", rationale: "catecholamine_pathway" },
        LateralDef { head_a: "serotonin", head_b: "oxytocin", rationale: "cooperative_modulatory" },
        LateralDef { head_a: "endorphin", head_b: "cortisol", rationale: "pain_stress_balance" },
    ]
}

fn lateral_tubes_tier_3() -> Vec<LateralDef> {
    [
        LateralDef { head_a: "brocas_area", head_b: "wernickes_area", rationale: "language_circuit" },
        LateralDef { head_a: "prefrontal_cortex", head_b: "amygdala", rationale: "executive_emotion_axis" },
        LateralDef { head_a: "hippocampus", head_b: "temporal_cortex", rationale: "memory_circuit" },
        LateralDef { head_a: "basal_ganglia", head_b: "motor_cortex", rationale: "motor_circuit" },
        LateralDef { head_a: "insula", head_b: "anterior_cingulate", rationale: "interoception_error" },
    ]
}

// Skip tubes: cross-tier bypass conduits
// Skip tubes are UNIDIRECTIONAL (lower tier -> higher tier only)

fn skip_tube_defs() -> Vec<SkipDef> {
    [
        SkipDef { sender_tier: 0, sender_name: "phenylalanine", receiver_tier: 3, receiver_name: "prefrontal_cortex", rationale: "amino_acid_executive_modulation" },
        SkipDef { sender_tier: 0, sender_name: "histidine", receiver_tier: 3, receiver_name: "hippocampus", rationale: "histamine_memory_consolidation" },
        SkipDef { sender_tier: 1, sender_name: "tryptophan", receiver_tier: 3, receiver_name: "insula", rationale: "serotonin_precursor_interoception" },
        SkipDef { sender_tier: 1, sender_name: "glutamate", receiver_tier: 3, receiver_name: "hippocampus", rationale: "excitatory_memory_formation" },
        SkipDef { sender_tier: 1, sender_name: "tyrosine", receiver_tier: 3, receiver_name: "basal_ganglia", rationale: "dopamine_precursor_reward" },
        SkipDef { sender_tier: 1, sender_name: "choline", receiver_tier: 3, receiver_name: "hippocampus", rationale: "cholinergic_memory_modulation" },
    ]
}


// ============================================================
// TRANSPORT RESULT
// ============================================================
// Return type for tube transport operations.

struct TransportInfo {
    name: String,
    formation: Float,
    formed: Bool,
    health: Float,
    transport: String,
    ion_delta: Float,
    organelle_scale: Float,
    sever_reason: String,
}

impl TransportInfo {
    fn dissolved(name: String, formation: Float, health: Float) -> TransportInfo {
        TransportInfo {
            name: name,
            formation: formation,
            formed: false,
            health: health,
            transport: "dissolved",
            ion_delta: 0.0,
            organelle_scale: 0.0,
            sever_reason: "",
        }
    }

    fn severed(name: String, formation: Float, health: Float, reason: String) -> TransportInfo {
        TransportInfo {
            name: name,
            formation: formation,
            formed: true,
            health: health,
            transport: "SEVERED",
            ion_delta: 0.0,
            organelle_scale: 0.0,
            sever_reason: reason,
        }
    }

    fn active(name: String, formation: Float, health: Float, ion_delta: Float, organelle_scale: Float) -> TransportInfo {
        TransportInfo {
            name: name,
            formation: formation,
            formed: true,
            health: health,
            transport: "active",
            ion_delta: ion_delta,
            organelle_scale: organelle_scale,
            sever_reason: "",
        }
    }
}


// ============================================================
// SINGLE DENDRITIC NANOTUBE
// ============================================================
// A physical conduit that transports cargo through two channels:
//   - Ion channel: scalar state modulation (fast, cheap)
//   - Organelle channel: output amplification (slow, powerful)
//
// Formation is dynamic. The tube forms when the combined state of
// sender and receiver exceeds the formation threshold. The formation
// gate is LEARNED, not hardcoded.
//
// Health monitoring severs the tube if pathological cargo is detected,
// preventing disease-state propagation between heads.

temporal struct DendriticNanotube {
    // Formation gate (learned, dynamic)
    // H-H rate function: alpha/(alpha+beta) -> [0, 1]
    // NOT sigmoid. NOT tanh. Consistent with H-H gating.
    state formation_w_alpha: Float = 2.0,
    state formation_b_alpha: Float = -0.5,
    state formation_w_beta: Float = -2.0,
    state formation_b_beta: Float = 0.5,

    // Ion transport: sender_state * gain + bias -> delta for receiver_state
    state ion_gain: Float,
    state ion_bias: Float = 0.0,

    // Organelle transport: amplification derived from sender energy
    // receiver_output *= (1 + |organelle_gain| * sender_energy)
    state organelle_gain: Float,

    // Persistent tracking (survives across forward passes)
    state tube_health: Float = 1.0,
    state is_formed: Bool = false,
    state total_transports: Int = 0,
    state severed_count: Int = 0,

    const tube_name: String,
    const config: DNTConfig,
}

impl Evolve for DendriticNanotube {
    fn evolve(&mut self, dt: Duration) {
        // Tubes are reactive, not autonomous.
        // State changes happen during transport, not temporal evolution.
        // Health recovery could be modeled here in future.
    }
}

impl DendriticNanotube {
    fn new(config: DNTConfig, tube_name: String) -> DendriticNanotube {
        DendriticNanotube {
            formation_w_alpha: 2.0,
            formation_b_alpha: -0.5,
            formation_w_beta: -2.0,
            formation_b_beta: 0.5,
            ion_gain: config.ion_gain_init,
            ion_bias: 0.0,
            organelle_gain: config.organelle_gain_init,
            tube_health: 1.0,
            is_formed: false,
            total_transports: 0,
            severed_count: 0,
            tube_name: tube_name,
            config: config,
        }
    }

    // Dynamic formation gate.
    //
    // Tubes form when the combined neurochemical state of sender
    // and receiver reaches a learned threshold. Both being active
    // increases formation probability. Both being depleted decreases it.
    //
    // Returns formation signal in [0, 1] via H-H rate function.
    fn compute_formation(&self, sender_state: Float, receiver_state: Float) -> Float {
        let combined = sender_state + receiver_state

        // H-H alpha rate
        let alpha_arg = self.formation_w_alpha * combined + self.formation_b_alpha
        if alpha_arg < -3.0 { alpha_arg = -3.0 }
        if alpha_arg > 3.0 { alpha_arg = 3.0 }
        let alpha_f = exp(alpha_arg)

        // H-H beta rate
        let beta_arg = self.formation_w_beta * combined + self.formation_b_beta
        if beta_arg < -3.0 { beta_arg = -3.0 }
        if beta_arg > 3.0 { beta_arg = 3.0 }
        let beta_f = exp(beta_arg)

        // m_inf = alpha / (alpha + beta) -- NOT sigmoid
        alpha_f / (alpha_f + beta_f + 0.00000001)
    }

    // Assess cargo for pathological content.
    //
    // Returns health score in [0, 1].
    // Extreme values = pathological.
    fn assess_cargo_health(&self, value: Float) -> Float {
        let abs_value = value
        if abs_value < 0.0 { abs_value = 0.0 - abs_value }

        let limit = self.config.pathological_value_limit

        if abs_value > limit {
            let health = 1.0 - (abs_value - limit) / limit
            if health < 0.0 { return 0.0 }
            return health
        }
        1.0
    }

    // Transport cargo through the nanotube.
    //
    // Args:
    //     sender_state: scalar neurochemical state of sender head
    //     receiver_state: scalar neurochemical state of receiver head
    //     sender_energy: energy from sender output for organelle transport
    //                    (negative value = ion transport only, no organelle)
    //
    // Returns:
    //     TransportInfo with ion_delta, organelle_scale, and status
    fn transport(&mut self, sender_state: Float, receiver_state: Float, sender_energy: Float) -> TransportInfo {
        // Check formation
        let formation = self.compute_formation(sender_state, receiver_state)
        let formed = formation > self.config.formation_threshold

        self.is_formed = formed

        if !formed {
            return TransportInfo::dissolved(self.tube_name, formation, self.tube_health)
        }

        // === ION TRANSPORT (fast, scalar) ===
        let ion_cargo = sender_state * self.ion_gain + self.ion_bias
        let cargo_health = self.assess_cargo_health(ion_cargo)

        if cargo_health < self.config.health_sever_threshold {
            // Pathological cargo detected -- SEVER the tube
            self.tube_health = cargo_health
            self.is_formed = false
            self.severed_count = self.severed_count + 1
            return TransportInfo::severed(
                self.tube_name, formation, cargo_health, "pathological_ion_cargo"
            )
        }

        let ion_delta = ion_cargo * cargo_health

        // === ORGANELLE TRANSPORT (slow, capacity sharing) ===
        // sender_energy < 0 signals "no organelle transport this pass"
        let organelle_scale = 0.0
        if sender_energy >= 0.0 {
            let energy_health = self.assess_cargo_health(sender_energy)

            if energy_health < self.config.health_sever_threshold {
                // Pathological organelle cargo -- sever
                self.tube_health = energy_health
                return TransportInfo::severed(
                    self.tube_name, formation, energy_health, "pathological_organelle_cargo"
                )
            }

            // Amplification: receiver output *= (1 + |gain| * energy * health)
            let abs_gain = self.organelle_gain
            if abs_gain < 0.0 { abs_gain = 0.0 - abs_gain }
            organelle_scale = 1.0 + abs_gain * sender_energy * energy_health
        }

        // Update tracking
        self.tube_health = cargo_health
        self.total_transports = self.total_transports + 1

        TransportInfo::active(self.tube_name, formation, cargo_health, ion_delta, organelle_scale)
    }

    fn reset(&mut self) {
        self.tube_health = 1.0
        self.is_formed = false
        self.total_transports = 0
        self.severed_count = 0
    }
}


// ============================================================
// TUBE ENTRY
// ============================================================
// Tracks one tube's position in the topology: which heads it connects.

struct LateralEntry {
    sender_idx: Int,
    receiver_idx: Int,
    tube_key: String,
}

struct SkipEntry {
    sender_tier: Int,
    sender_idx: Int,
    receiver_tier: Int,
    receiver_idx: Int,
    tube_key: String,
}


// ============================================================
// NANOTUBE NETWORK (manages all tubes in the hierarchy)
// ============================================================
// Manages all dendritic nanotubes across the cascading hierarchy.
//
// Handles:
//   - Lateral tubes: private channels between heads in the same tier
//   - Skip tubes: bypass conduits from lower to higher tiers
//   - Health monitoring across the entire tube network
//   - Dynamic formation/dissolution tracking
//   - Telemetry for cascade_info reporting
//
// Usage in CascadingHierarchy forward pass:
//     After each tier runs:
//       1. apply_lateral() for within-tier private communication
//       2. apply_skip() for cross-tier bypass state modulation
//     The ion deltas modify receiver head states.
//     The organelle scales modify receiver head outputs.

struct NetworkHealth {
    total_tubes: Int,
    formed: Int,
    severed_ever: Int,
    healthy: Int,
    avg_health: Float,
    lateral_count: Int,
    skip_count: Int,
}

struct TubeReport {
    tube_type: String,
    name: String,
    formed: Bool,
    health: Float,
    transports: Int,
    severed_count: Int,
}

temporal struct NanotubeNetwork {
    state lateral_tubes: Vec<DendriticNanotube>,
    state skip_tubes: Vec<DendriticNanotube>,
    state lateral_topology_t2: Vec<LateralEntry>,
    state lateral_topology_t3: Vec<LateralEntry>,
    state skip_topology: Vec<SkipEntry>,

    const config: DNTConfig,
}

impl Evolve for NanotubeNetwork {
    fn evolve(&mut self, dt: Duration) {
        // Evolve all tubes (currently no-op, but future health recovery)
        for tube in self.lateral_tubes {
            tube.evolve(dt)
        }
        for tube in self.skip_tubes {
            tube.evolve(dt)
        }
    }
}

impl NanotubeNetwork {
    fn new(config: DNTConfig, tier_head_names: Vec<Vec<String>>) -> NanotubeNetwork {
        // tier_head_names: index = tier number, value = list of head names
        // e.g. tier_head_names[2] = ["dopamine", "serotonin", ...]

        let mut lateral_tubes: Vec<DendriticNanotube> = []
        let mut skip_tubes: Vec<DendriticNanotube> = []
        let mut lateral_topology_t2: Vec<LateralEntry> = []
        let mut lateral_topology_t3: Vec<LateralEntry> = []
        let mut skip_topology: Vec<SkipEntry> = []

        // Build lateral tubes for tier 2
        let t2_defs = lateral_tubes_tier_2()
        let t2_names = tier_head_names[2]
        for def in t2_defs {
            let idx_a = find_head_index(t2_names, def.head_a)
            let idx_b = find_head_index(t2_names, def.head_b)
            if idx_a < 0 { continue }
            if idx_b < 0 { continue }

            // A -> B tube (bidirectional: create both directions)
            let name_ab = "lat_t2_" + def.head_a + "_to_" + def.head_b
            lateral_tubes = lateral_tubes + [DendriticNanotube::new(config, name_ab)]
            lateral_topology_t2 = lateral_topology_t2 + [LateralEntry {
                sender_idx: idx_a, receiver_idx: idx_b, tube_key: name_ab,
            }]

            // B -> A tube
            let name_ba = "lat_t2_" + def.head_b + "_to_" + def.head_a
            lateral_tubes = lateral_tubes + [DendriticNanotube::new(config, name_ba)]
            lateral_topology_t2 = lateral_topology_t2 + [LateralEntry {
                sender_idx: idx_b, receiver_idx: idx_a, tube_key: name_ba,
            }]
        }

        // Build lateral tubes for tier 3
        let t3_defs = lateral_tubes_tier_3()
        let t3_names = tier_head_names[3]
        for def in t3_defs {
            let idx_a = find_head_index(t3_names, def.head_a)
            let idx_b = find_head_index(t3_names, def.head_b)
            if idx_a < 0 { continue }
            if idx_b < 0 { continue }

            let name_ab = "lat_t3_" + def.head_a + "_to_" + def.head_b
            lateral_tubes = lateral_tubes + [DendriticNanotube::new(config, name_ab)]
            lateral_topology_t3 = lateral_topology_t3 + [LateralEntry {
                sender_idx: idx_a, receiver_idx: idx_b, tube_key: name_ab,
            }]

            let name_ba = "lat_t3_" + def.head_b + "_to_" + def.head_a
            lateral_tubes = lateral_tubes + [DendriticNanotube::new(config, name_ba)]
            lateral_topology_t3 = lateral_topology_t3 + [LateralEntry {
                sender_idx: idx_b, receiver_idx: idx_a, tube_key: name_ba,
            }]
        }

        // Build skip tubes (unidirectional: lower -> higher)
        let s_defs = skip_tube_defs()
        for def in s_defs {
            let s_names = tier_head_names[def.sender_tier]
            let r_names = tier_head_names[def.receiver_tier]
            let idx_s = find_head_index(s_names, def.sender_name)
            let idx_r = find_head_index(r_names, def.receiver_name)
            if idx_s < 0 { continue }
            if idx_r < 0 { continue }

            let tube_name = "skip_t" + str(def.sender_tier) + "_" + def.sender_name
                + "_to_t" + str(def.receiver_tier) + "_" + def.receiver_name
            skip_tubes = skip_tubes + [DendriticNanotube::new(config, tube_name)]
            skip_topology = skip_topology + [SkipEntry {
                sender_tier: def.sender_tier,
                sender_idx: idx_s,
                receiver_tier: def.receiver_tier,
                receiver_idx: idx_r,
                tube_key: tube_name,
            }]
        }

        NanotubeNetwork {
            lateral_tubes: lateral_tubes,
            skip_tubes: skip_tubes,
            lateral_topology_t2: lateral_topology_t2,
            lateral_topology_t3: lateral_topology_t3,
            skip_topology: skip_topology,
            config: config,
        }
    }

    // Apply lateral DNTs within a tier.
    //
    // After a tier runs its forward pass, this method:
    //   1. Reads sender/receiver head states
    //   2. Computes ion transport (state modulation)
    //   3. Computes organelle transport (output amplification)
    //   4. Modifies receiver head states (ion delta)
    //
    // Returns list of TransportInfo for telemetry.
    fn apply_lateral(&mut self, tier_idx: Int, tier: &mut CascadeTier) -> Vec<TransportInfo> {
        let mut all_info: Vec<TransportInfo> = []

        let topology = self.lateral_topology_t2
        if tier_idx == 3 { topology = self.lateral_topology_t3 }

        for entry in topology {
            // Find the tube by name
            let tube = self.find_lateral_tube(entry.tube_key)

            let sender_head = tier.heads[entry.sender_idx]
            let receiver_head = tier.heads[entry.receiver_idx]

            let sender_state = sender_head.read()
            let receiver_state = receiver_head.read()

            // No organelle transport for lateral (use -1.0 sentinel)
            let info = tube.transport(sender_state, receiver_state, -1.0)

            // Apply ion delta to receiver state
            if info.formed {
                if info.transport == "active" {
                    let new_state = receiver_head.concentration + info.ion_delta
                    // Clamp to [0, 1]
                    if new_state < 0.0 { new_state = 0.0 }
                    if new_state > 1.0 { new_state = 1.0 }
                    receiver_head.concentration = new_state
                }
            }

            all_info = all_info + [info]
        }

        all_info
    }

    // Apply skip DNTs from a sender tier to higher tiers.
    //
    // Called after a tier runs. Modifies receiving tier head states
    // BEFORE the receiving tier's forward pass.
    //
    // Args:
    //     sender_tier_idx: which tier just ran
    //     sender_tier: the sender CascadeTier
    //     all_tiers: all four tiers for accessing receivers
    //
    // Returns list of TransportInfo for telemetry.
    fn apply_skip(&mut self, sender_tier_idx: Int, sender_tier: &CascadeTier, all_tiers: Vec<&mut CascadeTier>) -> Vec<TransportInfo> {
        let mut all_info: Vec<TransportInfo> = []

        for i in 0..len(self.skip_topology) {
            let entry = self.skip_topology[i]

            if entry.sender_tier != sender_tier_idx {
                continue
            }

            let tube = self.find_skip_tube(entry.tube_key)

            let sender_head = sender_tier.heads[entry.sender_idx]
            let receiver_tier = all_tiers[entry.receiver_tier]
            let receiver_head = receiver_tier.heads[entry.receiver_idx]

            let sender_state = sender_head.read()
            let receiver_state = receiver_head.read()

            // No organelle transport for skip (use -1.0 sentinel)
            let info = tube.transport(sender_state, receiver_state, -1.0)

            // Apply ion delta to receiver head in higher tier
            if info.formed {
                if info.transport == "active" {
                    let new_state = receiver_head.concentration + info.ion_delta
                    if new_state < 0.0 { new_state = 0.0 }
                    if new_state > 1.0 { new_state = 1.0 }
                    receiver_head.concentration = new_state
                }
            }

            all_info = all_info + [info]
        }

        all_info
    }

    // Aggregate health status across all tubes.
    fn get_network_health(&self) -> NetworkHealth {
        let mut total = 0
        let mut formed = 0
        let mut severed_total = 0
        let mut healthy = 0
        let mut health_sum = 0.0

        for tube in self.lateral_tubes {
            total = total + 1
            if tube.is_formed { formed = formed + 1 }
            if tube.severed_count > 0 { severed_total = severed_total + 1 }
            health_sum = health_sum + tube.tube_health
            if tube.tube_health > 0.7 { healthy = healthy + 1 }
        }

        for tube in self.skip_tubes {
            total = total + 1
            if tube.is_formed { formed = formed + 1 }
            if tube.severed_count > 0 { severed_total = severed_total + 1 }
            health_sum = health_sum + tube.tube_health
            if tube.tube_health > 0.7 { healthy = healthy + 1 }
        }

        let avg_health = 1.0
        if total > 0 {
            avg_health = health_sum / total
        }

        NetworkHealth {
            total_tubes: total,
            formed: formed,
            severed_ever: severed_total,
            healthy: healthy,
            avg_health: avg_health,
            lateral_count: len(self.lateral_tubes),
            skip_count: len(self.skip_tubes),
        }
    }

    // Detailed per-tube status report.
    fn get_tube_report(&self) -> Vec<TubeReport> {
        let mut report: Vec<TubeReport> = []

        for tube in self.lateral_tubes {
            report = report + [TubeReport {
                tube_type: "lateral",
                name: tube.tube_name,
                formed: tube.is_formed,
                health: tube.tube_health,
                transports: tube.total_transports,
                severed_count: tube.severed_count,
            }]
        }

        for tube in self.skip_tubes {
            report = report + [TubeReport {
                tube_type: "skip",
                name: tube.tube_name,
                formed: tube.is_formed,
                health: tube.tube_health,
                transports: tube.total_transports,
                severed_count: tube.severed_count,
            }]
        }

        report
    }

    // Reset all tubes to initial state.
    fn reset_all_tubes(&mut self) {
        for tube in self.lateral_tubes {
            tube.reset()
        }
        for tube in self.skip_tubes {
            tube.reset()
        }
    }

    // Internal: find a lateral tube by key name.
    fn find_lateral_tube(&mut self, key: String) -> &mut DendriticNanotube {
        for tube in self.lateral_tubes {
            if tube.tube_name == key {
                return tube
            }
        }
        // Should never reach here if topology is consistent
        self.lateral_tubes[0]
    }

    // Internal: find a skip tube by key name.
    fn find_skip_tube(&mut self, key: String) -> &mut DendriticNanotube {
        for tube in self.skip_tubes {
            if tube.tube_name == key {
                return tube
            }
        }
        self.skip_tubes[0]
    }
}


// ============================================================
// UTILITY
// ============================================================

// Find the index of a head name in a list. Returns -1 if not found.
fn find_head_index(names: Vec<String>, target: String) -> Int {
    for i in 0..len(names) {
        if names[i] == target {
            return i
        }
    }
    -1
}

// Convert an integer to string (for tube name construction).
fn str(n: Int) -> String {
    // Built-in conversion
    ""
}
