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;
}