ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# vuinguyen's solution

## to All Your Base in the Objective-C Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Convert a number, represented as a sequence of digits in one base, to any other base.

Implement general base conversion. Given a number in base a, represented as a sequence of digits, convert it to base b.

## Note

• Try to implement the conversion yourself. Do not use something else to perform the conversion for you.

In positional notation, a number in base b can be understood as a linear combination of powers of b.

The number 42, in base 10, means:

(4 * 10^1) + (2 * 10^0)

The number 101010, in base 2, means:

(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)

The number 1120, in base 3, means:

(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)

I think you got the idea!

Yes. Those three numbers above are exactly the same. Congratulations!

## Setup

There are two different methods of getting set up to run the tests with Objective-C:

• Create an Xcode project with a test target which will run the tests.
• Use the ruby gem `objc` as a test runner utility.

Both are described in more detail here: http://exercism.io/languages/objective-c

### Submitting Exercises

When submitting an exercise, make sure your solution file is in the same directory as the test code.

The submit command will look something like:

``````exercism submit <path-to-exercism-workspace>/objective-c/all-your-base/AllYourBase.m
``````

You can find the Exercism workspace by running `exercism debug` and looking for the line beginning with Workspace.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### AllYourBaseTest.m

``````#import <XCTest/XCTest.h>

#if __has_include("AllYourBaseExample.h")
# import "AllYourBaseExample.h"
# else
# import "AllYourBase.h"
#endif

@interface AllYourBaseTest : XCTestCase

@end

@implementation AllYourBaseTest

- (void)testSingleBitOneToDecimal {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:2 inputDigits:@[@1] outputBase:10];
NSArray<NSNumber *> *expected = @[@1];
XCTAssertEqualObjects(result, expected);
}

- (void)testBinaryToSingleDecimal {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:2 inputDigits:@[@1, @0, @1] outputBase:10];
NSArray<NSNumber *> *expected = @[@5];
XCTAssertEqualObjects(result, expected);
}

- (void)testSingleDecimalToBinary {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:10 inputDigits:@[@5] outputBase:2];
NSArray<NSNumber *> *expected = @[@1, @0, @1];
XCTAssertEqualObjects(result, expected);
}

- (void)testBinaryToMultipleDecimal {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:2 inputDigits:@[@1, @0, @1, @0, @1, @0] outputBase:10];
NSArray<NSNumber *> *expected = @[@4, @2];
XCTAssertEqualObjects(result, expected);
}

- (void)testDecimalToBinary {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:10 inputDigits:@[@4, @2] outputBase:2];
NSArray<NSNumber *> *expected = @[@1, @0, @1, @0, @1, @0];
XCTAssertEqualObjects(result, expected);
}

NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:3 inputDigits:@[@1, @1, @2, @0] outputBase:16];
NSArray<NSNumber *> *expected = @[@2, @10];
XCTAssertEqualObjects(result, expected);
}

NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:16 inputDigits:@[@2, @10] outputBase:3];
NSArray<NSNumber *> *expected = @[@1, @1, @2, @0];
XCTAssertEqualObjects(result, expected);
}

- (void)test15BitInteger {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:97 inputDigits:@[@3, @46, @60] outputBase:73];
NSArray<NSNumber *> *expected = @[@6, @10, @45];
XCTAssertEqualObjects(result, expected);
}

- (void)testEmptyList {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:2 inputDigits:@[] outputBase:10];
NSArray<NSNumber *> *expected = @[];
XCTAssertEqualObjects(result, expected);
}

- (void)testSingleZero {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:10 inputDigits:@[@0] outputBase:2];
NSArray<NSNumber *> *expected = @[];
XCTAssertEqualObjects(result, expected);
}

- (void)testMultipleZeros {
NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:10 inputDigits:@[@0, @0, @0] outputBase:2];
NSArray<NSNumber *> *expected = @[];
XCTAssertEqualObjects(result, expected);
}

NSArray<NSNumber *> *result = [AllYourBase outputDigitsForInputBase:7 inputDigits:@[@0, @6, @0] outputBase:10];
NSArray<NSNumber *> *expected = @[@4, @2];
XCTAssertEqualObjects(result, expected);
}

