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 += ''; } if (th == null) { th = ['']; } txt += '\n'; for (var o = 0; o < th.length; o++) { txt += ''; if (th[o] != '') { txt += ''; } 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 += '
' + window.txt_abc[i] + '
' + th[o] + '
\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 += ''; } txt += '\n'; for (var i = 0; i <= high; i++) { txt += ''; } txt += '\n
' + i + '
' + (counts[i] || '–') + '
\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 += ''; for (var i = from; i <= to; i++) { txt += ''; } txt += '\n'; for (var p = 0; p < parts.length; p++) { txt += ''; for (var kl = from; kl <= to; kl++) { txt += ''; } txt += '\n'; } txt += ''; for (var kl = from; kl <= to; kl++) { txt += '' } txt += '\n
keylen' + i + '
' + titles[p] + '' + IC_w_keylen(parts[p], kl) + '
chr / keylen' + parseInt(parts.slice(-1)[0].length / kl) + '
\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 += ''; for (var i = 4; i <= 18; i++) { txt += ''; } txt += '\n'; txt += ''; 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 += ''; } txt += '\n'; txt += ''; 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 += ''; } txt += '\n'; txt += ''; for (var kl = 4; kl <= 18; kl++) { txt += '' } return txt + '\n
keylen' + i + '
Type A' + IC_w_kl_pattern(window.runes, kl, pattern) + '
Type B' + IC_w_kl_pattern(window.runes, kl, pattern) + '
chr / keylen' + parseInt(window.runes.length / kl) + '
\n'; } function pattern_shift_table() { var txt = '\n'; txt += ''; for (var i = 4; i <= 18; i++) { txt += ''; } txt += '\n'; for (var shift = 0; shift <= 17; shift++) { txt += ''; 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 += ''; } txt += '\n'; } txt += ''; for (var kl = 4; kl <= 18; kl++) { txt += '' } return txt + '\n
keylen' + i + '
<<' + shift + '' + IC_w_kl_pattern(window.runes, kl, pattern) + '
chr / keylen' + parseInt(window.runes.length / kl) + '
\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(); }