Master Coder Challenge #3 : Number systems converter

About competition

Quite recently Cybercom company announced a Master Coder challange. The competition will be divided into 10 stages, each of them involving a programming task. Despite missing two first assignments I have taken up the gauntlet and completed task #3.

The goal in this stage was to create a number system converter. Converter should be able to handle systems in the range of 2 to 36. For the working application (successfully converting numbers between systems) you would get 200 points. It was possible to get additional 20 points for introducing an exceptional solution. However, it was prohibited to use conditional statements, and conditional expressions. Thus, for every if, else or switch in your code you would be given respectively -5, -10 or -20 points. Numbers should be represented by following characters:
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

You can view/download the entire application I have created for this challenge under this address.

Change of requirements

To make it harder after the stage had already started organizers announced three updates to the initial requirements. Thus if you are interested in completing this task on your own you might want to skip sections: Update #1, Update #2 and Update #3 of this post untill you are dev completed with initial requirements. It will be more fun to see whether your primary implementation was extendable enough to cater for new requirements as well.

Update #1

Input data should be validated before processing (not implementing this would result with -150 penalty points). Alas, validation rules were not specified by organizers, following are the one I have introduced in mine application:

  • checkIfNumericSystemIsInScope(inputSystem);
  • checkIfNumericSystemIsInScope(outputSystem);
  • checkIfNumberContainsOnlySupportedCharacters(numberToConvert);
  • checkIfSeparatorIsPresentOnlyOnce(numberToConvert);
  • checkIfFractionalPartIsFourDigitsLongAtMost(numberToConvert);
  • checkIfNumberContainsCharsPermittedForInputSystem(inputSystem, numberToConvert);
  • checkIfNumberIsNotNegative(numberToConvert);

Update #2

Converter should handle number systems in the range of 2 to 62 (-100 penalty points for not impelementing this task). Numbers should be represented by following characters:
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, g, h, i, j, k, l, m, n, o, p ,q, r, s, t, u, v, w, x, y, z

Update #3

Application should now support conversion of rational numbers as well. These numbers should be represented using floating point with precision of four digits after decimal point. (-50 penalty points for not implementing this task).

No if challenge

I have identified three types of cases in my application where using if statemets could be avoided but doing so would most definiately make my code less coherent and clean. Hence, I thought maybe I should not be loosing my time on ‘inventing’ some complex logic to avoid ifs but instead I would better come up with a way to imitate their behaviour. These three cases were as follows:
1. Perform an action if a particular condition is met.
2. Return one value if a condition is met or otherwise return other value.
3. Perfom an action for every element from collection. However, if one of them satisfies a break condition, stop the whole iteration and perform a ‘close down’ action.

Perfom an action if condition is met

For this purpose I have used a Map to store two possible behaviours: what should be done if condition is met and what should be done otherwise. Later it turned out that logic of my application does not require performing any particular action on the latter case, thus it has been defaulted to NoAction implementation.

I have created a builder that would allow me to use this construction in an elegant way:

Function addNumberToResultList() was implemented as follows:

Beneath is the builder class supporting this syntax:

Return value based on a condition

Again, I have used a Map to separate values that should be returned either when a condition was satisfied or not. This time though I have provided two forms of how a developer can pass values to the map.

First one is pretty obvious and implies that a returned value is known or can be safely evaluated when an if construction is being called from the code. Therefore it requires to be given a value that should be returned if condition is met. The static method that should be used to create this construction is shown belown.

You can use it in the following way:

The second way, implies that it is only safe to evaluate the value that should be returned by if statement if condition is satisfied. For instance imagine a situation with standard java if, here exception IndexOutOfBoundsException will be never thrown as line 4 will not be executed unless the list is not empty (thanks to check in line 3).

If we use try to achieve the same behaviour using my construction:

It will throw an error if list is empty as it will evaluate expression list.get(0) before checking the condition. To avoid this behaviour a lazy evaluation mechanism was constructed:

The class handling this construction will only execute returnCalculationResult() when the condition passed the statement is satisfied. You can check the whole class under this location.

Conditional foreach

When preparing the representation of a rational number in a particular numeral system my algorithm would add extra zeros if the resultant representation had less than four digits after decimal point. Of course I needed to remove these extra zeros. Let’s image a calculated representation would be as follows: ABSDS.10S0. To remove additional zeros I first needed to reverse the string representation of fractional part of this number. In this case I would end up with following string: 0S01. Now iterating from left to right I wanted to ignore every zero I encountered, however when a character different from 0 was spotted I needed to rewrite this character and all the following characters. After reversing the resultant string again I would get the fractional part of number without trailing zeros. Following construction was created to achieve this result:

Please note only forEach and perform methods are required in this statement, and the implementation of the latter is the essence of whole construction. When perform method is executed every element in the list provided in forEach method is being used as an argument when calling a method perform from ForEachAction provided to the perform method. However, before this method can be executed, in every iteration we check whether the break condition is satisfied. If it is, programme will exit the for loop and instead execute the action defined by onBreak method. Default implementations for break condition and on break action are respectively: DoNotBreak and DoNothing. Full code for the for each support can be found under this location. Below the implementation of perform method is presented.

Bidi map

For mapping characters present in number systems to their decimal values I have used Apache Collection’s Bidi Map, which is an implementation of bidirectional map. Using this map you are not only able to retrieve a value by its key, but also by the value you can easily retrieve the key under which it is stored. Thanks to that using only one collection I was able to both easily translate the number given in the input system to its decimal value and than this decimal value to the corresponding characters from the output number system. Please visit class’ documentation page under this address for more information.

Range map

Another interesting map I have used in the project is Guava’s Range Map. It allows developer to specify a range of keys under which a particular value should be stored. In my case, I wanted to specify what kind of precision should be used in calculations depending on the value of number system, so that the calculations could be more precise when it was required and faster whenever it was possible. To specify the key one needs to use Range class which comes with a number of builder methods providing support for diverse ranges (please find below a list of supported ranges). Please visit class’ documentation page under this address for more information.

Spock test

I was not able to resist and even with limited time I had to accomplish this task, I have created a number of tests in Spock – a Groovy framework for unit tests. As I am going to write a number of separate posts on this topic I will only leave here a small teaser to show you how neatly tests can look like.

External links

Please join discussion