var MIN_IOC = 0;
var MAX_IOC = 0;
var MIN_CHR = 0;
var MAX_CHR = 0;
function byID(x) { return document.getElementById(x); }
function loadFile(url, callback, async=true) {
var xobj = new XMLHttpRequest();
// xobj.overrideMimeType('text/plain');
xobj.open('GET', url, async);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == '200') {
callback(xobj.responseText);
}
};
xobj.send(null);
}
function load_from_file() {
const fname = byID('fileload').value;
loadFile('../pages/' + fname + '.txt', function(res) {
if (!res) { res = 'Error loading file.'; }
byID('input').value = res;
start_analyze();
});
}
function dt_dd(title, content, clss = '', id = '') {
var attr = '';
if (id != '') { attr += ' id="' + id + '"'; }
if (clss != '') { attr += ' class="' + clss + '"'; }
return '
' + title + '\n\n' + content + '\n';
}
function num_stream(stream) {
var txt = '';
for (var i = 0; i < stream.length; i++) {
var val = stream[i];
var title = '';
const typ = typeof stream[i];
if (typ != 'string' && typ != 'number') {
title = ' title="' + val[1] + '"';
val = val[0];
}
txt += '' + val + '
';
}
return txt + '\n';
}
function ngram_table(rows, id, th = null) {
var txt = '\n';
txt += '';
if (th != null) { txt += ' | '; }
for (var i = 0; i < window.txt_abc.length; i++) {
txt += '' + window.txt_abc[i] + ' | ';
}
if (th == null) { th = ['']; }
txt += '
\n';
for (var o = 0; o < th.length; o++) {
txt += '';
if (th[o] != '') { txt += '| ' + th[o] + ' | '; }
for (var i = 0; i < window.txt_abc.length; i++) {
const key = th[o] + window.txt_abc[i];
const val = rows[key] || '';
const title = val ? ' title="' + key + '"' : '';
txt += '' + val + ' | ';
}
txt += '
\n';
}
txt += '
\n';
return txt;
}
function apply_colors(tableid, tag, low = null, high = null) {
var nodes = byID(tableid).querySelectorAll(tag);
if (low == null || high == null) { // either both or none
low = 9999;
high = 0;
for (var i = 0; i < nodes.length; i++) {
const tt = nodes[i].innerHTML;
if (tt == '' || tt == '–' || tt == '.' || tt == 'NaN') { continue; }
const val = Number(tt);
if (val > high) { high = val; }
if (val < low) { low = val; }
}
}
for (var i = 0; i < nodes.length; i++) {
const tt = nodes[i].innerHTML;
if (tt == '' || tt == '–' || tt == '.' || tt == 'NaN') { continue; }
var x = Number(tt);
var clss = 0;
if (x > low) {
if (x > high) { x = high; }
clss = parseInt((x - low) / (high - low) * 14) + 1
}
nodes[i].classList = ['m' + clss];
}
}
function pick_ngrams(c, grams, limit) {
var items = Object.keys(grams).map(function(key) {
return [key, grams[key]];
});
items.sort(function(fst, snd) { return snd[1] - fst[1]; });
const select = items.slice(0, limit);
var z = '';
for (var i = 0; i < select.length; i++) {
z += '' + select[i][0] + ':
' + select[i][1] + '
';
}
if (items.length > limit) {
z += '+' + (items.length - limit) + ' others'
}
return dt_dd(c + '-grams:', z, 'tabwidth');
}
function sec_counts() {
var ngrams = [];
for (var s = 0; s < 4; s++) {
var arrset = {};
for (var i = 0; i < window.runes.length - s; i++) {
var key = window.runes[i];
for (var u = 0; u < s; u++) {
key += window.runes[i + u + 1];
}
arrset[key] = (arrset[key] || 0) + 1;
}
ngrams.push(arrset);
}
var txt = 'Letters: ' + window.runes.length + '
\n';
txt += 'Words: ' + window.words.length + '
\n';
txt += '\n';
var wlens = [];
for (var i = 0; i < window.words.length; i++) {
wlens.push([window.words[i].length, window.words[i] + ' (' + window.eng_words[i].join('') + ')']);
}
txt += dt_dd('Word lengths:', num_stream(wlens), 'ioc-list small two', 'strm-wlen');
txt += dt_dd('1-grams:', ngram_table(ngrams[0], 'tbl-1g'));
txt += dt_dd('2-grams:', ngram_table(ngrams[1], 'tbl-2g', window.txt_abc));
txt += pick_ngrams(3, ngrams[2], 100);
txt += pick_ngrams(4, ngrams[3], 50);
txt += '
\n';
byID('sec_counts').innerHTML = txt;
apply_colors('strm-wlen', 'div');
apply_colors('tbl-1g', 'td');
apply_colors('tbl-2g', 'td');
}
function IC_direct(prob, count) {
var sum = 0;
var ioc = 0;
for (k in prob) {
sum += prob[k];
ioc += prob[k] * (prob[k] - 1);
}
return ioc / ((sum * (sum - 1)) / count);
}
function count_table(id, nums) {
var counts = [];
var high = 0;
for (var i = nums.length - 1; i >= 0; i--) {
const val = nums[i][0];
if (val > high) { high = val; }
counts[val] = (counts[val] || 0) + 1;
}
var txt = '\n';
for (var i = 0; i <= high; i++) { txt += '| ' + i + ' | '; }
txt += '
\n';
for (var i = 0; i <= high; i++) { txt += '| ' + (counts[i] || '–') + ' | '; }
txt += '
\n
\n';
txt += '
⤳ IoC: ' + IC_direct(counts, high + 1).toFixed(3) + '';
return txt;
}
function sec_double() {
var num_a = [];
var num_b = [];
var num_c = [];
for (var i = 0; i < window.runes.length - 1; i++) {
const a = window.txt_abc.indexOf(window.runes[i]);
const b = window.txt_abc.indexOf(window.runes[i + 1]);
const c = window.txt_abc.indexOf(window.runes[i + 2]);
const x = (b - a + window.txt_abc.length) % window.txt_abc.length;
num_a.push(x == 0 ? [1, 'offset: ' + i + ', char: ' + window.txt_abc[a]] : '.');
num_b.push([x, 'offset: ' + i]);
if (c != -1) { // is -1 for the last entry
const y = (c - a + window.txt_abc.length) % window.txt_abc.length;
num_c.push([y, 'offset: ' + i]);
}
}
var txt = '';
txt += dt_dd('Double Letters:', num_stream(num_a), 'ioc-list small one', 'strm-dbls');
txt += dt_dd('Letter Difference (2nd – 1st):', num_stream(num_b), 'ioc-list small two', 'strm-diff2a');
txt += dt_dd('Count Difference (2nd – 1st):', count_table('tbl-ldiff2a', num_b));
for (var i = num_b.length - 1; i >= 0; i--) {
num_b[i][0] = Math.min(num_b[i][0], window.txt_abc.length - num_b[i][0])
}
txt += dt_dd('Letter Difference (2nd, Absolute):', num_stream(num_b), 'ioc-list small two', 'strm-diff2b');
txt += dt_dd('Count Difference (2nd, Absolute):', count_table('tbl-ldiff2b', num_b));
txt += dt_dd('Letter Difference (3rd – 1st):', num_stream(num_c), 'ioc-list small two', 'strm-diff3a');
txt += dt_dd('Count Difference (3rd – 1st):', count_table('tbl-ldiff3a', num_c));
for (var i = num_c.length - 1; i >= 0; i--) {
num_c[i][0] = Math.min(num_c[i][0], window.txt_abc.length - num_c[i][0])
}
txt += dt_dd('Letter Difference (3rd, Absolute):', num_stream(num_c), 'ioc-list small two', 'strm-diff3b');
txt += dt_dd('Count Difference (3rd, Absolute):', count_table('tbl-ldiff3b', num_c));
byID('sec_double').innerHTML = '\n' + txt + '
\n';
apply_colors('strm-dbls', 'div', 0, 1);
for (var i = 2; i <= 3; i++) {
apply_colors('strm-diff' + i + 'a', 'div', 0, window.txt_abc.length - 1);
apply_colors('strm-diff' + i + 'b', 'div', 0, parseInt((window.txt_abc.length - 1) / 2));
apply_colors('tbl-ldiff' + i + 'a', 'td');
apply_colors('tbl-ldiff' + i + 'b', 'td');
}
}
function IC(nums) {
var prob = {};
for (var i = nums.length - 1; i >= 0; i--) {
prob[nums[i]] = (prob[nums[i]] || 0) + 1;
}
var ioc = 0;
for (k in prob) { ioc += prob[k] * (prob[k] - 1); }
return ioc / ((nums.length * (nums.length - 1)) / window.txt_abc.length);
}
function mod_str(text, mod) {
var groups = [];
for (var i = mod; i > 0; i--) { groups.push(''); }
for (var i = 0; i < text.length; i++) {
groups[i % mod] += text[i];
}
return groups;
}
function IC_w_keylen(text, keylen) {
if (text.length < 2 * keylen) { return NaN; }
const groups = mod_str(text, keylen);
var ioc = 0;
for (var i = groups.length - 1; i >= 0; i--) {
ioc += IC(groups[i]);
}
return Number(ioc / keylen).toFixed(2);
}
function IC_w_kl_pattern(text, keylen, pattern) {
if (text.length < 2 * keylen) { return NaN; }
var groups = [];
for (var i = keylen; i > 0; i--) { groups.push(''); }
for (var i = 0; i < text.length; i++) {
groups[pattern[i]] += text[i];
}
var ioc = 0;
for (var i = groups.length - 1; i >= 0; i--) {
ioc += IC(groups[i]);
}
return Number(ioc / keylen).toFixed(2);
}
function keylen_table(id, titles, parts, from = 1, to = 16) {
var txt = '\n';
txt += '| keylen | ';
for (var i = from; i <= to; i++) { txt += '' + i + ' | '; }
txt += '
\n';
for (var p = 0; p < parts.length; p++) {
txt += '| ' + titles[p] + ' | ';
for (var kl = from; kl <= to; kl++) {
txt += '' + IC_w_keylen(parts[p], kl) + ' | ';
}
txt += '
\n';
}
txt += '| chr / keylen | ';
for (var kl = from; kl <= to; kl++) {
txt += '' + parseInt(parts.slice(-1)[0].length / kl) + ' | '
}
txt += '
\n
\n';
return txt;
}
function sec_ioc() {
const tbl_a = keylen_table('tbl-ioc-1', ['IoC'], [window.runes], 1, 16);
const tbl_b = keylen_table('tbl-ioc-2', ['IoC'], [window.runes], 17, 32);
byID('sec_ioc').innerHTML = tbl_a + '
' + tbl_b;
apply_colors('tbl-ioc-1', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-1', 'tr.small>td', MIN_CHR, MAX_CHR);
apply_colors('tbl-ioc-2', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-2', 'tr.small>td', MIN_CHR, MAX_CHR);
}
function sec_ioc_mod() {
var txt = '\n';
for (var mod = 2; mod <= 3; mod++) {
const groups = mod_str(window.runes, mod);
var titles = [];
for (var i = 0; i < mod; i++) {
titles.push('x%' + mod + ' == ' + i);
}
const tbl = keylen_table('tbl-ioc-mod' + mod, titles, groups, 2, 18);
txt += dt_dd('Modulo ' + mod, tbl);
}
byID('sec_ioc_mod').innerHTML = txt + '
\n';
apply_colors('tbl-ioc-mod2', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-mod2', 'tr.small>td', MIN_CHR, MAX_CHR);
apply_colors('tbl-ioc-mod3', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-mod3', 'tr.small>td', MIN_CHR, MAX_CHR);
}
function pattern_mirror_table() {
var txt = '\n';
txt += '| keylen | ';
for (var i = 4; i <= 18; i++) { txt += '' + i + ' | '; }
txt += '
\n';
txt += '| Type A | ';
for (var kl = 4; kl <= 18; kl++) {
var pattern = [];
for (var i = 0; i < window.runes.length; i++) {
var ki = i % (kl * 2);
if (ki >= kl) { ki = kl * 2 - 1 - ki; }
pattern.push(ki);
}
txt += '' + IC_w_kl_pattern(window.runes, kl, pattern) + ' | ';
}
txt += '
\n';
txt += '| Type B | ';
for (var kl = 4; kl <= 18; kl++) {
var pattern = [];
for (var i = 0; i < window.runes.length; i++) {
var ki = i % (kl * 2 - 2);
if (ki >= kl) { ki = kl * 2 - 2 - ki; }
pattern.push(ki);
}
txt += '' + IC_w_kl_pattern(window.runes, kl, pattern) + ' | ';
}
txt += '
\n';
txt += '| chr / keylen | ';
for (var kl = 4; kl <= 18; kl++) {
txt += '' + parseInt(window.runes.length / kl) + ' | '
}
return txt + '
\n
\n';
}
function pattern_shift_table() {
var txt = '\n';
txt += '| keylen | ';
for (var i = 4; i <= 18; i++) { txt += '' + i + ' | '; }
txt += '
\n';
for (var shift = 0; shift <= 17; shift++) {
txt += '| <<' + shift + ' | ';
for (var kl = 4; kl <= 18; kl++) {
if (shift >= kl) {
txt += '– | ';
continue;
}
var pattern = [];
for (var i = 0; i < window.runes.length; i++) {
pattern.push((i + shift * parseInt(i / kl)) % kl);
}
txt += '' + IC_w_kl_pattern(window.runes, kl, pattern) + ' | ';
}
txt += '
\n';
}
txt += '| chr / keylen | ';
for (var kl = 4; kl <= 18; kl++) {
txt += '' + parseInt(window.runes.length / kl) + ' | '
}
return txt + '
\n
\n';
}
function sec_ioc_pattern() {
var txt = '\n';
txt += dt_dd('Mirror Pattern', pattern_mirror_table());
txt += dt_dd('Shift Pattern', pattern_shift_table());
byID('sec_ioc_pattern').innerHTML = txt + '
\n';
apply_colors('tbl-ioc-pattern-mirror', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-pattern-mirror', 'tr.small>td', MIN_CHR, MAX_CHR);
apply_colors('tbl-ioc-pattern-shift', 'tr:not(.small)>td', MIN_IOC, MAX_IOC);
apply_colors('tbl-ioc-pattern-shift', 'tr.small>td', MIN_CHR, MAX_CHR);
}
function sec_ioc_flow() {
const wsize = [20, 30, 50, 80, 120];
txt = '\n'
for (var i = 0; i < 5; i++) {
var nums = [];
for (var o = 0; o <= window.runes.length - wsize[i]; o++) {
const sub = window.runes.slice(o, o + wsize[i]);
nums.push([IC(sub).toFixed(2), 'offset: ' + o]);
}
txt += dt_dd('Window size ' + wsize[i] + ':', num_stream(nums), 'ioc-list small four', 'strm-ioc-flow-' + wsize[i]);
}
byID('sec_ioc_flow').innerHTML = txt + '
\n';
for (var i = 0; i < 5; i++) {
apply_colors('strm-ioc-flow-' + wsize[i], 'div', MIN_IOC - 0.1, MAX_IOC);
}
}
function ioc_head(desc, letters) {
return desc + ' (IoC: ' + IC(letters).toFixed(3) + '):';
}
function sec_conceal() {
var txt = '';
for (var n = 1; n <= 8; n++) {
txt += 'Pick every ' + n + '. word
\n\n';
for (var u = 0; u < n; u++) {
if (n > 1) {
txt += 'Start with ' + (u + 1) + '. word
\n';
}
var subwords = '';
var subrunes = [];
var subfirst = [];
var sublast = [];
for (var i = u; i < window.eng_words.length; i += n) {
const word = window.eng_words[i];
for (var w = 0; w < word.length; w++) {
subwords += word[w];
}
subwords += ' ';
subrunes = subrunes.concat(word);
subfirst.push(word[0]);
sublast.push(word[word.length - 1]);
}
txt += dt_dd(ioc_head('Words', subrunes), subwords);
for (var i = 0; i < 2; i++) {
const letters = [subfirst, sublast][i];
const desc = ['first', 'last'][i];
var divarr = '';
for (var o = 0; o < letters.length; o++) {
divarr += '' + letters[o] + '
';
}
txt += dt_dd(ioc_head('Pick every ' + desc + ' letter', letters), divarr, 'runelist');
}
}
txt += '
\n'
}
byID('sec_conceal').innerHTML = txt;
}
function start_analyze() {
window.txt_abc = byID('abc').value;
var abc_123 = {};
var trans = byID('abc_123').value.trim().split(' ');
if (trans.length == 0 || trans.length == 1 && !trans[0]) {
trans = window.txt_abc;
} else if (trans.length != window.txt_abc.length) {
alert('Alphabet and translation length mismatch! (' + window.txt_abc.length + ' vs. ' + trans.length + ')');
return;
}
for (var i = trans.length - 1; i >= 0; i--) {
abc_123[window.txt_abc[i]] = trans[i];
}
window.txt_src = byID('input').value;
const no_nl = byID('chk_nonl').checked;
var words = ' ';
var runes = '';
for (var i = 0; i < window.txt_src.length; i++) {
const letter = window.txt_src[i];
if (window.txt_abc.includes(letter)) {
runes += letter;
words += letter;
} else if (no_nl && letter == '\n') {
continue;
} else if (words.slice(-1) != ' ') {
words += ' '
}
}
window.runes = runes;
window.words = words.trim().split(' ');
var eng_words = [];
for (var i = 0; i < window.words.length; i++) {
const word = window.words[i];
var this_word = [];
for (var u = 0; u < word.length; u++) {
this_word.push(abc_123[word[u]]);
}
eng_words.push(this_word);
}
window.eng_words = eng_words;
MIN_CHR = parseInt(window.txt_abc.length * 0.9);
MAX_CHR = parseInt(window.txt_abc.length * 3);
MIN_IOC = parseFloat(byID('ioc_min').value || byID('ioc_min').placeholder);
MAX_IOC = parseFloat(byID('ioc_max').value || byID('ioc_max').placeholder);
sec_counts();
sec_double();
sec_ioc();
sec_ioc_mod();
sec_ioc_pattern();
sec_ioc_flow();
sec_conceal();
}