Skip to content

Instantly share code, notes, and snippets.

@RobSeder
Created October 5, 2014 03:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RobSeder/e5051341a51f163c04fd to your computer and use it in GitHub Desktop.
Save RobSeder/e5051341a51f163c04fd to your computer and use it in GitHub Desktop.
Radix calculator. Converts a value from one radix to another (from base-2 to base-36)
/// <summary>
/// Interface for a class who converts a value from one radix to another.
/// </summary>
public interface IRadixCalculator
{
/// <summary>
/// Converts a value from one base radix to another.
/// </summary>
/// <param name="value">The value to process. Can contain strings if they are
/// part of that base.</param>
/// <param name="sourceRadix">The radix which the <paramref name="value"/> is. 2
/// through 36 allowed. (e.g. 10 for base-10, 16 for hexidecimal)</param>
/// <param name="destinationRadix">The radix to which to convert <paramref name="value"/>.
/// 2 through 36 allowed. (e.g. 10 for base-10, 16 for hexidecimal)</param>
/// <returns>The same value, but converted to the new radix.</returns>
/// <exception cref="ArgumentException">When <paramref name="value"/> is null or
/// empty.</exception>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source or destination radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new radix.</exception>
String CalculateRadix(String value, Int32 sourceRadix, Int32 destinationRadix);
}
/// <summary>
/// Interface for a class who converts a string value to a number, and vice versa.
/// For example, converting the base-10 number 123456 into base-16 1E240 and back.
/// </summary>
/// <remarks>Code from: http://rosettacode.org/wiki/Non-decimal_radices/Convert </remarks>
public interface IStringAndNumberConverter
{
/// <summary>
/// Converts a string to a number.
/// </summary>
/// <returns>The number representation of <paramref name="value"/>.</returns>
/// <param name="value">The string to convert.</param>
/// <param name="sourceRadix">The base, or radix of <paramref name="value"/>
/// (between 2 and 36).</param>
/// <exception cref="ArgumentException">When <paramref name="value"/> is null or
/// empty.</exception>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new value.</exception>
/// <remarks>Code from: http://rosettacode.org/wiki/Non-decimal_radices/Convert </remarks>
Int64 StringToLong(String value, Int32 sourceRadix);
/// <summary>
/// Converts a number to a string.
/// </summary>
/// <returns>The string representation of the value.</returns>
/// <param name="value">The number to convert.</param>
/// <param name="sourceRadix">The base, or radix of <paramref name="value"/>
/// (between 2 and 36).</param>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new value.</exception>
/// <remarks>Code from: http://rosettacode.org/wiki/Non-decimal_radices/Convert </remarks>
String LongToString(Int64 value, Int32 sourceRadix);
}
/// <summary>
/// Class who converts a value from one radix to another.
/// </summary>
public class RadixCalculator : IRadixCalculator
{
/// <summary>
/// Gets the current converter that will be used to convert numbers and
/// to strings and vice versa.
/// </summary>
public IStringAndNumberConverter Converter { get; protected set; }
/// <summary>
/// Creates a new instance of this type.
/// </summary>
/// <param name="converter">The converter to use.</param>
/// <exception cref="ArgumentNullException">When
/// <paramref name="converter"/> is null.</exception>
public RadixCalculator(IStringAndNumberConverter converter)
{
if (converter == null)
throw new ArgumentNullException("converter");
Converter = converter;
}
/// <summary>
/// Converts a value from one base radix to another.
/// </summary>
/// <param name="value">The value to process. Can contain strings if they are
/// part of that base.</param>
/// <param name="sourceRadix">The radix which the <paramref name="value"/> is. 2
/// through 36 allowed. (e.g. 10 for base-10, 16 for hexidecimal)</param>
/// <param name="destinationRadix">The radix to which to convert <paramref name="value"/>.
/// 2 through 36 allowed. (e.g. 10 for base-10, 16 for hexidecimal)</param>
/// <returns>The same value, but converted to the new radix.</returns>
/// <exception cref="ArgumentException">When <paramref name="value"/> is null or
/// empty.</exception>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source or destination radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new radix.</exception>
public String CalculateRadix(String value, Int32 sourceRadix, Int32 destinationRadix)
{
if (String.IsNullOrWhiteSpace(value))
throw new ArgumentException("Argument \"value\" cannot be null or empty.", "value");
if (sourceRadix < 2 || sourceRadix > 36)
throw new ArgumentOutOfRangeException("sourceRadix", "Argument \"sourceRadix\" must be between 2 and 36.");
if (destinationRadix < 2 || destinationRadix > 36)
throw new ArgumentOutOfRangeException("destinationRadix", "Argument \"destinationRadix\" must be between 2 and 36.");
Int64 numericResult = Converter.StringToLong(value, sourceRadix);
String stringResult = ProcessRadixConversion(destinationRadix, numericResult);
return stringResult;
}
private static String ProcessRadixConversion(Int32 destinationRadix, Int64 value)
{
if (destinationRadix < 2 || destinationRadix > 36)
throw new ArgumentOutOfRangeException("destinationRadix", "Argument \"destinationRadix\" must be between 2 and 36.");
StringBuilder output = new StringBuilder();
// Keep processing until we run out of things to process
while (value > 0)
{
// We need to get the whole number by doing division
Int64 wholeNumber = value / destinationRadix;
// and the remainder by doing "mod"
Int64 remainder = (value % destinationRadix);
// We inject the remainder in the front of the value
if (remainder > 9)
{
// We need to start using numbers for this value, so go back to zero
// and add 65, the decimal value for "A"
remainder = remainder - 10;
remainder = remainder + 65; // New starting place
output.Insert(0, (Char)remainder);
}
else
{
output.Insert(0, remainder);
}
// Then process the whole number that is left.
value = wholeNumber;
}
// We've processed all the positions, return the converted output.
return output.ToString();
}
}
/// <summary>
/// Class who converts a string value to a number, and vice versa.
/// For example, converting the base-10 number 123456 into base-16 1E240 and back.
/// </summary>
public class StringAndNumberConverter : IStringAndNumberConverter
{
/// <summary>
/// Converts a string to a number.
/// </summary>
/// <returns>The number representation of <paramref name="value"/>.</returns>
/// <param name="value">The string to convert.</param>
/// <param name="sourceRadix">The base, or radix of <paramref name="value"/>
/// (between 2 and 36).</param>
/// <exception cref="ArgumentException">When <paramref name="value"/> is null or
/// empty.</exception>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new value.</exception>
/// <remarks>Code from: http://rosettacode.org/wiki/Non-decimal_radices/Convert </remarks>
public Int64 StringToLong(String value, Int32 sourceRadix)
{
if (String.IsNullOrWhiteSpace(value))
throw new ArgumentException("Argument \"value\" cannot be null or empty.", "value");
if (sourceRadix < 2 || sourceRadix > 36)
throw new ArgumentOutOfRangeException("sourceRadix", "Argument \"sourceRadix\" must be between 2 and 36.");
checked
{
Int32 valueLength = value.Length;
Int64 result = 0;
Boolean isNegative = false;
for (Int32 valueIndex = 0; valueIndex < valueLength; valueIndex++)
{
Char c = value[valueIndex];
Int32 num;
if (c == '-')
{
if (valueIndex != 0)
throw new ArgumentException("Argument \"value\" may only have a negative sign as the first character of the string.", "value");
isNegative = true;
continue;
}
if (c > 0x2F && c < 0x3A)
// Numeric character (subtract from 0x30 ('0') to get numerical value)
num = c - 0x30;
else if (c > 0x40 && c < 0x5B)
// Uppercase letter
// Subtract from 0x41 ('A'), then add 10
num = c - 0x37; // 0x37 = 0x41 - 10
else if (c > 0x60 && c < 0x7B)
// Lowercase letter
// Subtract from 0x61 ('a'), then add 10
num = c - 0x57; // 0x57 = 0x61 - 10
else
throw new ArgumentException("Argument \"value\" contains an invalid character '" + c + "'", "value");
// Check that the digit is allowed by the base.
if (num >= sourceRadix)
throw new ArgumentException("Argument \"value\" contains a character '" + c + "' which is not allowed in base " + sourceRadix, "value");
// Multiply the result by the base, then add the next digit
result *= sourceRadix;
result += num;
}
if (isNegative)
result = -result;
return result;
}
}
/// <summary>
/// Converts a number to a string.
/// </summary>
/// <returns>The string representation of the value.</returns>
/// <param name="value">The number to convert.</param>
/// <param name="sourceRadix">The base, or radix of <paramref name="value"/>
/// (between 2 and 36).</param>
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="value"/>
/// contains a character which is out of range. (e.g. having an "A" in a base-10
/// value) of if the source radix is less than 2 or greater than 36.</exception>
/// <exception cref="InvalidOperationException">When there was an error calculating
/// the new value.</exception>
/// <remarks>Code from: http://rosettacode.org/wiki/Non-decimal_radices/Convert </remarks>
public String LongToString(Int64 value, Int32 sourceRadix)
{
if (sourceRadix < 2 || sourceRadix > 36)
throw new ArgumentOutOfRangeException("sourceRadix", "Argument \"sourceRadix\" must be between 2 and 36.");
if (sourceRadix == 10)
return value.ToString();
checked
{
Int64 longBase = sourceRadix;
StringBuilder output = new StringBuilder();
if (value < 0)
{
value = -value;
output.Append('-');
}
Int64 div = 1;
while (value/div >= sourceRadix)
{
// Continue multiplying the dividend by the base until it reaches the greatest power of
// the base which is less than or equal to the number.
div *= sourceRadix;
}
while (true)
{
Byte digit = (Byte)(value / div);
if (digit < 10)
// Numeric character (0x30 = '0')
output.Append((char)(digit + 0x30));
else
// Alphabetic character (for digits > 10) (0x61 = 'a')
output.Append((char)(digit + 0x57)); // 0x61 - 10
if (div == 1)
// Stop when the dividend reaches 1
break;
value %= div;
div /= sourceRadix;
}
return output.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment