Infinibase

Release Date: 2012

Language Used:

Objective-C

Software Used:

Xcode

Platforms:

iOS

About:

Infinibase was my second iOS app, one that revolved around converting numbers between bases. It started as a simple app to convert between the popular bases (2, 8, 10 and 16), but was later upgraded to handle bases 2 through 36. Inspiration for this app comes from an intro to programming class, where an assignment tasked us with coding a command line binary to decimal converter.

This app uses custom drawing code to generate a keyboard tailored to the currently selected base system. This is to prevent inappropriate input as well as to enhance the user experience.

Infinibase's name comes from the fact that it is capable of converting theoretically limitless numbers. This is thanks to its usage of the GMP Multi-Precision Arithmetic Library, which can create numbers whose precision is only limited by the memory of the device the app is running on.

Code Samples

Anything to decimal conversion

The first part of the base conversion is to convert whatever the input is to base-10. Base-10 can be easily converted to any other base, so it is the best place to start.

// converts any base to decimal
// start with the first number on the right of the input
// multiply that by the base of the number to the power of the numbers slot (first number is slot 0)
// repeat for each number in the input, add the results together
// example: 2132 base-4 to base-10
// (2 * 4^3) + (1 * 4^2) + (3 * 4^1) + (2 * 4^0)
// 128 + 16 + 12 + 2 = 158
// 2132 base-4 = 158 base-10
- (NSString *)convertNonDecimalNumberToDecimal:(NSString *)input fromBase:(int)oldBase {
NSString *inputString = input; // copy the input into input string, so we can remove the first character each loop
NSString *finalString = @""; // this will be the string version of finalNumber
NSString *currentNumber; // current digit that the loop will be working on
mpz_t currentPower; // current power we use on the current number - starts with 0
mpz_t finalNumber; // final number - incremented each loop
mpz_init(currentPower);
mpz_init(finalNumber);
// we use 0 and 1 a lot, no need to keep creating them in the loop
mpz_t zero;
mpz_t one;
mpz_init(zero);
mpz_init(one);
mpz_set_si(zero, 0);
mpz_set_si(one, 1);
mpz_t currentNumberInt;
mpz_t x;
mpz_init(currentNumberInt);
mpz_init(x);
// the base we are converting from
mpz_t oldBaseGMP;
mpz_init_set_si(oldBaseGMP, oldBase);
mpz_t oldBaseRootCurrentPower;
mpz_init(oldBaseRootCurrentPower);
// run the loop while inputString still has digits left
do {
// get the first number on the right of the input string (2 (string))
currentNumber = [inputString substringFromIndex:[inputString length]-1];
// assign the current number to currentNumberInt (2 (number))
mpz_set_str(currentNumberInt, [currentNumber UTF8String], oldBase);
// set oldBaseRootCurrentPower to oldBaseGMP ^ currentPower (4 ^ 0)
[self GMPPowWithResult:oldBaseRootCurrentPower withBase:oldBaseGMP power:currentPower];
// multiply the current digit by the result of oldBaseRootCurrentPower (2 * 4 ^ 0)
mpz_mul(x, currentNumberInt, oldBaseRootCurrentPower);
// set finalNumber to finalNumber + x
mpz_add(finalNumber, finalNumber, x);
// set currentPower to currentPower + 1
mpz_add(currentPower, currentPower, one);
// reset oldBaseRootCurrentPower to 0
mpz_set_si(oldBaseRootCurrentPower, 0);
// remove the first digit from inputString
inputString = [inputString substringToIndex:[inputString length]-1];
} while ([inputString length] != 0);
// get a base-10 version of the fianlNumber as a char array, convert that to an NSString
char *finalChar = mpz_get_str(NULL, 10, finalNumber);
finalString = [NSString stringWithFormat:@"%s", finalChar];
// memory cleanup
mpz_clears(currentPower, finalNumber, zero, one, currentNumberInt, x, oldBaseGMP, oldBaseRootCurrentPower, NULL);
return finalString;
}

Decimal to anything conversion

The second part of the base conversion is to convert the number in base-10 form to bases 2 through 36 (excluding base-10).

