|
| 1 | +(function (exports) { |
| 2 | + 'use strict'; |
| 3 | + |
| 4 | + /** |
| 5 | + * Burrows Wheeler. |
| 6 | + * |
| 7 | + * This algorithm is commonly used as a step in the process of compressing data, |
| 8 | + * it rearranges a character string into runs of similar characters making algorithms |
| 9 | + * like move-to-front transform and run-length encoding reach higher compression |
| 10 | + * rates. This implementation only supports characters with ascii code greater than $(36) as |
| 11 | + * 36 is used at the process of encode and decode. |
| 12 | + * |
| 13 | + * @example |
| 14 | + * const burrows = require('path-to-algorithms/src/burrows-wheeler/burrows-wheeler').burrowsWheeler; |
| 15 | + * const s = 'ananabanana'; |
| 16 | + * const encodedStr = burrows.encode(s); |
| 17 | + * console.log(encodedStr); |
| 18 | + * const decodedStr = burrows.decode(encodedStr); |
| 19 | + * console.log(decodedStr); |
| 20 | + * |
| 21 | + * @module compression/burrows-wheeler/burrows-wheeler |
| 22 | + */ |
| 23 | + exports.burrowsWheeler = function() { |
| 24 | + |
| 25 | + } |
| 26 | + |
| 27 | + /** |
| 28 | + * Consumes n^2 space. |
| 29 | + */ |
| 30 | + exports.burrowsWheeler.encode = function(str) { |
| 31 | + str = '$' + str; |
| 32 | + var combinations = []; |
| 33 | + for (let i = 0; i < str.length; i += 1) { |
| 34 | + combinations.push(str.substring(i) + str.substring(0, i)); |
| 35 | + } |
| 36 | + var sorted = combinations.sort(); |
| 37 | + var result = []; |
| 38 | + for (let i = 0; i < sorted.length; i += 1) { |
| 39 | + result.push(combinations[i][str.length - 1]); |
| 40 | + } |
| 41 | + return result.join(''); |
| 42 | + } |
| 43 | + |
| 44 | + exports.burrowsWheeler.decode = function(encodedStr) { |
| 45 | + const sortedCharSequence = encodedStr.split('').sort().join(''); |
| 46 | + const leftSide = {}; |
| 47 | + const rightSide = {}; |
| 48 | + var maxEachCharLeft = {}; |
| 49 | + var maxEachCharRight = {}; |
| 50 | + |
| 51 | + for (let i = 0; i < encodedStr.length; i += 1) { |
| 52 | + var idLeft = sortedCharSequence[i]; |
| 53 | + if (idLeft in maxEachCharLeft) { |
| 54 | + maxEachCharLeft[idLeft] = maxEachCharLeft[idLeft] + 1; |
| 55 | + } else { |
| 56 | + maxEachCharLeft[idLeft] = 1; |
| 57 | + } |
| 58 | + idLeft += String(maxEachCharLeft[idLeft]); |
| 59 | + |
| 60 | + var idRight = encodedStr[i]; |
| 61 | + if (idRight in maxEachCharRight) { |
| 62 | + maxEachCharRight[idRight] = maxEachCharRight[idRight] + 1; |
| 63 | + } else { |
| 64 | + maxEachCharRight[idRight] = 1; |
| 65 | + } |
| 66 | + idRight += String(maxEachCharRight[idRight]); |
| 67 | + |
| 68 | + leftSide[idLeft] = {char: sortedCharSequence[i], right: idRight}; |
| 69 | + rightSide[idRight] = {char: encodedStr[i], left: idRight}; |
| 70 | + } |
| 71 | + var result = ''; |
| 72 | + var firstChar = sortedCharSequence[0]; |
| 73 | + var searchChar = firstChar + '1'; |
| 74 | + var endChar = searchChar; |
| 75 | + while (rightSide[leftSide[searchChar].right].left !== endChar) { |
| 76 | + result += leftSide[searchChar].char; |
| 77 | + searchChar = rightSide[leftSide[searchChar].right].left; |
| 78 | + } |
| 79 | + result += leftSide[searchChar].char; |
| 80 | + result = result.substring(1).split('').reverse().join(''); |
| 81 | + return result; |
| 82 | + } |
| 83 | + |
| 84 | +}(typeof exports === 'undefined' ? window : exports)); |
0 commit comments