export function rank(arr) {
  const sorted = arr.map((val, ind) => ({ val, ind })).sort((a, b) => a.val - b.val);
  const ranks = new Array(arr.length);
  for (let i = 0; i < sorted.length; i++) {
    ranks[sorted[i].ind] = i + 1;
  }
  // Handle ties (average rank) if needed, but simple rank is fine for large N
  return ranks;
}

export function spearman(x, y) {
  if (x.length !== y.length || x.length === 0) return null;
  const rankX = rank(x);
  const rankY = rank(y);
  const n = x.length;
  let dSqSum = 0;
  for (let i = 0; i < n; i++) {
    dSqSum += Math.pow(rankX[i] - rankY[i], 2);
  }
  return 1 - ((6 * dSqSum) / (n * (Math.pow(n, 2) - 1)));
}

// Simple mean
export function mean(arr) {
  if (!arr || arr.length === 0) return 0;
  return arr.reduce((a, b) => a + b, 0) / arr.length;
}

// Simple standard deviation
export function std(arr, m) {
  if (!arr || arr.length <= 1) return 1;
  const variance = arr.reduce((sum, v) => sum + Math.pow(v - m, 2), 0) / (arr.length - 1);
  return Math.sqrt(variance) || 1;
}

/**
 * Calculate Newey-West standard error for a time-series of means
 * Lags = 0 is equivalent to standard error.
 * For N-month overlapping returns, lag = N - 1.
 */
export function neweyWestStdErr(ts, lags) {
  const n = ts.length;
  if (n <= 1) return 1;
  const m = mean(ts);
  
  // Variance
  let s0 = 0;
  for (let i = 0; i < n; i++) {
    s0 += Math.pow(ts[i] - m, 2);
  }
  s0 = s0 / n;

  // Covariances
  let sLags = 0;
  for (let l = 1; l <= lags; l++) {
    let cov = 0;
    for (let i = l; i < n; i++) {
      cov += (ts[i] - m) * (ts[i - l] - m);
    }
    cov = cov / n;
    const weight = 1 - (l / (lags + 1));
    sLags += 2 * weight * cov;
  }

  const S = s0 + sLags;
  return Math.sqrt(S / n) || (std(ts, m) / Math.sqrt(n)); // fallback
}

/**
 * Compute turnover given two sets of weights.
 * w1, w2 are objects: { 'AAPL': 0.05, 'MSFT': 0.02, ... }
 * turnover = 0.5 * sum(|w2_i - w1_i|)
 */
export function calculateTurnover(wOld, wNew) {
  if (!wOld) return 1.0; // 100% turnover on first month
  let turnover = 0;
  const allTickers = new Set([...Object.keys(wOld), ...Object.keys(wNew)]);
  for (const t of allTickers) {
    const oldWt = wOld[t] || 0;
    const newWt = wNew[t] || 0;
    turnover += Math.abs(newWt - oldWt);
  }
  return 0.5 * turnover;
}
