<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8" />
  <title>Cálculo de Incertezas com Correlação – Tabelas Editáveis (Preservando Dados)</title>

  <!-- 1) Biblioteca Math.js (para derivadas e avaliação, incluindo trig/log/exp) -->
  <script src="math.min.js"></script>

  <!-- 2) Biblioteca SheetJS (XLSX) para gerar e ler arquivo Excel -->
  <script src="xlsx.full.min.js"></script>

  <!-- 3) Biblioteca jStat (para quantil t-Student) -->
  <script src="jstat.min.js"></script>

  <style>
    body {
      font-family: "Segoe UI", Tahoma, sans-serif;
      margin: 20px;
      background: #fafafa;
    }

    header, .toolbar {
      display: flex;
      align-items: center;
      margin-bottom: 10px;
    }

    h1 {
      flex: 1;
      color: #444;
      margin: 0;
    }

    button.help-btn {
      background-color: #ffcc00;
      border: none;
      border-radius: 50%;
      font-size: 16px;
      width: 32px;
      height: 32px;
      cursor: pointer;
      margin-left: 10px;
    }

    .buttons-group {
      margin-top: 10px;
    }

    label {
      font-weight: bold;
      color: #555;
      display: block;
      margin-top: 10px;
    }

    input[type="text"], input[type="file"] {
      width: 100%;
      box-sizing: border-box;
      margin: 4px 0 10px 0;
      padding: 6px;
    }

    table {
      border-collapse: collapse;
      margin-top: 10px;
      width: 100%;
      background: #fff;
    }
    th, td {
      border: 1px solid #ccc;
      padding: 8px;
      text-align: center;
      vertical-align: middle;
    }
    th {
      background-color: #e9e9ff;
      font-weight: 600;
      color: #333;
    }
    tr:nth-child(even) {
      background-color: #fcfcfc;
    }

    .section-title {
      margin-top: 20px;
      color: #333;
      border-bottom: 2px solid #ccc;
      padding-bottom: 4px;
      font-size: 1.2rem;
    }

    .results {
      margin-top: 20px;
      background-color: #f0f9ff;
      padding: 10px;
      border: 1px solid #cce6ff;
      border-radius: 4px;
    }

    hr {
      margin: 20px 0;
    }

    /* Botões */
    button {
      margin-right: 5px;
      padding: 6px 12px;
      border: none;
      cursor: pointer;
      border-radius: 4px;
      font-size: 14px;
    }
    button:hover {
      opacity: 0.9;
    }
    .primary-btn {
      background-color: #2864dc;
      color: #fff;
    }
    .secondary-btn {
      background-color: #aaa;
      color: #fff;
    }
    .accent-btn {
      background-color: #28c;
      color: #fff;
    }

    /* Campos contenteditable */
    [contenteditable="true"] {
      outline: 1px dotted #999;
      min-width: 2em;
    }
  </style>
