Integer minRadix = 2;
Integer maxRadix = 36;
"The `Integer` value of the given string representation
of an integer, or `null` if the string does not represent
an integer or if the mathematical integer it represents
is too large in magnitude to be represented by an
`Integer`.
The syntax accepted by this function is the same as the
syntax for an `Integer` literal in the Ceylon language
except that it may optionally begin with a sign
character (`+` or `-`).
A radix can be given in input to specify what is the base
to take in consideration for the parsing. radix has to be
between `minRadix` and `maxRadix` included.
The list of available digits starts from `0` to `9` followed
by `a` to `z`.
When parsing in a specific base, the first `radix` digits
from the available digits list can be used.
This function is not case sensitive; `a` and `A` both
correspond to the `a` digit which decimal value is `10`.
`_` character can be used to separate groups of digits
for bases 2, 10 and 16 as for `Integer` literal in the
Ceylon language. For any other bases, no grouping is
supported."
throws (`class AssertionException`,
"if `radix` is not between `minRadix` and `maxRadix`")
shared Integer? parseInteger(String string, Integer radix = 10) {
assert (radix >= minRadix, radix <= maxRadix);
variable Integer ii = 0;
Integer max = runtime.minIntegerValue / radix;
Boolean negative;
if (exists char = string[ii]) {
if (char == '-') {
negative = true;
ii++;
} else if (char == '+') {
negative = false;
ii++;
} else {
negative = false;
}
} else {
return null;
}
Integer limit = negative then runtime.minIntegerValue else -runtime.maxIntegerValue;
Integer length = string.size;
variable Integer result = 0;
variable Integer sep = -1;
variable Integer digitIndex = 0;
variable Integer groupingSize = -1;
while (ii < length) {
Character ch;
if (exists char = string[ii]) {
ch = char;
} else {
return null;
}
if (ch == '_') {
if (sep == -1) {
if (exists digitGroupSize =
computeDigitGroupingSize(radix, digitIndex, string, ii),
digitIndex <= digitGroupSize) {
groupingSize = digitGroupSize;
sep = digitIndex;
} else {
return null;
}
} else if ((digitIndex - sep) == groupingSize) {
return null;
} else {
sep = digitIndex;
}
} else {
if (sep != -1 &&
(digitIndex - sep) == (groupingSize + 1)) {
return null;
}
if (ii + 1 == length &&
radix == 10 &&
ch in ['k','M','G','T','P']) {
// The magnitude
if (exists magnitude = computeMagnitude(radix, string[ii++])) {
if ((limit / magnitude) < result) {
result *= magnitude;
break;
} else { // overflow
return null;
}
} else {
return null;
}
} else if (exists digit = parseDigit(ch, radix)) {
if (result < max) { // overflow
return null;
}
result *= radix;
if (result < limit + digit) { // overflow
return null;
}
// += would be much more obvious, but it doesn't work for minIntegerValue
result -= digit;
} else { // Invalid digit
return null;
}
}
ii++;
digitIndex++;
}
// check for insufficient digits after the last _
if (sep != -1 &&
(digitIndex - sep) != (groupingSize + 1)) {
return null;
}
if (digitIndex == 0) {
return null;
}
return negative then result else -result;
}
Integer? computeDigitGroupingSize(Integer radix,
Integer digitIndex, String string, Integer ii) {
Integer? groupingSize;
if (radix == 2) {
groupingSize = 4;
} else if (radix == 10) {
groupingSize = 3;
} else if (radix == 16) {
if (digitIndex <= 2,
exists char = string[ii + 3],
char == '_') {
groupingSize = 2;
} else {
groupingSize = 4;
}
} else {
groupingSize = null;
}
return groupingSize;
}
Integer? computeMagnitude(Integer radix, Character? char) {
Integer? power;
if (exists char) {
if (char == 'P') {
power = 15;
} else if (char == 'T') {
power = 12;
} else if (char == 'G') {
power = 9;
} else if (char == 'M') {
power = 6;
} else if (char == 'k') {
power = 3;
} else {
power = null;
}
} else {
power = null;
}
if (exists power) {
return radix^power;
}
return null;
}
Integer aInt = 'a'.integer;
Integer zeroInt = '0'.integer;
Integer? parseDigit(Character digit, Integer radix) {
Integer figure;
Integer digitInt = digit.integer;
if (0<=digitInt-zeroInt<10) {
figure=digitInt-zeroInt;
}
else if (0<=digitInt-aInt<26) {
figure=digitInt-aInt+10;
}
else {
return null;
}
return figure<radix then figure;
}
"The string representation of `integer` in the `radix` base.
`radix` must be between `minRadix` and `maxRadix` included.
If `integer` is negative, returned string will start by character `-`"
throws (`class AssertionException`,
"if `radix` is not between `minRadix` and `maxRadix`")
shared String formatInteger(Integer integer, Integer radix = 10) {
assert (radix >= minRadix, radix <= maxRadix);
if (integer == 0) {
return "0";
}
StringBuilder digits = StringBuilder();
Integer insertIndex;
variable Integer i;
if (integer < 0) {
digits.append("-");
insertIndex = 1;
i = integer;
} else {
insertIndex = 0;
i = -integer;
}
while (i != 0) {
Integer d = -(i % radix);
Character c;
if (0<=d<10) {
c = (d+zeroInt).character;
}
else if (10<=d<36) {
c = (d-10+aInt).character;
}
else {
assert (false);
}
digits.insertCharacter(insertIndex, c);
i = (i + d) / radix;
}
return digits.string;
}