Project: s41467-025-60800-7 | Function: add_tiplength (cpp)

system: You are an expert C++ programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow C++ best practices and maintain consistent code style.
Return **only** the complete C++ function code.

user: Complete this C++ function:

**DESCRIPTION:** Adds stochastic growth segments to dendritic tips with persistence length-based angular diffusion and collision detection with other branches

**KEY TERMS:** {
  "l_add": "length to add",
  "Tip_Persis": "persistence length",
  "dtheta": "angular deviation",
  "gaussdev": "Gaussian random number",
  "branch_collision": "collision with other branches",
  "soma_collision": "collision with soma",
  "Tip_intv": "discretization interval",
  "state[i]": "tip state after collision"
}

**CONTEXT CODE:** #include <stdio.h>
#include <stdlib.h>

void add_tiplength(int i, double l_add){
  // Uses external storage for tip lengths; if not present in the compilation unit,
  // link-time will fail (these symbols are expected to exist elsewhere in the program).
  extern int N_tips;
  extern double *tip_length;

  if(i < 0 || i >= N_tips) return;
  if(!tip_length) return;

  tip_length[i] += l_add;
  // Prevent negative physical lengths
  if(tip_length[i] < 0.0) tip_length[i] = 0.0;
}

void Tip_Dynamics(){
  // External state used by the simulation (must be defined elsewhere).
  extern int N_tips;
  extern int *state;        // 1 = growing, 0 = paused, -1 = shrinking
  extern double *v;         // tip velocities
  extern double dt;         // time step
  extern double *tip_length;

  // Transition rates (per unit time)
  extern double Kgs, Kgp;   // growing -> shrinking / paused
  extern double Ksg, Ksp;   // shrinking -> growing / paused
  extern double Kpg, Kps;   // paused   -> growing / shrinking

  // Velocity distribution parameters
  // Vg_mean, Vg_sig are parameters for log-normal (log-space mean & std)
  // Vs_mean, Vs_sig similar for shrinking magnitudes
  // Vp_sig is std dev for paused (zero-mean Gaussian noise)
  extern double Vg_mean, Vg_sig;
  extern double Vs_mean, Vs_sig;
  extern double Vp_sig;

  if(N_tips <= 0) return;
  if(!state || !v) return;
  // simple uniform RNG and Box-Muller for normals
  auto uniform01 = []() -> double {
    // (rand()+1)/(RAND_MAX+2) to avoid exact 0 or 1
    return (static_cast<double>(rand()) + 1.0) / (static_cast<double>(RAND_MAX) + 2.0);
  };

  // Box-Muller with state captured in static variables via lambda closure
  struct NormalRNG {
    bool has_spare = false;
    double spare = 0.0;
    double operator()() {
      if(has_spare){
        has_spare = false;
        return spare;
      }
      double u1, u2;
      do {
        u1 = (static_cast<double>(rand()) + 1.0) / (static_cast<double>(RAND_MAX) + 2.0);
        u2 = (static_cast<double>(rand()) + 1.0) / (static_cast<double>(RAND_MAX) + 2.0);
      } while(u1 <= 1e-15);
      double r = sqrt(-2.0 * log(u1));
      double theta = 2.0 * 3.14159265358979323846 * u2;
      spare = r * cos(theta);
      has_spare = true;
      return r * sin(theta);
    }
  } normal_rng;

  // Clip dt to non-negative
  double local_dt = dt;
  if(local_dt < 0.0) local_dt = 0.0;

  for(int i = 0; i < N_tips; ++i){
    int s = state[i];
    // Determine transition probabilities over dt
    double p1 = 0.0, p2 = 0.0; // two possible outgoing transitions (order dependent on state)
    if(s == 1){ // growing: can go to shrinking (Kgs) or paused (Kgp)
      p1 = Kgs * local_dt;
      p2 = Kgp * local_dt;
      if(p1 < 0.0) p1 = 0.0; if(p2 < 0.0) p2 = 0.0;
      if(p1 > 1.0) p1 = 1.0; if(p2 > 1.0) p2 = 1.0;
      double r = uniform01();
      if(r < p1){
        s = -1;
      } else if(r < p1 + p2){
        s = 0;
      } else {
        s = 1;
      }
    } else if(s == -1){ // shrinking: can go to growing (Ksg) or paused (Ksp)
      p1 = Ksg * local_dt;
      p2 = Ksp * local_dt;
      if(p1 < 0.0) p1 = 0.0; if(p2 < 0.0) p2 = 0.0;
      if(p1 > 1.0) p1 = 1.0; if(p2 > 1.0) p2 = 1.0;
      double r = uniform01();
      if(r < p1){
        s = 1;
      } else if(r < p1 + p2){
        s = 0;
      } else {
        s = -1;
      }
    } else { // paused (0): can go to growing (Kpg) or shrinking (Kps)


Complete the function implementation replacing "throw std::runtime_error("Not implemented");".