Integer minRadix = 2;
Integer maxRadix = 36;

"The [[Integer]] value of the given 
 [[string representation|string]] of an integer value in the 
 base given by [[radix]], or `null` if the string does not 
 represent an integer in that base, or if the mathematical 
 integer it represents is too large in magnitude to be 
 represented by an instance of the class `Integer`.
 
 The syntax accepted by this function depends upon the 
 given [[base|radix]]:
 
 - For base 10, the accepted syntax 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 `-`) and may not contain grouping underscore 
   characters. That is, an optional sign character, followed
   be a string of decimal digits, followed by an optional SI
   magnitude: `k`, `M`, `G`, `T`, or `P`.
 - For other bases, the accepted syntax is an optional sign
   character, followed by a string of digits of the given
   base. 
 
 The given `radix` specifies the base of the string 
 representation. 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 is used. This function is not case sensitive; 
 `a` and `A` both correspond to the digit `a` whose decimal 
 value is `10`.
 
     Integer: Base10 | BaseN
     Base10: Sign? Base10Digits Magnitude
     BaseN: Sign? BaseNDigits
     Sign: '+' | '-'
     Magnitude: 'k' | 'M' | 'G' | 'T' | 'P'
     Base10Digits: ('0'..'9')+
     BaseNDigits: ('0'..'9'|'a'..'z'|'A'..'Z')+"
throws (`class AssertionError`, 
        "if [[radix]] is not between [[minRadix]] and 
         [[maxRadix]]")
see (`function formatInteger`,
     `function parseFloat`)
tagged("Numbers", "Basic types")
shared Integer? parseInteger(
            "The string representation to parse."
            String string,
            "The base, between [[minRadix]] and [[maxRadix]] 
             inclusive."
            Integer radix = 10) {
    
    assert (minRadix <= radix <= maxRadix); 
    
    Integer start;
    Integer max = runtime.minIntegerValue / radix;
    
    // Parse the sign
    Boolean negative;
    if (exists char = string.first) {
        if (char == '-') {
            negative = true;
            start = 1;
        }
        else if (char == '+') {
            negative = false;
            start = 1;
        }
        else {
            negative = false;
            start = 0;
        }
    }
    else {
        return null;
    }
    
    Integer limit = negative 
            then runtime.minIntegerValue 
            else -runtime.maxIntegerValue;
    
    Integer length = string.size;
    variable Integer result = 0;
    variable Integer digitIndex = 0;
    variable Integer index = start;
    while (index < length) {
        assert (exists ch = string.getFromFirst(index));

        if (index + 1 == length && 
                radix == 10 && 
                ch in "kMGTP") {
            // The SI-style magnitude
            if (exists exp = parseIntegerExponent(ch)) {
                Integer magnitude = 10^exp;
                if ((limit / magnitude) < result) {
                    result *= magnitude;
                    break;
                }
                else {
                    // overflow
                    return null;
                }
            }
            else {
                return null;
            }
        }
        else if (exists digit = parseDigit(ch, radix)) {
            // A regular digit
            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 character
            return null;
        }
        
        index++;
        digitIndex++;
    }
    
    if (digitIndex == 0) {
        return null;
    }
    else {
        return negative then result else -result;
    }
}

Integer? parseIntegerExponent(Character char) {
    switch (char)
    case ('P') {
        return 15;
    } 
    case ('T') {
        return 12; 
    } 
    case ('G') {
        return 9; 
    } 
    case ('M') {
        return 6;
    } 
    case ('k') {
        return 3;
    }
    else {
        return null;
    }
}

Integer aIntLower = 'a'.integer;
Integer aIntUpper = '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-aIntLower<26) {
        figure=digitInt-aIntLower+10;
    }
    else if (0<=digitInt-aIntUpper<26) {
        figure=digitInt-aIntUpper+10;
    }
    else {
        return null;
    }
    return figure<radix then figure;
}