/**
 * engines/c/dynamics_lib.c
 * -------------------------
 * 纯计算 DLL：无文件 I/O，所有数据由 Python 以 NumPy 数组传入，
 * 结果直接写入 Python 预分配的输出数组。
 * 算法与 main.c 和 compute.py 保持完全一致。
 *
 * 编译（Windows DLL）:
 *   gcc -O3 -march=native -shared -o build/dynamics_c.dll dynamics_lib.c -lm
 * 编译（Linux .so）:
 *   gcc -O3 -march=native -shared -fPIC -o build/dynamics_c.so dynamics_lib.c -lm
 * 编译（macOS .dylib）:
 *   gcc -O3 -march=native -dynamiclib -o build/dynamics_c.dylib dynamics_lib.c -lm
 */

#ifdef _WIN32
#  define EXPORT __declspec(dllexport)
#else
#  define EXPORT __attribute__((visibility("default")))
#endif

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* ── 驱动力结构体 ─────────────────────────────────────────── */
typedef struct {
    int     n_drivers;
    const int    *idx;         /* [n_drivers] 0-based local atom index */
    const double *amp;         /* [n_drivers*3] (ax,ay,az) interleaved */
    const double *freq;        /* [n_drivers*3] */
    const double *phi;         /* [n_drivers*3] radians */
    const double *eq;          /* [n_drivers*3] equilibrium positions */
    const double *ncycles;     /* [n_drivers] 0=unlimited */
    const int    *has_period;  /* [n_drivers] */
    /* mutable freeze positions (allocated internally) */
    double *freeze;            /* [n_drivers*3] */
} Drivers;

/* ── 加速度：保守力（弹簧键 + 均匀重力场）────────────────── */
static void accel_conservative(
    int n, const double *x, const double *y, const double *z,
    const double *m,
    double Gx, double Gy, double Gz,
    int gravity_field, int elastic_force,
    int n_bonds, const int *bond_pairs,
    const double *bond_k, const double *bond_r0,
    double *ax, double *ay, double *az)
{
    for (int i = 0; i < n; i++) {
        ax[i] = gravity_field ? Gx : 0.0;
        ay[i] = gravity_field ? Gy : 0.0;
        az[i] = gravity_field ? Gz : 0.0;
    }

    if (!elastic_force || n_bonds == 0) return;

    for (int b = 0; b < n_bonds; b++) {
        int  ii = bond_pairs[b*2];
        int  jj = bond_pairs[b*2+1];
        double dx = x[jj] - x[ii];
        double dy = y[jj] - y[ii];
        double dz = z[jj] - z[ii];
        double dist = sqrt(dx*dx + dy*dy + dz*dz);
        if (dist < 1e-12) continue;
        double k  = bond_k[b];
        double r0 = bond_r0[b];
        double fac = k * (dist - r0) / dist;
        double fx = fac * dx, fy = fac * dy, fz_b = fac * dz;
        ax[ii] += fx / m[ii];  ay[ii] += fy / m[ii];  az[ii] += fz_b / m[ii];
        ax[jj] -= fx / m[jj];  ay[jj] -= fy / m[jj];  az[jj] -= fz_b / m[jj];
    }
}

/* ── 完整加速度（含阻尼）────────────────────────────────── */
static void accel_full(
    int n, const double *x, const double *y, const double *z,
    const double *vx, const double *vy, const double *vz,
    const double *m,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    int n_bonds, const int *bond_pairs,
    const double *bond_k, const double *bond_r0,
    double *ax, double *ay, double *az)
{
    accel_conservative(n, x, y, z, m, Gx, Gy, Gz,
                       gravity_field, elastic_force,
                       n_bonds, bond_pairs, bond_k, bond_r0,
                       ax, ay, az);
    if (damping_force) {
        for (int i = 0; i < n; i++) {
            ax[i] -= Bx * vx[i] / m[i];
            ay[i] -= By * vy[i] / m[i];
            az[i] -= Bz * vz[i] / m[i];
        }
    }
}

