Represent features like [Read, Write, Modify, …] and their combinations into a single field.
Sometimes when building your Software system and while designing a set of modules, you find that every module of these modules could -or couldn’t- support some features of a predefined list of features. At the end, there should be some field or property which maps each module to its supported features.
For example, let’s say that we are working on a game where every character in the game would -or wouldn’t- be able to:
▶ Walk
▶ Run
▶ Speak
▶ Scream
▶ Fight
▶ …
A character might support the features Walk, Run, and Speak only. Another character might support the features Walk, Scream, and Fight only. And so on,…
Now, let’s say that you want to have only one field or property on the Character class to represent these features so that at any time using the value of this property you can know all the singular features supported by a certain Character instance.
How would you do this? Someone might say:
Use Flagged Enumerations, it is easy.
Yes, this is right, but, do you know the concept that is already used in Flagged Enumerations?
This is what this article is about.
Agenda
Flagged Enumerations are supported by almost all -if not all- Object Oriented Programming (OOP) languages. They are used for the cases we described in the Problem Definition section above.
However, if you look closely you would find that Flagged Enumerations is just an implementation of an important concept based on Binary Tables and the Bitwise Operations that could be applied.
That’s why in this article we are going to explore some basics of Binary Tables and the Bitwise Operations.
This is our agenda:
Binary Tables.
Bitwise Operations:
Representing Features With Bits.
Flagged Enumerations.
If you think you already have experience with one or more of these topics, feel free to skip to the next one. You can search the article by the headlines included into the agenda above.
Binary Tables
For simplicity, let’s say that Binary is a way of representing numbers into a series of symbols where each symbol could be presented by “0” or “1”. These symbols are given the name Bits. In other words, each Decimal number could be presented into a series of “0” and “1” written in a certain order.
So, if we assume that we want to format a Decimal number using only one Bit knowing that the Bit could be “0” or “1”.
Then we should know that we have these possibilities only:
▶ 0
▶ 1
In this case, we can say that we can represent only two Decimal numbers which are 0 and 1.
But, what if I told you that we would now add another Bit so that the total number of Bits is two instead of one.
Then we should know that we have these possibilities only:
▶ Bit 1 = 0, Bit 2 = 0
▶ Bit 1 = 1, Bit 2 = 0
▶ Bit 1 = 0, Bit 2 = 1
▶ Bit 1 = 1, Bit 2 = 1
In this case, we can say that we can represent only four Decimal numbers which are 0, 1, 2, and 3.
Actually, let’s stop here for a while and try to understand what actually happened.
What we can notice here:
We know that each Bit could be one of two values; “0” or “1”.
And this applies on all Bits including the new one we are going to add.
This means that if we want to know the number of combinations we would get using two Bits, we should multiply { the number of the possible values for the first Bit } and { the number of the possible values for the second Bit }.
Therefore the total number of combinations for two Bits should be
Following the same concept, if we add a third Bit, the total number of combinations for three Bits should be
Then generalizing this idea, we can say that the total number of combinations for n Bits should be
The final thing to note here is that when we say that we represent 8 Decimal numbers, Zero is also included.
Therefore, for three Bits, we can represent the Decimal numbers 0, 1, 2, 3, 4, 5, 6, and 7
Having that said, let’s see how to put this into the famous Binary Table format.
Let’s start with the 1 Bit Binary Table:
The main rule here is that we start with “0”, not “1”.
Now, when adding a second Bit to get a 2 Bit Binary Table, we add it as follows:
As we can see, to add a new Bit, we do the following:
Copy the previous Binary Table.
Extend the rows by copying the already existing ones and pasting them at the bottom (as noted by the red arrow).
Add the new Bit column to the Left.
Fill all the cells corresponding to the rows on the previous table (highlighted in green) with “0”.
Fill the rest of the new column cells with “1”.
Now, you have a 2 Bit Binary Table.
Again, when adding a third Bit to get a 3 Bit Binary Table, we add it as follows:
As we can see, to add a new Bit, we do the following:
Copy the previous Binary Table.
Extend the rows by copying the already existing ones and pasting them at the bottom (as noted by the red arrow).
Add the new Bit column to the Left.
Fill all the cells corresponding to the rows on the previous table (highlighted in green) with “0”.
Fill the rest of the new column cells with “1”.
Now, you have a 3 Bit Binary Table.
Now you might be asking about the relation between every bits sequence and the Decimal number. Is there a relation?
The answer is yes, there is a relation.
Let’s use the 3 Bit Binary Table as an example.
To calculate the Decimal number corresponding to every bits sequence, we calculate the sum of every bit multiplied by (2 raised to the power of the bit zero-order). Not clear, right?
Check this image and you would understand it:
Or even simpler like this:
Now you know how it works.
This is not all. We now know how to convert a Binary number into the corresponding Decimal number. But, what about converting a Decimal number to the corresponding Binary number?
There is a simple way to do it. Let me show you.
Let’s say that you want to convert the Decimal number “7” to its Binary form.
This is how we do it:
Start with 7.
Divide it by 2.
We get 3 and a remainder of 1.
Divide 3 by 2.
We get 1 and a remainder of 1.
Divide 1 by 2.
We get 0 and a remainder of 1.
Divide 0 by 2.
We get 0 and a remainder of 0.
This is where we stop.
The answer is 0111.
Another example. Let’s say that you want to convert the Decimal number “35” to its Binary form.
Following the same process, we will end up with this:
Now you might be a little bit confused and not sure where this way of conversion is coming from. Let me prove it for you.
As we explained before, to convert a Binary number (BitN, …Bit2, Bit1) to its Decimal number (X), we do the following:
X = (2 ^ 0) Bit1 + (2 ^ 1) Bit2 +… (2 ^ (N-1)) BitN
X = Bit1 + (2 ^ 1) Bit2 +… (2 ^ (N-1)) BitN
X = 2 (Bit1/2 + Bit2 +… (2 ^ (N-2)) BitN)
X / 2 = Bit1/2 + Bit2 +… (2 ^ (N-2)) BitN
X / 2 = Bit1/2 + 2 (Bit2/2 +… (2 ^ (N-3)) BitN)
Now, when we divided 7 by 2, this is what actually happened:
7 = 1 + 2 * (3)
7 / 2 = (1 / 2) + 3
Following the same rule, dividing the Decimal number X by 2 should mean:
X / 2 = Remainder / 2 + Result
Now, comparing these two bold equations, we can see that:
Remainder of first division on 2 should map to Bit1.
Result of first division on 2 should map to the rest of bits.
So, repeating the same operation on the Result of first division on 2, should get Bit2, and so on,…
I hope it is now clear.
Bitwise Operations
These are some operations that can be applied on the level of Bits.
According to Wikipedia:
In computer programming, a bitwise operation operates on a bit string, a bit array or a binary numeral (considered as a bit string) at the level of its individual bits. It is a fast and simple action, basic to the higher-level arithmetic operations and directly supported by the processor. Most bitwise operations are presented as two-operand instructions where the result replaces one of the input operands.
On simple low-cost processors, typically, bitwise operations are substantially faster than division, several times faster than multiplication, and sometimes significantly faster than addition. While modern processors usually perform addition and multiplication just as fast as bitwise operations due to their longer instruction pipelines and other architectural design choices, bitwise operations do commonly use less power because of the reduced use of resources.
On the next sections, we would explain some of the Bitwise Operators on which Flagged Enumerations depend.
These Bitwise Operators are:
AND Bitwise Operator.
OR Bitwise Operator.
XOR Bitwise Operator.
NOT Bitwise Operator.
AND Bitwise Operator
Is true (“1”) only when both bits are “1”. Otherwise, it would return False (“0”).
If you have an input bit, whenever you AND it with “0”, you are sure that the result would be “0”. Also, if you AND it with “1” and the result is “1”, you are now sure that the input was “1”. This is useful when you need to check if a certain bit in a Bit Sequence is “1”.
For example, you are given the sequence 011010 as an input and you want to know if the fourth bit (from the right) is “1”.
This is what you can do:
You AND this input sequence with the masking sequence 001000 where all bits are “0” and only the fourth bit (from the right) is “1”. If the result is identical to the mask sequence, then the fourth bit is “1”. Otherwise, it is “0”.
Using the same example and following the same methodology, let’s say that we want to check if the third bit (from the right) is “1”.
See, the result is not identical to the mask.
Also, if you have an input bit, whenever you AND it with “0”, you are sure that the result would be “0”. Also, if you AND it with “1” and the result is “1”, you are now sure that the input was “1”. This is useful when you need to set a certain bit in a Bit Sequence to “0”.
For example, you are given the sequence 011010 as an input and you want to set the fourth bit (from the right) to “0” without affecting the other bits.
This is what you can do:
You AND this input sequence with the masking sequence 110111 where all bits are “1” and only the fourth bit (from the right) is “0”. Doing this, you end up with the same exact input sequence except that the fourth bit (from the right) is now set to “0”.
Even if the bit was already set to “0”, doing this would not affect the input as follows:
As you can see, the result is exactly identical to the input.
OR Bitwise Operator
Is always true (“1”) except when both bits are “0”. This is why it could be used to check for
If you have an input bit, whenever you OR it with “0”, you are sure that the result would be the same as the input. Also, if you OR it with “1”, you are sure that the result would be “1” whatever the input is. This is useful when you need to set a certain bit to “1” without changing the other bits.
For example, you are given the sequence 011010 as an input and you want to set the third bit (from the right) to “1” without changing the other bits.
This is what you can do:
You OR this input sequence with the masking sequence 001000 where all bits are “0” and only the third bit (from the right) is “1”. Doing this, you end up with the same exact input sequence except that the third bit (from the right) is now set to “1”.
Even if the bit was already set to “1”, doing this would not affect the input as follows:
As you can see, the result is exactly identical to the input.
XOR Bitwise Operator
Is true (“1”) when both bits are not the same. Otherwise, it would return False (“0”).
If you have an input bit, whenever you XOR it with “1”, you are sure that the result would be the opposite of the input. This is useful when you need to toggle (set if is already unset, unset if is already set) a certain bit without changing the other bits.
For example, you are given the sequence 011010 as an input and you want to toggle the fourth bit (from the right) without changing the other bits.
This is what you can do:
You OR this input sequence with the masking sequence 001000 where all bits are “0” and only the third bit (from the right) is “1”. Doing this, you end up with the same exact input sequence except that the fourth bit (from the right) is now set to “0” instead of “1”.
Following the same way, if you want to toggle the third bit (from the right) without changing the other bits.
This is what you can do:
You OR this input sequence with the masking sequence 000100 where all bits are “0” and only the third bit (from the right) is “1”. Doing this, you end up with the same exact input sequence except that the third bit (from the right) is now set to “1” instead of “0”.
NOT Bitwise Operator
Inverts the Bit so that if it is “0” the result would be “1” and vice versa.
Now What?
Now, you should be able to understand Binary Tables and Bitwise Operations, but still, you might be asking:
How could this help with representing features into a single field or understanding Flagged Enumerations?
I would answer your question in the next sections but I want you to keep in mind the following important notes:
We can use the AND Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to check if a certain bit is “1”.
We can use the AND Bitwise Operator with a mask (mainly all “1” except the bit we are interested into) to set a certain bit to “0”.
We can use the OR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to set a certain bit to “1” without affecting the other bits.
We can use the XOR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to toggle (set if is already unset, unset if is already set) a certain bit without affecting the other bits.
Having that said, let’s jump to the next sections.
Representing Features With Bits
If you have read the previous sections of this article, you would have noticed that we proved that AND and OR Bitwise Operators are useful for checking and setting bits.
As a quick summary:
We can use the AND Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to check if a certain bit is “1”.
We can use the AND Bitwise Operator with a mask (mainly all “1” except the bit we are interested into) to set a certain bit to “0”.
We can use the OR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to set a certain bit to “1” without affecting the other bits.
We can use the XOR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to toggle (set if is already unset, unset if is already set) a certain bit without affecting the other bits.
Now, let’s go back to our original problem. We want to be able to represent the availability or absence of certain features in a single field.
Let’s say that we have an object and this object could support any combination of he following features:
Feature 1
Feature 2
Feature 3
In other words, the object could support Feature 1 only, or Feature 1 and Feature 2, or Feature 1 and Feature 3, or …you get the point.
Does this somehow remind you of something? Could it be like this:
As you can see, we generated all the possible combinations of the features in the form of a Binary Table. When we put “0” in a cell, this means that the feature is not available. On the other hand, when we put “1” in a cell, this means that the feature is available.
This comes with a huge benefit as now we can represent any combination of features availability with the corresponding Decimal number, in other words, a single field of type integer.
Now you might ask:
When I have a certain value for this integer field, how can I know what features are available?
The answer is simply by using the AND Bitwise Operator. You remember when we said that it could be used to check if a certain bit is “1”? The only thing we need now is to choose the right mask sequence. But, how to get this mask?
As we said before, the mask should be all “0” except for the bit we are interested into.
Therefore, applying the same concept on our example, if we are checking for the Feature 1, then the mask should be the decimal/integer number corresponding to 001 which is equal to 1. So, if the result of {integer value} AND {1} equals 1, then Feature 1 is available, otherwise, it is absent.
The same way, if we are checking for the Feature 2, then the mask should be the decimal/integer number corresponding to 010 which is equal to 2. So, if the result of {integer value} AND {2} equals 2, then Feature 2 is available, otherwise, it is absent.
Furthermore, if we are checking for the Feature 3, then the mask should be the decimal/integer number corresponding to 100 which is equal to 4. So, if the result of {integer value} AND {4} equals 4, then Feature 3 is available, otherwise, it is absent.
Great, so now we know how to check if a certain feature is available or not.
Is this all? certainly No.
Now you might ask:
If I want to set the available features for my object to Feature 1 and Feature 3 for example, how can I do it?
The answer is simply by using the OR Bitwise Operator. You remember when we said that it could be used to set a certain bit to “1”? The only thing we need now is to choose the right mask sequence. But, how to get this mask?
As we said before, the mask should be all “0” except for the bit we are interested into.
Therefore, applying the same concept on our example, if we want to set the availability of Feature 1 to true without affecting the other features, then the mask should be the decimal/integer number corresponding to 001 which is equal to 1. So, all what we need to do is {integer value} OR {1}.
The same way, if we want to set the availability of Feature 2 to true without affecting the other features, then the mask should be the decimal/integer number corresponding to 010 which is equal to 2. So, all what we need to do is {integer value} OR {2}.
Furthermore, if we want to set the availability of Feature 3 to true without affecting the other features, then the mask should be the decimal/integer number corresponding to 100 which is equal to 4. So, all what we need to do is {integer value} OR {4}.
Now you might ask:
If I want to unset the available features for my object by removing Feature 1 and Feature 3 for example, how can I do it?
The answer is simply by using the AND Bitwise Operator. You remember when we said that it could be used to set a certain bit to “0”? The only thing we need now is to choose the right mask sequence. But, how to get this mask?
As we said before, the mask should be all “1” except for the bit we are interested into.
Therefore, applying the same concept on our example, if we want to set the availability of Feature 1 to false without affecting the other features, then the mask should be the decimal/integer number corresponding to 110 which is equal to 6. So, all what we need to do is {integer value} AND {6}.
The same way, if we want to set the availability of Feature 2 to false without affecting the other features, then the mask should be the decimal/integer number corresponding to 101 which is equal to 5. So, all what we need to do is {integer value} AND {5}.
Furthermore, if we want to set the availability of Feature 3 to false without affecting the other features, then the mask should be the decimal/integer number corresponding to 011 which is equal to 3. So, all what we need to do is {integer value} AND {3}.
Now you might ask:
If I want to toggle the availability of features for my object, how can I do it?
The answer is simply by using the XOR Bitwise Operator. You remember when we said that it could be used to toggle a certain bit? The only thing we need now is to choose the right mask sequence. But, how to get this mask?
As we said before, the mask should be all “0” except for the bit we are interested into.
Therefore, applying the same concept on our example, if we want to toggle the availability of Feature 1 without affecting the other features, then the mask should be the decimal/integer number corresponding to 001 which is equal to 1. So, all what we need to do is {integer value} XOR {1}.
The same way, if we want to toggle the availability of Feature 2 without affecting the other features, then the mask should be the decimal/integer number corresponding to 010 which is equal to 2. So, all what we need to do is {integer value} XOR {2}.
Furthermore, if we want to toggle the availability of Feature 3 without affecting the other features, then the mask should be the decimal/integer number corresponding to 100 which is equal to 4. So, all what we need to do is {integer value} XOR {4}.
Now, after having all your questions answered, did you notice anything about the masks we used for all the operations above?
Let me help you visualize it:
Is it obvious now?
Let me tell you this, the masks we used are actually the rows corresponding to the decimal/integer values 1, 2, and 4.
You might say:
When we used the AND operator to unset the features, we used different masks.
Yes, but actually those masks could be get by using the NOT Bitwise Operator on the masks highlighted into the image.
This means that, we can save these 3 masks in integer variables then we can use them to do all our check, set, and unset operations. Whenever we need to get the other three masks, we just apply NOT to these saved ones.
If you need to understand more about this, just continue reading to the next sections on this article.
Flagged Enumerations
As we said before, almost all -if not all- Object Oriented Programming (OOP) languages support Flagged Enumerations.
Flagged Enumerations are actually using the same concepts we explained on the previous sections of this article to handle all the operations we might need to represent the availability or absence on some features into a single field.
For example, this is how to define a flagged enumeration in .NET C#
As you can see, it is an enum decorated with a Flags attribute. Every entry in this enum represents a mask and that’s why the enumeration entries are assigned values in the format of powers of 2.
Now, using this enumeration, you can do the following:
What we should notice here:
We used the AND (&) operator to check if a certain feature is set.
We used the AND (&) operator in combination with the NOT (~) operator to unset a certain feature.
We used the OR (|) operator to set a certain feature.
We used the XOR (^) operator to toggle (set if is already unset, unset if is already set) a certain feature.
Running this we would get this result:
Feature1, Feature3
True
True
False
Feature1
Feature1, Feature2
Feature1
Feature1, Feature2
Worth to mention here is that if in the future a new feature is added, you can easily extend your solution by adding the new feature at the bottom of the Features enumeration entries.
This way you make sure that the already persisted (may be in a database or something similar) values are still valid and not affected by the added feature.
Summary
In this article we explained the following topics:
Binary Tables.
Bitwise Operations (AND, OR, XOR, and NOT).
Representing Features With Bits.
Flagged Enumerations.
We also explained that:
We can use the AND Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to check if a certain bit is “1”.
We can use the AND Bitwise Operator with a mask (mainly all “1” except the bit we are interested into) to set a certain bit to “0”.
We can use the OR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to set a certain bit to “1” without affecting the other bits.
We can use the XOR Bitwise Operator with a mask (mainly all “0” except the bit we are interested into) to toggle (set if is already unset, unset if is already set) a certain bit without affecting the other bits.
Now, you should have enough knowledge to start using Flagged Enumerations in your Object Oriented Programming (OOP) language.
That’s it, hope you found reading this article as interesting as I found writing it.
Comments