0

I was going through a book on Data Structures and Algorithm with JavaScript when I found this piece of codes.

I need someone to help me explain the logic behind the code here, also the logic behind the value of var i in each method.

  1. var i = (this._front + length) & (this._size - 1); //explain this in push()

  2. var i = (this._front + length - 1) & (this._size - 1); // explain this in pop()

  3. var i = (((( this._front - 1 ) & ( size - 1) ) ^ size ) - size );// explain this in unshift()

Please explain the general logic for each method, I have an issue with the use of & operator in the above statements, please why the use of & instead of %

var CircularDequeue = (()=> {
    class CircularDequeue {
        constructor() {
            // pseudo realistic 2^x value
            this._size = 1024;
            this._length = 0;
            this._front = 0;
            this._data = [];
        }

        push (item) {
            // get the length of the array
            var length = this._length;

            // calculate the end
            var i = (this._front + length) & (this._size - 1);

            // assign value to the current end of the data
            this._data[i] = item;

            // increment length for quick look up
            this._length = length + 1;

            // return new length
            return this._length;
        }

        pop () {
            // get the length of the array
            var length = this._length;

            // calculate the end
            var i = (this._front + length - 1) & (this._size - 1);

            // copy the value to return
            var ret = this._data[i];

            // remove the value from data
            this._data[i] = undefined;

            // reduce length for look up
            this._length = length - 1;

            // return value
            return ret;
       }

       shift () {
            // get the current front of queue
            var front = this._front;

            // capture return value
            var ret = this._data[front];

            // reset value in the data
            this._data[front] = undefined;

            // calculate the new front of the queue
            this._front = (front + 1) & (this._size - 1);

            // reduce the size
            this._length = this._length - 1;

            // return the value
            return ret;

        }

      unshift (item) {
            // get the size
            var size = this._size;

            // calculate the new front
            var i = (((( this._front - 1 ) & ( size - 1) ) ^ size ) -
            size );

            // add the item
            this._data[i] = item;

            // increment the length
            this._length = this._length + 1;

            // update the new front
            this._front = i;

            // return the acknowledgement of the addition of the new
            item
            return this._length;
        }
    }

    return CircularDequeue;
})();

module.exports = CircularDequeue;

I have tried to understand this logic but the use of bitwise & in calculating the values of var i instead of modulo operator(%) keeps confusing me

General Grievance
  • 4,555
  • 31
  • 31
  • 45
  • The bitwise & is very similar to modulus when all bits are 1.. eg. `1024-1`, = `1111111111`, but will fail of course if the number is not a binary No, -1. It's generally a technique for performance reasons.. So doing this with this stack you would need to make sure the sizes are binary.. `2 4 8 16 32 64 128 256 512 1024 4096....etc` – Keith Mar 23 '22 at 12:50
  • Not that's it's something your asking, there is one major flaw with this code. No overflow checking, so be careful if you use in production, as it could really create some confusing errors... :) – Keith Mar 23 '22 at 13:04
  • You are right, thank you @keith , I never thought of the similar result of & and % when all bits is 1 – Prince Chisomaga Mar 23 '22 at 19:41

1 Answers1

0

In this code something & (size - 1) is equivalent to something % size because size is a power of 2, and seeing the comment in the constructor, it is supposed to be a power of 2.

I don't see a good reason why the following has been done:

(((( this._front - 1 ) & ( size - 1) ) ^ size ) - size )

The first part ( this._front - 1 ) & ( size - 1) is always going to be a non-negative number that is less than size.

^ size will set a bit that is 0 (because the intermediate value is less than size) and then - size will clear that same bit again. So that ^ size ) - size part is a non-operation. It can be left out.

It is unclear why the author of this code preferred to work with the & operator than the %, as the latter one would also work if the size would not have been a power of two, while the & operator will only work as intended when size is a power of 2.

To see how & works, take for example that the left side is 1025, which means it is out of range. In binary 1025 is 10000000001. On the other hand we have size which is 1024. size - 1 in binary is 1111111111.

So we have this & operation:

10000000001
 1111111111
----------- &
 0000000001

So this operation effectively removes any excess bits from the left side operand, whether they come from a negative value or from a value that is not less than size.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • I wonder if the author did this when asm.js was popular, and maybe the non-operation also acted like a hint. – Keith Mar 23 '22 at 13:02
  • Thank you so much for this detailed explanation though the use of ^ size is still not clear to me. The title of the book is 'Hands-On Data Structures and Algorithms with JavaScript' by Kashyap Mukkamala, published January 2018 – Prince Chisomaga Mar 23 '22 at 19:34
  • `^ size` is a XOR operator, which *toggles* bits (look it up). Since `size` is a power of 2, there is only one 1-bit that has this effect on the other operand. – trincot Mar 23 '22 at 19:40
  • So let's say the part before `^` evaluates to (binary) 01110010110. And `size` is 1024, so in binary 10000000000. The bit of interest is that single 1 in `size`. The corresponding bit in the left operand is 0 (always!). The XOR rule is that 0^1=1, so the result is 11110010110. Then `-size` is easy: it comes down to removing that bit again, so we get back to the original 01110010110. – trincot Mar 23 '22 at 19:59
  • @trincot please can you share any link to aid me understand this properly, I have a foundation on bitwise operators but its application here is still not clear to me. I mean what inspired all this bitwise manipulations, this is my first experience with bitwise inspired logics in javaScript. – Prince Chisomaga Mar 23 '22 at 21:45
  • It is not specific to JavaScript. It works that way in C, Java, Python, ...etc. It comes down to elementary bit operations, which are like boolean algebra on bits. Just search for "how do bitwise operators work XOR" or something like that. – trincot Mar 24 '22 at 07:31