/* ── 边界：反弹（与 main.c limit_in_box 一致）────────────── */
static inline void _limit1(double *p, double *v, double lo, double hi) {
    if (*p > hi) { *p = hi; *v = -fabs(*v); }
    if (*p < lo) { *p = lo; *v =  fabs(*v); }
}

/* ── 边界：回绕（与 main.c wrap_position 一致）──────────── */
static inline void _wrap1(double *p, double lo, double hi) {
    if (*p > hi) *p = lo;
    if (*p < lo) *p = hi;
}

/* ── 边界 + 固定约束（与 main.c apply_step 末尾一致）──────── */
static void apply_boundary_and_constraints(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    const int *fixed, const double *pos_init,
    double box_a)
{
    double lo = -box_a, hi = box_a;

    /* 反弹 */
    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
        _limit1(&x[i], &vx[i], lo, hi);
        _limit1(&y[i], &vy[i], lo, hi);
        _limit1(&z[i], &vz[i], lo, hi);
    }

    /* 回绕 */
    for (int i = 0; i < n; i++) {
        _wrap1(&x[i], lo, hi);
        _wrap1(&y[i], lo, hi);
        _wrap1(&z[i], lo, hi);
    }

    /* 逐自由度固定约束：与 main.c 和 Python apply_fixed_constraints 一致 */
    for (int i = 0; i < n; i++) {
        if (fixed[i*3+0]) { x[i] = pos_init[i*3+0]; vx[i] = 0.0; }
        if (fixed[i*3+1]) { y[i] = pos_init[i*3+1]; vy[i] = 0.0; }
        if (fixed[i*3+2]) { z[i] = pos_init[i*3+2]; vz[i] = 0.0; }
    }
}

/* ══════════════════════════════════════════════════════════
 * 蛙跳法（与 main.c leapfrog_step 完全一致）
 * x(t), v(t-dt/2) → x(t+dt), v(t+dt/2)
 * 无阻尼：纯辛积分。有阻尼：半隐式处理 α = B·dt/(2m)
 * ══════════════════════════════════════════════════════════ */
static void leapfrog_step(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    const double *m, const int *fixed,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    int n_bonds, const int *bp, const double *bk, const double *br0,
    double dt)
{
    double *ax = (double*)alloca(n*sizeof(double)*3);
    double *ay = ax+n; double *az = ay+n;

    accel_conservative(n, x, y, z, m, Gx, Gy, Gz,
                       gravity_field, elastic_force,
                       n_bonds, bp, bk, br0, ax, ay, az);

    int has_damp = damping_force && (Bx != 0.0 || By != 0.0 || Bz != 0.0);
    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
        if (has_damp) {
            double ax_ = Bx*dt/(2.0*m[i]);
            double ay_ = By*dt/(2.0*m[i]);
            double az_ = Bz*dt/(2.0*m[i]);
            vx[i] = (vx[i]*(1.0-ax_) + ax[i]*dt) / (1.0+ax_);
            vy[i] = (vy[i]*(1.0-ay_) + ay[i]*dt) / (1.0+ay_);
            vz[i] = (vz[i]*(1.0-az_) + az[i]*dt) / (1.0+az_);
        } else {
            vx[i] += ax[i]*dt;
            vy[i] += ay[i]*dt;
            vz[i] += az[i]*dt;
        }
        x[i] += vx[i]*dt;
        y[i] += vy[i]*dt;
        z[i] += vz[i]*dt;
    }
}

/* ══════════════════════════════════════════════════════════
 * 显式欧拉法（与 main.c explicit_euler_step 一致）
 * ══════════════════════════════════════════════════════════ */
static void euler_step(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    const double *m, const int *fixed,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    int n_bonds, const int *bp, const double *bk, const double *br0,
    double dt)
{
    double *ax = (double*)alloca(n*sizeof(double)*3);
    double *ay = ax+n; double *az = ay+n;
    accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz,
               gravity_field, elastic_force, damping_force,
               n_bonds, bp, bk, br0, ax, ay, az);
    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
        x[i] += vx[i]*dt;  y[i] += vy[i]*dt;  z[i] += vz[i]*dt;
        vx[i]+= ax[i]*dt;  vy[i]+= ay[i]*dt;  vz[i]+= az[i]*dt;
    }
}

/* ══════════════════════════════════════════════════════════
 * 隐式欧拉法（与 main.c implicit_euler_step 完全一致）
 *
 * main.c 逻辑：
 *   1. 用 v_next ≈ (v + G·dt)/(1 + γ·dt) 预测（只含重力+阻尼，不含弹簧）
 *   2. 用 (x, v_next) 计算完整加速度 a_next
 *   3. v += a_next·dt;  x += v·dt
 * ══════════════════════════════════════════════════════════ */
static void implicit_euler_step(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    const double *m, const int *fixed,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    int n_bonds, const int *bp, const double *bk, const double *br0,
    double dt)
{
    double *vxn = (double*)alloca(n*sizeof(double)*3);
    double *vyn = vxn+n; double *vzn = vyn+n;

    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) {
            vxn[i] = vyn[i] = vzn[i] = 0.0; continue;
        }
        double gx = Bx / m[i], gy = By / m[i], gz = Bz / m[i];
        vxn[i] = (vx[i] + Gx*dt) / (1.0 + gx*dt);
        vyn[i] = (vy[i] + Gy*dt) / (1.0 + gy*dt);
        vzn[i] = (vz[i] + Gz*dt) / (1.0 + gz*dt);
    }

    double *ax = (double*)alloca(n*sizeof(double)*3);
    double *ay = ax+n; double *az = ay+n;
    accel_full(n, x, y, z, vxn, vyn, vzn, m, Gx, Gy, Gz, Bx, By, Bz,
               gravity_field, elastic_force, damping_force,
               n_bonds, bp, bk, br0, ax, ay, az);

    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
        vx[i] += ax[i]*dt;
        vy[i] += ay[i]*dt;
        vz[i] += az[i]*dt;
        x[i]  += vx[i]*dt;
        y[i]  += vy[i]*dt;
        z[i]  += vz[i]*dt;
    }
}

/* ══════════════════════════════════════════════════════════
 * 中点法（与 main.c midpoint_step 完全一致）
 *
 * main.c 逻辑：
 *   1. a = accel(x, v)
 *   2. xm = x + 0.5·v·dt;  vm = v + 0.5·a·dt
 *   3. x  = x + vm·dt      (位置更新用 vm，即中点速度)
 *   4. am = accel(xm, vm)
 *   5. v  = v + am·dt
 * ══════════════════════════════════════════════════════════ */