- (void)testNegativeDigit {
NSArray<NSNumber *> *inputDigits = @[@1, @(-1), @1, @0, @1, @0];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:2 inputDigits:inputDigits outputBase:10]);
}

- (void)testInvalidPositiveDigit {
NSArray<NSNumber *> *inputDigits = @[@1, @2, @1, @0, @1, @0];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:2 inputDigits:inputDigits outputBase:10]);
}

- (void)testFirstBaseIsOne {
NSArray<NSNumber *> *inputDigits = @[];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:1 inputDigits:inputDigits outputBase:10]);
}

- (void)testSecondBaseIsOne {
NSArray<NSNumber *> *inputDigits = @[@1, @0, @1, @0, @1, @0];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:2 inputDigits:inputDigits outputBase:1]);
}

- (void)testFirstBaseIsZero {
NSArray<NSNumber *> *inputDigits = @[];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:0 inputDigits:inputDigits outputBase:10]);
}

- (void)testSecondBaseIsZero {
NSArray<NSNumber *> *inputDigits = @[@7];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:10 inputDigits:inputDigits outputBase:0]);
}

- (void)testFirstBaseIsNegative {
NSArray<NSNumber *> *inputDigits = @[@1];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:-2 inputDigits:inputDigits outputBase:10]);
}

- (void)testSecondBaseIsNegative {
NSArray<NSNumber *> *inputDigits = @[@1];
XCTAssertThrows([AllYourBase outputDigitsForInputBase:2 inputDigits:inputDigits outputBase:-7]);
}

@end``````
``````//
//  AllYourBase.m
//  AllYourBase
//
//  Created by Vui Nguyen on 1/4/17.
//

#import "AllYourBase.h"

@implementation AllYourBase

typedef NS_ENUM(NSInteger, AllYourBaseErrorCodes)
{
negativeDigit,
invalidPositiveDigit,
invalidInputBase,
invalidOutputBase,
allZeroInput
};

// outputDigits is the function that the test file "AllYourBaseTest.m" calls, to
// return the array of digits given the following: input base, input digits, and output base
+(NSArray*) outputDigitsForInputBase: (int)inputBase inputDigits:(NSArray*)inputDigits outputBase:(int)outputBase
{
// array of outputDigits to return
NSMutableArray * outputDigits = [[NSMutableArray alloc] init];

// a single digit in outputDigits
int newDigit = 0;

// this will be set to the original value represented by inputDigits and later
// used in calculations to getoutputDigits
double remainder = 0.0;

// Declare our error and exception variables
NSError * error = nil;
NSException * exception = nil;

// inputBase must be greater than 1
if (inputBase < 2)
{
exception = [[NSException alloc] initWithName:@"InvalidInputBaseException" reason:@"Encountered Invalid Input Base" userInfo:nil];
@throw exception;
}

// outputBase must be greater than 1
if (outputBase < 2)
{
exception = [[NSException alloc] initWithName:@"InvalidOutputBaseException" reason:@"Encountered Invalid Output Base" userInfo:nil];
@throw exception;
}

// If we have empty inputDigits, let's handle that
if (inputDigits.count == 0)
{
NSLog(@"outputDigits are: @%@", outputDigits);
return outputDigits;
}

// set remainder to our original value represented by inputDigits
remainder = [self calcValueWithBaseDigits:inputBase inputDigits:inputDigits error:&error];

// handle all errors returned by calcValueWithBaseDigits
if (error)
{
if (error.code == allZeroInput)
{
NSLog(@"zero input digits, outputDigits are: @%@", outputDigits);
return outputDigits; // return empty outputDigits as required by AllYourBaseTest.m
}
else if (error.code == negativeDigit)
{
exception = [[NSException alloc] initWithName:@"NegativeDigitException" reason:@"Encountered Negative Digit" userInfo:nil];
@throw exception;
}
else if (error.code == invalidPositiveDigit)
{
exception = [[NSException alloc] initWithName:@"InvalidPositiveDigitException" reason:@"Encountered Invalid Positive Digit" userInfo:nil];
@throw exception;
}
}

int power = 0;

// Calculate the digit that we will be starting at, or largest power
// e.g. The largest "power" necessary to represent a value of 4792 (decimal) is 10^3
// (the power is 3).
// The floor function rounds the calculation down
while ( floor( ( remainder / pow( outputBase, (power+1) ) ) )  > 0 )
{
power = power + 1;
}

double powerValue = 0.0;

// Calculate the individual digits needed to represent the remainder returned by
// calcValueWithBaseDigits, and append them to outputDigits
// for example, if the remainder value is 4792, the first power we start with is 3
while (power >= 0)
{
// calculate powerValue, e.g. 10^3 (1000) is the first powerValue in our example
powerValue = pow(outputBase, power);

// newDigit is the leftmost outputDigit remaining: the number 4 in our example
// (4 * 10^3 = 4000)
newDigit = (remainder / powerValue);

// fmod acts like a modulus function to move the calculation to the next-right outputDigit
// remainder in our example would be 792
remainder = fmod(remainder, powerValue);

// you have to go through all this trouble just to get "newDigit" added to outputDigits
// newDigit is 4 in our example

// decrement power.  It will go to 2 in our example
power = power - 1;
}

NSLog(@"outputDigits: @%@", outputDigits);
return outputDigits;
}

