Below, I have created a rudimentary script to decipher a columnar transposition. I am wondering if there is a more efficient way to determine the number of the rows in a column? Also, is there a way to build the resulting string without creating a matrix in the interim?
Overview
Quick refresher for columnar transpositions.
Method 1
Given a key of 3
,
the text 'Attack at dawn'
is transposed into three columns...
1 2 3
A t t
a c k
a t D
a w n
...joining each column; left->right, top->bottom; will result in the following text:
Aaaa tctw tkDn
Method 2
Given a key 'table'
,
the text 'Attack at dawn'
is transposed into five columns (length of the key)...
t a b l e
A t t a c
k a t D a
w n
...joining the columns, as described above, result in the following text:
tAkw atan btt laD eca
Code
Is there a better way to determine how many rows will be present for each column?
function decrypt(ciphertext, key) {
var chars = normalizeText(ciphertext);
var len = chars.length;
var isStr = typeof key === 'string';
var cols = isStr ? key.length : key;
var rows = Math.ceil(len / cols);
var mod = divMod(len, cols)[1];
var matrix = Matrix(rows, cols, '');
var counter = 0;
for (var col = 0; col < cols; col++) {
var remainder = mod !== 0 && col >= mod ? 1 : 0;
for (var row = 0; row < rows - remainder; row++) {
matrix[row][col] = chars[counter++];
}
}
return (function(plaintext, startIndex) {
return plaintext.substr(startIndex);
}(matrix.reduce(function(result, row) {
return result + row.join('');
}, ''), isStr ? cols : 0));
}
Demo
[ [ 'Aaaa tctw tkDn' , 3 ],
[ 'tAkw atan btt laD eca', 'table' ]
].forEach(function(cipher) {
println(decrypt(cipher[0], cipher[1]));
});
function decrypt(ciphertext, key) {
var chars = normalizeText(ciphertext);
var len = chars.length;
var isStr = typeof key === 'string';
var cols = isStr ? key.length : key;
var rows = Math.ceil(len / cols);
var mod = divMod(len, cols)[1];
var matrix = Matrix(rows, cols, '');
var counter = 0;
for (var col = 0; col < cols; col++) {
var remainder = mod !== 0 && col >= mod ? 1 : 0;
for (var row = 0; row < rows - remainder; row++) {
matrix[row][col] = chars[counter++];
}
}
return (function(plaintext, startIndex) {
return plaintext.substr(startIndex);
}(matrix.reduce(function(result, row) {
return result + row.join('');
}, ''), isStr ? cols : 0));
}
function divMod(y, x) {
return [~~(y / x), y % x];
}
function normalizeText(ciphertext) {
return ciphertext.replace(/ +/g, "").split('');
}
function Matrix(rows, cols, defaultVal) {
defaultVal = defaultVal !== undefined ? defaultVal : 0;
var arr = [];
for (var i = 0; i < rows; i++) {
arr.push([]);
arr[i].push(new Array(cols));
for (var j = 0; j < cols; j++) {
arr[i][j] = defaultVal;
}
}
return arr;
}
function print(str, targetEl) {
return (function (el) {
return el.html(el.html() + (str || ''));
}(targetEl || $('#out')));
}
function println(str, targetEl) {
print((str || '') + '<br />', targetEl)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="out"></div>