static void midpoint_step(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    const double *m, const int *fixed,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    int n_bonds, const int *bp, const double *bk, const double *br0,
    double dt)
{
    /* Allocate in one block for cache locality */
    double *buf = (double*)alloca(n*sizeof(double)*9);
    double *ax  = buf;
    double *ay  = ax+n; double *az  = ay+n;
    double *xm  = az+n; double *ym  = xm+n; double *zm  = ym+n;
    double *vxm = zm+n; double *vym = vxm+n; double *vzm = vym+n;

    accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz,
               gravity_field, elastic_force, damping_force,
               n_bonds, bp, bk, br0, ax, ay, az);

    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) {
            xm[i]=x[i]; ym[i]=y[i]; zm[i]=z[i];
            vxm[i]=vym[i]=vzm[i]=0.0; continue;
        }
        xm[i]  = x[i]  + 0.5*vx[i]*dt;
        ym[i]  = y[i]  + 0.5*vy[i]*dt;
        zm[i]  = z[i]  + 0.5*vz[i]*dt;
        vxm[i] = vx[i] + 0.5*ax[i]*dt;
        vym[i] = vy[i] + 0.5*ay[i]*dt;
        vzm[i] = vz[i] + 0.5*az[i]*dt;
        /* position updated with midpoint velocity (same as main.c) */
        x[i]   = x[i]  + vxm[i]*dt;
        y[i]   = y[i]  + vym[i]*dt;
        z[i]   = z[i]  + vzm[i]*dt;
    }

    double *axm = (double*)alloca(n*sizeof(double)*3);
    double *aym = axm+n; double *azm = aym+n;
    accel_full(n, xm, ym, zm, vxm, vym, vzm, m, Gx, Gy, Gz, Bx, By, Bz,
               gravity_field, elastic_force, damping_force,
               n_bonds, bp, bk, br0, axm, aym, azm);

    for (int i = 0; i < n; i++) {
        if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
        vx[i] += axm[i]*dt;
        vy[i] += aym[i]*dt;
        vz[i] += azm[i]*dt;
    }
}

/* ── 驱动力（与 main.c apply_driving_force 一致）────────── */
static void apply_driving(
    int n, double *x, double *y, double *z,
    double *vx, double *vy, double *vz,
    double t, int step, double dt, Drivers *drv)
{
    (void)n;
    if (!drv || drv->n_drivers == 0) return;
    const double TWO_PI = 2.0 * 3.14159265358979323846;

    for (int d = 0; d < drv->n_drivers; d++) {
        int idx = drv->idx[d];
        double fx = drv->freq[d*3+0];
        double fy = drv->freq[d*3+1];
        double fz = drv->freq[d*3+2];

        if (drv->has_period[d]) {
            double mf = fabs(fx) > fabs(fy) ? fabs(fx) : fabs(fy);
            if (fabs(fz) > mf) mf = fabs(fz);
            int period_steps = 0;
            if (mf > 1e-12)
                period_steps = (int)(drv->ncycles[d] / mf / dt);
            if (step > period_steps) {
                x[idx] = drv->freeze[d*3+0];
                y[idx] = drv->freeze[d*3+1];
                z[idx] = drv->freeze[d*3+2];
                vx[idx] = vy[idx] = vz[idx] = 0.0;
                continue;
            }

            double px = drv->eq[d*3+0] + drv->amp[d*3+0]*cos(TWO_PI*fx*t + drv->phi[d*3+0]);
            double py = drv->eq[d*3+1] + drv->amp[d*3+1]*cos(TWO_PI*fy*t + drv->phi[d*3+1]);
            double pz = drv->eq[d*3+2] + drv->amp[d*3+2]*cos(TWO_PI*fz*t + drv->phi[d*3+2]);
            if (step == period_steps) {
                drv->freeze[d*3+0] = px;
                drv->freeze[d*3+1] = py;
                drv->freeze[d*3+2] = pz;
            }
        }

        x[idx]  = drv->eq[d*3+0] + drv->amp[d*3+0]*cos(TWO_PI*fx*t + drv->phi[d*3+0]);
        y[idx]  = drv->eq[d*3+1] + drv->amp[d*3+1]*cos(TWO_PI*fy*t + drv->phi[d*3+1]);
        z[idx]  = drv->eq[d*3+2] + drv->amp[d*3+2]*cos(TWO_PI*fz*t + drv->phi[d*3+2]);
        vx[idx] = -drv->amp[d*3+0]*TWO_PI*fx*sin(TWO_PI*fx*t + drv->phi[d*3+0]);
        vy[idx] = -drv->amp[d*3+1]*TWO_PI*fy*sin(TWO_PI*fy*t + drv->phi[d*3+1]);
        vz[idx] = -drv->amp[d*3+2]*TWO_PI*fz*sin(TWO_PI*fz*t + drv->phi[d*3+2]);
    }
}

