N.h: Code:

#ifndef N_H
#define N_H
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
//N for Natural number
class N {
private:
static unsigned int radix;
public:
static std::vector<char> digits;
unsigned long long n; //the internal representation of this N object
//default constructor
N() : n(0) {}
//other constructor(s)
N(unsigned long long k) : n(k) {}
//copy constructor
N(const N &num) : n(num.n) {}
//destructor
virtual ~N() {}
//assignment operator(s)
virtual const N& operator=(const N &num) {
n = num.n;
return *this;
}
virtual const N& operator=(unsigned long long k) {
n = k;
return *this;
}
//casting operators
virtual operator unsigned long long() const {
return n;
}
virtual operator std::string() const {
return toString();
}
//static accessor(s)
static unsigned int getRadix() {
return radix;
}
//static mutator(s)
static void setRadix(unsigned int r) {
if( 2 <= r && r <= digits.size() )
radix = r;
}
//used internally at initialization, but can be used to reinitialize digits
static std::vector<char> generateInitialDigits() {
char arr[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const unsigned int N = sizeof(arr) / sizeof(char);
std::vector<char> digits;
digits.reserve(N);
for( int i = 0; i < N; i++ )
digits.push_back( arr[i] );
return digits;
}
//converts n into a unique permutation of digits
virtual std::string toString() const {
//check for the exceptional case
if( n == 0 )
return std::string(1, digits[0]);
//determine the number of digits needed to represent this permutation
unsigned int len = 0;
len = 1 + (int)( log((double)n) / log((double)radix) );
//allocate memory to hold the string representation of our permutation
char *str = new char[len + 1]; //adding 1 for the null terminator '\0'
str[len] = '\0';
//figure out each digit using division (both the quotient and remainder)
unsigned long long k = n; //make a modifiable copy of n
for( unsigned int i = len - 1; k > 0; i-- ) {
str[i] = digits[k % radix];
k /= radix;
//repeating until k == 0
}
//convert char[] to C++ string, and return
std::string r = str;
delete[] str;
return r;
}
};
//initialize static fields
std::vector<char> N::digits = N::generateInitialDigits();
unsigned int N::radix = 10;
//overloaded iostream operators
std::ostream& operator<<(std::ostream &out, const N &num) {
return out << num.toString();
}
std::istream& operator>>(std::istream &in, N &num) {
unsigned long long k;
in >> k;
num.n = k;
return in;
}
#endif

N-test.cpp: Code:

#include <iostream>
#include "N.h"
using namespace std;
void report(const N &n) {
cout << "Base " << N::getRadix() << ": " << n << endl;
}
int main() {
//create a number, and represent it in different bases
N n = 200;
report(n);
N::setRadix(2);
report(n);
N::setRadix(5);
report(n);
N::setRadix(16);
report(n);
//what happens when we try to change to a base too big or too small
N::setRadix(20);
report(n);
N::setRadix(1);
report(n);
//how is zero represented
N zero = 0;
report(zero);
return 0;
}

Output: Code:

Base 10: 200
Base 2: 11001000
Base 5: 1300
Base 16: C8
Base 16: C8
Base 16: C8
Base 16: 0