///|
/// ropls demo: PCA, PLS(-DA), OPLS-DA, VIP, cross-validation statistics
/// and response permutation testing, following the Bioconductor ropls API.
///
/// Run with: moon run examples/ropls_demo

///|
/// Round to 3 decimals for compact printed output.
fn r3(v : Double) -> String {
  ((v * 1000.0).round() / 1000.0).to_string()
}

///|
fn fmt_vec(v : Array[Double]) -> String {
  let s = StringBuilder()
  s.write_char('[')
  for i in 0.. 0 {
      s.write_string(", ")
    }
    s.write_string(r3(v[i]))
  }
  s.write_char(']')
  s.to_string()
}

///|
fn fmt_strs(v : Array[String]) -> String {
  let s = StringBuilder()
  s.write_char('[')
  for i in 0.. 0 {
      s.write_string(", ")
    }
    s.write_string(v[i])
  }
  s.write_char(']')
  s.to_string()
}

///|
/// Deterministic two-group dataset (12 samples, 5 variables). Variables 0-2
/// separate the groups; variables 3-4 are pure noise.
fn make_x() -> Array[Array[Double]] {
  let x : Array[Array[Double]] = Array(capacity=0)
  for i in 0..<12 {
    let shift = if i < 6 { -2.0 } else { 2.0 }
    let row = [
      shift + 0.35 * @math.sin((i + 1).to_double()),
      -shift + 0.35 * @math.cos((2 * i + 1).to_double()),
      shift + 0.20 * (i.to_double() - 5.5),
      0.30 * @math.sin((3 * i).to_double()),
      0.30 * @math.cos((2 * i + 3).to_double()),
    ]
    x.push(row)
  }
  x
}

///|
fn make_y(x : Array[Array[Double]]) -> Array[Array[Double]] {
  // Continuous response mostly driven by variables 0 and 2.
  let y : Array[Array[Double]] = Array(capacity=0)
  for i in 0..(?[^\/<>]+?)\<\/a\>/g
          },
          hljs.COMMENT(
            '//', // begin
            '\n', // end
          )
        ]
      }
    }

    hljs.registerLanguage('moonbit', moonbitLanguageFn);
    hljs.highlightAll();
    hljs.initLineNumbersOnLoad();

    const number = window.location.href.split('#')[1];

    function waitForLineNumbers() {
      setTimeout(function () {
        const target = document.querySelector(`.hljs-ln-line[data-line-number="${number}"]`);
        if (target == null) waitForLineNumbers();
        else target.scrollIntoView();
      }, 50);
    }

    waitForLineNumbers()