Best way to get two nibbles out of a byte in javascript? - javascript

I'm parsing a binary file in javascript that is storing two pieces of information per byte, one per nibble. The values are, of course, 0-16 and 0-16.
In all other parts of the file format, each byte represents one piece of information, so I have been using the following to successfully get the number values I need:
var num = str.charCodeAt(0) & 0xFF;
But I'm stuck at trying to figure out how to get the 0-16 value of the first nibble, and the same for the 2nd nibble from my single byte character "str".
Appreciate any help on this.

var num = str.charCodeAt(0) & 0xFF;
var nibble1 = num & 0xF;
var nibble2 = num >> 4;

You can do:
var num = str.charCodeAt(0);
var lower_nibble = (num & 0xF0) >> 4;
var higher_nibble = num & 0x0F;
How does it work?
Lets suppose the bit representation of num is abcdwxyz and we want to extract abcd as higher nibble and wxyz as lower nibble.
To extract the lower nibble we just mask the higher nibble by bitwise anding the number with 0x0F:
a b c d w x y z
&
0 0 0 0 1 1 1 1
---------------
0 0 0 0 w x y z = lower nibble.
To extract the higher nibble we first mask the lower nibble by bitwise anding with 0xF0 as:
a b c d w x y z
&
1 1 1 1 0 0 0 0
---------------
a b c d 0 0 0 0
and then we bitwise right- shift the result right 4 times to get rid of the trailing zeros.
Bitwise right shifting a variable 1 time will make it loose the rightmost bit and makes the left most bit zero:
a b c d w x y z
>> 1
----------------
0 a b c d w x y
Similarly bitwise right shifting 2 times will introduce result in :
a b c d w x y z
>> 2
----------------
0 0 a b c d w x
and bitwise right shift 4 times gives:
a b c d w x y z
>> 4
----------------
0 0 0 0 a b c d
as clearly seen the result is the higher nibble of the byte (abcd).

Since I'm favoriting this, I wanted to add some things I just wrote that might be useful. Perhaps others will find it useful as well.
Below's jsFiddle
Prototypes:
Number.prototype.fromCharCode = function () {return String.fromCharCode(this); };
String.prototype.byte = function (val){ var a = new Array();
for(var i=(val||0),n=val===0?0:this.length-1; i<=n; i++){
a.push(this.charCodeAt(i) & 0xFF);
}
return a;
};
String.prototype.HiNibble = function (val){
var b = this.byte(val);
var a = new Array();
for(var i=0,n=b.length-1; i<=n; i++){a.push(b[i] >> 4);}
return a;
};
String.prototype.LoNibble = function (val){
var b = this.byte(val);
var a = new Array();
for(var i=0,n=b.length-1; i<=n; i++){a.push(b[i] & 0xF);}
return a;
};
Example Calls:
var str = new String("aB");
console.log(str.byte()); // [ 97, 66 ]
console.log(str.HiNibble()); // [ 6, 4 ]
console.log(str.LoNibble()); // [ 1, 2 ]
console.log(str.byte(0)); // [ 97 ]
console.log(str.HiNibble(0)); // [ 6 ]
console.log(str.LoNibble(0)); // [ 1 ]
var bar = "c";
console.log(bar.byte()); // [ 99 ]
console.log(bar.HiNibble()); // [ 6 ]
console.log(bar.LoNibble()); // [ 3 ]
var foobar = (65).fromCharCode(); // from an integer (foobar=="A")
console.log(foobar.byte()); // [ 65 ]
console.log(foobar.HiNibble()); // [ 4 ]
console.log(foobar.LoNibble()); // [ 1 ]
Just added for possible help, but is not used in the above:
/* Useful function that I modified
Originally from: http://www.navioo.com/javascript/dhtml/Ascii_to_Hex_and_Hex_to_Ascii_in_JavaScript_1158.html
*/
function AscHex(x,alg){
hex = "0123456789ABCDEF";
someAscii = ' !"#$%&\''
+ '()*+,-./0123456789:;=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\'
+ ']^_`abcdefghijklmnopqrstuvwxyz{|}';
r = "";
if(alg=="A2H"){
for(var i=0,n=x.length;i<n;i++){
let=x.charAt(i);
pos=someAscii.indexOf(let)+32;
h16=Math.floor(pos/16);
h1=pos%16;
r+=hex.charAt(h16)+hex.charAt(h1);
}
}
if(alg=="H2A"){
for(var i=0,n=x.length;i<n;i++){
let1=x.charAt(2*i);
let2=x.charAt(2*i+1);
val=hex.indexOf(let1)*16+hex.indexOf(let2);
r+=someAscii.charAt(val-32);
}
}
return r;
}
console.log(AscHex('65','A2H')); // A