</head>
<body>

  <header>
    <h1>Cálculo de Incertezas com Correlação</h1>
    <!-- Botão de Ajuda -->
    <button class="help-btn" onclick="exibirAjuda()">?</button>
  </header>

  <!-- Barra de carregamento -->
  <div class="toolbar">
    <div style="flex:1;">
      <label for="arquivoExcel">Carregar arquivo Excel (.xlsx):</label>
      <input type="file" id="arquivoExcel" accept=".xlsx" />
    </div>
    <button class="accent-btn" onclick="carregarExcel()">Carregar</button>
  </div>

  <hr />

  <!-- Entrada para o modelo matemático -->
  <label for="modelo">Modelo matemático (ex.: Y = sin(X1) + log(X2)): </label>
  <input type="text" id="modelo" placeholder="Y = sin(X1) + log(X2)" onblur="gerarTabelas()" />

  <!-- Entrada para o nível de confiança (padrão 95%) -->
  <label for="confianca">Nível de confiança (ex.: 95):</label>
  <input type="text" id="confianca" value="95" />

  <!-- Área onde as tabelas editáveis serão inseridas -->
  <div id="tabelas-container"></div>

  <!-- Botões para calcular e salvar em Excel -->
  <div class="buttons-group">
    <button class="primary-btn" onclick="calcularIncerteza()">Calcular</button>
    <button class="secondary-btn" onclick="salvarExcel()">Salvar em Excel</button>
  </div>

  <!-- Exibir resultados -->
  <div class="results" id="resultados"></div>

  <script>
    /**
     * Objetos globais para armazenar dados antigos (antes de recriar as tabelas).
     * Assim, quando mudamos o modelo e chamamos gerarTabelas() de novo,
     * não perdemos o que já estava preenchido.
     */
    let dadosVariaveisAntigos = {};     // Ex.: { "X1": { valor:"1.0", U:"0.2", k:"2.0", v:"10" }, ... }
    let dadosCorrelacoesAntigas = {};   // Ex.: { "X1": { "X1":"1.0", "X2":"0.3", ... }, ... }

    // Lista atual de variáveis detectadas no modelo
    let listaVariaveis = [];

    // Variáveis globais para guardar resultados (apenas para exibir na tela)
    let valorModeloGlobal = 0;
    let ucGlobal = 0;
    let veffGlobal = 0;
    let kFinalGlobal = 0;
    let UexpGlobal = 0;

    /**
     * Exibe um alerta com instruções básicas ao clicar em "?"
     */
    function exibirAjuda() {
      alert(
        "Calculo de Incertezas de acordo com GUM!\n\n" +
        "1) Digite o modelo na caixa 'Modelo matemático' (por exemplo: Y = sin(X1) + log(X2)).\n" +
        "   • Você pode usar funções trigonométricas (sin, cos, tan, etc.),\n" +
        "     logaritmos - logaritmo na base 10 (log10), logaritmo em qualquer base (log(x, base)), logaritmo natural (log) -, exponenciais (exp),\n" +
        "     raízes (sqrt). Caso necessário, consulte a biblioteca Math.js.\n\n" +
        "2) Aperte TAB (ou clique fora) para gerar as tabelas.\n" +
        "3) Na Tabela de Variáveis, edite os valores, incertezas etc.\n" +
        "4) Na Tabela de Correlações, edite os coeficientes de correlação.\n" +
        "5) Clique em 'Calcular' para ver o resultado.\n" +
        "6) Para salvar ou carregar de arquivo Excel, use os botões adequados.\n\n"+
        "criado por: Fábio Ouverney Costa (focosta@inmetro.gov.br)"
      );
    }

    /**
     * Lê o que está atualmente nas tabelas (variáveis e correlação)
     * e atualiza dadosVariaveisAntigos / dadosCorrelacoesAntigas.
     */
    function armazenarDadosAtuais() {
      // Para cada variável na listaVariaveis atual, ler as células (se existirem)
      listaVariaveis.forEach(vi => {
        if (!dadosVariaveisAntigos[vi]) {
          dadosVariaveisAntigos[vi] = { valor:"1.0", U:"0.2", k:"2.0", v:"10" };
        }
        const tdValor = document.getElementById(`valor_${vi}`);
        const tdU     = document.getElementById(`U_${vi}`);
        const tdK     = document.getElementById(`k_${vi}`);
        const tdV     = document.getElementById(`v_${vi}`);

        if (tdValor && tdU && tdK && tdV) {
          dadosVariaveisAntigos[vi].valor = tdValor.textContent.trim();
          dadosVariaveisAntigos[vi].U     = tdU.textContent.trim();
          dadosVariaveisAntigos[vi].k     = tdK.textContent.trim();
          dadosVariaveisAntigos[vi].v     = tdV.textContent.trim();
        }
      });

      // Para a matriz de correlação
      listaVariaveis.forEach(vi => {
        if (!dadosCorrelacoesAntigas[vi]) {
          dadosCorrelacoesAntigas[vi] = {};
        }
        listaVariaveis.forEach(vj => {
          const tdCorr = document.getElementById(`corr_${vi}_${vj}`);
          if (tdCorr) {
            dadosCorrelacoesAntigas[vi][vj] = tdCorr.textContent.trim();
          }
        });
      });
    }

    /**
     * Gera as tabelas de variáveis e correlações (editáveis),
     * mantendo os dados já inseridos anteriormente.
     */
    function gerarTabelas() {
      // 1) Primeiro, armazena o que está na tela (se houver algo)
      armazenarDadosAtuais();

      // 2) Extrai variáveis do modelo
      const inputModelo = document.getElementById('modelo').value.trim();
      if (!inputModelo) return;

      const partes = inputModelo.split("=");
      if (partes.length < 2) return;
      const expressao = partes[1].trim();

  	// ---------- NOVO MÉTODO: usar parse e traverse ----------
  	let raiz;
  	try {
 	   raiz = math.parse(expressao);
 	 } catch (err) {
  	  // Se a expressão estiver inválida, avise e saia
  	  alert("Expressão inválida: " + err);
   	 return;
 	 }

  const varsDetectadas = [];
  raiz.traverse((node, path, parent) => {
    if (node.type === 'SymbolNode') {
      const nome = node.name;
      // Verifica se "nome" é uma função de math.js ou constante
      const isFunc = (typeof math[nome] === 'function');
      const isConst = (nome === 'pi' || nome === 'e' || nome === 'Infinity');
      if (!isFunc && !isConst) {
        varsDetectadas.push(nome);
      }
    }
  });


// Remove duplicadas
  listaVariaveis = [...new Set(varsDetectadas)];

      // 3) Se não há variáveis, mostra mensagem
      const divContainer = document.getElementById('tabelas-container');
      divContainer.innerHTML = "";
      if (listaVariaveis.length === 0) {
        divContainer.innerHTML = "<p>Nenhuma variável identificada no modelo.</p>";
        return;
      }

      // ========== Tabela de Variáveis ==========
      let htmlVars = `<h3 class="section-title">Tabela de Variáveis</h3>`;
      htmlVars += `<table>
        <thead>
          <tr>
            <th>Variável</th>
            <th>Valor esperado</th>
            <th>Incerteza expandida (U)</th>
            <th>Fator de abrangência (k)</th>
            <th>Graus de liberdade (ν)</th>
          </tr>
        </thead>
        <tbody>`;

      listaVariaveis.forEach(vi => {
        // Tentar recuperar dados antigos, senão defaults
        let infoAntiga = dadosVariaveisAntigos[vi] || { valor:"1.0", U:"0.2", k:"2.0", v:"10" };

        htmlVars += `
          <tr>
            <td><strong>${vi}</strong></td>
            <td contenteditable="true" id="valor_${vi}">${infoAntiga.valor}</td>
            <td contenteditable="true" id="U_${vi}">${infoAntiga.U}</td>
            <td contenteditable="true" id="k_${vi}">${infoAntiga.k}</td>
            <td contenteditable="true" id="v_${vi}">${infoAntiga.v}</td>
          </tr>
        `;
      });

      htmlVars += `</tbody></table>`;

      // ========== Tabela de Correlação ==========
      let htmlCorr = `<h3 class="section-title">Tabela de Correlações</h3>`;
      htmlCorr += `<table>
        <thead>
          <tr><th></th>`;
      listaVariaveis.forEach(varCol => {
        htmlCorr += `<th>${varCol}</th>`;
      });
      htmlCorr += `</tr></thead><tbody>`;

      listaVariaveis.forEach((vi, i) => {
        htmlCorr += `<tr><th>${vi}</th>`;
        listaVariaveis.forEach((vj, j) => {
          // Se tivermos algo antigo, usa. Se não, default 1.0 na diagonal, 0.0 fora
          let valorCorr = "0.0";
          if (dadosCorrelacoesAntigas[vi] && dadosCorrelacoesAntigas[vi][vj] !== undefined) {
            valorCorr = dadosCorrelacoesAntigas[vi][vj];
          } else {
            if (i === j) valorCorr = "1.0";
          }
          htmlCorr += `
            <td contenteditable="true" id="corr_${vi}_${vj}">
              ${valorCorr}
            </td>
          `;
        });
        htmlCorr += `</tr>`;
      });
      htmlCorr += `</tbody></table>`;

      // 4) Insere no container
      divContainer.innerHTML = htmlVars + htmlCorr;
    }

    /**
     * Calcula o quantil da distribuição t-Student
     * para um nível de confiança (conf, em %) e df graus de liberdade.
     */
    function calcularFatorCobertura(conf, df) {
      if (df <= 0) return 1.0;
      const alpha = 1 - (conf / 100);
      const p = 1 - alpha / 2;
      return jStat.studentt.inv(p, df);
    }

    /**
     * Função principal de cálculo:
     *  - Incerteza combinada (u_c) com correlação,
     *  - Welch-Satterthwaite (sem correlação) para obter ν_eff,
     *  - Fator de cobertura (k) e U expandida.
     */
    function calcularIncerteza() {
      const inputModelo = document.getElementById('modelo').value.trim();
      const partes = inputModelo.split("=");
      if (partes.length < 2) {
        alert("Modelo inválido. Use o formato: Y = expressão");
        return;
      }
      if (listaVariaveis.length === 0) {
        alert("Nenhuma variável para calcular. Verifique o modelo.");
        return;
      }

      const expressao = partes[1].trim();

      // Ler dados das variáveis
      const valores = {};
      const incertPadrao = {}; // u(x_i) = U_i / k_i
      const dof = {};

      listaVariaveis.forEach(variavel => {
        const valStr = document.getElementById(`valor_${variavel}`).textContent;
        const uStr   = document.getElementById(`U_${variavel}`).textContent;
        const kStr   = document.getElementById(`k_${variavel}`).textContent;
        const vStr   = document.getElementById(`v_${variavel}`).textContent;

        const valNum = parseFloat(valStr) || 0;
        const uNum   = parseFloat(uStr)   || 0;
        const kNum   = parseFloat(kStr)   || 1;
        const vNum   = parseFloat(vStr)   || 1;

        valores[variavel]      = valNum;
        incertPadrao[variavel] = uNum / kNum;
        dof[variavel]          = vNum;
      });

      // Ler correlação
      const correlation = {};
      listaVariaveis.forEach(vi => {
        correlation[vi] = {};
        listaVariaveis.forEach(vj => {
          const corrStr = document.getElementById(`corr_${vi}_${vj}`).textContent;
          correlation[vi][vj] = parseFloat(corrStr) || 0;
        });
      });

      // Avaliar o modelo no ponto de valores esperados
      let valorModelo = 0;
      try {
        const f = math.parse(expressao);
        valorModelo = f.evaluate(valores);
      } catch (err) {
        alert("Erro ao avaliar o modelo. Verifique a expressão.");
        return;
      }

      // Derivadas
      const deriv = {};
      try {
        listaVariaveis.forEach(vi => {
          const df_dxi = math.derivative(expressao, vi);
          deriv[vi] = df_dxi.evaluate(valores);
        });
      } catch (err) {
        alert("Erro ao calcular derivadas. Verifique o modelo.");
        return;
      }

      // 1) u_c^2 (com correlação)
      let uc2 = 0;
      listaVariaveis.forEach(vi => {
        uc2 += (deriv[vi] * incertPadrao[vi]) ** 2;
      });
      for (let i = 0; i < listaVariaveis.length; i++) {
        for (let j = i+1; j < listaVariaveis.length; j++) {
          const vi = listaVariaveis[i];
          const vj = listaVariaveis[j];
          const r_ij = correlation[vi][vj] || 0;
          uc2 += 2 * (deriv[vi] * incertPadrao[vi]) *
                     (deriv[vj] * incertPadrao[vj]) * r_ij;
        }
      }
      const uc = Math.sqrt(uc2);

      // 2) u_c_no_corr^2 (sem correlação)
      let ucNoCorr2 = 0;
      listaVariaveis.forEach(vi => {
        ucNoCorr2 += (deriv[vi] * incertPadrao[vi]) ** 2;
      });
      const ucNoCorr = Math.sqrt(ucNoCorr2);

      // 3) Welch-Satterthwaite sobre u_c_no_corr
      let somaWS = 0;
      listaVariaveis.forEach(vi => {
        const numWS = Math.pow(deriv[vi] * incertPadrao[vi], 4);
        somaWS += (numWS / dof[vi]);
      });
      let veff = Infinity;
      if (somaWS > 0) {
        veff = Math.pow(ucNoCorr, 4) / somaWS;
      }

      // 4) Fator k e U expandida
      const confianca = parseFloat(document.getElementById("confianca").value) || 95;
      const kFinal = calcularFatorCobertura(confianca, veff);
      const Uexp = kFinal * uc;

      // Exibir
      valorModeloGlobal = valorModelo;
      ucGlobal = uc;
      veffGlobal = veff;
      kFinalGlobal = kFinal;
      UexpGlobal = Uexp;

      const divResultados = document.getElementById('resultados');
      divResultados.innerHTML = `
        <h2>Resultados</h2>
        <p><strong>Valor esperado do modelo:</strong> ${valorModelo.toFixed(6)}</p>
        <p><strong>Incerteza padrão combinada (u<sub>c</sub>) (com correlação):</strong> ${uc.toFixed(6)}</p>
        <p><strong>Graus de liberdade efetivos (ν<sub>eff</sub>) (sem correlação):</strong> ${veff.toFixed(2)}</p>
        <p><strong>Fator de cobertura (k) para ${confianca}%:</strong> ${kFinal.toFixed(3)}</p>
        <p><strong>Incerteza expandida (U<sub>modelo</sub>):</strong> ${Uexp.toFixed(6)}</p>
      `;
    }

    /**
     * Salvar em Excel (3 abas):
     *  1) "Modelo_e_Confianca"
     *  2) "Variaveis"
     *  3) "Correlacoes"
     */
    function salvarExcel() {
      // Modelo + confiança
      const inputModelo = document.getElementById('modelo').value.trim();
      const inputConfianca = document.getElementById('confianca').value.trim();

      // 1) Planilha "Modelo_e_Confianca"
      const sheetMC = [];
      sheetMC.push(["Modelo e Nível de Confiança"]);
      sheetMC.push([]);
      sheetMC.push(["Modelo matemático:", inputModelo]);
      sheetMC.push(["Nível de confiança:", inputConfianca]);
      const wsModeloConf = XLSX.utils.aoa_to_sheet(sheetMC);

      // 2) Planilha "Variaveis"
      const sheetVars = [];
      sheetVars.push(["Variável", "Valor esperado", "U", "k", "ν"]);
      listaVariaveis.forEach(variavel => {
        const valStr = document.getElementById(`valor_${variavel}`).textContent;
        const uStr   = document.getElementById(`U_${variavel}`).textContent;
        const kStr   = document.getElementById(`k_${variavel}`).textContent;
        const vStr   = document.getElementById(`v_${variavel}`).textContent;
        sheetVars.push([variavel, valStr, uStr, kStr, vStr]);
      });
      const wsVariaveis = XLSX.utils.aoa_to_sheet(sheetVars);

      // 3) Planilha "Correlacoes"
      const sheetCorr = [];
      const headerRow = [""].concat(listaVariaveis);
      sheetCorr.push(headerRow);

      listaVariaveis.forEach((vi, i) => {
        const row = [vi];
        listaVariaveis.forEach((vj, j) => {
          const corrStr = document.getElementById(`corr_${vi}_${vj}`).textContent;
          row.push(corrStr);
        });
        sheetCorr.push(row);
      });
      const wsCorrelacoes = XLSX.utils.aoa_to_sheet(sheetCorr);

      // Monta workbook
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, wsModeloConf, "Modelo_e_Confianca");
      XLSX.utils.book_append_sheet(wb, wsVariaveis, "Variaveis");
      XLSX.utils.book_append_sheet(wb, wsCorrelacoes, "Correlacoes");

      XLSX.writeFile(wb, "resultado_incertezas.xlsx");
    }

    /**
     * Carregar Excel e preencher
     * (modelo, confiança, variáveis, correlações).
     */
    function carregarExcel() {
      const fileInput = document.getElementById("arquivoExcel");
      if (!fileInput.files.length) {
        alert("Selecione um arquivo .xlsx para carregar.");
        return;
      }
      const file = fileInput.files[0];

      const reader = new FileReader();
      reader.onload = function(e) {
        const data = new Uint8Array(e.target.result);

        let wb;
        try {
          wb = XLSX.read(data, { type: "array" });
        } catch (error) {
          alert("Erro ao ler o arquivo Excel. Verifique se é um .xlsx válido.");
          return;
        }

        // Verifica abas
        if (!wb.Sheets["Modelo_e_Confianca"] || 
            !wb.Sheets["Variaveis"] || 
            !wb.Sheets["Correlacoes"]) {
          alert("O arquivo não possui as abas esperadas: 'Modelo_e_Confianca', 'Variaveis', 'Correlacoes'.");
          return;
        }

        // 1) Ler "Modelo_e_Confianca"
        const wsMC = wb.Sheets["Modelo_e_Confianca"];
        const dataMC = XLSX.utils.sheet_to_json(wsMC, { header: 1 });
        let modeloLido = "";
        let confiancaLida = "";
        dataMC.forEach(row => {
          if (!row || row.length < 2) return;
          if (row[0] === "Modelo matemático:") {
            modeloLido = row[1];
          }
          if (row[0] === "Nível de confiança:") {
            confiancaLida = row[1];
          }
        });
        document.getElementById("modelo").value = modeloLido || "";
        document.getElementById("confianca").value = confiancaLida || "";

        // 2) Precisamos regenerar as tabelas (para ter ids)
        gerarTabelas();

        // 3) Ler "Variaveis"
        const wsVars = wb.Sheets["Variaveis"];
        const dataVars = XLSX.utils.sheet_to_json(wsVars, { header: 1 });
        for (let i = 1; i < dataVars.length; i++) {
          const row = dataVars[i];
          if (!row || row.length < 5) continue;
          const nomeVar = row[0];
          if (!listaVariaveis.includes(nomeVar)) continue;

          const valStr = row[1];
          const uStr   = row[2];
          const kStr   = row[3];
          const vStr   = row[4];

          document.getElementById(`valor_${nomeVar}`).textContent = valStr;
          document.getElementById(`U_${nomeVar}`).textContent     = uStr;
          document.getElementById(`k_${nomeVar}`).textContent     = kStr;
          document.getElementById(`v_${nomeVar}`).textContent     = vStr;
        }

        // 4) Ler "Correlacoes"
        const wsCorr = wb.Sheets["Correlacoes"];
        const dataCorr = XLSX.utils.sheet_to_json(wsCorr, { header: 1 });
        if (dataCorr.length > 1) {
          const header = dataCorr[0];
          const colVars = header.slice(1);

          for (let i = 1; i < dataCorr.length; i++) {
            const row = dataCorr[i];
            if (!row || row.length < 2) continue;
            const vi = row[0];
            if (!listaVariaveis.includes(vi)) continue;

            for (let j = 1; j < row.length; j++) {
              const vj = colVars[j - 1];
              if (!listaVariaveis.includes(vj)) continue;

              const corrVal = row[j];
              document.getElementById(`corr_${vi}_${vj}`).textContent = corrVal;
            }
          }
        }

        alert("Arquivo carregado com sucesso!");
      };

      reader.readAsArrayBuffer(file);
    }
  </script>
</body>
</html>
