import {
  ColorScale,
  axisstyle,
  drawBoxplot,
  first_genetrack_tolist,
  makeSsmLink,
  sayerror
} from "./chunk-SA7APTJR.js";
import {
  Menu
} from "./chunk-HYOEWQ5P.js";
import {
  axisBottom
} from "./chunk-LOZEKOES.js";
import {
  linear,
  log
} from "./chunk-OAWQ6LOO.js";
import {
  roundValue
} from "./chunk-TLT4YIG3.js";
import {
  rgb_default
} from "./chunk-OMR2DT66.js";

// plots/regression.results.js
var refGrp_NA = "NA";
var forestcolor = "#126e08";
var boxplotcolor = forestcolor;
var RegressionResults = class {
  constructor(opts) {
    this.opts = opts;
    this.app = opts.app;
    this.parent = opts.parent;
    this.type = "regression";
    setInteractivity(this);
    setRenderers(this);
    const holder = this.opts.holder;
    holder.append("div").style("margin-top", "10px").style("padding-top", "20px").style("font-size", "1.2em").style("opacity", 0.3).html("Results");
    this.dom = {
      holder,
      err_div: holder.append("div"),
      snplocusBlockDiv: holder.append("div"),
      // is where newDiv() and displayResult_oneset() writes to
      oneSetResultDiv: holder.append("div").style("margin", "10px"),
      tip: new Menu({ padding: "9px" })
    };
  }
  async main() {
    try {
      this.config = this.parent.config;
      this.state = this.parent.state;
      if (!this.state.formIsComplete || this.parent.inputs.hasError || this.config.hasUnsubmittedEdits && !this.hasUnsubmittedEdits_nullify_singleuse) {
        delete this.snplocusBlock;
        this.dom.snplocusBlockDiv.selectAll("*").remove();
        this.dom.holder.style("display", "none");
        return;
      }
      delete this.hasUnsubmittedEdits_nullify_singleuse;
      if (this.snplocusBlock) {
        this.snplocusBlock.cloakOn();
      }
      this.parent.inputs.dom.submitBtn.text("Running...");
      this.parent.dom.inputs.style("opacity", 0.5).style("pointer-events", "none");
      const data = await this.app.vocabApi.getRegressionData(this.getDataRequestOpts());
      if (data.error) throw data.error;
      this.dom.err_div.style("display", "none");
      this.dom.oneSetResultDiv.selectAll("*").remove();
      this.dom.holder.style("display", "block");
      await this.displayResult(data);
      const results_y = this.dom.holder.node().getBoundingClientRect().top + window.scrollY;
      const nav_height = document.querySelector(".sjpp-nav").getBoundingClientRect().height;
      window.scroll({ behavior: "smooth", top: results_y - nav_height });
    } catch (e) {
      this.hasError = true;
      this.dom.holder.style("display", "block");
      this.dom.err_div.style("display", "block");
      sayerror(this.dom.err_div, "Error: " + (e.error || e));
      this.parent.inputs.dom.submitBtn.property("disabled", true);
      console.error(e);
    } finally {
      this.parent.dom.inputs.style("opacity", 1).style("pointer-events", "auto");
    }
  }
  // creates an opts object for the vocabApi.getRegressionData()
  getDataRequestOpts() {
    const c = this.config;
    const opts = {
      regressionType: c.regressionType,
      outcome: c.outcome,
      independent: c.independent,
      includeUnivariate: c.includeUnivariate
    };
    opts.filter = this.parent.filter;
    opts.filter0 = this.state.termfilter.filter0;
    return opts;
  }
  getIndependentInput(tid2) {
    for (const i of this.parent.inputs.independent.inputLst) {
      if (!i.term) continue;
      if (i.term.term && i.term.term.snps) {
        for (const snp2 of i.term.term.snps) {
          if (snp2.snpid == tid2) {
            const tw = {
              id: tid2,
              q: {
                geneticModel: i.term.q.geneticModel
              },
              term: {
                id: tid2,
                name: tid2
              },
              interactions: i.term.interactions,
              effectAllele: i.term.q.snp2effAle[tid2],
              gt2count: snp2.gt2count
            };
            if (i.term.q.snp2refGrp) {
              tw.refGrp = i.term.q.snp2refGrp[tid2];
            }
            if (snp2.mlst) {
              const m = snp2.mlst.find((j) => j.alt == i.term.q.snp2effAle[tid2]);
              if (m) {
                tw.term.name = m.mname;
              } else {
                tw.term.name = snp2.mlst[0].mname;
              }
            }
            return { term: tw };
          }
        }
      }
      if (i.term.$id == tid2) return i;
    }
    return {
      term: {
        id: tid2,
        q: { mode: "continuous" },
        term: { name: tid2 }
      }
    };
  }
};
function setInteractivity(self) {
}
function setRenderers(self) {
  self.displayResult = async (result) => {
    const snplocusInput = self.parent.inputs.independent.inputLst.find((i) => i.term && i.term.term.type == "snplocus");
    if (snplocusInput) {
      if (!self.snplocusBlock) {
        self.dom.snplocusBlockDiv.append("div").style("margin-top", "30px").style("opacity", 0.3).text("Click on a variant within the browser to view its regression results");
        self.snplocusBlock = await createGenomebrowser(self, snplocusInput, result.resultLst);
      } else {
        await updateMds3Tk(self, snplocusInput, result.resultLst);
      }
      return;
    }
    delete self.snplocusBlock;
    self.dom.snplocusBlockDiv.selectAll("*").remove();
    if (!result.resultLst[0] || !result.resultLst[0].data) throw "result is not [ {data:{}} ]";
    self.displayResult_oneset(result.resultLst[0].data);
  };
  self.displayResult_oneset = (result) => {
    self.dom.oneSetResultDiv.selectAll("*").remove();
    self.dom.LDresultDiv = self.dom.oneSetResultDiv.append("div");
    self.mayshow_warn(result);
    if (result.sampleSize) self.newDiv("Sample size:", result.sampleSize);
    if (result.eventCnt) self.newDiv("Number of events:", result.eventCnt);
    self.mayshow_headerRow(result);
    self.mayshow_splinePlots(result);
    self.mayshow_residuals(result);
    self.mayshow_coefficients(result);
    self.mayshow_coxDisclaimer();
    self.mayshow_totalSnpEffect(result);
    self.mayshow_type3(result);
    self.mayshow_tests(result);
    self.mayshow_other(result);
    self.mayshow_fisher(result);
    self.mayshow_wilcoxon(result);
    self.mayshow_cuminc(result);
  };
  self.newDiv = (label, label2, getrow) => {
    const div = self.dom.oneSetResultDiv.append("div").style("margin", "20px 0px 10px 0px").attr("name", label);
    const row2 = div.append("div");
    row2.append("span").style("text-decoration", "underline").text(label);
    if (label2) {
      row2.append("span").html(label2).style("margin-left", "5px");
    }
    return getrow ? row2 : div.append("div").style("margin-left", "20px");
  };
  self.mayshow_warn = (result) => {
    if (!result.warnings) return;
    const div = self.newDiv("Warnings");
    const warnings = new Set(result.warnings);
    for (const line of warnings) {
      div.append("p").style("margin", "5px").text(line);
    }
  };
  self.mayshow_headerRow = (result) => {
    if (!result.headerRow) return;
    const k = result.headerRow.k;
    const v = result.headerRow.v;
    const snplocusInput = self.parent.inputs.independent.inputLst.find((i) => i.term && i.term.term.type == "snplocus");
    if (snplocusInput) {
      const snp2 = snplocusInput.term.term.snps.find((snp3) => snp3.snpid == v.snpid);
      const m = snp2.mlst[0];
      m.chr = snp2.chr;
      const row2 = self.newDiv(k, null, true);
      const snpLabelDom = row2.append("span").text(`${m.chr}:${m.pos + 1} ${m.ref && m.alt ? m.ref + ">" + m.alt : ""}`).style("margin-left", "5px");
      const urlConfig = self.app.vocabApi.termdbConfig.urlTemplates?.ssm || self.app.vocabApi.termdbConfig.queries?.snvindel?.ssmUrl;
      if (urlConfig) {
        const separateUrls = makeSsmLink(urlConfig, m, snpLabelDom, self.parent.genomeObj.name);
        if (separateUrls?.length) {
          row2.append("span").style("margin-left", "10px").html(separateUrls.join(" "));
        }
      }
      let labels;
      const gt_label = `Genotypes: ${v.gtcounts.join(", ")}`;
      if (v.monomorphic) {
        labels = [gt_label];
      } else {
        const effale_label = `Effect allele: ${v.effAle}`;
        const af_label = `Allele frequency: ${v.af}`;
        labels = [effale_label, af_label, gt_label];
      }
      row2.append("span").html(`&nbsp;&#65372;&nbsp;${labels.join("&nbsp;&#65372;&nbsp;")}`);
    } else {
      self.newDiv(k, v);
    }
  };
  self.mayshow_splinePlots = (result) => {
    if (!result.splinePlots) return;
    const div = self.newDiv("Cubic spline plots");
    div.style("display", "flex").style("align-items", "center");
    result.splinePlots.sort((a, b) => {
      if (a.type == "univariate" && b.type == "multivariate") return -1;
      if (a.type == "multivariate" && b.type == "univariate") return 1;
      return 0;
    });
    for (const plot of result.splinePlots) {
      const plotDiv = div.append("div").style("margin", "0px 50px 5px 0px");
      plotDiv.append("img").attr("src", plot.src).attr("width", 670);
    }
  };
  self.mayshow_residuals = (result) => {
    if (!result.residuals) return;
    const div = self.newDiv(result.residuals.label);
    const table = div.append("table").style("border-spacing", "8px").attr("name", "sjpp-residuals-table");
    const tr1 = table.append("tr").style("opacity", 0.4);
    const tr2 = table.append("tr");
    for (let i = 0; i < result.residuals.header.length; i++) {
      tr1.append("td").text(result.residuals.header[i]);
      tr2.append("td").text(result.residuals.rows[i]);
    }
  };
  self.mayshow_cuminc = async (result) => {
    if (!result.cuminc) return;
    const holder = self.newDiv(
      "Cumulative incidence test:"
      /*, 'p-value = ' + result.cuminc.pvalue*/
    );
    const _ = await import("./cuminc-II7NFIFP.js");
    const plotter = new _.Cuminc({
      holder,
      config: {
        term: self.config.outcome,
        term2: {
          term: {
            name: "Variant",
            values: {
              1: { key: 1, label: "Has minor allele" },
              2: { key: 2, label: "No minor allele" }
            }
          }
        }
      }
    });
    if (result.cuminc.ci_data) {
      plotter.main(result.cuminc.ci_data);
    } else {
      holder.append("div").style("margin", "20px").text(result.cuminc.msg);
    }
  };
  self.mayshow_wilcoxon = (result) => {
    if (!result.wilcoxon) return;
    const div = self.newDiv("Wilcoxon rank sum test:", "p-value = " + result.wilcoxon.pvalue);
    if (result.wilcoxon.boxplots) {
      const bs = result.wilcoxon.boxplots;
      const boxplotHeight = 20, boxplotWidth = 400, leftLabelWidth = 160, axisheight = 40, labpad = 20, vpad = 10;
      const scale = linear().domain([bs.minv, bs.maxv]).range([0, boxplotWidth]);
      const svg = div.append("svg").style("margin-top", "10px").attr("width", leftLabelWidth + labpad + boxplotWidth + 10).attr("height", vpad * 3 + boxplotHeight * 2 + axisheight);
      const g = svg.append("g").attr("transform", `translate(${leftLabelWidth + labpad},${vpad})`);
      drawBoxplot({
        g: g.append("g"),
        bp: bs.hasEff,
        scale,
        rowheight: boxplotHeight,
        color: boxplotcolor,
        labpad
      });
      drawBoxplot({
        g: g.append("g").attr("transform", `translate(0,${boxplotHeight + vpad})`),
        bp: bs.noEff,
        scale,
        rowheight: boxplotHeight,
        color: boxplotcolor,
        labpad
      });
      {
        const axisg = g.append("g").attr("transform", `translate(0,${boxplotHeight * 2 + vpad * 2})`);
        const axis = axisBottom().scale(scale);
        axisstyle({
          axis: axisg.call(axis),
          color: boxplotcolor,
          showline: true
        });
        axisg.append("text").text(self.config.outcome.term.name).attr("font-size", 15).attr("x", boxplotWidth / 2).attr("y", axisheight - 5).attr("text-anchor", "middle").attr("fill", boxplotcolor);
      }
    }
  };
  self.mayshow_fisher = (result) => {
    if (!result.fisher) return;
    const div = self.newDiv(
      result.fisher.isChi ? "Chi-square test:" : "Fisher's exact test:",
      "p-value = " + result.fisher.pvalue
    );
    const table = div.append("table").style("margin", "20px").style("border-spacing", "5px").style("border-collapse", "separate");
    for (const r of result.fisher.rows) {
      const tr = table.append("tr");
      for (const c of r) {
        tr.append("td").text(c);
      }
    }
  };
  self.mayshow_coefficients = (result) => {
    if (!result.coefficients) {
      if (result.coefficients_uni && result.coefficients_multi) {
        self.mayshow_coefficients_uniMulti(result);
      }
      return;
    }
    const div = self.newDiv(result.coefficients.label);
    const table = div.append("table").style("border-spacing", "0px").attr("data-testid", "sjpp_regression_resultCoefficientTable");
    let header;
    {
      const tr2 = table.append("tr").style("opacity", 0.4);
      header = result.coefficients.header;
      tr2.append("td").text(header.shift()).style("padding", "8px");
      tr2.append("td").text(header.shift()).style("padding", "8px");
      if (self.config.regressionType == "cox") {
        header.shift();
        header.shift();
      }
      header.splice(1, 2, "95% CI");
      self.fillDataHeaders(header, tr2);
    }
    self.independentTws = Object.keys(result.coefficients.terms).map((tid2) => self.getIndependentInput(tid2).term);
    let varcount = 0;
    const intercept = result.coefficients.intercept;
    if (intercept) {
      const tr2 = table.append("tr").style("background", ++varcount % 2 ? "#eee" : "none");
      tr2.append("td").text(intercept.shift()).style("padding", "8px");
      tr2.append("td").text(intercept.shift()).style("padding", "8px");
      tr2.append("td");
      self.fillCoefDataCols({ tr: tr2, cols: intercept, isIntercept: true });
    }
    const forestPlotter = self.getForestPlotter(result.coefficients.terms, result.coefficients.interactions);
    let rowcolor;
    for (const tid2 in result.coefficients.terms) {
      const termdata = result.coefficients.terms[tid2];
      const tw = self.getIndependentInput(tid2).term;
      rowcolor = ++varcount % 2 ? "#eee" : "none";
      let tr2 = table.append("tr").style("background", rowcolor);
      const termNameTd = tr2.append("td").style("padding", "8px");
      fillCoefficientTermname(tw, termNameTd);
      if (termdata.fields) {
        const cols = termdata.fields;
        {
          const td = tr2.append("td").style("padding", "8px");
          fillColumn2coefficientsTable(td, tw);
        }
        if (self.config.regressionType == "cox") {
          cols.shift();
          cols.shift();
        }
        forestPlotter(tr2.append("td"), cols);
        self.fillCoefDataCols({ tr: tr2, cols, tw });
      } else if (termdata.categories) {
        const orderedCategories = [];
        const input = self.getIndependentInput(tid2);
        if (input.orderedLabels) {
          for (const k of input.orderedLabels) {
            if (termdata.categories[k]) orderedCategories.push(k);
          }
        }
        for (const k in termdata.categories) {
          if (!orderedCategories.includes(k)) orderedCategories.push(k);
        }
        termNameTd.attr("rowspan", orderedCategories.length).style("vertical-align", "top");
        let isfirst = true;
        for (const k of orderedCategories) {
          if (!isfirst) {
            tr2 = table.append("tr").style("background", rowcolor);
          }
          const cols = termdata.categories[k];
          const td = tr2.append("td").style("padding", "8px");
          fillColumn2coefficientsTable(td, tw, k);
          if (self.config.regressionType == "cox") {
            if (tw.q.mode == "spline") {
              cols.shift();
              cols.shift();
            } else {
              const [samplesize_ref, samplesize_c] = cols.shift().split("/");
              const [eventcnt_ref, eventcnt_c] = cols.shift().split("/");
              if (isfirst) {
                const variableBottomDiv = termNameTd.select(".sjpcb-coef-variable-bottom");
                variableBottomDiv.style("align-items", "baseline");
                const refGrpDiv = variableBottomDiv.selectAll("div").filter((d, i) => i === 1);
                refGrpDiv.append("div").html(`n=${samplesize_ref}<br>events=${eventcnt_ref}`);
              }
              td.append("div").style("font-size", ".8em").html(`n=${samplesize_c}<br>events=${eventcnt_c}`);
            }
          }
          forestPlotter(tr2.append("td"), cols);
          self.fillCoefDataCols({ tr: tr2, cols, tw, categoryKey: k });
          isfirst = false;
        }
      } else {
        tr2.append("td").text("ERROR: no .fields[] or .categories{}");
      }
    }
    for (const i of result.coefficients.interactions) {
      rowcolor = ++varcount % 2 ? "#eee" : "none";
      let tr2 = table.append("tr").style("background", rowcolor);
      const term1 = self.getIndependentInput(i.term1).term;
      const term2 = self.getIndependentInput(i.term2).term;
      {
        const td = tr2.append("td").style("padding", "8px");
        fillTdName(td.append("div"), term1 ? term1.term.name + " : " : row.term1 + " : ");
        fillTdName(td.append("div"), term2 ? term2.term.name : row.term2);
        td.attr("rowspan", i.categories.length).style("vertical-align", "top");
      }
      let isfirst = true;
      for (const c of i.categories) {
        if (!isfirst) tr2 = table.append("tr").style("background", rowcolor);
        const td = tr2.append("td").style("padding", "8px");
        fillColumn2coefficientsTable(td.append("div"), term1, c.category1);
        fillColumn2coefficientsTable(td.append("div"), term2, c.category2);
        const cols = c.lst;
        if (self.config.regressionType == "cox") {
          cols.shift();
          cols.shift();
        }
        forestPlotter(tr2.append("td"), cols);
        self.fillCoefDataCols({ tr: tr2, cols, tw: term1, tw2: term2, categoryKey: c.category1, categoryKey2: c.category2 });
        isfirst = false;
      }
    }
    const tr = table.append("tr");
    tr.append("td");
    tr.append("td");
    forestPlotter(tr.append("td"));
    for (const v of header) tr.append("td");
  };
  self.mayshow_coefficients_uniMulti = (result) => {
    if (!result.coefficients_uni || !result.coefficients_multi) return;
    const div = self.newDiv(result.coefficients_uni.label);
    div.style("margin-bottom", "200px");
    const table = div.append("table").style("border-spacing", "0px").attr("data-testid", "sjpp_regression_resultCoefficientTable");
    let header_uni, header_multi;
    {
      const tr_label = table.append("tr").style("opacity", 0.4);
      const tr2 = table.append("tr").style("opacity", 0.4);
      header_uni = result.coefficients_uni.header;
      header_multi = result.coefficients_multi.header;
      tr2.append("td").text(header_uni.shift()).style("padding", "8px");
      tr_label.append("td").style("padding", "8px");
      header_multi.shift();
      tr2.append("td").text(header_uni.shift()).style("padding", "8px");
      tr_label.append("td").style("padding", "8px");
      header_multi.shift();
      if (self.config.regressionType == "cox") {
        header_uni.shift();
        header_uni.shift();
        header_multi.shift();
        header_multi.shift();
      }
      header_uni.splice(1, 2, "95% CI");
      header_multi.splice(1, 2, "95% CI");
      self.fillDataHeaders(header_uni, tr2, tr_label, "Univariate");
      tr2.append("td").style("width", "2px");
      tr_label.append("td").style("width", "2px");
      self.fillDataHeaders(header_multi, tr2, tr_label, "Multivariable-adjusted");
    }
    const forestPlotter_uni = self.getForestPlotter(result.coefficients_uni.terms, result.coefficients_uni.interactions);
    const forestPlotter_multi = self.getForestPlotter(
      result.coefficients_multi.terms,
      result.coefficients_multi.interactions
    );
    self.independentTws = Object.keys(result.coefficients_uni.terms).map((tid2) => self.getIndependentInput(tid2).term);
    let varcount = 0, rowcolor;
    for (const tid2 in result.coefficients_uni.terms) {
      const termdata = result.coefficients_uni.terms[tid2];
      const termdata_multi = result.coefficients_multi.terms[tid2];
      const tw = self.getIndependentInput(tid2).term;
      rowcolor = ++varcount % 2 ? "#eee" : "none";
      let tr2 = table.append("tr").style("background", rowcolor);
      const termNameTd = tr2.append("td").style("padding", "8px");
      fillCoefficientTermname(tw, termNameTd);
      if (termdata.fields) {
        const cols = termdata.fields;
        const cols_multi = termdata_multi.fields;
        {
          const td = tr2.append("td").style("padding", "8px");
          fillColumn2coefficientsTable(td, tw);
        }
        if (self.config.regressionType == "cox") {
          cols.shift();
          cols.shift();
          cols_multi.shift();
          cols_multi.shift();
        }
        forestPlotter_uni(tr2.append("td"), cols);
        self.fillCoefDataCols({ tr: tr2, cols, tw, isUnivariate: true });
        tr2.append("td").style("width", "2px");
        forestPlotter_multi(tr2.append("td"), cols_multi);
        self.fillCoefDataCols({ tr: tr2, cols: cols_multi, tw });
      } else if (termdata.categories) {
        const orderedCategories = [];
        const input = self.getIndependentInput(tid2);
        if (input.orderedLabels) {
          for (const k of input.orderedLabels) {
            if (termdata.categories[k]) orderedCategories.push(k);
          }
        }
        for (const k in termdata.categories) {
          if (!orderedCategories.includes(k)) orderedCategories.push(k);
        }
        termNameTd.attr("rowspan", orderedCategories.length).style("vertical-align", "top");
        let isfirst = true;
        for (const k of orderedCategories) {
          if (!isfirst) {
            tr2 = table.append("tr").style("background", rowcolor);
          }
          const cols = termdata.categories[k];
          const cols_multi = termdata_multi.categories[k];
          const td = tr2.append("td").style("padding", "8px");
          fillColumn2coefficientsTable(td, tw, k);
          if (self.config.regressionType == "cox") {
            if (tw.q.mode == "spline") {
              cols.shift();
              cols.shift();
              cols_multi.shift();
              cols_multi.shift();
            } else {
              const [samplesize_ref, samplesize_c] = cols.shift().split("/");
              const [eventcnt_ref, eventcnt_c] = cols.shift().split("/");
              if (isfirst) {
                const variableBottomDiv = termNameTd.select(".sjpcb-coef-variable-bottom");
                variableBottomDiv.style("align-items", "baseline");
                const refGrpDiv = variableBottomDiv.selectAll("div").filter((d, i) => i === 1);
                refGrpDiv.append("div").html(`n=${samplesize_ref}<br>events=${eventcnt_ref}`);
              }
              td.append("div").style("font-size", ".8em").html(`n=${samplesize_c}<br>events=${eventcnt_c}`);
              cols_multi.shift();
              cols_multi.shift();
            }
          }
          forestPlotter_uni(tr2.append("td"), cols);
          self.fillCoefDataCols({ tr: tr2, cols, tw, categoryKey: k, isUnivariate: true });
          tr2.append("td").style("width", "2px");
          forestPlotter_multi(tr2.append("td"), cols_multi);
          self.fillCoefDataCols({ tr: tr2, cols: cols_multi, tw, categoryKey: k });
          isfirst = false;
        }
      } else {
        tr2.append("td").text("ERROR: no .fields[] or .categories{}");
      }
    }
    const tr = table.append("tr");
    tr.append("td");
    tr.append("td");
    forestPlotter_uni(tr.append("td"));
    for (const v of header_uni) tr.append("td");
    tr.append("td").style("width", "2px");
    forestPlotter_multi(tr.append("td"));
    for (const v of header_multi) tr.append("td");
  };
  self.fillDataHeaders = (header, tr, tr_label, label) => {
    const startColN = tr.selectAll("td").size();
    tr.append("td");
    header.forEach((h, i, arr) => {
      if (i === 0) {
        const est = h;
        const estTd = tr.append("td").style("padding", "8px").text(est);
        const estInfo = estTd.append("sup").style("cursor", "default").html("&nbsp;&#9432;");
        estInfo.on("mouseover", (event) => {
          const tip = self.dom.tip.clear();
          tip.d.append("div").text("Hover over each value to view explanation of the result");
          tip.showunder(event.target);
        });
        estInfo.on("mouseout", () => self.dom.tip.hide());
      } else {
        const td = tr.append("td").text(h).style("padding", "8px");
        if (i === arr.length - 1) td.style("font-style", "italic");
      }
    });
    if (tr_label) {
      const endColN = tr.selectAll("td").size();
      tr_label.append("td").attr("colspan", endColN - startColN).style("padding", "0px 8px").style("text-align", "center").append("div").text(label).style("border-bottom", "1px solid").style("padding", "5px");
    }
  };
  self.fillCoefDataCols = (arg) => {
    const { tr, cols, tw } = arg;
    const est = cols.shift();
    const estSpan = tr.append("td").style("padding", "8px").style("cursor", "default").append("span").text(est);
    estSpan.on("mouseover", (event) => {
      if (tw && tw.q.mode == "spline") return;
      const tip = self.dom.tip.clear();
      let estimateMsg = self.getEstimateMsg(Object.assign({ est: Number(est) }, arg));
      if (tw) {
        const pvalue = Number(cols[cols.length - 1]);
        estimateMsg += `<br><br><span style="font-style: italic">This association is ${pvalue < 0.05 ? "statistically significant (P < 0.05)" : "not statistically significant (P \u2265 0.05)</span>"}.`;
      }
      tip.d.append("div").style("max-width", "500px").html(estimateMsg);
      tip.showunder(event.target);
    });
    estSpan.on("mouseout", () => self.dom.tip.hide());
    tr.append("td").html(`${cols.shift()} &ndash; ${cols.shift()}`).style("padding", "8px");
    for (const v of cols) tr.append("td").text(v).style("padding", "8px");
  };
  self.getEstimateMsg = (arg) => {
    const { est, tw, tw2, categoryKey, categoryKey2, isIntercept, isUnivariate } = arg;
    const independentTws = self.independentTws;
    const outcomeTw = self.config.outcome;
    const regtype = self.config.regressionType;
    const category = tw?.term?.values && tw.term.values[categoryKey] ? tw.term.values[categoryKey].label : categoryKey;
    const category2 = tw2?.term?.values && tw2.term.values[categoryKey2] ? tw2.term.values[categoryKey2].label : categoryKey2;
    const refGrp = tw?.term?.values && tw.term.values[tw.refGrp] ? tw.term.values[tw.refGrp].label : tw?.refGrp;
    const refGrp2 = tw2?.term?.values && tw2.term.values[tw2.refGrp] ? tw2.term.values[tw2.refGrp].label : tw2?.refGrp;
    let msg;
    if (regtype == "linear") {
      msg = tw2 ? getInteractionMsg() : `Mean ${styleVariable(outcomeTw)} is`;
      msg += ` ${Math.abs(est)} units`;
      if (isIntercept) {
        const baselines = getBaselines(independentTws);
        return `${msg} when ${joinVariables(baselines)}.`;
      }
      msg += ` ${est < 0 ? "lower" : "higher"} `;
    } else if (regtype == "logistic") {
      msg = tw2 ? getInteractionMsg() : `Odds of ${styleVariable(outcomeTw, outcomeTw.nonRefGrp)} is`;
      if (isIntercept) {
        const baselines = getBaselines(independentTws);
        return `${msg} ${est} when ${joinVariables(baselines)}.`;
      }
      msg += est > 1 ? ` ${est} times higher ` : ` ${roundValue(1 / est, 3)} times lower `;
    } else if (regtype == "cox") {
      msg = tw2 ? getInteractionMsg() : `Hazard (instantaneous rate) of ${styleVariable(outcomeTw, outcomeTw.eventLabel)} is`;
      msg += est > 1 ? ` ${est} times higher ` : ` ${roundValue(1 / est, 3)} times lower `;
    } else {
      throw "regression type not recognized";
    }
    const interactions = [];
    const interactionsBaselines = [];
    if (tw.interactions?.length && !tw2) {
      for (const tid2 of tw.interactions) {
        const t = self.getIndependentInput(tid2).term;
        if (t.term.snps) {
          for (const snp2 of t.term.snps) interactions.push(snp2.snpid);
        } else {
          interactions.push(tid2);
        }
      }
      if (!interactions.length) throw "interactions[] is empty";
      const interactingTws = independentTws.filter((t) => interactions.includes(t.$id || t.id));
      interactionsBaselines.push(...getBaselines(interactingTws));
    }
    if (category) {
      msg += `in ${joinVariables([styleVariable(tw, category), ...interactionsBaselines])} compared to ${joinVariables([
        styleVariable(tw, refGrp),
        ...interactionsBaselines
      ])}`;
    } else if (tw.q.mode == "continuous") {
      msg += `for every one unit increase of ${styleVariable(tw)}`;
      if (interactionsBaselines.length) msg += ` when ${joinVariables(interactionsBaselines)}`;
    } else if (tw.q.geneticModel === 0) {
      msg += `for every additional ${tw.effectAllele} allele of ${styleVariable(tw)}`;
      if (interactionsBaselines.length) msg += ` when ${joinVariables(interactionsBaselines)}`;
    } else if (tw.q.geneticModel == 1 || tw.q.geneticModel == 2) {
      const gts = Object.keys(tw.gt2count);
      const testGts = gts.filter((gt) => {
        if (tw.q.geneticModel == 1) {
          return gt.includes(tw.effectAllele);
        } else {
          return gt.replace(/[^a-zA-Z]/g, "").split("").every((c) => c == tw.effectAllele);
        }
      });
      const refGts = gts.filter((gt) => !testGts.includes(gt));
      msg += `in ${joinVariables([
        styleVariable(tw, testGts.join(", ")),
        ...interactionsBaselines
      ])} compared to ${joinVariables([styleVariable(tw, refGts.join(", ")), ...interactionsBaselines])}`;
    }
    const tids = [tw.$id || tw.id];
    if (tw.interactions?.length) {
      if (tw2) tids.push(tw2.$id || tw2.id);
      else tids.push(...interactions);
    }
    const covariates = independentTws.filter((t) => !tids.includes(t.$id || t.id)).map((t) => styleVariable(t));
    if (regtype == "cox") {
      covariates.push(outcomeTw.q.timeScale == "time" ? '"Years of follow-up"' : '"Attained age during follow-up"');
    }
    if (!covariates.length || isUnivariate) return msg + ".";
    return msg + `, adjusting for ${joinVariables(covariates)}.`;
    function styleVariable(tw3, category3) {
      const spans = [
        `<span class="term_name_btn sja_filter_tag_btn" style="padding: 3px 6px; margin: 2.5px 0px; border-radius: ${category3 ? "6px 0px 0px 6px" : "6px"};">${tw3.term.name.length < 40 ? tw3.term.name : tw3.term.name.substring(0, 35) + " ..."}</span>`
      ];
      if (category3) {
        spans.push(
          `<span class="ts_summary_btn sja_filter_tag_btn" style="padding: 3px 6px; margin: 2.5px 0px; border-radius: 0px 6px 6px 0px; font-style: italic;">${category3}</span>`
        );
      }
      return `<div style="display: inline; white-space: nowrap; font-size: 0.9em">${spans.join("")}</div>`;
    }
    function getInteractionMsg() {
      let msg2 = regtype == "linear" ? `The difference in mean ${styleVariable(outcomeTw)}` : regtype == "logistic" ? `The difference in odds of ${styleVariable(outcomeTw, outcomeTw.nonRefGrp)}` : `The difference in hazard (instantaneous rate) of ${styleVariable(outcomeTw, outcomeTw.eventLabel)}`;
      if (category2) {
        msg2 += ` between ${styleVariable(tw2, category2)} and ${styleVariable(tw2, refGrp2)} is`;
      } else if (tw2.q.mode == "continuous") {
        msg2 += ` for every one unit increase of ${styleVariable(tw2)} is`;
      } else if (tw2.q.geneticModel === 0) {
        msg2 += ` for every additional ${tw2.effectAllele} allele of ${styleVariable(tw2)} is`;
      } else if (tw2.q.geneticModel == 1 || tw2.q.geneticModel == 2) {
        const gts = Object.keys(tw2.gt2count);
        const testGts = gts.filter((gt) => {
          if (tw2.q.geneticModel == 1) {
            return gt.includes(tw2.effectAllele);
          } else {
            return gt.replace(/[^a-zA-Z]/g, "").split("").every((c) => c == tw2.effectAllele);
          }
        });
        const refGts = gts.filter((gt) => !testGts.includes(gt));
        msg2 += ` between ${styleVariable(tw2, testGts.join(", "))} and ${styleVariable(tw2, refGts.join(", "))} is`;
      }
      return msg2;
    }
    function getBaselines(tws) {
      const baselines = tws.map((tw3) => {
        if (tw3.q.mode != "spline" && "refGrp" in tw3 && tw3.refGrp != refGrp_NA) {
          const refGrp3 = tw3?.term?.values && tw3.term.values[tw3.refGrp] ? tw3.term.values[tw3.refGrp].label : tw3?.refGrp;
          return styleVariable(tw3, refGrp3);
        } else if (tw3.q.mode == "continuous") {
          return styleVariable(tw3, "0");
        } else if (tw3.q.geneticModel === 0) {
          return styleVariable(tw3, `No ${tw3.effectAllele} alleles`);
        } else if (tw3.q.geneticModel == 1 || tw3.q.geneticModel == 2) {
          const gts = Object.keys(tw3.gt2count);
          const refGts = gts.filter((gt) => {
            if (tw3.q.geneticModel == 1) {
              return !gt.includes(tw3.effectAllele);
            } else {
              return !gt.replace(/[^a-zA-Z]/g, "").split("").every((c) => c == tw3.effectAllele);
            }
          });
          return styleVariable(tw3, refGts.join(", "));
        }
      });
      return baselines;
    }
    function joinVariables(variables) {
      if (!variables.length) return "";
      else if (variables.length == 1) return variables[0];
      else if (variables.length == 2) return variables.join(" and ");
      else return `${variables.slice(0, -1).join(", ")}, and ${variables.slice(-1)}`;
    }
  };
  self.mayshow_coxDisclaimer = () => {
    const disclaimer = self.app.vocabApi.termdbConfig.regression?.settings?.coxDisclaimer;
    if (disclaimer && self.config.regressionType == "cox") {
      self.dom.oneSetResultDiv.append("div").style("white-space", "wrap").attr("data-testid", "sjpp-regression-result-coxDisclaimer").style("margin", "20px 0px 20px 10px").style("font-size", ".8em").style("text-align", "left").text(disclaimer);
    }
  };
  self.mayshow_totalSnpEffect = (result) => {
    if (!result.totalSnpEffect) return;
    const div = self.newDiv(result.totalSnpEffect.label);
    const table = div.append("table").style("border-spacing", "0px");
    {
      const tr2 = table.append("tr").style("opacity", 0.4);
      for (const v of result.totalSnpEffect.header) {
        tr2.append("td").text(v).style("padding", "8px");
      }
    }
    const tr = table.append("tr").style("background", "#eee");
    for (const v of result.totalSnpEffect.lst) {
      tr.append("td").text(v).style("padding", "8px");
    }
    const snp2 = self.getIndependentInput(result.totalSnpEffect.snp).term;
    const interactions = result.totalSnpEffect.interactions.map((interaction) => {
      return {
        t1: self.getIndependentInput(interaction.term1).term,
        t2: self.getIndependentInput(interaction.term2).term
      };
    });
    const bottomInfo = `Total: total effect of removing the snp (${snp2.term.name}) and its interactions (${interactions.map((interaction) => interaction.t1.term.name + " : " + interaction.t2.term.name).join(" ; ")}) from the model`;
    div.append("div").style("margin", "20px 0px 20px 10px").style("font-size", ".8em").style("text-align", "left").style("color", "#999").text(bottomInfo);
  };
  self.mayshow_type3 = (result) => {
    if (!result.type3 || self.app.vocabApi.termdbConfig.regression?.settings?.hideType3) return;
    const div = self.newDiv(result.type3.label);
    const table = div.append("table").style("border-spacing", "0px");
    {
      const tr = table.append("tr").style("opacity", 0.4);
      for (const v of result.type3.header) {
        tr.append("td").text(v).style("padding", "8px");
      }
    }
    if (self.config.regressionType != "cox") {
      const tr = table.append("tr").style("background", "#eee");
      for (const v of result.type3.intercept) {
        tr.append("td").text(v).style("padding", "8px");
      }
    }
    let rowcount = self.config.regressionType == "cox" ? 1 : 0;
    for (const tid2 in result.type3.terms) {
      const termdata = result.type3.terms[tid2];
      const tw = self.getIndependentInput(tid2).term;
      let tr = table.append("tr").style("background", rowcount++ % 2 ? "#eee" : "none");
      const termNameTd = tr.append("td").style("padding", "8px");
      fillTdName(termNameTd, tw.term.name);
      for (const v of termdata) {
        tr.append("td").text(v).style("padding", "8px");
      }
    }
    for (const row2 of result.type3.interactions) {
      const tr = table.append("tr").style("background", rowcount++ % 2 ? "#eee" : "none");
      const t1 = self.getIndependentInput(row2.term1).term;
      const t2 = self.getIndependentInput(row2.term2).term;
      const td = tr.append("td").style("padding", "8px");
      fillTdName(td.append("div"), t1.term.name + " : ");
      fillTdName(td.append("div"), t2.term.name);
      for (const v of row2.lst) {
        tr.append("td").text(v).style("padding", "8px");
      }
    }
  };
  self.mayshow_tests = (result) => {
    if (!result.tests || self.app.vocabApi.termdbConfig.regression?.settings?.hideTests) return;
    const div = self.newDiv(result.tests.label);
    const table = div.append("table").style("border-spacing", "0px");
    const header = table.append("tr").style("opacity", 0.4);
    for (const cell of result.tests.header) {
      header.append("td").text(cell).style("padding", "8px");
    }
    let rowcount = 0;
    for (const row2 of result.tests.rows) {
      const tr = table.append("tr").style("background", rowcount++ % 2 ? "none" : "#eee");
      for (const cell of row2) {
        tr.append("td").text(cell).style("padding", "8px");
      }
    }
  };
  self.mayshow_other = (result) => {
    if (!result.other) return;
    const div = self.newDiv(result.other.label);
    const table = div.append("table").style("border-spacing", "8px");
    for (let i = 0; i < result.other.header.length; i++) {
      const tr = table.append("tr");
      tr.append("td").style("opacity", 0.4).text(result.other.header[i]);
      tr.append("td").text(result.other.rows[i]);
    }
  };
  self.getForestPlotter = (terms, interactions) => {
    let midIdx, CIlow, CIhigh, axislab, baselineValue, capMin, capMax;
    if (self.config.regressionType == "linear") {
      midIdx = 0;
      CIlow = 1;
      CIhigh = 2;
      axislab = "Beta value";
      baselineValue = 0;
      capMin = null;
      capMax = null;
    } else if (self.config.regressionType == "logistic") {
      midIdx = 0;
      CIlow = 1;
      CIhigh = 2;
      axislab = "Odds ratio";
      baselineValue = 1;
      capMin = 0.1;
      capMax = 10;
    } else if (self.config.regressionType == "cox") {
      midIdx = 0;
      CIlow = 1;
      CIhigh = 2;
      axislab = "Hazard ratio";
      baselineValue = 1;
      capMin = 0.1;
      capMax = 10;
    } else {
      throw "unknown regressionType";
    }
    const values = [];
    for (const tid2 in terms) {
      const d = terms[tid2];
      if (d.fields) {
        numbers2array(d.fields);
      } else {
        for (const k in d.categories) {
          numbers2array(d.categories[k]);
        }
      }
    }
    for (const i of interactions) {
      for (const k of i.categories) {
        numbers2array(k.lst);
      }
    }
    if (values.length == 0) {
      return () => {
      };
    }
    values.sort((a, b) => a - b);
    if (capMin == null) {
      capMin = values[0];
      capMax = values[values.length - 1];
    }
    const width = 180;
    const height = 20;
    const xleftpad = 10, xrightpad = 10;
    const scale = get_scale(values);
    return (td, lst) => {
      if (!scale) {
        return;
      }
      const svg = td.append("svg").attr("width", width + xleftpad + xrightpad).attr("height", height);
      const g = svg.append("g").attr("transform", "translate(" + xleftpad + ",0)");
      if (!lst) {
        const tickFormat = self.config.regressionType == "logistic" ? ".1r" : void 0;
        const axis = axisBottom().ticks(4, tickFormat).scale(scale);
        axisstyle({
          axis: g.call(axis),
          color: forestcolor,
          showline: true
        });
        const fontsize = 12;
        g.append("text").attr("fill", forestcolor).text(axislab).attr("x", width / 2).attr("y", height + fontsize);
        svg.attr("height", height + fontsize);
        return;
      }
      {
        const x = scale(baselineValue);
        g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", height).attr("stroke", "#ccc");
      }
      const mid = Number(lst[midIdx]), cilow = Number(lst[CIlow]), cihigh = Number(lst[CIhigh]);
      if (Number.isNaN(mid)) {
        return;
      }
      g.append("circle").attr("cx", scale(Math.min(Math.max(mid, capMin), capMax))).attr("cy", height / 2).attr("r", 3).attr("fill", forestcolor);
      if (Number.isNaN(cilow) || Number.isNaN(cihigh)) {
        return;
      }
      g.append("line").attr("x1", scale(Math.min(Math.max(cilow, capMin), capMax))).attr("y1", height / 2).attr("x2", scale(Math.min(Math.max(cihigh, capMin), capMax))).attr("y2", height / 2).attr("stroke", forestcolor);
    };
    function numbers2array(_lst) {
      const lst = self.config.regressionType == "cox" ? _lst.slice(2) : _lst;
      const m = Number(lst[midIdx]);
      if (!Number.isNaN(m)) values.push(m);
      const l = Number(lst[CIlow]), h = Number(lst[CIhigh]);
      if (!Number.isNaN(l) && !Number.isNaN(h)) {
        values.push(l);
        values.push(h);
      }
    }
    function get_scale(values2) {
      if (self.config.regressionType == "logistic") {
        let i = 0;
        while (values2[i] <= 0) {
          i++;
        }
        if (i >= values2.length || values2[i] <= 0) {
          return;
        }
        const min = values2[i];
        const max = values2[values2.length - 1];
        return log().domain([Math.max(min, capMin), Math.min(max, capMax)]).range([0, width]).nice();
      }
      if (self.config.regressionType == "linear" || self.config.regressionType == "cox") {
        return linear().domain([Math.max(values2[0], capMin), Math.min(values2[values2.length - 1], capMax)]).range([0, width]);
      }
      throw "unknown type";
    }
  };
}
function fillTdName(td, name) {
  if (name.length < 40) {
    td.text(name);
  } else {
    td.text(name.substring(0, 35) + " ...").attr("aria-label", name);
  }
}
function fillCoefficientTermname(tw, td) {
  fillTdName(td, tw.term.name || tid);
  const hasRefGrp = "refGrp" in tw && tw.refGrp != refGrp_NA && tw.q.mode != "spline";
  if (hasRefGrp || tw.effectAllele) {
    const bottomDiv = td.append("div").attr("class", "sjpcb-coef-variable-bottom").style("display", "flex").style("align-items", "center").style("margin-top", "2px").style("font-size", ".8em");
    let label;
    if (hasRefGrp) {
      label = tw.term.values && tw.term.values[tw.refGrp] ? tw.term.values[tw.refGrp].label : tw.refGrp;
    } else {
      label = tw.effectAllele;
    }
    bottomDiv.append("div").style("padding", "1px 5px").style("border", "1px solid #aaa").style("border-radius", "10px").style("font-size", ".7em").text(hasRefGrp ? "REF" : "EFFECT ALLELE");
    bottomDiv.append("div").style("padding", "1px 3px").text(label);
  }
}
function make_mds3_variants(tw, resultLst, regressionType) {
  const mlst = [];
  for (const snp2 of tw.term.snps) {
    const m = {
      chr: snp2.chr,
      pos: snp2.pos,
      ssm_id: snp2.snpid
      // needed for highlighting dot
    };
    mlst.push(m);
    const effAle = tw.q.snp2effAle[snp2.snpid];
    const m2 = snp2.mlst.find((i) => i.alt == effAle);
    if (m2) {
      Object.assign(m, m2);
    } else {
      Object.assign(m, snp2.mlst[0]);
    }
    m.regressionPvalue = "NA";
    m.mlpv = 0;
    const thisresult = resultLst.find((i) => i.id == snp2.snpid);
    if (!thisresult) {
      m.regressionResult = {
        data: {
          err: ["No result for this variant at " + snp2.snpid]
        }
      };
      continue;
    }
    m.regressionResult = thisresult;
    const d = thisresult.data;
    if (!d) throw ".data{} missing";
    if (d.type3) {
      const v = getSnpPvalueFromRegressionResults(d, snp2.snpid);
      if (v == void 0) {
      } else {
        m.regressionPvalue = v;
        m.mlpv = -Math.log10(v);
      }
      if (!d.coefficients || !d.coefficients.terms) throw ".data.coefficients.terms{} missing";
      const r = d.coefficients.terms[snp2.snpid];
      if (!r) throw "snp missing from data.coefficients.terms{}";
      if (Array.isArray(r.fields)) {
        m.regressionEstimate = regressionType == "cox" ? r.fields[2] : r.fields[0];
      } else if (r.categories) {
        const lst = [];
        for (const gt in r.categories) {
          lst.push(gt + ":" + regressionType == "cox" ? r.categories[gt][2] : r.categories[gt][0]);
        }
        m.regressionEstimate = " " + lst.join(" ");
      } else {
        throw "unknown way to get snp estimates from coefficients table";
      }
    } else if (d.fisher) {
      m.regressionPvalue = d.fisher.pvalue;
      m.mlpv = -Math.log10(d.fisher.pvalue);
      m.shape = "filledTriangle";
    } else if (d.wilcoxon) {
      m.regressionPvalue = d.wilcoxon.pvalue;
      m.mlpv = -Math.log10(d.wilcoxon.pvalue);
      m.shape = "filledTriangle";
    } else if (d.cuminc) {
      m.regressionPvalue = d.cuminc.pvalue;
      m.mlpv = -Math.log10(d.cuminc.pvalue);
      m.shape = "filledTriangle";
    } else {
      m.shape = "emptyCircle";
    }
  }
  return mlst;
}
async function createGenomebrowser(self, input, resultLst) {
  const arg = {
    holder: self.dom.snplocusBlockDiv,
    genome: self.parent.genomeObj,
    chr: input.term.q.chr,
    start: input.term.q.start,
    stop: input.term.q.stop,
    nobox: true,
    tklst: [],
    onCoordinateChange: async (rglst) => {
      for (const t of self.snplocusBlock.tklst) {
        if (t.type == "mds3") delete t.skewer.hlssmid;
      }
      const { chr, start, stop } = rglst[0];
      const overrideTw = {
        term: {
          id: input.term.term.id,
          type: "snplocus"
        },
        q: JSON.parse(JSON.stringify(input.term.q))
      };
      overrideTw.q.chr = chr;
      overrideTw.q.start = start;
      overrideTw.q.stop = stop;
      const _2 = await import("./snplocus-IL5Z4XWV.js");
      await _2.fillTW(overrideTw, self.app.vocabApi);
      self.hasUnsubmittedEdits_nullify_singleuse = true;
      input.pill.runCallback(overrideTw);
    }
  };
  arg.tklst.push({
    type: "mds3",
    // tkt.mds3
    name: "Variants",
    skewerModes: [
      {
        type: "numeric",
        byAttribute: "mlpv",
        // corresponds to the "mlpv" attribute in m{}, can be anything
        label: "-log10 p-value",
        inuse: true,
        tooltipPrintValue: (m) => getMtooltipValues(m, self.config.regressionType)
      }
    ],
    custom_variants: make_mds3_variants(input.term, resultLst, self.config.regressionType),
    legend: {
      customShapeLabels: {
        filledCircle: "common variants analyzed by model-fitting",
        filledTriangle: "rare variants analyzed by " + (self.config.regressionType == "linear" ? "Wilcoxon rank sum test" : self.config.regressionType == "logistic" ? "Fisher's exact test" : "Cumulative incidence test"),
        emptyCircle: "monomorphic variants skipped"
      }
    },
    click_snvindel: async (m) => {
      self.displayResult_oneset(structuredClone(m.regressionResult.data));
      await mayCheckLD(m, input, self);
      const result_y = self.dom.oneSetResultDiv.node().getBoundingClientRect().top + window.scrollY;
      const nav_height = document.querySelector(".sjpp-nav").getBoundingClientRect().height;
      window.scroll({ behavior: "smooth", top: result_y - nav_height });
    }
  });
  first_genetrack_tolist(self.parent.genomeObj, arg.tklst);
  const _ = await import("./block-TTN2IQAH.js");
  return new _.Block(arg);
}
async function updateMds3Tk(self, input, resultLst) {
  const tk = self.snplocusBlock.tklst.find((i) => i.type == "mds3");
  tk.custom_variants = make_mds3_variants(input.term, resultLst, self.config.regressionType);
  const r = self.snplocusBlock.rglst[0];
  if (r.chr == input.term.q.chr && r.start == input.term.q.start && r.stop == input.term.q.stop) {
    tk.load();
  } else {
    await self.snplocusBlock.jump_1basedcoordinate(input.term.q);
  }
  self.snplocusBlock.cloakOff();
}
var LDcolor0 = "#2E6594";
var LDcolor1 = "#ff0000";
var LDcolorScale = rgb_default(LDcolor0, LDcolor1);
async function mayCheckLD(m, input, self) {
  if (!input.term.q.restrictAncestry) {
    return;
  }
  const tk = self.snplocusBlock.tklst.find((i) => i.type == "mds3");
  if (!tk || !tk.skewer || !tk.skewer.nmg) return;
  for (const m2 of tk.custom_variants) delete m2.regressionR2;
  const wait = self.dom.LDresultDiv.append("span").text("Loading LD data...");
  try {
    const data = await self.app.vocabApi.getLDdata(input.term.q.restrictAncestry.name, m);
    if (data.error) throw data.error;
    if (data.nodata || !data.lst || data.lst.length == 0) {
      wait.text("No LD data");
      tk.skewer.nmg.selectAll(".sja_aa_disk_fill").attr("fill", (m2) => m2.shapeCircle ? "none" : tk.color4disc(m2));
      return;
    }
    tk.skewer.nmg.selectAll(".sja_aa_disk_fill").attr("fill", (m2) => {
      if (m2.pos == m.pos && m2.ref == m.ref && m2.alt == m.alt) {
        return LDcolor1;
      }
      for (const i of data.lst) {
        if (i.pos == m2.pos && i.alleles == m2.ref + "." + m2.alt) {
          m2.regressionR2 = i.r2;
          return LDcolorScale(i.r2);
        }
      }
      return LDcolorScale(0);
    });
    wait.html(input.term.q.restrictAncestry.name + " LD r<sup>2</sup>");
    showLDlegend(self.dom.LDresultDiv, LDcolorScale);
  } catch (e) {
    wait.text("Error: " + (e.message || e));
  }
}
function showLDlegend(div, colorScale) {
  const colorbardiv = div.append("span").style("margin-left", "10px");
  const colorlst = [];
  for (let i = 0; i <= 1; i += 0.1) {
    colorlst.push(colorScale(i));
  }
  const axisheight = 20;
  const barheight = 15;
  const xpad = 10;
  const axiswidth = 150;
  const domain = colorlst.map((d, i) => i / (colorlst.length - 1));
  new ColorScale({
    holder: colorbardiv,
    domain,
    topTicks: true,
    width: xpad * 2 + axiswidth,
    height: axisheight + barheight,
    barheight,
    barwidth: axiswidth,
    fontSize: 12,
    colors: colorlst,
    position: `${xpad},${axisheight}`,
    tickSize: 6
  });
}
function getMtooltipValues(m, regressionType) {
  const lst = [{ k: "p-value", v: m.regressionPvalue }];
  if (m.regressionResult.AFstr) {
    lst.push({ k: "AF", v: m.regressionResult.AFstr });
  }
  if (m.regressionEstimate) {
    if (regressionType == "linear") lst.push({ k: "beta", v: m.regressionEstimate });
    else if (regressionType == "logistic") lst.push({ k: "odds ratio", v: m.regressionEstimate });
    else if (regressionType == "cox") lst.push({ k: "hazard ratio", v: m.regressionEstimate });
    else throw "unknown regression type";
  }
  if (m.regressionR2) {
    lst.push({ k: "LD r2", v: m.regressionR2 });
  }
  return lst;
}
function getSnpPvalueFromRegressionResults(d, snpid) {
  let str;
  if (d.totalSnpEffect) {
    str = d.totalSnpEffect.lst[d.totalSnpEffect.lst.length - 1];
  } else {
    if (!d.type3.terms) throw ".data{type3:{terms}} missing";
    if (!d.type3.terms[snpid]) throw snpid + " missing in type3.terms{}";
    if (!Array.isArray(d.type3.terms[snpid])) throw `type3.terms[${snp.snpid}] not array`;
    str = d.type3.terms[snpid][d.type3.terms[snpid].length - 1];
  }
  const v = Number(str);
  if (Number.isFinite(v)) {
    return v;
  }
  return void 0;
}
function fillColumn2coefficientsTable(div, tw, categoryKey) {
  if (categoryKey) {
    div.text(tw && tw.term.values && tw.term.values[categoryKey] ? tw.term.values[categoryKey].label : categoryKey);
    return;
  }
  div.style("opacity", 0.3);
  if ("geneticModel" in tw.q) {
    const v = tw.q.geneticModel;
    div.text(v == 0 ? "(additive)" : v == 1 ? "(dominant)" : "(recessive)");
    return;
  }
  if (tw.q.mode) {
    div.text("(" + tw.q.mode + ")");
    return;
  }
}

export {
  RegressionResults,
  showLDlegend
};
//# sourceMappingURL=chunk-B6CSYKZJ.js.map