Convert the string to buffer and then nipples:
function bufferToNibbles(key) {
// this will convert ascii string to hex values
// buffer will get sequence of bytes
const buffer = Buffer.from(key);
const nibbles = [];
for (let i = 0; i < buffer.length; i++) {
let q = i * 2;
nibbles[q] = buffer[i] >> 4;
++q;
nibbles[q] = buffer[i] % 16;
}
return nibbles;
}

Related

Convert to base 42 in javascript

I want convert a integer form base 10 to base 42 in Javascript,
by default Javascript can do to base 36 not more.
How can I do to resolve this problem ?
To begin you need some sort of symbol system to represent digits. In base 10 we have 10: 0 - 9. In base 16 you have 16 0 - 9 and A – F. Binary, of course you only have 2: 0 and 1.
So for base 42 you'll need 42 types of digit. Since this just seems like an exercise maybe include upper and lower case digits (I'm sure there's a better scheme if you put your mind to it):
const symbols = "0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f".split(" ")
So the numbers 0 - 41 will be 0 through f. 42 will start at 10 42**2 will be 100 etc.
To get the right-most digit, you can can take the number modulus the base. Then divide by the base (and take the floor) and do it again until your number is reduced to zero. With the above scheme here is counting to 100 base 42:
const symbols = "0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f".split(" ")
function toBase(n, base){
if (n === 0 ) return symbols[n]
res = ""
while (n) {
res = symbols[n % base] + res
n = Math.floor(n / base)
}
return res
}
console.log(Array.from({length: 100}, (_, i) => toBase(i, 42)).join(", "))

How to manipulate bits in Javascript?

Let's say I have a variable X = 4
How to create a number with a binary representation 1111 (ones with length X) using bitwise operators ( & | ~ << ^ ) and take a position and toggle the bit at that position to zero (0).
Example:
X = initial(4) // X should be : 1111
Y = solution(X, 2) // Y should be 1101
Z = solution(Y, 3) // Z should be 1001
Yes, you'd use Math.pow (or on modern browsers, the exponentiation operator, **) and the bitwise operators to do that.
function initial(digits) {
return Math.pow(2, digits) - 1;
}
function solution(value, bit) {
return value & ~(1 << (bit - 1)); // () around `bit - 1` aren't necessary,
// but I find it clearer
}
var X = initial(4); // X should be : 1111
console.log(X.toString(2));
var Y = solution(X, 2); // Y should be 1101
console.log(Y.toString(2));
var Z = solution(Y, 3); // Z should be 1001
console.log(Z.toString(2));
Or — doh! — comments on the question point out that you can create the initial number without Math.pow or exponentiation:
function initial(digits) {
return (1 << digits) - 1;
}
function solution(value, bit) {
return value & ~(1 << (bit - 1)); // () around `bit - 1` aren't necessary,
// but I find it clearer
}
var X = initial(4); // X should be : 1111
console.log(X.toString(2));
var Y = solution(X, 2); // Y should be 1101
console.log(Y.toString(2));
var Z = solution(Y, 3); // Z should be 1001
console.log(Z.toString(2));

rotate a matrix 45 degrees in javascript