/* ══════════════════════════════════════════════════════════
 * 导出函数：run_dynamics
 *
 * 与 main.c 的计算顺序完全一致：
 *   1. leapfrog 初始化 v(-dt/2)
 *   2. 初始驱动 t=0
 *   3. 预热循环（不记录）
 *   4. 记录循环：drive → record → step → boundary → constraints
 *
 * 参数说明（所有数组均为 C-contiguous 行优先 float64/int32）：
 *   n_atoms     原子数
 *   pos_init    初始位置 [n_atoms*3]  x0,y0,z0, x1,y1,z1, ...
 *   vel_init    初始速度 [n_atoms*3]
 *   masses      质量     [n_atoms]
 *   fixed       自由度约束 [n_atoms*3]  int32, 1=固定
 *   n_bonds     键数
 *   bond_pairs  键对     [n_bonds*2]  int32, 0-based local index
 *   bond_k      刚度     [n_bonds]
 *   bond_r0     平衡键长 [n_bonds]
 *   box_a       盒子半边长
 *   dt          时间步长
 *   NT          总步数（含预热）
 *   NSTEP       抽帧间隔
 *   warmup_steps 预热步数
 *   method_id   0=euler 1=implicit 2=midpoint 3=leapfrog
 *   Gx/Gy/Gz    均匀重力场加速度分量
 *   Bx/By/Bz    阻尼系数分量
 *   gravity_field / elastic_force / damping_force  力开关
 *   gravity_strength  原子间引力强度（暂未实现，留接口）
 *   n_drivers   驱动原子数
 *   drv_idx     驱动原子局部索引  [n_drivers]  int32
 *   drv_amp     振幅 [n_drivers*3]
 *   drv_freq    频率 [n_drivers*3]
 *   drv_phi     初相（弧度）[n_drivers*3]
 *   drv_eq      平衡位置 [n_drivers*3]
 *   drv_ncycles 周期数  [n_drivers]  0=不限
 *   drv_has_period  [n_drivers]  int32
 *   n_frames    输出帧数（Python 预计算：(NT-warmup)/NSTEP 向上取整）
 *   out_x/y/z/vx/vy/vz  输出数组 [n_frames*n_atoms]  由 Python 预分配
 *   progress_cb 进度回调（可为 NULL）
 *
 * 返回：0=成功，负数=错误
 * ══════════════════════════════════════════════════════════ */
