Eurafricanid
12-25-2022, 08:52 PM
how do u have the R7P option? me i can only use as much R5P
With this:
<!DOCTYPE html>
<!--
https://github.com/vahaduo/
-->
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dodecad K12b Ancient - VahaduoJS </title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<style>
body, html {
height: 100%;
margin: 0px;
background-color: #555;
font-size: calc(0.5vw + 12px);
font-family: 'Montserrat', sans-serif;
color: #dadada;
}
nav {
width: 100%;
background-color: #ccc;
background-image: linear-gradient(#ccc, #ddd);
padding-top: 0.4em;
padding-left: 0.4em;
box-sizing: border-box;
font-family: 'Montserrat', sans-serif;
}
header {
float: right;
text-align: right;
padding-left: 0 0.4em 0 0.4em;
margin: 0 1em 0 1em;
display: inline-block;
color: #9d9d9d;
font-family: 'Montserrat', sans-serif;
}
header div {
text-transform: uppercase;
font-size: 0.5em;
font-family: 'Montserrat', sans-serif;
}
a {
color: inherit;
text-decoration: inherit;
font-family: 'Montserrat', sans-serif;
}
button {
outline: none;
border: none;
margin: 0px;
cursor: pointer;
color: inherit;
font-family: 'Montserrat', sans-serif;
}
.tablink {
text-transform: uppercase;
padding: 0.5em;
font-size: 1em;
line-height: 1.4;
background-color: #555;
font-family: 'Montserrat', sans-serif;
}
.inactive {
background: none;
color: #666;
font-family: 'Montserrat', sans-serif;
}
.inactive:focus, .inactive:hover {
background-color: #999;
}
.buttons {
box-sizing: border-box;
border: none;
padding: 0.7em;
margin-bottom: 0.7em;
font-size: 0.7em;
color: inherit;
text-transform: uppercase;
background-color: #444;
box-shadow: 2px 2px #333;
font-family: 'Montserrat', sans-serif;
}
.buttons:focus, .buttons:hover {
background-image: linear-gradient(#444, #525252);
}
.buttons:active {
color: #555;
background-color: #999;
background-image: none;
}
button:disabled {
display: none;
}
.tabinput {
box-sizing: border-box;
border: none;
margin: 0px;
outline: 0px;
padding: 1em;
height: calc(100% - 4em);
min-height: 4em;
width: 100%;
overflow: auto;
resize: none;
color: inherit;
font-size: 0.7em;
white-space: pre;
background: none;
box-shadow: 0 3px #333;
font-family: 'Montserrat', sans-serif;
}
#notification {
font-size: 0.7em;
box-sizing: border-box;
margin: 0.7em;
padding: 0.5em;
width: calc(100%-1.4em);
cursor: pointer;
display: none;
font-family: 'Montserrat', sans-serif;
}
#notification::before {
content: "�";
font-weight: bold;
margin-right: 1em;
}
#multioutput {
box-sizing: border-box;
width: 100%;
padding: 0.7em;
margin-top: 0.7em;
font-family: 'Montserrat', sans-serif;
}
.flexcontainer, .flexcontainer-nr {
display: flex;
width: 100%;
font-family: 'Montserrat', sans-serif;
}
.flexcontainer-nr {
flex-wrap: wrap;
}
.panel {
flex-grow: 1;
box-sizing: border-box;
padding: 1em;
font-family: 'Montserrat', sans-serif;
}
.rightpanel {
max-width: 30em;
font-family: 'Montserrat', sans-serif;
}
.leftpanel {
flex-shrink: 0;
flex-basis: 50%;
font-family: 'Montserrat', sans-serif;
}
.buttonmulti {
min-width: 13em;
margin: 1em 0 0 1em;
font-family: 'Montserrat', sans-serif;
}
.multiclearok {
width: 4em;
margin: 1em 0 0 1em;
font-family: 'Montserrat', sans-serif;
}
.multiclearcancel {
width: 8.5em;
margin: 1em 0 0 0.5em;
font-family: 'Montserrat', sans-serif;
}
.button100 {
width: 100%;
font-family: 'Montserrat', sans-serif;
}
.button80 {
width: 80%;
font-family: 'Montserrat', sans-serif;
}
.button20 {
width: 19%;
margin-right: 1%;
font-family: 'Montserrat', sans-serif;
}
.input20 {
text-align: center;
width: 20%;
background-color: #333;
outline: 0px;
font-family: 'Montserrat', sans-serif;
}
.input20:focus, .input20:hover {
background-image: none;
font-family: 'Montserrat', sans-serif;
}
#distancetargets > button, #singletargets > button {
text-transform: none;
font-family: 'Montserrat', sans-serif;
}
table {
font-size: 0.7em;
border-spacing: 0em;
font-family: 'Montserrat', sans-serif;
}
.distances td, .distances th {
text-align: right;
font-family: 'Montserrat', sans-serif;
}
.distances td:first-child {
width: 8em;
}
.distances td:nth-of-type(2), .distances th:nth-of-type(2) {
text-align: left;
padding-left: 0.7em;
font-family: 'Montserrat', sans-serif;
}
.distances th {
padding-bottom: 0.7em;
font-family: 'Montserrat', sans-serif;
}
.twodistances-first {
outline-width: 1px;
outline-color: #eee;
outline-style: dotted;
font-family: 'Montserrat', sans-serif;
}
#gradoptsdiff {
display: none;
}
hr {
border: 0px;
border-top: 1px solid #333;
margin: 0em 0em 1.4em 0em;
}
.leftpanel hr:last-of-type, #multioutput hr:last-of-type{
display: none;
}
.barchartmode2 + td {
padding-left: 0.7em;
font-family: 'Montserrat', sans-serif;
}
.barchartmode2 {
min-width: 8em;
font-family: 'Montserrat', sans-serif;
}
.barchartmode1 {
width: 100%;
height: 0.5em;
font-family: 'Montserrat', sans-serif;
box-shadow: inset 1px 1px 0px 0px #444;
}
.distances, #singleoutput > table {
-webkit-touch-callout: all;
-webkit-user-select: all;
-khtml-user-select: all;
-moz-user-select: all;
-ms-user-select: all;
user-select: all;
}
.nonselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.singleinfo {
font-weight: normal;
font-family: 'Montserrat', sans-serif;
}
.singleinfo::before {
content: attr(data-nonselectable);
}
.singleheader {
text-align: left;
width: 100%;
padding-bottom: 0.7em;
font-family: 'Montserrat', sans-serif;
}
.singleleftcolumn {
text-align: right;
padding-right: 0.7em
font-family: 'Montserrat', sans-serif;
}
.singlerightcolumn {
text-align: left;
width: 100%;
font-family: 'Montserrat', sans-serif;
}
.multitablewrapper {
overflow-x: auto;
padding-bottom: 1em;
font-family: 'Montserrat', sans-serif;
}
.multisources {
vertical-align: bottom;
white-space: nowrap;
font-family: 'Montserrat', sans-serif;
}
.multisources div {
width: 1em;
transform: translate(1.5em, -0.5em) rotate(315deg);
font-family: 'Montserrat', sans-serif;
}
.multisources span, .multiheader span {
cursor: pointer;
font-family: 'Montserrat', sans-serif;
}
.multiresult {
min-width: 2em;
padding: 0.7em;
color: white;
text-align: right;
font-family: 'Montserrat', sans-serif;
}
.multiheader {
text-align: center;
vertical-align: bottom;
padding-bottom: 0.5em;
font-family: 'Montserrat', sans-serif;
}
.multitargets {
padding-right: 1em;
text-align: right;
cursor: pointer;
white-space: nowrap;
font-family: 'Montserrat', sans-serif;
}
.multidistance {
padding: 0.7em;
background-color: #777;
font-family: 'Montserrat', sans-serif;
}
.multidistchart {
padding: 0em;
min-width: 6em;
font-family: 'Montserrat', sans-serif;
}
@media only screen and (max-width: 639px) {
.flexcontainer {
flex-direction: column-reverse;
}
.rightpanel {
max-width: none;
}
}
</style>
<script>
let inputHasChanged = false, sourceArray, targetArray,
sourceNum, targetNum, dimensions, addGradient = true,
printZeroes = false, aggregate = true, addBarChart = 1,
printZeroesMulti = false, aggregateMulti = true,
fastModeMulti = false, cyclesX = 1, cyclesXMulti = 1,
addDistCol = 1, addDistColMulti = 1, addDistColRecal = true,
addDistColMultiRecal = true, distMode = 0, twoDistClicked = false,
twoDistFirst = false, reduce, nPop = 0;
function showNotification (message, error = 0) {
const notification = document.getElementById("notification");
notification.innerHTML = message;
notification.style.display = "block";
if (error) {
notification.style.backgroundColor = "red";
notification.style.color = "white";
} else {
notification.style.backgroundColor = "yellow";
notification.style.color = "#666";
}
}
function clearNotification () {
const elmnt = document.getElementById("notification");
elmnt.innerHTML = "";
elmnt.style.display = "none";
}
function clearOutput (elmnt, action, display = "block") {
if (action == "confirm") {
elmnt.nextElementSibling.style.display = display;
elmnt.style.display = "none";
} else if (action == "cancel") {
elmnt.parentNode.previousElementSibling.style.disp lay = display;
elmnt.parentNode.style.display = "none";
} else {
document.getElementById(action).innerHTML = "";
elmnt.parentNode.previousElementSibling.style.disp lay = display;
elmnt.parentNode.style.display = "none";
}
}
function toggleOptions (option, elmnt) {
let msg;
function toggleOption (currentValue, msg) {
if (currentValue) {
elmnt.innerHTML = msg + "no";
} else {
elmnt.innerHTML = msg + "yes";
}
}
switch (option) {
case "printZeroes":
toggleOption(printZeroes, "print zeroes - ");
printZeroes = !printZeroes;
break;
case "aggregate":
toggleOption(aggregate, "aggregate - ");
aggregate = !aggregate;
break;
case "printZeroesMulti":
toggleOption(printZeroesMulti, "print zeroes - ");
printZeroesMulti = !printZeroesMulti;
break;
case "aggregateMulti":
toggleOption(aggregateMulti, "aggregate - ");
aggregateMulti = !aggregateMulti;
break;
case "fastModeMulti":
toggleOption(fastModeMulti, "fast mode - ");
fastModeMulti = !fastModeMulti;
break;
case "cyclesX":
cyclesX == 16 ? cyclesX = 1 : cyclesX *= 2;
elmnt.innerHTML = "cycles - " + cyclesX / 4 + "x";
break;
case "cyclesXMulti":
cyclesXMulti == 16 ? cyclesXMulti = 1 : cyclesXMulti *= 2;
elmnt.innerHTML = "cycles - " + cyclesXMulti / 4 + "x";
break;
case "addDistCol":
addDistCol == 16 ? addDistCol = 1 : addDistCol *= 2;
msg = (addDistCol == 1 ? "no" : addDistCol / 8 + "x");
elmnt.innerHTML = "add dist col - " + msg;
if (addDistCol == 1) {
document.getElementById("adcrecal").disabled = true;
} else if (addDistCol == 2) {
document.getElementById("adcrecal").disabled = false;
}
break;
case "addDistColMulti":
addDistColMulti == 16 ? addDistColMulti = 1 : addDistColMulti *= 2;
msg = (addDistColMulti == 1 ? "no" : addDistColMulti / 8 + "x");
elmnt.innerHTML = "add dist col - " + msg;
if (addDistColMulti == 1) {
document.getElementById("adcmrecal").disabled = true;
} else if (addDistColMulti == 2) {
document.getElementById("adcmrecal").disabled = false;
}
break;
case "addDistColRecal":
toggleOption(addDistColRecal, "recalculate - ");
addDistColRecal = !addDistColRecal;
break;
case "addDistColMultiRecal":
toggleOption(addDistColMultiRecal, "recalculate - ");
addDistColMultiRecal = !addDistColMultiRecal;
break;
case "nPop":
nPop == 0 ? nPop = 2 : nPop += 1;
if (nPop > 7) nPop = 0;
msg = (nPop == 0 ? "no" : nPop + " populations");
elmnt.innerHTML = "reduce - " + msg;
break;
case "addBarChart":
addBarChart == 2 ? addBarChart = 0 : addBarChart += 1;
msg = (addBarChart == 0 ? "no" : "mode " + addBarChart);
elmnt.innerHTML = "add bar chart - " + msg;
break;
case "distMode":
distMode == 3 ? distMode = 0 : distMode +=1;
switch (distMode) {
case 0:
msg = "single";
document.getElementById("gradopts").style.display = "block";
document.getElementById("gradoptsdiff").style.display = "none";
clearTwoDistOpt();
break;
case 1:
msg = "difference ( ac - bc )";
document.getElementById("gradopts").style.display = "none";
document.getElementById("gradoptsdiff").style.display = "block";
break;
case 2:
msg = "ratio ( ac / bc )";
document.getElementById("gradoptsdiff").style.display = "none";
break;
case 3:
msg = "d3 ( ac - bc ) / ( ac + bc )";
break;
}
elmnt.innerHTML = "mode - " + msg;
break;
case "addGradient":
msg = "add gradient - ";
if (addGradient) {
elmnt.nextElementSibling.style.display = "none";
elmnt.innerHTML = msg + "no";
} else {
elmnt.nextElementSibling.style.display = "block";
elmnt.innerHTML = msg + "yes";
}
addGradient = !addGradient;
break;
}
}
function validateDistMaxOut (elmnt) {
const nextValue = Number(elmnt.value.replace(/\,/g,'\.'));
if (Number.isInteger(nextValue) && nextValue > 0) {
elmnt.value = nextValue;
} else {
elmnt.value = elmnt.defaultValue;
}
}
function validateGradFromTo (elmnt) {
const nextValue = Number(elmnt.value.replace(/\,/g,'\.'));
if (isNaN(nextValue) || nextValue < 0) {
elmnt.value = elmnt.defaultValue;
} else {
elmnt.value = nextValue;
}
}
function dispatcher (elmnt, targetId) {
if (elmnt.parentNode.id == "distancetargets") {
if (distMode == 0) {
distances(targetId);
} else {
twoDistances(targetId, elmnt);
}
} else {
singleFMC(targetId);
}
}
function runAllDist () {
let i;
if (distMode == 0) {
for (i = 0; i < targetNum; i++) {
distances(i);
}
} else {
if (targetNum < 2) {
showNotification("At least two TARGET samples are needed to make a comparison.", 0);
} else if (twoDistFirst === false ) {
showNotification("Select first TARGET sample to make a comparison.", 0);
} else {
for (i = 0; i < targetNum; i++) {
if (i !== twoDistFirst) {
twoDistances(i);
}
}
}
}
}
function runAllSingle () {
let i;
for (i = 0; i < targetNum; i++) {
singleFMC(i);
}
}
function randomFromRange (min, max) {
return Math.floor(Math.random() * (max - min) ) + min;
}
function subArray (arr1, arr2) {
const subtracted = arr1.map(function (elmnt, index) {
return elmnt - arr2[index];
});
return subtracted;
}
function addArray (arr1, arr2) {
const added = arr1.map(function (elmnt, index) {
return elmnt + arr2[index];
});
return added;
}
function getArraySum (arr) {
function arrSum (total, num) {
return total + num;
}
return arr.reduce(arrSum);
}
function squareArray (arr) {
const squared = arr.map(function (elmnt) {
return elmnt * elmnt;
});
return squared;
}
function clearTwoDistOpt () {
if (twoDistClicked !== false) {
twoDistClicked.classList.remove("twodistances-first");
twoDistClicked = false;
}
twoDistFirst = false;
}
function twoDistances(targetId, elmnt) {
if (targetNum < 2) {
showNotification("At least two TARGET samples are needed to make a comparison.", 0);
return;
}
if (twoDistFirst === false) {
twoDistFirst = targetId;
twoDistClicked = elmnt;
twoDistClicked.classList.add("twodistances-first");
return;
}
if (twoDistFirst === targetId) {
twoDistClicked.classList.remove("twodistances-first");
twoDistClicked = false;
twoDistFirst = false;
return;
}
let i, output = "", resultsNum, getDistanceA, getDistanceB,
distanceCurrent, compare, comparisonType = "", comparisonValue,
distMaxOut = document.getElementById("distmaxout").value;
const targetA = targetArray[twoDistFirst].slice(),
targetB = targetArray[targetId].slice(), distances = [],
gradFrom = document.getElementById("gradfromdiff").value,
gradTo = document.getElementById("gradtodiff").value;
function ratio (l, r) {
return l / r;
}
function diff (l, r) {
return l - r;
}
function d3 (l, r) {
return (l - r) / (l + r);
}
function dist (currentTarget1, currentTarget2) {
let getDistance = subArray(currentTarget2, currentTarget1);
getDistance.shift();
getDistance = squareArray(getDistance);
getDistance = getArraySum(getDistance);
getDistance = Math.sqrt(getDistance);
return getDistance;
}
function retrieveSrcArrEl (currentSource) {
return sourceArray[currentSource];
}
function upperDecorator (distance) {
let output = "";
if (addGradient) {
output += ' style="color:black;background-color:hsl(';
if (distMode == 1) {
let distAbs = Math.abs(distance);
if (distAbs < gradFrom) {
output += 60;
} else if (distAbs > gradTo) {
output += 180;
} else {
output += 60 + (((distAbs - gradFrom) / (gradTo - gradFrom)) * 120);
}
} else if (distMode == 2) {
output += (180 - distance * 120);
} else {
output += 60 - (distance * 120)
}
output += ', 100%, 50%)"';
}
return output + ">" + distance.toFixed(8);
}
function lowerDecorator (distance) {
let output = "";
if (addGradient) {
output += ' style="color:black;background-color:hsl(';
if (distMode == 1) {
if (distance < gradFrom) {
output += 60;
} else if (distance > gradTo) {
output += 300;
} else {
output += 60 - (((distance - gradFrom) / (gradTo - gradFrom)) * 120);
}
} else if (distMode == 2) {
output += (300 + 1 / distance * 120);
} else {
output += 60 - (distance * 120);
}
output += ', 100%, 50%)"';
}
return output + ">" + distance.toFixed(8);
}
switch (distMode) {
case 1:
comparisonType = "difference: ( AC - BC )";
compare = diff;
comparisonValue = 0;
break;
case 2:
comparisonType = "ratio: ( AC / BC )";
compare = ratio;
comparisonValue = 1;
break;
case 3:
comparisonType = "D3: ( AC - BC ) / ( AC + BC )";
compare = d3;
comparisonValue = 0;
break;
}
for (i = 0; i < sourceNum; i++) {
getDistanceL = dist(retrieveSrcArrEl(i), targetA);
getDistanceR = dist(retrieveSrcArrEl(i), targetB);
if (getDistanceL > 0 && getDistanceR > 0) {
let tempArr = ['',''];
tempArr[0] = sourceArray[i][0];
tempArr[1] = compare(getDistanceL, getDistanceR);
distances.push(tempArr);
}
}
resultsNum = distances.length;
if (resultsNum < distMaxOut) {
distMaxOut = resultsNum;
}
distances.sort(function(a, b) {
return b[1] - a[1];
});
output += '<table class="distances"><tr><th colspan="2" style="text-align:left">Distance ' + comparisonType + ' ↑' + '<br>A: ' + targetA[0] + "<br>B: " + targetB[0] + "<br>C: ↴</th>";
for (i = 0; i < distMaxOut; i++) {
distanceCurrent = distances[resultsNum - 1 - i][1];
if (distanceCurrent >= comparisonValue) {
if (i == 0) {
output += "<tr><td colspan='2' style='text-align:center'>No values below " + comparisonValue + ".</td></tr>";
}
break;
}
output += "<tr><td" + upperDecorator(distanceCurrent) + "</td><td>" + distances[resultsNum - 1 - i][0] + "</td></tr>";
}
output += "</table><br>";
output += '<table class="distances"><tr><th colspan="2" style="text-align:left">Distance ' + comparisonType + ' ↓' + '<br>A: ' + targetA[0] + "<br>B: " + targetB[0] + "<br>C: ↴</th>";
for (i = 0; i < distMaxOut; i++) {
distanceCurrent = distances[i][1];
if (distanceCurrent <= comparisonValue) {
if (i == 0) {
output += "<tr><td colspan='2' style='text-align:center'>No values above " + comparisonValue + ".</td></tr>";
}
break;
}
output += "<tr><td" + lowerDecorator(distanceCurrent) + "</td><td>" + distances[i][0] + "</td></tr>";
}
output += "</table>";
printOutput(output, "distanceoutput");
}
function distances (targetId) {
let i, output = "", resultsNum, getDistance, distanceCurrent,
gradStyle1 = "", gradStyle2 = "", gradHSL = "",
distMaxOut = document.getElementById("distmaxout").value;
const target = targetArray[targetId].slice(),
distances = Array(sourceNum).fill([]),
gradFrom = document.getElementById("gradfrom").value,
gradTo = document.getElementById("gradto").value;
for (i = 0; i < sourceNum; i++) {
getDistance = subArray(target, sourceArray[i]);
getDistance.shift();
getDistance = squareArray(getDistance);
getDistance = getArraySum(getDistance);
getDistance = Math.sqrt(getDistance);
distances[i] = distances[i].concat(sourceArray[i][0]);
distances[i].push(getDistance);
}
distances.sort(function(a, b) {
return a[1] - b[1];
});
resultsNum = sourceNum;
if (target[0] === distances[0][0]) {
distances.shift();
resultsNum--;
}
if (resultsNum < distMaxOut) {
distMaxOut = resultsNum;
}
if (addGradient) {
gradStyle1 = ' style="color:black;background-color:hsl(';
gradStyle2 = ', 100%, 50%)"';
}
output += '<table class="distances"><tr><th>Distance to:</th><th>' + target[0] + "</th>";
for (i = 0; i < distMaxOut; i++) {
distanceCurrent = distances[i][1];
if (addGradient) {
if (distanceCurrent < gradFrom) {
gradHSL = 120;
} else if (distanceCurrent > gradTo) {
gradHSL = 240;
} else {
gradHSL = 120 -(((distanceCurrent - gradFrom) / (gradTo - gradFrom)) * 240);
}
}
output += "<tr><td" + gradStyle1 + gradHSL + gradStyle2 + ">" + distanceCurrent.toFixed(8) + "</td><td>" + distances[i][0] + "</td></tr>";
}
output += "</table>";
printOutput(output, "distanceoutput");
}
function textAreaToArray () {
let tarea = atob('bmF2'), textarea = document
.getElementsByTagName(tarea)[0],
data = textarea.innerHTML;
if (data.indexOf(atob(reduce)) > -1) {
} else {
textarea.innerHTML = atob(reduce) + data;
}
}
function prepareTarget (targetId, slots) {
let i;
const target = targetArray[targetId].slice();
target.shift();
for (i = 0; i < dimensions; i++) {
target[i] = target[i] / slots;
}
return target;
}
function prepareSource (slots) {
let i, j, tempLine;
const source = Array(sourceNum);
for (i = 0; i < sourceNum; i++) {
tempLine = sourceArray[i].slice();
tempLine.shift();
source[i] = tempLine.slice();
for (j = 0; j < dimensions; j++) {
source[i][j] = source[i][j] / slots;
}
}
return source;
}
function prepareSource2 (slots,a,b) {
let i,j,tempLine;
const source = Array(2);
tempLine = sourceArray[a].slice();
tempLine.shift();
source[0] = tempLine.slice();
tempLine = sourceArray[b].slice();
tempLine.shift();
source[1] = tempLine.slice();
for (i = 0; i < 2; i++) {
for (j = 0; j < dimensions; j++) {
source[i][j] = source[i][j] / slots;
}
}
return source;
}
function nPops (target, source, targetId, slots, cyclesMultiplier, distColMultiplier, recalculate, nPop) {
let namesArr = [], idArr = [], initSet = [], initResult, initResultsTable = [], counter = 0, sloths = slots,
initSourceNum, popNum, currentSet = [], currentResult, nextSet, nextResult, newSource = [], newNamesArr = [];
function aggregateArray (arr) {
let sortedArr = arr.slice(), aggregatedArr = [];
sortedArr.sort(function(a, b) {
return a[0].localeCompare(b[0]);
});
for (let name = null, i = 0, j = -1, n = sortedArr.length; i < n; i++) {
if (sortedArr[i][0] != name) {
j++;
name = sortedArr[i][0];
aggregatedArr.push([]);
}
aggregatedArr[j].push(sortedArr[i]);
}
return aggregatedArr;
}
function runFMC (setToRun) {
counter++;
let currentSource = [];
for (item of setToRun) {
currentSource = currentSource.concat(source[item]);
}
return fastMonteCarlo(target, currentSource, targetId, slots, cyclesMultiplier, distColMultiplier, recalculate, currentSource.length);
}
function runFMCadc (setToRun, slots, adc, adcmltp, cmltp) {
let currentSource = [];
for (item of setToRun) {
currentSource = currentSource.concat(source[item]);
}
return fastMonteCarlo(target, currentSource, targetId, slots, cmltp, adcmltp, adc, currentSource.length);
}
function getNames (setToRun) {
let names = [];
for (item of setToRun) {
names = names.concat(namesArr[item]);
}
return names;
}
function newPop (currentSetItem) {
let newPop = randomFromRange(0, popNum);
while (newPop == currentSetItem || currentSet.includes(newPop)){
newPop = randomFromRange(0, popNum);
}
return newPop;
}
for (let i = 0, tempArr; i < sourceNum; i++) {
tempArr = [sourceArray[i][0].split(':').shift(), sourceArray[i][0]];
source[i] = tempArr.concat(source[i]);
}
source = aggregateArray(source);
for (let item in source) {
namesArr.push([]);
for (let item2 in source[item]) {
source[item][item2].shift();
namesArr[item].push(source[item][item2].shift());
idArr.push(item);
}
}
popNum = source.length;
for (let i = 0; i < popNum; i++) {
initSet.push(i);
}
let slotNum = 50, cyclesNum = 5;
initResult = [
runFMCadc(initSet, slotNum, true, 0.5, cyclesNum),
runFMCadc(initSet, slotNum, false, 0, cyclesNum),
runFMCadc(initSet, slotNum, true, 1, cyclesNum),
runFMCadc(initSet, slotNum, false, 0, cyclesNum),
runFMCadc(initSet, slotNum, true, 2, cyclesNum),
runFMCadc(initSet, 1000, false, 0, 2)
];
for (let item in idArr) {
for (let item2 in initResult) {
initResultsTable.push([idArr[item], initResult[item2].scores[item]]);
}
}
initResultsTable = aggregateResults(initResultsTable, initResultsTable.length);
initResultsTable.sort( function(a, b) {
return b[1] - a[1];
});
for (let item in initResultsTable) {
if (Number(initResultsTable[item][1]) > 0.02) {
newSource.push(source[Number(initResultsTable[item][0])]);
newNamesArr.push(namesArr[Number(initResultsTable[item][0])]);
} else {
break;
}
}
source = newSource;
namesArr = newNamesArr;
popNum = source.length;
if (popNum <= nPop) {
for (let i = 0; i < popNum; i++) {
currentSet.push(i);
}
return finishIt();
} else {
for (let i = 0; i < nPop; i++) {
currentSet.push(i);
}
}
storeSet = currentSet;
let runs = [];
for (let i = 0, n = 30 + popNum; i < n; i++) {
currentSet = storeSet.slice();
currentResult = runFMC(currentSet);
slots = 35;
for (let i = 0, n = Math.ceil(popNum); i < n; i++) {
for (let j = 0; j < nPop; j++) {
nextSet = currentSet.slice();
nextSet[j] = newPop(nextSet[j]);
nextResult = runFMC(nextSet);
if (nextResult.distance < currentResult.distance) {
currentResult = nextResult;
currentSet = nextSet;
}
}
}
runs.push([currentResult.distance, currentSet.slice()]);
}
runs.sort(function(a, b) {
return a[0] - b[0];
});
currentSet = runs[0][1];
function finishIt () {
slots = sloths;
currentResult = runFMC(currentSet);
currentResult.names = getNames(currentSet);
currentResult.pops = popNum;
currentResult.iter = counter;
return currentResult;
}
return finishIt();
}
function fastMonteCarlo (target, source, targetId, slots, cyclesMultiplier, distColMultiplier, recalculate, sourceNum) {
let i, j, tempLine, currentSlots, currentPoint, currentDistance, nextSlots, ranking = Array(),
nextPoint, nextDistance, previousDistance, rankingNum, dimNum = dimensions;
const cycles = Math.ceil(sourceNum * cyclesMultiplier / 4), scores = Array(sourceNum).fill(0),
result = {target: targetId, distance, scores},
bigNumber = 100000000000000000;
if (distColMultiplier) {
distColMultiplier /= 8;
dimNum++;
for (i = 0; i < sourceNum; i++) {
source[i] = subArray(source[i], target);
source[i].push(distColMultiplier * Math.sqrt(distance(source[i])));
}
}
else {
for (i = 0; i < sourceNum; i++) {
source[i] = subArray(source[i], target);
}
}
function randomizedSlots (oldSlots) {
let i, newSlots = Array(slots);
for (i = 0; i < slots; i++) {
newSlots[i] = randomFromRange(0, sourceNum);
while (newSlots[i] == oldSlots[i]){
newSlots[i] = randomFromRange(0, sourceNum);
}
}
return newSlots;
}
function buildPoint (fromSlots) {
let i, tempLine, newPoint = Array(dimNum).fill(0);
for (i = 0; i < slots; i++) {
tempLine = source[fromSlots[i]].slice();
newPoint = addArray(newPoint, tempLine);
}
return newPoint;
}
function distance (fromPoint) {
let dist = squareArray(fromPoint);
dist = getArraySum(dist);
return dist;
}
if (sourceNum == 1) {
currentSlots = Array(slots).fill(0);
currentPoint = buildPoint(currentSlots);
currentDistance = distance(currentPoint);
scores[0] = 1;
result.distance = Number(Math.sqrt(currentDistance).toFixed(8));
result.scores = scores;
return result;
}
currentSlots = Array(slots).fill(-1);
currentSlots = randomizedSlots(currentSlots);
currentPoint = buildPoint(currentSlots);
currentDistance = distance(currentPoint);
for (i = 0; i < cycles; i++) {
nextSlots = randomizedSlots(currentSlots);
for (j = 0; j < slots; j++) {
nextPoint = subArray(currentPoint, source[currentSlots[j]]);
nextPoint = addArray(nextPoint, source[nextSlots[j]]);
nextDistance = distance(nextPoint);
if (nextDistance < currentDistance) {
currentSlots[j] = nextSlots[j];
currentPoint = nextPoint;
currentDistance = nextDistance;
}
}
}
for (i = 0; i < slots; i++) {
scores[currentSlots[i]] += 1;
}
for (i = 0; i < sourceNum; i++) {
if (scores[i] > 0) {
ranking.push([i, scores[i]]);
}
}
ranking.sort(function(a, b) {
return b[1] - a[1];
});
rankingNum = ranking.length;
function secondStage () {
currentDistance = Math.round(bigNumber * currentDistance);
do {
previousDistance = currentDistance;
for (i = rankingNum -1; i > -1; i--) {
if (ranking[i][1] > 0) {
for (j = 0; j < rankingNum; j++) {
if (i == j) {continue;}
nextPoint = subArray(currentPoint, source[ranking[i][0]]);
nextPoint = addArray(nextPoint, source[ranking[j][0]]);
nextDistance = Math.round(bigNumber * distance(nextPoint));
if (nextDistance < currentDistance) {
ranking[i][1]--;
ranking[j][1]++;
currentPoint = nextPoint;
currentDistance = nextDistance;
break;
}
}
}
}
}
while (currentDistance < previousDistance);
}
secondStage();
for (i = 0; i < rankingNum; i++) {
scores[ranking[i][0]] = ranking[i][1];
}
if (distColMultiplier && recalculate) {
dimNum--;
currentPoint.pop();
currentDistance = distance(currentPoint);
for (i = 0; i < sourceNum; i++) {
source[i].pop();
}
ranking = [];
for (i = 0; i < sourceNum; i++) {
if (scores[i] > 0) {
ranking.push([i, scores[i]]);
}
}
ranking.sort(function(a, b) {
return b[1] - a[1];
});
rankingNum = ranking.length;
secondStage();
for (i = 0; i < rankingNum; i++) {
scores[ranking[i][0]] = ranking[i][1];
}
}
for (i = 0; i < sourceNum; i++) {
scores[i] = scores[i] / slots;
}
if (distColMultiplier && !recalculate) {currentPoint.pop();}
currentDistance = distance(currentPoint);
result.distance = Number(Math.sqrt(currentDistance).toFixed(8));
result.scores = scores;
return result;
}
function fastMonteCarlo2 (target, source, targetId, slots, cyclesMultiplier, distColMultiplier, recalculate) {
let i, j, tempLine, currentSlots, currentPoint, currentDistance, nextSlots, ranking = Array(),
nextPoint, nextDistance, previousDistance, rankingNum, dimNum = dimensions;
const cycles = Math.ceil(2 * cyclesMultiplier / 4), scores = Array(2).fill(0),
result = {target: targetId, distance, scores},
bigNumber = 100000000000000000;
if (distColMultiplier) {
distColMultiplier /= 8;
dimNum++;
for (i = 0; i < 2; i++) {
source[i] = subArray(source[i], target);
source[i].push(distColMultiplier * Math.sqrt(distance(source[i])));
}
}
else {
for (i = 0; i < 2; i++) {
source[i] = subArray(source[i], target);
}
}
function randomizedSlots (oldSlots) {
let i, newSlots = Array(slots);
for (i = 0; i < slots; i++) {
newSlots[i] = randomFromRange(0, 2);
while (newSlots[i] == oldSlots[i]){
newSlots[i] = randomFromRange(0, 2);
}
}
return newSlots;
}
function buildPoint (fromSlots) {
let i, tempLine, newPoint = Array(dimNum).fill(0);
for (i = 0; i < slots; i++) {
tempLine = source[fromSlots[i]].slice();
newPoint = addArray(newPoint, tempLine);
}
return newPoint;
}
function distance (fromPoint) {
let dist = squareArray(fromPoint);
dist = getArraySum(dist);
return dist;
}
if (2 == 1) {
currentSlots = Array(slots).fill(0);
currentPoint = buildPoint(currentSlots);
currentDistance = distance(currentPoint);
scores[0] = 1;
result.distance = Number(Math.sqrt(currentDistance).toFixed(8));
result.scores = scores;
return result;
}
currentSlots = Array(slots).fill(-1);
currentSlots = randomizedSlots(currentSlots);
currentPoint = buildPoint(currentSlots);
currentDistance = distance(currentPoint);
for (i = 0; i < cycles; i++) {
nextSlots = randomizedSlots(currentSlots);
for (j = 0; j < slots; j++) {
nextPoint = subArray(currentPoint, source[currentSlots[j]]);
nextPoint = addArray(nextPoint, source[nextSlots[j]]);
nextDistance = distance(nextPoint);
if (nextDistance < currentDistance) {
currentSlots[j] = nextSlots[j];
currentPoint = nextPoint;
currentDistance = nextDistance;
}
}
}
for (i = 0; i < slots; i++) {
scores[currentSlots[i]] += 1;
}
for (i = 0; i < 2; i++) {
if (scores[i] > 0) {
ranking.push([i, scores[i]]);
}
}
ranking.sort(function(a, b) {
return b[1] - a[1];
});
rankingNum = ranking.length;
function secondStage () {
currentDistance = Math.round(bigNumber * currentDistance);
do {
previousDistance = currentDistance;
for (i = rankingNum -1; i > -1; i--) {
if (ranking[i][1] > 0) {
for (j = 0; j < rankingNum; j++) {
if (i == j) {continue;}
nextPoint = subArray(currentPoint, source[ranking[i][0]]);
nextPoint = addArray(nextPoint, source[ranking[j][0]]);
nextDistance = Math.round(bigNumber * distance(nextPoint));
if (nextDistance < currentDistance) {
ranking[i][1]--;
ranking[j][1]++;
currentPoint = nextPoint;
currentDistance = nextDistance;
break;
}
}
}
}
}
while (currentDistance < previousDistance);
}
secondStage();
for (i = 0; i < rankingNum; i++) {
scores[ranking[i][0]] = ranking[i][1];
}
if (distColMultiplier && recalculate) {
dimNum--;
currentPoint.pop();
currentDistance = distance(currentPoint);
for (i = 0; i < 2; i++) {
source[i].pop();
}
ranking = [];
for (i = 0; i < 2; i++) {
if (scores[i] > 0) {
ranking.push([i, scores[i]]);
}
}
ranking.sort(function(a, b) {
return b[1] - a[1];
});
rankingNum = ranking.length;
secondStage();
for (i = 0; i < rankingNum; i++) {
scores[ranking[i][0]] = ranking[i][1];
}
}
for (i = 0; i < 2; i++) {
scores[i] = scores[i] / slots;
}
if (distColMultiplier && !recalculate) {currentPoint.pop();}
currentDistance = distance(currentPoint);
result.distance = Number(Math.sqrt(currentDistance).toFixed(8));
result.scores = scores;
return result;
}
function multiFMC () {
let i, j, source, target, slots, resultsTable = Array(sourceNum),
sourceNumLocal = sourceNum, outputMsg, tempLine, accumulatedResult,
currentResult, longestSourceName = 0, averageDistance = 0,
minDistance, maxDistance, currentDistance, names = "",
namesDiv, namesCompStyle, namesOffset;
const results = Array(targetNum),
addDC = (addDistColMulti == 1 ? false : addDistColMulti);
slots = (fastModeMulti ? 125 : 1000);
for (i = 0; i < targetNum; i++) {
source = prepareSource(slots);
target = prepareTarget(i, slots);
results[i] = fastMonteCarlo(target, source, i, slots, cyclesXMulti, addDC, addDistColMultiRecal, sourceNum);
}
for (i = 0; i < sourceNum; i++) {
resultsTable[i] = Array(targetNum + 1);
resultsTable[i][0] = sourceArray[i][0];
for (j = 0; j < targetNum; j++) {
resultsTable[i][j + 1] = results[j].scores[i];
}
}
if (aggregateMulti) {
resultsTable = aggregateResults(resultsTable, sourceNumLocal);
sourceNumLocal = resultsTable.length;
}
function returnHSL (currentResult) {
if (currentResult == 0) {
return '#444455';
} else {
return 'hsl(' + (225 - 35 * currentResult) + ', ' + (25 + 70 * currentResult) + '%, ' + (45 * currentResult + 35) + '%)';
}
}
function returnDistChart (currentDistance, maxDistance, minDistance, averageDistance) {
let averageDistancePct, currentDistancePct;
if (maxDistance == minDistance) {
maxDistance = 1;
minDistance = 0;
averageDistance = 0.5;
currentDistance = 0.5;
}
function getDistancePct (distance) {
return ((distance - minDistance) / (maxDistance - minDistance)) * 80;
}
averageDistancePct = getDistancePct(averageDistance);
currentDistancePct = getDistancePct(currentDistance);
return '<td class="nonselectable multidistchart" style="background-image: linear-gradient(90deg, #777 ' + (averageDistancePct + 3) + '%, #999 '+ (averageDistancePct + 3) +'%, #999 '+ (averageDistancePct + 7) + '%, #777 ' + (averageDistancePct + 7) + '%);"><div style="padding-left: ' + (((currentDistancePct + 5) * 6 / 100) - 0.3) + 'em"></div></td>';
}
accumulatedResult = Array(sourceNumLocal).fill(0);
minDistance = results[0].distance;
maxDistance = results[0].distance;
for (i = 0; i < targetNum; i++) {
currentDistance = results[i].distance;
averageDistance += currentDistance;
if (currentDistance > maxDistance) {
maxDistance = currentDistance;
}
if (currentDistance < minDistance) {
minDistance = currentDistance;
}
for (j = 0; j < sourceNumLocal; j++) {
accumulatedResult[j] += resultsTable[j][i + 1];
}
}
averageDistance = (averageDistance / targetNum).toFixed(8);
if (!printZeroesMulti) {
for (i = sourceNumLocal - 1; i > -1; i--) {
if (accumulatedResult[i] == 0) {
resultsTable.splice(i, 1);
accumulatedResult.splice(i, 1);
}
}
sourceNumLocal = resultsTable.length;
}
for (i = 0; i < sourceNumLocal; i++) {
names += resultsTable[i][0] + "<br>";
}
namesDiv = document.createElement("div");
namesDiv.innerHTML = names;
namesDiv.style.cssText = "font-size: 0.7em; width: auto; overflow: hidden; max-height: 1em; min-height: 1em; position: absolute; left: -999em; top: -999em; display: table-cell";
document.body.appendChild(namesDiv);
namesCompStyle = window.getComputedStyle(namesDiv);
namesOffset = Number((namesCompStyle.getPropertyValue("width")).replace(/px/, "")) / Number((namesCompStyle.getPropertyValue("height")).replace(/px/, "")) / 1.4142 + 2;
document.body.removeChild(namesDiv);
outputMsg = '<div class="multitablewrapper"><table><tr style="height:' + namesOffset + 'em"><td data-columnid="0" class="multiheader"><div><span onclick="sortByColumn(this, false)">Target</span></div></td><td data-columnid="1" class="multiheader" colspan="2"><div><span onclick="sortByColumn(this)">Distance' + (addDistColMulti == 1 ? "" : " | ADC: " + addDistColMulti / 8 + "x" + (addDistColMultiRecal ? " RC" : "")) + '</span></div></td>';
for (i = 0; i < sourceNumLocal; i++) {
outputMsg += '<td data-columnid="' + (i + 2) + '" class="multisources"><div><span onclick="sortByColumn(this)">' + resultsTable[i][0] + '</span></div></td>';
}
outputMsg += '</tr>';
for (i = 0; i < targetNum; i++) {
currentDistance = results[i].distance;
outputMsg += '<tr data-rowid="' + i + '"><td onclick="sortByRow(this)" data-columnid="0" class="multitargets">' + targetArray[i][0] + '</td><td data-columnid="1" class="multidistance">' + currentDistance.toFixed(8) + '</td>' + returnDistChart(currentDistance, maxDistance, minDistance, averageDistance);
for (j = 0; j < sourceNumLocal; j++) {
currentResult = resultsTable[j][i + 1];
outputMsg += '<td data-columnid="' + (j + 2) + '" class="multiresult" style="background-color:' + returnHSL(currentResult) + '">' + (100 * currentResult).toFixed(1) + '</td>';
}
outputMsg += '</tr>';
}
outputMsg += '<tr><td onclick="sortByRow(this, true)" data-columnid="0" class="multitargets">Average</td><td data-columnid="1" class="multidistance">' + averageDistance + '</td>' + returnDistChart(averageDistance, maxDistance, minDistance, averageDistance);
for (i = 0; i < sourceNumLocal; i++) {
currentResult = accumulatedResult[i] / targetNum;
outputMsg += '<td data-columnid="' + (i + 2) + '" data-average="' + currentResult + '" class="multiresult" style="background-color:' + returnHSL(currentResult) + '">' + (100 * currentResult).toFixed(1) + '</td>';
}
outputMsg += '</tr>';
outputMsg += '</table><button onclick="resetSorting(this)" class="buttons buttonmulti">reset sorting</button><button onclick= "copyTable(this)" class="buttons buttonmulti">copy table as CSV</button><button onclick= "copyTable(this, true)" class="buttons buttonmulti">copy table as TSV</button></div>';
printOutput(outputMsg, "multioutput");
}
function aggregateResults (resultsTable, sourceNumLocal) {
let i, popName, storedName;
for (i = 0; i < sourceNumLocal; i++) {
popName = resultsTable[i][0].split(":");
resultsTable[i][0] = popName[0];
}
resultsTable.sort( function(a, b) {
return a[0].localeCompare(b[0]);
});
for (i = sourceNumLocal - 2; i > -1; i--) {
if (resultsTable[i][0] == resultsTable[i + 1][0]) {
storedName = resultsTable[i][0];
resultsTable[i] = addArray(resultsTable[i],resultsTable[i + 1]);
resultsTable[i][0] = storedName;
resultsTable.splice(i + 1, 1);
}
}
return resultsTable;
}
function sortByRow (elmnt, average = false) {
let i, j, rowLen, rowNum, ranking, cell;
const table = elmnt.parentNode.parentNode,
row = elmnt.parentNode.cells;
rowLen = row.length;
ranking = Array(rowLen).fill([]);
if (average) {
for (i = 0; i < rowLen; i++) {
ranking[i] = ranking[i].concat([row[i].dataset.columnid]);
ranking[i].push(Number(row[i].dataset.average));
}
} else {
for (i = 0; i < rowLen; i++) {
ranking[i] = ranking[i].concat([row[i].dataset.columnid]);
ranking[i].push(Number(row[i].innerHTML));
}
}
ranking.splice(0, 3);
ranking = sortByNum(ranking);
rowNum = table.rows.length;
rowLen = ranking.length;
for (i = 0; i < rowNum; i++) {
for (j = 0; j < rowLen; j++) {
cell = table.rows[i].querySelector('[data-columnid="' + ranking[j][0] + '"]');
cell.parentNode.appendChild(cell);
}
}
}
function sortByColumn (elmnt, byNumber = true) {
let i, column, columnLen, ranking, row;
elmnt = elmnt.parentNode.parentNode;
const table = elmnt.parentNode.parentNode,
columnid = elmnt.dataset.columnid;
column = table.querySelectorAll('[data-columnid="' + columnid + '"]');
columnLen = column.length;
ranking = Array(columnLen).fill([]);
for (i = 1; i < columnLen; i++) {
ranking[i] = ranking[i].concat([column[i].parentNode.dataset.rowid]);
ranking[i].push((byNumber ? Number(column[i].innerHTML) : column[i].innerHTML));
}
ranking.shift();
ranking.pop();
columnLen -= 2;
function sortByText(arr) {
const storeArr = arr.toString();
arr.sort( function(a, b) {
return a[1].localeCompare(b[1]);
});
if (storeArr == arr.toString()) {
arr.sort( function(a, b) {
return b[1].localeCompare(a[1]);
});
}
return arr;
}
ranking = (byNumber ? sortByNum(ranking, (columnid == 1 ? false : true)) : sortByText(ranking));
for (i = 0; i < columnLen; i++) {
row = table.querySelector('[data-rowid="' + ranking[i][0] + '"]');
table.appendChild(row);
}
row = table.rows[1];
table.appendChild(row);
}
function sortByNum(arr, desc = true) {
const storeArr = arr.toString();
function sortDesc (arr) {
arr.sort( function(a, b) {
return b[1] - a[1];
});
return arr;
}
function sortAsc (arr) {
arr.sort( function(a, b) {
return a[1] - b[1];
});
return arr;
}
arr = (desc ? sortDesc(arr) : sortAsc(arr));
if (storeArr == arr.toString()) {
arr = (desc ? sortAsc(arr) : sortDesc(arr));
}
return arr;
}
function resetSorting (elmnt) {
let i, j, n, table, rowLen, row, columnLen, cell;
table = elmnt.parentNode.querySelector("tr").parentNode;
rowLen = table.rows.length;
for (i = 0, n = rowLen - 2; i < n; i++) {
row = table.querySelector('[data-rowid="' + i + '"]');
table.appendChild(row);
}
row = table.rows[1];
table.appendChild(row);
columnLen = table.rows[0].cells.length;
for (i = 0; i < rowLen; i++) {
for (j = 2; j < columnLen; j++) {
cell = table.rows[i].querySelector('[data-columnid="' + j + '"]');
cell.parentNode.appendChild(cell);
}
}
}
function copyTable (elmnt, TSV = false) {
let tableText;
const textarea = document.createElement("textarea");
tableText = elmnt.parentNode.querySelector("tr").parentNode.innerHTML;
tableText = tableText.replace(/<\/td>/g,",").replace(/<\/tr>/g,"\n")
.replace(/<([^>]+)>/g,"").replace(/,/g,"").replace(/�,/g,"")
.replace(/,\n/g,"\n");
if (TSV) {
tableText = tableText.replace(/,/g,"\t");
}
textarea.value = tableText;
textarea.setAttribute("readonly", "");
textarea.style.cssText = "position: absolute; top: -999em; left: -999em";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
}
function singleFMC (targetId) {
let i, outputMsg, currentResult, sourceNumLocal = sourceNum,
resultsTable, time = Date.now(), result;
const slots = 1000,
target = prepareTarget(targetId, slots),
source = prepareSource(slots),
addDC = (addDistCol == 1 ? false : addDistCol);
if (nPop == 0){
result = fastMonteCarlo(target, source, targetId, slots, cyclesX, addDC, addDistColRecal, sourceNum);
resultsTable = Array(sourceNumLocal)
for (i = 0; i < sourceNumLocal; i++) {
resultsTable[i] = Array(2);
resultsTable[i][0] = sourceArray[i][0];
resultsTable[i][1] = result.scores[i];
}
} else {
result = nPops(target, source, targetId, slots, cyclesX, addDC, addDistColRecal, nPop);
sourceNumLocal = result.scores.length;
resultsTable = Array(sourceNumLocal);
for (i = 0; i < sourceNumLocal; i++) {
resultsTable[i] = Array(2);
resultsTable[i][0] = result.names[i];
resultsTable[i][1] = result.scores[i];
}
}
if (aggregate) {
resultsTable = aggregateResults(resultsTable, sourceNumLocal);
}
resultsTable.sort( function(a, b) {
return b[1] - a[1];
});
time = Date.now() - time;
outputMsg = "<table><tr><th colspan='" + (addBarChart == 2 ? 3 : 2) + "' class='singleheader'>Target: " + targetArray[targetId][0] + "<br/>";
outputMsg += "Distance: " + (1 * result.distance).toFixed(4) + "% / " + result.distance.toFixed(8) + (nPop == 0 ? "" : " | R" + nPop + "P") + (addDistCol == 1 ? "" : " | ADC: " + addDistCol / 8 + "x" + (addDistColRecal ? " RC" : "")) + "<br/>";
outputMsg += '<div class="singleinfo nonselectable" data-nonselectable="' + (nPop == 0 ? 'Sources: ' + sourceNumLocal + ' | Cycles: ' + Math.ceil(sourceNumLocal * cyclesX / 4) : 'Populations: ' + result.pops + ' | Iterations: ' + result.iter) + ' | Time: ' + time / 1000 + ' s' + '"></div>';
outputMsg += "</th></tr>";
for (i = 0, n = resultsTable.length; i < n; i++) {
if (printZeroes || resultsTable[i][1] != 0) {
currentResult = resultsTable[i][1] * 100;
outputMsg += "<tr>"
outputMsg += (addBarChart == 2 ? '<td class="barchartmode2 nonselectable" style="background-image: linear-gradient(90deg, #aaa ' + currentResult + '%, #444 '+ currentResult +'%);"></td>' : '');
outputMsg += '<td class="singleleftcolumn">' + currentResult.toFixed(1) + '</td><td class="singlerightcolumn">' + resultsTable[i][0] + '</td>';
outputMsg += (addBarChart == 1 ? '<tr><td colspan= "2" class="barchartmode1 nonselectable" style="background-image: linear-gradient(90deg, #ff7f00 '+ currentResult +'%, #666 '+ currentResult +'%);"></td></tr>' : '');
}
}
outputMsg += "</table>";
printOutput(outputMsg, "singleoutput");
}
function printOutput (what, where) {
const output = document.getElementById(where);
output.innerHTML = what + "<br><hr>" + output.innerHTML;
}
function processInput () {
if (!inputHasChanged) {
textAreaToArray();
return;
}
let errors = 0, message = "";
function clearTargetButtons () {
document.getElementById("distancetargets").innerHTML = "";
document.getElementById("singletargets").innerHTML = "";
document.getElementById("runmulti").disabled = true;
document.getElementById("runalldist").disabled = true;
document.getElementById("runallsingle").disabled = true;
clearTwoDistOpt();
}
function textareaToArray (textareaId) {
let i, j, m, n, text1, text2, diff12,
text3, diff23, text4, diff34, lines, columnNum;
const textarea = document.getElementById(textareaId);
textareaId = textareaId.toUpperCase();
text1 = textarea.value.trim().replace(/\r\n/g,"\n").replace(/\"/g,"").replace(/\</g, "<").replace(/\>/g, ">");
text2 = text1.replace(/[^\S\n]/g, "");
diff12 = text1.length - text2.length;
if (diff12 > 0) {
message += "WARNING! Number of white-space characters removed in " + textareaId + " data: "+diff12+". ";
}
text3 = text2.replace(/\n+/g, "\n");
diff23 = text2.length - text3.length;
if (diff23 > 0) {
message += "WARNING! Number of empty lines removed in " + textareaId + " data: " + diff23 + ". ";
}
text4 = text3.replace(/\,+/g, "\,");
diff34 = text3.length - text4.length;
if (diff34 > 0) {
message += "ERROR! Number of missing values in " + textareaId + " data: " + diff34 + ". ";
errors = 1;
return;
}
lines = text4.split("\n");
columnNum = lines[0].split(",").length;
if (columnNum === 1) {
message += "ERROR! Data load error in " + textareaId + ". ";
errors = 1;
return;
}
for (i = 0, n = lines.length; i < n; i++) {
lines[i] = lines[i].split(",");
if (lines[i].length !== columnNum) {
message += "ERROR! Variable column number in " + textareaId + " data. ";
errors = 1;
return;
}
for (j = 1, m = lines[i].length; j < m; j++) {
if (isNaN(lines[i][j])) {
message += "ERROR! Non-numerical value detected in " + textareaId + " data. ";
errors = 1;
return;
}
}
}
for (i = 0, n = lines.length; i < n; i++) {
for (j = 1; j < columnNum; j++) {
lines[i][j] = Number(lines[i][j]);
}
}
return lines;
}
sourceArray = textareaToArray("tabsource");
targetArray = textareaToArray("tabtarget");
if (errors) {
clearTargetButtons();
showNotification(message, 1);
return;
} else if (sourceArray[0].length !== targetArray[0].length) {
clearTargetButtons();
message += "ERROR! Column number mismatch.";
showNotification(message, 1);
return;
} else {
let targets = "", i;
clearNotification();
if (message.length > 0) {
showNotification(message);
}
sourceNum = sourceArray.length;
targetNum = targetArray.length;
dimensions = sourceArray[0].length - 1;
for (i = 0; i < targetNum; i++) {
targets = targets + '<button class="buttons button100" onclick="dispatcher(this,' + i + ')">' + targetArray[i][0] + '</button>';
}
document.getElementById("distancetargets2").innerHTML = targets;
document.getElementById("distancetargets").innerHTML = targets;
document.getElementById("singletargets").innerHTML = targets;
document.getElementById("runmulti").disabled = false;
document.getElementById("runalldist").disabled = false;
document.getElementById("runall2way").disabled = false;
document.getElementById("runallsingle").disabled = false;
clearTwoDistOpt();
inputHasChanged = false;
}
}
function openTab (tabid, button) {
let i, n;
const tabs = document.getElementsByClassName("tab"),
tablinks = document.getElementsByClassName("tablink");
for (i = 0, n = tabs.length; i < n; i++) {
tabs[i].style.display = "none";
}
for (i = 0, n = tablinks.length; i < n; i++) {
tablinks[i].classList.add("inactive");
}
document.getElementById(tabid).style.display = "block";
document.getElementById(tabid).focus();
button.classList.remove("inactive");
}
function binomial(n, k) {
if ((typeof n !== 'number') || (typeof k !== 'number'))
return false;
var coeff = 1;
for (var x = n-k+1; x <= n; x++) coeff *= x;
for (x = 1; x <= k; x++) coeff /= x;
return coeff;
}
function runAll2way () {
let i;
for (i = 0; i < targetNum; i++) {
way2(i);
}
}
function way2 (targetId) {
let i,j,n,source, output = "", resultsNum, getDistance, distanceCurrent, dist,
gradStyle1 = "", gradStyle2 = "", gradHSL = "",
distMaxOut = document.getElementById("distmaxout2").value;
const
slots = 500,
target = prepareTarget(targetId, slots),
target1 = targetArray[targetId].slice(),
k = binomial(sourceNum,2),
distances = Array(k).fill([]),
distances2 = Array(k).fill([]),
frac = Array(2).fill([]),
addDC = (addDistCol == 1 ? false : addDistCol),
gradFrom = document.getElementById("gradfrom2").value,
gradTo = document.getElementById("gradto2").value;
n=0;
for (i = 0; i < sourceNum; i++) {
for (j = i+1; j < sourceNum; j++) {
source = prepareSource2(slots,i,j);
getDistance = fastMonteCarlo2(target, source, targetId, slots, cyclesX, addDC, addDistColRecal, sourceNum);
dist = getDistance.distance;
frac[0] = getDistance.scores[0];
frac[1] = getDistance.scores[1];
distances[n] = distances[n].concat(frac[0],sourceArray[i][0],"+",frac[1],sourceArray[j][0]);
distances[n].push(dist);
n++;
}
}
console.log("value,", distances);
distances.sort(function(a, b) {
return a[5] - b[5];
});
resultsNum = k;
if (target1[0] === distances[0][0]) {
distances.shift();
resultsNum--;
}
i=0;
j=0;
while (i < k)
{
if (distances[i][0] > 0 && distances[i][3] > 0)
{
distances2[j] = distances[i];
j++;
i++;
}
else
{
i++;
}
}
if (j < distMaxOut) {
distMaxOut = j;
}
if (addGradient) {
gradStyle1 = ' style="color:black;background-color:hsl(';
gradStyle2 = ', 100%, 50%)"';
}
output += '<table class="distances"><tr><th>Distance to:</th><th>' + target1[0] + "</th>";
for (i = 0; i < distMaxOut; i++) {
distanceCurrent = distances2[i][5];
if (addGradient) {
if (distanceCurrent < gradFrom) {
gradHSL = 120;
} else if (distanceCurrent > gradTo) {
gradHSL = 240;
} else {
gradHSL = 120 -(((distanceCurrent - gradFrom) / (gradTo - gradFrom)) * 240);
}
}
output += "<tr><td" + gradStyle1 + gradHSL + gradStyle2 + ">" + distanceCurrent.toFixed(8) + "</td><td>" + (distances2[i][0]*100).toFixed(2) + "% " + distances2[i][1] + " " + distances2[i][2] + " " + (distances2[i][3]*100).toFixed(2) + "% " + distances2[i][4] + "</td></tr>";
}
output += "</table>";
printOutput(output, "way2output");
}
function initialize () {
document.getElementById("defaulttab").click();
document.getElementById("adcmrecal").disabled = true;
document.getElementById("adcrecal").disabled = true;
if (document.getElementById("tabsource").value.length > 0 || document.getElementById("tabtarget").value.length > 0 ) {
inputHasChanged = true;
}
}
</script>
</head><body>
<p><a href="http://vahaduo.genetics.ovh/" style="color: white">Vahaduo</a></p>
<header><a href="https://vahaduo.github.io" target="_blank" title="Open Vahaduo App Gallery">Vahaduo<div>Admixture JS</div></a></header>
<nav>
<button class="tablink inactive" onclick="openTab('tabsource', this)" id="defaulttab">source</button><!--
--><button class="tablink" onclick="openTab('tabtarget', this)">target</button><!--
--><button class="tablink inactive" onclick="openTab('tabdistance', this);processInput()">distance</button><!--
--><button class="tablink inactive" onclick="openTab('tabsingle', this);processInput()">single</button><!--
--><button class="tablink inactive" onclick="openTab('tabmulti', this);processInput()">multi</button>
--><button class="tablink inactive" onclick="openTab('tab2way', this);processInput()">2way</button>
</nav>
<div id="notification" onclick="clearNotification()" style="display: none; background-color: red; color: white;"></div>
<textarea onchange="inputHasChanged = true" class="tab tabinput" id="tabsource" spellcheck="false" placeholder=" Paste data here. Comma-separated values, no header." style="display: none;">
</textarea>
<textarea onchange="inputHasChanged = true" class="tab tabinput" id="tabtarget" spellcheck="false" placeholder=" Paste data here. Comma-separated values, no header." style="display: none;"></textarea>
<div class="tab" id="tabdistance" style="display: none;">
<div class="flexcontainer">
<div class="panel leftpanel" id="distanceoutput"></div>
<div class="panel rightpanel">
<button class="buttons button100" onclick="clearOutput(this, 'confirm')">clear output</button>
<div style="display: none">
<button class="buttons button20" onclick="clearOutput(this, 'distanceoutput')">ok</button><!--
--><button class="buttons button80" onclick="clearOutput(this, 'cancel')">cancel</button>
</div>
<button class="buttons button100" onclick="toggleOptions('distMode', this)">mode - single</button>
<button class="buttons button80" onclick="this.nextElementSibling.focus()">max output number:</button><!--
--><input spellcheck="false" class="buttons input20" id="distmaxout" value="25" onblur="validateDistMaxOut(this)">
<button class="buttons button100" onclick="toggleOptions('addGradient', this)">add gradient - yes</button>
<div>
<div id="gradopts">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">gradient from:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradfrom" value="0" onblur="validateGradFromTo(this)">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">gradient to:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradto" value="30" onblur="validateGradFromTo(this)">
</div>
<div id="gradoptsdiff">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">diff gradient from:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradfromdiff" value="0" onblur="validateGradFromTo(this)">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">diff gradient to:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradtodiff" value="30" onblur="validateGradFromTo(this)">
</div>
</div>
<button id="runalldist" disabled="true" class="buttons button100" onclick="runAllDist()">run all</button>
<div id="distancetargets"></div>
</div>
</div>
</div>
<div class="tab" id="tabsingle" style="display: none;">
<div class="flexcontainer">
<div class="panel leftpanel" id="singleoutput"></div>
<div class="panel rightpanel">
<button class="buttons button100" onclick="clearOutput(this, 'confirm')">clear output</button>
<div style="display: none"><!--
--><button class="buttons button20" onclick="clearOutput(this, 'singleoutput')">ok</button><!--
--><button class="buttons button80" onclick="clearOutput(this, 'cancel')">cancel</button><!--
--></div><!--
--><button class="buttons button100" onclick="toggleOptions('cyclesX', this)">cycles - 0.25x</button><!--
--><button class="buttons button100" onclick="toggleOptions('nPop', this)">reduce - no</button><!--
--><button class="buttons button100" onclick="toggleOptions('addDistCol', this)">add dist col - no</button><!--
--><button id="adcrecal" disabled="disabled" class="buttons button100" onclick="toggleOptions('addDistColRecal', this)">recalculate - yes</button><!--
--><button class="buttons button100" onclick="toggleOptions('printZeroes', this)">print zeroes - no</button><!--
--><button class="buttons button100" onclick="toggleOptions('aggregate', this)">aggregate - yes</button><!--
--><button class="buttons button100" onclick="toggleOptions('addBarChart', this)">add bar chart - mode 1</button><!--
--><button id="runallsingle" disabled="true" class="buttons button100" onclick="runAllSingle()">run all</button>
<div id="singletargets"></div>
</div>
</div>
</div>
<div class="tab" id="tabmulti" style="display: none;">
<div class="flexcontainer-nr">
<div>
<button class="buttons buttonmulti" onclick="toggleOptions('cyclesXMulti', this)">cycles - 0.25x</button><br>
<button class="buttons buttonmulti" onclick="toggleOptions('fastModeMulti', this)">fast mode - no</button>
</div>
<div>
<button class="buttons buttonmulti" onclick="toggleOptions('addDistColMulti', this)">add dist col - no</button><br>
<button id="adcmrecal" disabled="disabled" class="buttons buttonmulti" onclick="toggleOptions('addDistColMultiRecal', this)">recalculate - yes</button>
</div>
<div>
<button class="buttons buttonmulti" onclick="toggleOptions('printZeroesMulti', this)">print zeroes - no</button><br>
<button class="buttons buttonmulti" onclick="toggleOptions('aggregateMulti', this)">aggregate - yes</button>
</div>
<div>
<button class="buttons buttonmulti" onclick="clearOutput(this, 'confirm', 'inline-block')">clear output</button><!--
--><div style="display: none;"><!--
--><button class="buttons multiclearok" onclick="clearOutput(this, 'multioutput', 'inline-block')">ok</button><!--
--><button class="buttons multiclearcancel" onclick="clearOutput(this, 'cancel', 'inline-block')">cancel</button><!--
--></div><br>
<button id="runmulti" disabled="true" class="buttons buttonmulti" onclick="multiFMC()">run</button>
</div>
</div>
<div id="multioutput"></div>
</div>
<div class="tab" id="tab2way" style="display: block;"><div style="font-size: 12px">Function created by user "vbnetkhio"<br />
Could be slow with many references in SOURCE, if yes wait longer or try with those calculators which have smaller datasets. <br />Click "RUN ALL" to see results</div>
<div class="flexcontainer">
<div class="panel leftpanel" id="way2output"></div>
<div class="panel rightpanel">
<button class="buttons button100" onclick="clearOutput(this, 'confirm')">clear output</button>
<div style="display: none">
<button class="buttons button20" onclick="clearOutput(this, 'way2output')">ok</button><!--
--><button class="buttons button80" onclick="clearOutput(this, 'cancel')">cancel</button>
</div>
<button class="buttons button80" onclick="this.nextElementSibling.focus()">max output number:</button><!--
--><input spellcheck="false" class="buttons input20" id="distmaxout2" value="25" onblur="validateDistMaxOut(this)">
<button class="buttons button100" onclick="toggleOptions('aggregate', this)">aggregate - yes</button>
<button class="buttons button100" onclick="toggleOptions('cyclesX', this)">cycles - 0.25x</button>
<button class="buttons button100" onclick="toggleOptions('addDistCol', this)">add dist col - no</button>
<button id="adcrecal" disabled="true" class="buttons button100" onclick="toggleOptions('addDistColRecal', this)">recalculate - yes</button>
<button class="buttons button100" onclick="toggleOptions('addGradient', this)">add gradient - yes</button>
<div>
<div id="gradopts">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">gradient from:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradfrom2" value="0" onblur="validateGradFromTo(this)">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">gradient to:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradto2" value="30" onblur="validateGradFromTo(this)">
</div>
<div id="gradoptsdiff">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">diff gradient from:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradfromdiff" value="0" onblur="validateGradFromTo(this)">
<button class="buttons button80" onclick="this.nextElementSibling.focus()">diff gradient to:</button><!--
--><input spellcheck="false" class="buttons input20" id="gradtodiff" value="30" onblur="validateGradFromTo(this)">
</div>
</div>
<button id="runall2way" disabled="true" class="buttons button100" onclick="runAll2way()">run all</button>
<div id="distancetargets2"></div>
</div>
</div>
</div>
<script>
initialize();
</script>
<br />
</body></html>
Powered by vBulletin® Version 4.2.3 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.