// converts deciaml to any base
// first find the largest number to the power of the input base that will fit in the new number
// then start dividing in a similar fashion as any number to decimal: start with the largest base, decrement each time
// append the result to the end of the final number
// use the remainder as the divisor for the next run
// example: 223 base-10 to base-5
// largest base that will fit in 223: 3 (5^3 = 125, 5^4 = 625 which is too big)
// 223 / 5^3 = 1, remainder 98
// 98 / 5^2 = 3, remainder 23
// 23 / 5^1 = 4, remainder 3
// 3 / 5^0 = 3, remainder 0
// 223 base-10 = 1343 base-5
- (NSString *)convertNumberFromDecimal:(NSString *)input toBase:(int)newBase {
// we use 0 and 1 a lot, no need to keep creating them in the loop
mpz_t zero;
mpz_t one;
mpz_init(zero);
mpz_init(one);
mpz_set_si(zero, 0);
mpz_set_si(one, 1);
// used in the loop
mpz_t inputNumber;
mpz_t baseRootCurrentPower;
mpz_t divided;
mpz_t currentPower;
NSMutableString *finalString = [NSMutableString stringWithString:@""];
BOOL isAtCorrectPower = FALSE; // used for determining our initial power
// init some vars
mpz_inits(inputNumber, baseRootCurrentPower, divided, currentPower, NULL);
// set inputNumber to a number version of the input string, in base 10
mpz_set_str(inputNumber, [input UTF8String], 10);
// set base equal to newBase
mpz_t base;
mpz_init(base);
mpz_set_si(base, newBase);
// used in the loop, declaring once here
mpz_t inputNumberMinusBaseRootCurrentPower;
mpz_init(inputNumberMinusBaseRootCurrentPower);
// if the input is 0, no need to do any big conversions
if ([input isEqualToString:@"0"]) {
finalString = [NSMutableString stringWithString:@"0"];
} else {
// we need to find the power of the new base that will still fit inside the input number
do {
// store base^currentPower in baseRootCurrentPower
[self GMPPowWithResult:baseRootCurrentPower withBase:base power:currentPower];
// set inputNumberMinuxBaseRootCurrentPower to 0
mpz_set_si(inputNumberMinusBaseRootCurrentPower, 0);
// first, set inputNumberMinusBaseRootCurrentPower to inputNumber, then do the actual subtraction
mpz_set(inputNumberMinusBaseRootCurrentPower, inputNumber);
mpz_sub(inputNumberMinusBaseRootCurrentPower, inputNumberMinusBaseRootCurrentPower, baseRootCurrentPower);
// compares inputNumberMinusBaseRootCurrentPower and 0.
// if it is greater than 0, a positive number is returned
// if it is equal to 0, 0 is returned
// if it is less than 0, a negative number is returned
// so, if inputNumberMinusBaseRootCurrentPower >= 0, increment currentPower by 1
// otherwise we've found a power that will result in a number too great for the input number,
// so decrement currentPower by 1 and set the bool to true to get out of the loop
if (mpz_cmp(inputNumberMinusBaseRootCurrentPower, zero) >= 0) {
mpz_add(currentPower, currentPower, one);
} else {
mpz_sub(currentPower, currentPower, one);
isAtCorrectPower = TRUE;
}
// reset baseRootCurrentPower back to 0
mpz_set_si(baseRootCurrentPower, 0);
} while (isAtCorrectPower == FALSE);
// clamp currentPower to 0
if (mpz_cmp(currentPower, zero) < 0) {
mpz_set(currentPower, zero);
}
// perform this loop while currentPower is >= 0
do {
// store base^currentPower in baseRootCurrentPower
[self GMPPowWithResult:baseRootCurrentPower withBase:base power:currentPower];
// store inputNumber / baseRootCurrentPower in divided. this is the digit we will append to the final string
mpz_div(divided, inputNumber, baseRootCurrentPower);
// store inputNumber % baseRootCurrentPower in inputNumber. this is the number we will divide by on the next loop run
mpz_mod(inputNumber, inputNumber, baseRootCurrentPower);
// append the divided string to the end of the final string. run it through a special function to
// convert it to a letter if necessary (eg 11 = A)
[finalString appendString:[NSString stringWithFormat:@"%@", [self determineNumberOrLetter:mpz_get_ui(divided)]]];
// decrement currentPower by 1
mpz_sub(currentPower, currentPower, one);
} while (mpz_cmp(currentPower, zero) >= 0);
}
// memory cleanup
mpz_clears(zero, one, inputNumber, baseRootCurrentPower, divided, currentPower, base, inputNumberMinusBaseRootCurrentPower, NULL);
return finalString;
}