This forum is in archive mode. You will not be able to post new content.

Author Topic: [Java Tutorial] Upper-lower-case combinations with a bitmask  (Read 2489 times)

0 Members and 1 Guest are viewing this topic.

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Hello EZ.

I made the following code, because someone requested for my archivecracker that for a given wordlist all upper-lower-case combinations could be checked too.
That means if you have the word "word", it should check:

Code: [Select]
word
Word
wOrd
WOrd
woRd
WoRd
wORd
WORd
worD
WorD
wOrD
WOrD
woRD
WoRD
wORD
WORD

How to produce these words?
If you want some exercise you can stop reading here, create your own code and come back to compare. ;)

I think a simple way doing this is using bitmasks as the title suggests.

Bitmask howto

What is a bitmask
Imagine you have the following binary number as information:
Code: [Select]
1101011
Now you want to check if a certain bit is set or not. So you specify what bit you want to check by making a bitmask:
Code: [Select]
0001000
This means the fourth bit shall be checked.

Using the bitwise operator AND you will get a result that is either 0 or has the value of your bitmask:

Code: [Select]
1101011 AND
0001000
-------------
0001000

Back to the task

We want to use binary numbers to encode whether a character in a word is upper or lowercase. 0 means lowercase, 1 means uppercase.

Examples:

Code: [Select]
wORd
0110
Word
1000

The advantage is that we just have to count in binary to get all possible combinations. For the word "word" we have four characters, so we have to count from 0 up to (2^4 - 1) and get the following numbers:

Code: [Select]
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

That means the number of words we can produce can be calculated like this:

Code: [Select]
int numberOfCases = (int) Math.pow(2, word.length());
For every n (0 <= n < numberOfCases) we need the create the corresponding word with the help of a bitmask.

Example: We have the number 0110 and want to get word that belongs to it.
We start with the first character (at index because an array starts at 0) and want to know if it is upper- or lowercase.

That means we need a bitmask that looks like this for the first character:
1000

And like this for the second:
0100

And like this for the fourth:
0010

And like this for the last:
0001

So we calculate the bitmask for index i the following way:
Code: [Select]
int bitmask = (int) Math.pow(2, (length-1) - i);
Note: pow(2, (length-1) - i) can be simplified to pow(2, i) as it doesn't really matter if we generate the combinations from back to front or vice versa

Checking the bitmask and assigning the lower- or uppercase character at index i is done like this:

Code: [Select]
if ((bitmask & n) == bitmask) {
    modified[i] = Character.toUpperCase(originalWord[i]);
} else {
    modified[i] = Character.toLowerCase(originalWord[i]);
}

The operator & is the bitwise AND like I explained above.

Example:
Our index i is 2
We calculate our bitmask: 2^2 = 4
This is 0100 in binary

We check with the actual number that encodes our upper-lower-cases:
0100 AND
0110
-----
0100

The result is in this case 4 which is the value of our bitmask.
So we have to use an uppercase letter at index 2.

The source code
Code: [Select]
public class UpperLowerCombinator {

    public static void main(String[] args) {
        String word = "word";
        printAllCases(word);
    }

    private static void printAllCases(String word) {
        UpperLowerCombinator caser = new UpperLowerCombinator();
        for (String variant : caser.getAllCases(word)) {
            System.out.println(variant);
        }
    }

    private String[] getAllCases(String word) {
        int numberOfCases = (int) Math.pow(2, word.length());
        String[] variants = new String[numberOfCases];

        for (int i = 0; i < numberOfCases; i++) {
            variants[i] = getCase(i, word.toCharArray());
        }

        return variants;
    }

    private String getCase(int n, char[] originalWord) {
        int length = originalWord.length;
        char[] modified = new char[length];
        for (int i = 0; i < length; i++) {
            int bitmask = (int) Math.pow(2, i);
            if ((bitmask & n) == bitmask) {
                modified[i] = Character.toUpperCase(originalWord[i]);
            } else {
                modified[i] = Character.toLowerCase(originalWord[i]);
            }
        }
        return new String(modified);
    }

}


If you have any questions, post below.

Happy coding.
Deque
« Last Edit: May 15, 2013, 09:58:51 AM by Deque »

 



Want to be here? Contact Ande, Factionwars or Kulverstukas on the forum or at IRC.