Bitwise Operators Part 2

Why? Well, again, because it’ll make your code simpler than if you don’t use it! The internet is full of complex examples of bitwise operators which are, no doubt, difficult to see how we can use them in a real-life situation. This isn’t what you’ll find here! I propose to write a simple yet effective code that you’ll be able to adapt to some cases!

Some theory

They are portable! On Javascript or C#, they can be coded the same way!

As we quoted in Part 1 (from MPN page), Bitwise operators treat their operands as a sequence of 32 bits (zeroes and ones), rather than as decimal, hexadecimal, or octal numbers, and this is the key of our POC!

We’ll rely on the left shift operator (<<), the bitwise AND (&) and on the bitwise OR (|).

The Left Shift Operator

Every time we create a variable to give to a code a meaningful name, we use something like this:

const draft = 1;
const final = 2;
const submitted = 3;
const pendingApproval = 4;
const approved = 5;
const rejected = 6;
const deleted = 7;
const pendingDeletion = 8;

Now think the business unit of your company, defined some actions for a document flux, and it’ll be:

  • Can be deleted by the user;
  • Can be submitted;
  • Can be approved/rejected;
  • Can be permanently deleted by the company staff.

And now, your Project Manager tells you that the rules, based on the status, are very flexible and they could change! You’ll be on hell, and survive under pressure when the rules changes!

For the moment, they are:

  • Can be deleted by the user when the document is draft or final;
  • Can be submitted by approval when is final;
  • Can be approved/rejected when is submitted;
  • Can request the permanent deletion when is approved or rejected
  • Can be permanently deleted when is pendingDeletion;

Maybe your code will be something like this:

function performAction(action) {
   if(!action) throw new Error (... your error ...);
   
   if(action === 'delete' && (docStatus === draft || docStatus === final) {
      ... your code ...
   } else if (action === 'submit' && docStatus === final) {
      ... your code ...
   } else if (action === 'approve' && docStatus === pendingApproval) {
      ... your code ...
   } else if (action === 'requestForDeletion' && (docStatus === approved || docStatus === rejected)) {
      ... your code ...
   } else if (action === 'permanentlyDelete' && docStatus === pendingDeletion) {
      ... your code ...
   } else {
      ... your not allowed message ...
   }
}

Simple code, no big deal! You can change it quickly, but, on Friday, you finishing an important sprint, 4:30 p.m., you receive an URGENT demand:

The users are complaining about the need to set a document as a ‘final’ before submitting! They want to submit directly from the draft! You need change only a single line in the ‘if’, but, as the rules are flexible and they change every time the business unit detects they aren’t suitable for the business or the users are abandoning the platform because of them.

This is HELL! Stop the urgent sprint, refocus your mind to the rules, fix, test, submit for production, fill documentation, send emails, etc., etc., etc…! And finally, refocus to the sprint, and, life goes on.

You can start your ‘redemption’ changing the status ID with one suitable for the bitwise operators. Here the left shift is very useful!

Open your project, start the Live Server, and let’s code something on index.js!

const a = 1;
const b = 2;
const c = 4;
console.log(`binary representation of a=${a}`, a.toString(2));
console.log(`binary representation of b=${b}`, b.toString(2));
console.log(`binary representation of c=${c}`, c.toString(2));

This is what you’ll get:

Binary representation

You can clearly see the bit shifting from the right! It’ll be easier if we put the leading zeroes on the left:

1 = 00000001
2 = 00000010
4 = 00000100

So, to generate the IDs automatically, you can do this:

// array will receive the numbers
const ids = [];

// will return the new ID using
// the left shift operator based on
// the ids' array length.
function nextId() {
  const i = 1 << ids.length;
  return i;
}

// will generate 5 ids and push it on the ids array
for (let index = 0; index < 5; index++) {
  const newId = nextId();
  ids.push(newId);
}

// will log the result
ids.forEach(id => {
  console.log(`binary representation of ${id}`, id.toString(2));
});

And, the log will be this:

IDs generated automatically

Or, again, to be more visual:

1  = 00000001
2  = 00000010
4  = 00000100
8  = 00001000
16 = 00010000

The Left Shift is the key for our POC!

Left Shift: this operator shifts the first operand the specified number of bits to the left. Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3C%3C_(Left_shift)

PRO TIP: If in a job interview, they ask you if is possible to multiply or divide a number not using the Arithmetic Operators, your answer should be: Yes, using Bitwise Operators!

To multiply by 2, simply type: <number> << 1:

Multiply by 2 without Arithmetic Operators

To divide by 2, simply type the inverse: <number> >> 1:

Dividing by 2 without Arithmetic Operators

But, this is very important: the operator is not the “Right Shift”, it’s the Sign-propagating right shift!

Sign-propagating right shift: this operator shifts the first operand the specified number of bits to the right. Excess bits shifted off to the right are discarded. Copies of the leftmost bit are shifted in from the left. Since the new leftmost bit has the same value as the previous leftmost bit, the sign bit (the leftmost bit) does not change. Hence the name “sign-propagating”.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#%3E%3E_(Sign-propagating_right_shift)

Back to our POC…

The Bitwise OR Operator

We’ve already “solved” the problem of the statuses IDs (it’ll become clear in a minute, I promise!), but, how can we deal with the changing conditions?

Using the Bitwise OR Operator!

Bitwise OR: performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#(Bitwise_OR)

So… In our example, the statuses code are:

const draft = 1;
const final = 2;
const submitted = 4;
const pendingApproval = 8;
const approved = 16;
const rejected = 32;
const deleted = 64;
const pendingDeletion = 128;

And, reviewing the rules:

  • Can be deleted by the user when the document is draft or final;
  • Can be submitted by approval when is final;
  • Can be approved/rejected when is submitted;
  • Can request the permanent deletion when is approved or rejected
  • Can be permanently deleted when is pendingDeletion;

Let’s back to our index.js! Delete all the previous code and code along with me.

We can create rules this way:

const draft = 1;
const final = 2;
const submitted = 4;
const pendingApproval = 8;
const approved = 16;
const rejected = 32;
const deleted = 64;
const pendingDeletion = 128;

const canDelete = final | draft;
console.log(
  `The rule canDelete has the value of ${canDelete} or ${canDelete.toString(2)}`
);

const canSubmit = final;
console.log(
  `The rule canSubmit has the value of ${canSubmit} or ${canSubmit.toString(2)}`
);

const canApprove = submitted;
console.log(
  `The rule canApprove has the value of ${canApprove} or ${canApprove.toString(
    2
  )}`
);

const canRequestDeletion = approved | rejected;
console.log(
  `The rule canRequestDeletion has the value of ${canRequestDeletion} or ${canRequestDeletion.toString(
    2
  )}`
);

const canPermanentlyDelete = pendingDeletion;
console.log(
  `The rule canPermanentlyDelete has the value of ${canPermanentlyDelete} or ${canPermanentlyDelete.toString(
    2
  )}`
);

Saving the file you’ll see this:

Creating Rules using the Bitwise OR Operator

Again, to see it more clearly:

canrequestDeletion
approved = 00010000
rejected = 00100000
--------------------
The rule = 00110000 OR 48!

Hope it is becoming more evident to you WHY we use the left shift operator to create the IDs!

Now, with the Bitwise OR, we can create and persist rules.

The path for your complete redemption must have the next operator: The Bitwise AND Operator!

The Bitwise AND Operator

And finally, this is how we test if we can change the test inside the ‘if and elses’: using the Bitwise AND Operator!

Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#(Bitwise_AND)

Note the truth table of the Bitwise AND Operator: only 1 if both, A and B are 1, so, when you have a pendingApproval (16) status and try to delete (which rule canDelete is 3) the result is 0 (false). If you test with draft (1) or final (2), the result will be 1 or 2.

Bitwise Operator AND on JavaScript

Want check on C#? Check on .Net Fiddle https://dotnetfiddle.net/NLijms

Bitwise AND Operator on C#

To visualise the operation:

pendingApproval = 16 or 00010000
      canDelete =  3 or 00000011
---------------------------------
            a&b =       00000000

           draft = 1 or 00000001
       canDelete = 3 or 00000011
---------------------------------
                  a&b = 00000001

           final = 2 or 00000010
       canDelete = 3 or 00000011
---------------------------------
                  a&b = 00000010

And the complete code!

const draft = 1;
const final = 2;
const submitted = 4;
const pendingApproval = 8;
const approved = 16;
const rejected = 32;
const deleted = 64;
const pendingDeletion = 128;

const canDelete = final | draft;
const canSubmit = final;
const canApprove = submitted;
const canRequestDeletion = approved | rejected;
const canPermanentlyDelete = pendingDeletion;

const statuses = [
  draft,
  final,
  submitted,
  pendingApproval,
  approved,
  rejected,
  deleted,
  pendingDeletion
];

function getStatusName(status) {
  switch (status) {
    case 1:
      return 'draft';
    case 2:
      return 'final';
    case 4:
      return 'submitted';
    case 8:
      return 'pendingApproval';
    case 16:
      return 'approved';
    case 32:
      return 'rejected';
    case 64:
      return 'deleted';
    case 128:
      return 'pendingDeletion';
  }
}

function testStatus(currStatus) {
  if (currStatus & canDelete) {
    console.log(
      `The status ${getStatusName(currStatus)} - ${currStatus} CAN delete`
    );
  } else {
    console.error(
      `The status ${getStatusName(currStatus)} - ${currStatus} CANNOT delete`
    );
  }

  if (currStatus & canSubmit) {
    console.log(
      `The status ${getStatusName(currStatus)} - ${currStatus} CAN submit`
    );
  } else {
    console.error(
      `The status ${getStatusName(currStatus)} - ${currStatus} CANNOT submit`
    );
  }

  if (currStatus & canApprove) {
    console.log(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CAN approve/reject`
    );
  } else {
    console.error(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CANNOT approve/reject`
    );
  }

  if (currStatus & canRequestDeletion) {
    console.log(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CAN request permanent deletion`
    );
  } else {
    console.error(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CANNOT request permanent deletion`
    );
  }

  if (currStatus & canPermanentlyDelete) {
    console.log(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CAN permanently delete`
    );
  } else {
    console.error(
      `The status ${getStatusName(
        currStatus
      )} - ${currStatus} CANNOT permanently delete`
    );
  }
}

statuses.forEach(status => {
  testStatus(status);
});

Truthy or Falsy on JavaScript

To finalise, a little explanation about ‘truthy’ and ‘falsy’ on JS, or, why the ‘ifs’ worked even if I didn’t convert to a boolean value.

In JavaScript, a truthy value is a value that is considered true when encountered in a Boolean context. All values are truthy unless they are defined as falsy (i.e., except for false, 0, “”, null, undefined, and NaN).

JavaScript uses type coercion in Boolean contexts.

https://developer.mozilla.org/en-US/docs/Glossary/Truthy

A falsy value is a value that is considered false when encountered in a Boolean context.

JavaScript uses Type Conversion to coerce any value to a Boolean in contexts that require it, such as conditionals and loops.

https://developer.mozilla.org/en-US/docs/Glossary/Falsy

Now, we have all theory and, in the next post, we’ll cook it all together!

Stay tuned to follow this POC and Happy Coding!

Advertisements

One thought on “Bitwise Operators Part 2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s