EXPORT int run_dynamics(
    int n_atoms,
    const double *pos_init,
    const double *vel_init,
    const double *masses,
    const int    *fixed,
    int n_bonds,
    const int    *bond_pairs,
    const double *bond_k,
    const double *bond_r0,
    double box_a, double dt,
    int NT, int NSTEP, int warmup_steps, int method_id,
    double Gx, double Gy, double Gz,
    double Bx, double By, double Bz,
    int gravity_field, int elastic_force, int damping_force,
    double gravity_strength,
    int n_drivers,
    const int    *drv_idx,
    const double *drv_amp,
    const double *drv_freq,
    const double *drv_phi,
    const double *drv_eq,
    const double *drv_ncycles,
    const int    *drv_has_period,
    int n_frames,
    double *out_x, double *out_y, double *out_z,
    double *out_vx, double *out_vy, double *out_vz,
    void (*progress_cb)(int step, int total))
{
    (void)gravity_strength;   /* 原子间引力暂未实现 */

    int n = n_atoms;

    /* ── 工作数组 ── */
    double *x  = (double*)malloc(n*sizeof(double));
    double *y  = (double*)malloc(n*sizeof(double));
    double *z  = (double*)malloc(n*sizeof(double));
    double *vx = (double*)malloc(n*sizeof(double));
    double *vy = (double*)malloc(n*sizeof(double));
    double *vz = (double*)malloc(n*sizeof(double));
    if (!x||!y||!z||!vx||!vy||!vz) return -1;

    for (int i = 0; i < n; i++) {
        x[i]=pos_init[i*3+0]; y[i]=pos_init[i*3+1]; z[i]=pos_init[i*3+2];
        vx[i]=vel_init[i*3+0]; vy[i]=vel_init[i*3+1]; vz[i]=vel_init[i*3+2];
    }

    /* ── 驱动结构 ── */
    Drivers drv;
    drv.n_drivers  = n_drivers;
    drv.idx        = drv_idx;
    drv.amp        = drv_amp;
    drv.freq       = drv_freq;
    drv.phi        = drv_phi;
    drv.eq         = drv_eq;
    drv.ncycles    = drv_ncycles;
    drv.has_period = drv_has_period;
    drv.freeze     = NULL;
    if (n_drivers > 0) {
        drv.freeze = (double*)calloc(n_drivers*3, sizeof(double));
        if (!drv.freeze) { free(x);free(y);free(z);free(vx);free(vy);free(vz); return -2; }
    }

    /* ── 内联步进宏 ── */
#define DO_STEP() do { \
    switch (method_id) { \
    case 0: euler_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \
                       gravity_field,elastic_force,damping_force, \
                       n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \
    case 1: implicit_euler_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \
                       gravity_field,elastic_force,damping_force, \
                       n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \
    case 2: midpoint_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \
                       gravity_field,elastic_force,damping_force, \
                       n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \
    default: leapfrog_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \
                       gravity_field,elastic_force,damping_force, \
                       n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \
    } \
    apply_boundary_and_constraints(n,x,y,z,vx,vy,vz,fixed,pos_init,box_a); \
} while(0)

    /* ── 蛙跳法：初始化 v(-dt/2) = v(0) - 0.5·a_c(0)·dt ── */
    if (method_id == 3) {
        double *ax0 = (double*)alloca(n*sizeof(double)*3);
        double *ay0 = ax0+n; double *az0 = ay0+n;
        accel_conservative(n, x, y, z, masses, Gx, Gy, Gz,
                           gravity_field, elastic_force,
                           n_bonds, bond_pairs, bond_k, bond_r0,
                           ax0, ay0, az0);
        for (int i = 0; i < n; i++) {
            if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue;
            vx[i] -= 0.5*ax0[i]*dt;
            vy[i] -= 0.5*ay0[i]*dt;
            vz[i] -= 0.5*az0[i]*dt;
        }
    }

    /* ── 初始驱动 t=0（与 main.c 一致：leapfrog init 之后施加）── */
    if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, 0.0, 0, dt, &drv);

    /* ── 预热（不记录）── */
    for (int s = 0; s < warmup_steps; s++) {
        double tw = (s + 1) * dt;
        if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, tw, s, dt, &drv);
        DO_STEP();
    }

    /* ── 记录循环 ── */
    int record_steps = NT - warmup_steps;
    int prog_interval = record_steps / 100;
    if (prog_interval < 1) prog_interval = 1;
    int frame_idx = 0;

    for (int s = 0; s < record_steps; s++) {
        if (progress_cb && s % prog_interval == 0 && s > 0)
            progress_cb(s, record_steps);

        double t = (s + warmup_steps) * dt;
        if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, t, s, dt, &drv);

        /* 抽帧记录（drive 之后，step 之前，与 main.c 一致）*/
        if (s % NSTEP == 0 && frame_idx < n_frames) {
            int base = frame_idx * n;
            for (int i = 0; i < n; i++) {
                out_x [base+i] = x[i];   out_y [base+i] = y[i];   out_z [base+i] = z[i];
                out_vx[base+i] = vx[i];  out_vy[base+i] = vy[i];  out_vz[base+i] = vz[i];
            }
            frame_idx++;
        }
        DO_STEP();
    }

#undef DO_STEP

    free(x); free(y); free(z);
    free(vx); free(vy); free(vz);
    if (drv.freeze) free(drv.freeze);
    return 0;
}