given a matrix like this one:
1 2 3
4 5 6
7 8 9
which can be represented as a 2 dimensional array:
arr = [[1,2,3], [4,5,6], [7,8,9]];
rotate the array so that it is read diagonally at a 45 degree angle and prints out this:
1
4 2
7 5 3
8 6
9
I spent a while coming up with a solution that I don't even fully intuitively understand, but it works, at least for 3x3 and 4x4 matrices. I was hoping to see more logical and clean implementations.
Here's my solution:
arr = [[1,2,3,0],[4,5,6,0],[7,8,9,0], [0,0,0,0]];
// arr[i][j];
transform(arr);
function transform(ar) {
// the number of lines in our diagonal matrix will always be rows + columns - 1
var lines = ar.length + ar[0].length - 1;
// the length of the longest line...
var maxLen = ~~(ar.length + ar[0].length)/2;
var start = 1;
var lengths = [];
// this for loop creates an array of the lengths of each line, [1,2,3,2,1] in our case
for (i=0;i<lines; i++) {
lengths.push(start);
if (i+1 < maxLen) {
start++;
} else {
start--;
}
}
// after we make each line, we're going to append it to str
var str = "";
// for every line
for(j=0; j<lengths.length; j++) {
// make a new line
var line = "";
// i tried to do it all in one for loop but wasn't able to (idk if it's possible) so here we use a particular for loop while lengths of the lines are increasing
if (j < maxLen) {
// lengths[j] is equal to the elements in this line, so the for loop will run that many times and create that many elements
for(c=0; c<lengths[j]; c++) {
// if ar[r][c], the pattern here is that r increases along rows (as we add new lines), and decreases along columns. c stays the same as we add rows, and increases across columns
line += ar[lengths[j]-1-c][c] + " ";
// when we've added all the elements we need for this line, add it to str with a line break
if (c == lengths[j]-1) {
line += "\n"; str += line;
}
}
} else {
// when we're headed down or decreasing the length of each line
for (r=0; r<lengths[j]; r++) {
// the pattern here tripped me up, and I had to introduce another changing variable j-maxLen (or the distance from the center). r stays the same as rows increase and decreases across columns. c increases along rows and decreases across columns
line += ar[lengths[j]-r+j-maxLen][j-maxLen+r +1] + " ";
// that's all our elements, add the line to str;
if (r == lengths[j] -1) {
line += "\n"; str += line;
}
}
}
}
console.log(str);
}
The main idea is to partition the original matrix indexed by (i,j) according to i+j.
This is expressed in the code snippet rotated[i+j].push(arr[i][j]) below:
arr = [[1,2,3], [4,5,6], [7,8,9]];
var summax = arr.length + arr[0].length - 1; // max index of diagonal matrix
var rotated = []; // initialize to an empty matrix of the right size
for( var i=0 ; i<summax ; ++i ) rotated.push([]);
// Fill it up by partitioning the original matrix.
for( var j=0 ; j<arr[0].length ; ++j )
for( var i=0 ; i<arr.length ; ++i ) rotated[i+j].push(arr[i][j]);
// Print it out.
for( var i=0 ; i<summax ; ++i ) console.log(rotated[i].join(' '))
Output:
1
4 2
7 5 3
8 6
9
In Ruby
Produces same output:
puts arr.transpose.flatten.group_by.with_index { |_,k|
k.divmod(arr.size).inject(:+) }.values.map { |a| a.join ' ' }
function transform(ar) {
var result = [],
i, x, y, row;
for (i = 0; i < ar.length; i++) {
row = [];
for (x = 0, y = i; y >= 0; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
for (i = 1; i < ar[0].length; i++) {
row = [];
for (x = i, y = ar[0].length - 1; x < ar[0].length; x++, y--) {
row.push(ar[y][x]);
}
result.push(row);
}
return result;
}
This returns the rotated array, to print it out as you go just replace each result.push(row); line with console.log(row.join(" "));.
Here's my approach:
var origMatrix = [[1,2,3,4,5], [4,5,6,7,8], [9,10,11,12,13], [14,15,16,17,18], [19,20,21,22,23]];
var maxSize = origMatrix.length;//Presuming all internal are equal!
var rotatedMatrix = [];
var internalArray;
var keyX,keyY,keyArray;
for(var y=0;y<((maxSize * 2)-1);y++){
internalArray = [];
for(var x=0;x<maxSize;x++){
keyX = x;
keyY = y - x;
if(keyY > -1){
keyArray = origMatrix[keyY];
if(typeof(keyArray) != 'undefined' && typeof(keyArray[keyX]) != 'undefined'){
internalArray.push(keyArray[keyX]);
}
}
}
rotatedMatrix.push(internalArray);
}
//log results
for(var i=0;i<rotatedMatrix.length;i++){
console.log(rotatedMatrix[i]);
}
Here's a JSFiddle of it in action (open the Console to see the results)
The Idea: Walk the Diagonals and Clip
You could use the diagonal enumeration from Cantor, see Cantor pairing function,
which is used to show that the set N x N has the same cardinality as the set N (i.e. natural numbers can be mapped one to one to pairs of natural numbers) and combine it with a condition that one skips those values which lie outside the rectangular matrix.
The Cantor pairing function pi takes two natural numbers i and j, i.e. the pair (i, j) and maps it to a natural number k
pi : |N x |N -> |N : pi(i, j) = k
Use the reverse mapping to get this
pi^-1 : |N -> |N x |N : pi^-1(k) = (i, j)
i.e. one enumerates the cells of the "infinite Matrix" N x N diagonally.
So counting up k and applying the inverse function will give the proper pair of indices (i, j) for printing the rotated matrix.
Example:
0->(0, 0) 2->(0, 1) | 5->(0, 2) 9->(0, 3) . .
1->(1, 0) 4->(1, 1) | 8->(1, 2)
3->(2, 0) 7->(2, 2) |
---------------------+ <- clipping for 3 x 2 matrix
6->(3, 0)
.
.
Calculation of the Inverse Cantor Pair Function
For input k these formulas give the pair (i, j):
w = floor((sqrt(8*k + 1) - 1) / 2)
t = (w*w + w) / 2
j = k - t
i = w - j
See the link given above for a derivation.
Resulting Algorithm
Given a m x n matrix A: i from [0, .., m - 1] enumerates the rows, and j from [0, .., n - 1] enumerates the columns
Start at k = 0
calculate the corresponding index pair (i, j)
print the matrix value A[i, j] if the indices i and j lie within your matrix dimensions m and n
print a new line, once your i hit the top of the matrix, i.e. if i == 0
increment k
continue with step 2 until you arrived at the index pair (i, j) = (n - 1, n - 1)
Sample Implementation in JavaScript
Note: I tried this out in the MongoDB shell, using its print() function.
Helper functions
function sprint(k) {
var s = '' + k;
while (s.length < 3) {
s = ' ' + s;
}
return s;
}
function print_matrix(a) {
var m = a.row_size;
var n = a.column_size;
for (var i = 0; i < m; i++) {
var s = '';
for (var j = 0; j < n; j++) {
s += sprint(a.value[i][j]);
}
print(s);
}
}
The Inverse of the Cantor pairing function
// inverse of the Cantor pair function
function pi_inv(k) {
var w = Math.floor((Math.sqrt(8*k + 1) - 1) / 2);
var t = (w*w + w) /2;
var j = k - t;
var i = w -j;
return [i, j];
}
The algorithm
// "rotate" matrix a
function rot(a) {
var m = a.row_size;
var n = a.column_size;
var i_max = m - 1;
var j_max = n - 1;
var k = 0;
var s = '';
do {
var ij = pi_inv(k);
var i = ij[0];
var j = ij[1];
if ((i <= i_max) && (j <= j_max)) {
s += sprint(a.value[i][j]);
}
if (i == 0) {
print(s);
s = '';
}
k += 1
} while ((i != i_max) || (j != j_max));
print(s);
}
Example usage
// example
var a = {
row_size: 4,
column_size: 4,
value: [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ]
};
print('in:');
print_matrix(a);
print('out:');
rot(a);
Output for 4x4 Matrix
in:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
out:
1
5 2
9 6 3
13 10 7 4
14 11 8
15 12
16
This method works for any m x n Matrix, e.g. 4 x 5:
in:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
out:
1
6 2
11 7 3
16 12 8 4
17 13 9 5
18 14 10
19 15
20
or 4 x 3:
in:
1 2 3
4 5 6
7 8 9
10 11 12
out:
1
4 2
7 5 3
10 8 6
11 9
12

Javascript - convert integer to array of bits

I am trying in javascript to convert an integer (which I know will be between 0 and 32), to an array of 0s and 1s. I have looked around but couldn't find something that works..
So, if I have an integer as 22 (binary 10110), I would like to access it as:
Bitarr[0] = 0
Bitarr[1] = 1
Bitarr[2] = 1
Bitarr[3] = 0
Bitarr[4] = 1
Any suggestions?
Many thanks
convert to base 2:
var base2 = (yourNumber).toString(2);
access the characters (bits):
base2[0], base2[1], base2[3], etc...
Short (ES6)
Shortest (32 chars) version which fill last bits by zero. I assume that n is your number, b is base (number of output bits):
[...Array(b)].map((x,i)=>n>>i&1)
let bits = (n,b=32) => [...Array(b)].map((x,i)=>(n>>i)&1);
let Bitarr = bits(22,8);
console.log(Bitarr[0]); // = 0
console.log(Bitarr[1]); // = 1
console.log(Bitarr[2]); // = 1
console.log(Bitarr[3]); // = 0
console.log(Bitarr[4]); // = 1
var a = 22;
var b = [];
for (var i = 0; i < 5; i++)
b[i] = (a >> i) & 1;
alert(b);
Assuming 5 bits (it seemed from your question), so 0 <= a < 32. If you like you can make the 5 larger, upto 32 (bitshifting in JavaScript works with 32 bit integer).
This should do
for(int i = 0; i < 32; ++i)
Bitarr[i] = (my_int >> i) & 1;
You can convert your integer to a binary String like this. Note the base 2 parameter.
var i = 20;
var str = i.toString(2); // 10100
You can access chars in a String as if it were an array:
alert(str[0]); // 1
alert(str[1]); // 0
etc...
Building up on previous answers: you may want your array to be an array of integers, not strings, so here is a one-liner:
(1234).toString(2).split('').map(function(s) { return parseInt(s); });
Note, that shorter version, (11).toString(2).split('').map(parseInt) will not work (chrome), for unknown to me reason it converts "0"s to NaNs
In addition, this code gives 32length array
function get_bits(value){
var base2_ = (value).toString(2).split("").reverse().join("");
var baseL_ = new Array(32 - base2_.length).join("0");
var base2 = base2_ + baseL_;
return base2;
}
1 => 1000000000000000000000000000000
2 => 0100000000000000000000000000000
3 => 1100000000000000000000000000000
You might do as follows;
var n = 1071,
b = Array(Math.floor(Math.log2(n))+1).fill()
.map((_,i,a) => n >> a.length-1-i & 1);
console.log(b);
just for the sake of refernce:
(121231241).toString(2).split('').reverse().map((x, index) => x === '1' ? 1 << index : 0).reverse().filter(x => x > 0).join(' + ');
would give you:
67108864 + 33554432 + 16777216 + 2097152 + 1048576 + 524288 + 65536 + 32768 + 16384 + 4096 + 1024 + 512 + 256 + 128 + 8 + 1

How to create a function that converts a Number to a Bijective Hexavigesimal?

Maybe i am just not that good enough in math, but I am having a problem in converting a number into pure alphabetical Bijective Hexavigesimal just like how Microsoft Excel/OpenOffice Calc do it.
Here is a version of my code but did not give me the output i needed:
var toHexvg = function(a){
var x='';
var let="_abcdefghijklmnopqrstuvwxyz";
var len=let.length;
var b=a;
var cnt=0;
var y = Array();
do{
a=(a-(a%len))/len;
cnt++;
}while(a!=0)
a=b;
var vnt=0;
do{
b+=Math.pow((len),vnt)*Math.floor(a/Math.pow((len),vnt+1));
vnt++;
}while(vnt!=cnt)
var c=b;
do{
y.unshift( c%len );
c=(c-(c%len))/len;
}while(c!=0)
for(var i in y)x+=let[y[i]];
return x;
}
The best output of my efforts can get is: a b c d ... y z ba bb bc - though not the actual code above. The intended output is suppose to be a b c ... y z aa ab ac ... zz aaa aab aac ... zzzzz aaaaaa aaaaab, you get the picture.
Basically, my problem is more on doing the ''math'' rather than the function. Ultimately my question is: How to do the Math in Hexavigesimal conversion, till a [supposed] infinity, just like Microsoft Excel.
And if possible, a source code, thank you in advance.
Okay, here's my attempt, assuming you want the sequence to be start with "a" (representing 0) and going:
a, b, c, ..., y, z, aa, ab, ac, ..., zy, zz, aaa, aab, ...
This works and hopefully makes some sense. The funky line is there because it mathematically makes more sense for 0 to be represented by the empty string and then "a" would be 1, etc.
alpha = "abcdefghijklmnopqrstuvwxyz";
function hex(a) {
// First figure out how many digits there are.
a += 1; // This line is funky
c = 0;
var x = 1;
while (a >= x) {
c++;
a -= x;
x *= 26;
}
// Now you can do normal base conversion.
var s = "";
for (var i = 0; i < c; i++) {
s = alpha.charAt(a % 26) + s;
a = Math.floor(a/26);
}
return s;
}
However, if you're planning to simply print them out in order, there are far more efficient methods. For example, using recursion and/or prefixes and stuff.
Although #user826788 has already posted a working code (which is even a third quicker), I'll post my own work, that I did before finding the posts here (as i didnt know the word "hexavigesimal"). However it also includes the function for the other way round. Note that I use a = 1 as I use it to convert the starting list element from
aa) first
ab) second
to
<ol type="a" start="27">
<li>first</li>
<li>second</li>
</ol>
:
function linum2int(input) {
input = input.replace(/[^A-Za-z]/, '');
output = 0;
for (i = 0; i < input.length; i++) {
output = output * 26 + parseInt(input.substr(i, 1), 26 + 10) - 9;
}
console.log('linum', output);
return output;
}
function int2linum(input) {
var zeros = 0;
var next = input;
var generation = 0;
while (next >= 27) {
next = (next - 1) / 26 - (next - 1) % 26 / 26;
zeros += next * Math.pow(27, generation);
generation++;
}
output = (input + zeros).toString(27).replace(/./g, function ($0) {
return '_abcdefghijklmnopqrstuvwxyz'.charAt(parseInt($0, 27));
});
return output;
}
linum2int("aa"); // 27
int2linum(27); // "aa"
You could accomplish this with recursion, like this:
const toBijective = n => (n > 26 ? toBijective(Math.floor((n - 1) / 26)) : "") + ((n % 26 || 26) + 9).toString(36);
// Parsing is not recursive
const parseBijective = str => str.split("").reverse().reduce((acc, x, i) => acc + ((parseInt(x, 36) - 9) * (26 ** i)), 0);
toBijective(1) // "a"
toBijective(27) // "aa"
toBijective(703) // "aaa"
toBijective(18279) // "aaaa"
toBijective(127341046141) // "overflow"
parseBijective("Overflow") // 127341046141
I don't understand how to work it out from a formula, but I fooled around with it for a while and came up with the following algorithm to literally count up to the requested column number:
var getAlpha = (function() {
var alphas = [null, "a"],
highest = [1];
return function(decNum) {
if (alphas[decNum])
return alphas[decNum];
var d,
next,
carry,
i = alphas.length;
for(; i <= decNum; i++) {
next = "";
carry = true;
for(d = 0; d < highest.length; d++){
if (carry) {
if (highest[d] === 26) {
highest[d] = 1;
} else {
highest[d]++;
carry = false;
}
}
next = String.fromCharCode(
highest[d] + 96)
+ next;
}
if (carry) {
highest.push(1);
next = "a" + next;
}
alphas[i] = next;
}
return alphas[decNum];
};
})();
alert(getAlpha(27)); // "aa"
alert(getAlpha(100000)); // "eqxd"
Demo: http://jsfiddle.net/6SE2f/1/
The highest array holds the current highest number with an array element per "digit" (element 0 is the least significant "digit").
When I started the above it seemed a good idea to cache each value once calculated, to save time if the same value was requested again, but in practice (with Chrome) it only took about 3 seconds to calculate the 1,000,000th value (bdwgn) and about 20 seconds to calculate the 10,000,000th value (uvxxk). With the caching removed it took about 14 seconds to the 10,000,000th value.
Just finished writing this code earlier tonight, and I found this question while on a quest to figure out what to name the damn thing. Here it is (in case anybody feels like using it):
/**
* Convert an integer to bijective hexavigesimal notation (alphabetic base-26).
*
* #param {Number} int - A positive integer above zero
* #return {String} The number's value expressed in uppercased bijective base-26
*/
function bijectiveBase26(int){
const sequence = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const length = sequence.length;
if(int <= 0) return int;
if(int <= length) return sequence[int - 1];
let index = (int % length) || length;
let result = [sequence[index - 1]];
while((int = Math.floor((int - 1) / length)) > 0){
index = (int % length) || length;
result.push(sequence[index - 1]);
}
return result.reverse().join("")
}
I had to solve this same problem today for work. My solution is written in Elixir and uses recursion, but I explain the thinking in plain English.
Here are some example transformations:
0 -> "A", 1 -> "B", 2 -> "C", 3 -> "D", ..
25 -> "Z", 26 -> "AA", 27 -> "AB", ...
At first glance it might seem like a normal 26-base counting system
but unfortunately it is not so simple.
The "problem" becomes clear when you realize:
A = 0
AA = 26
This is at odds with a normal counting system, where "0" does not behave
as "1" when it is in a decimal place other than then unit.
To understand the algorithm, consider a simpler but equivalent base-2 system:
A = 0
B = 1
AA = 2
AB = 3
BA = 4
BB = 5
AAA = 6
In a normal binary counting system we can determine the "value" of decimal places by
taking increasing powers of 2 (1, 2, 4, 8, 16) and the value of a binary number is
calculated by multiplying each digit by that digit place's value.
e.g. 10101 = 1 * (2 ^ 4) + 0 * (2 ^ 3) + 1 * (2 ^ 2) + 0 * (2 ^ 1) + 1 * (2 ^ 0) = 21
In our more complicated AB system, we can see by inspection that the decimal place values are:
1, 2, 6, 14, 30, 62
The pattern reveals itself to be (previous_unit_place_value + 1) * 2.
As such, to get the next lower unit place value, we divide by 2 and subtract 1.
This can be extended to a base-26 system. Simply divide by 26 and subtract 1.
Now a formula for transforming a normal base-10 number to special base-26 is apparent.
Say the input is x.
Create an accumulator list l.
If x is less than 26, set l = [x | l] and go to step 5. Otherwise, continue.
Divide x by 2. The floored result is d and the remainder is r.
Push the remainder as head on an accumulator list. i.e. l = [r | l]
Go to step 2 with with (d - 1) as input, e.g. x = d - 1
Convert """ all elements of l to their corresponding chars. 0 -> A, etc.
So, finally, here is my answer, written in Elixir:
defmodule BijectiveHexavigesimal do
def to_az_string(number, base \\ 26) do
number
|> to_list(base)
|> Enum.map(&to_char/1)
|> to_string()
end
def to_09_integer(string, base \\ 26) do
string
|> String.to_charlist()
|> Enum.reverse()
|> Enum.reduce({0, nil}, fn
char, {_total, nil} ->
{to_integer(char), 1}
char, {total, previous_place_value} ->
char_value = to_integer(char + 1)
place_value = previous_place_value * base
new_total = total + char_value * place_value
{new_total, place_value}
end)
|> elem(0)
end
def to_list(number, base, acc \\ []) do
if number < base do
[number | acc]
else
to_list(div(number, base) - 1, base, [rem(number, base) | acc])
end
end
defp to_char(x), do: x + 65
end
You use it simply as BijectiveHexavigesimal.to_az_string(420). It also accepts on optional "base" arg.
I know the OP asked about Javascript but I wanted to provide an Elixir solution for posterity.
I have published these functions in npm package here:
https://www.npmjs.com/package/#gkucmierz/utils
Converting bijective numeration to number both ways (also BigInt version is included).
https://github.com/gkucmierz/utils/blob/main/src/bijective-numeration.mjs

Categories