// This is a "helper" function called by the outputDigits function. Its purpose is to
// calculate the value of the number given by inputDigits. The value can then be
// represented in the desired base by outputDigits.
+(double) calcValueWithBaseDigits: (int)inputBase inputDigits:(NSArray*)inputDigits error:(NSError **)error
{
// Error handling stuff
NSString * errorDomain = @"com.sunfishempire.allyourbase.errordomain";
NSInteger errorCode;
NSDictionary *errorUserInfo = nil;

// the "value" variable has to be declared a Double so we can do the
// calculation further down with the pow() function, which returns a Double
double value = 0.0;
NSUInteger backwardsIndex = inputDigits.count - 1;

// We need to reverse the digits in order to calculate the value correctly.
// I decided to create a reverseDigits array to make this code
// easier to follow rather than walking the array backwards.
// This also allows us to check for most of the possible errors
// before calling the pow() function, which is an expensive operation.
// Now, let's initialize the array to hold the digits reversed.
// For now, it's holding the same elements as inputDigits, but we'll change that shortly.
NSMutableArray * reverseDigits = [NSMutableArray arrayWithArray:inputDigits];

NSEnumerator * inputDigitsEnumerator = [inputDigits objectEnumerator];
NSNumber * digit;
int digitSum = 0;

// Let's do most of our error checking here before we waste time with calculations
// Now, let's populate the reverseDigits array
while (digit = [inputDigitsEnumerator nextObject])
{
// test for existence of a negative digit in inputDigits
if (digit.intValue < 0)
{
errorCode = negativeDigit;
*error = [[NSError alloc] initWithDomain:errorDomain code:errorCode userInfo:errorUserInfo];
return value;
}

// test for a digit that's too big for the input base (i.e., can't have a '2' digit for base 2!)
if (digit.intValue > (inputBase-1))
{
errorCode = invalidPositiveDigit;
*error = [[NSError alloc] initWithDomain:errorDomain code:errorCode userInfo:errorUserInfo];
return value;
}

// digitSum is needed to figure out if inputDigits array is all zeros
digitSum = digitSum + [digit intValue];
// insert digit n of inputDigits into the <size>-n slot of reverseDigits array
reverseDigits[backwardsIndex] = digit;
backwardsIndex = backwardsIndex - 1;
}

// test if the inputDigits array is all zeros.
if (digitSum == 0)
{
errorCode = allZeroInput;
*error = [[NSError alloc] initWithDomain:errorDomain code:errorCode userInfo:errorUserInfo];
return value;
}

NSEnumerator * reverseDigitsEnumerator = [reverseDigits objectEnumerator];
int index = 0;

// calculating the value of the input number represented by inputDigits
// Using 62 (decimal) as an example,
while (digit = [reverseDigitsEnumerator nextObject])
{
// cast the integer digit as a double for the pow() function
// in our example, the smallest digit is 2
double digitDouble = [digit doubleValue];
// The value is the existing value (0 to begin with), plus the digit (2) times
// the inputBase (10) to the index power (0): 2 * 10^0 = 2
value = value + (digitDouble * ( pow( (inputBase), (index) ) ));
// increment the index (and thus the power) for the next iteration
index = index + 1;
}

return value;
}

@end``````