void test_neg() { Fixnum* min = Fixnum::from(FIXNUM_MIN); Integer* b = min->neg(state); TS_ASSERT(kind_of<Bignum>(b)); TS_ASSERT_EQUALS(FIXNUM_MAX + 1, b->to_native()); Fixnum* max = Fixnum::from(FIXNUM_MAX); b = max->neg(state); TS_ASSERT(kind_of<Fixnum>(b)); TS_ASSERT_EQUALS(FIXNUM_MIN + 1, b->to_native()); TS_ASSERT_EQUALS(Fixnum::from(3)->neg(state), Fixnum::from(-3)); TS_ASSERT_EQUALS(Fixnum::from(-3)->neg(state), Fixnum::from(3)); }
Integer* Integer::from_cstr(STATE, const char* str, const char* end, int base, Object* strict) { if(base == 1 || base > 36) return nil<Integer>(); // Skip any combination of leading whitespace and underscores. Leading // whitespace is OK in strict mode, but underscores are not. while(isspace(*str) || *str == '_') { if(*str == '_') { if(CBOOL(strict)) { return nil<Integer>(); } else { return Fixnum::from(0); } } str++; } bool negative = false; if(*str == '-') { str++; negative = true; } else if(*str == '+') { str++; } int detected_base = 0; const char* str_start = str; /* Try to detect a base prefix. We have to do this even though we might * have been told the base, we have to know if we should discard the bytes * that make up the prefix if it's redundant with the passed in base. * * For example, if base == 16 and str == "0xa", we return 10. But if base * == 10 and str == "0xa", we fail because we rewind and try to process 0x * as part of the base 10 string. */ if(*str == '0') { str++; switch(*str++) { case 'b': case 'B': detected_base = 2; break; case 'o': case 'O': detected_base = 8; break; case 'd': case 'D': detected_base = 10; break; case 'x': case 'X': detected_base = 16; break; default: // If passed "017" and a base of 0, that is octal 15. Otherwise, it // is whatever those digits would be in the specified base. str--; detected_base = 8; } } // If base is less than 0, then it's just a hint for how to process it // if there is no base detected. if(base < 0) { if(detected_base == 0) { // Ok, no detected because, use the base hint and start over. base = -base; str = str_start; } else { base = detected_base; } // If 0 was passed in as the base, we use the detected base. } else if(base == 0) { // Default to 10 if there is no input and no detected base. if(detected_base == 0) { base = 10; str = str_start; } else { base = detected_base; } // If the passed in base and the detected base contradict each other, then // rewind and process the whole string as digits of the passed in base. } else if(base != detected_base) { // Rewind the stream and try and consume the prefix as digits in the // number. str = str_start; } int max_digits = DIGIT_BIT / digit_bits[base]; int count = 0; int digit = 0; mp_digit shift = base, value = 0; mp_int a = { 0, 0, 0, 0, 0, }; for( ; str < end; str++) { digit = digit_value[int(*str)]; if(digit >= 0 && digit < base) { if(++count <= max_digits) { value = value * base + digit; } else { if(!mp_isinitialized(&a)) { mp_init_set_long(XST, &a, value); for(int i = 0; i < max_digits - 1; i++) { shift *= base; } } else { mp_mul_d(XST, &a, shift, &a); mp_add_d(XST, &a, value, &a); } value = digit; count = 1; } continue; } // An underscore is valid iff it is followed by a valid character for // this base. if(*str == '_') { if(!*(str + 1) || *(str + 1) == '_') goto error_check; continue; } if(digit >= base) goto error_check; // Consume any whitespace characters. if(digit < -1) { while(digit_value[int(*++str)] < -1) /* skip whitespace */ ; goto error_check; } // Done parsing. break; } error_check: if(str < end && CBOOL(strict)) { if(mp_isinitialized(&a)) mp_clear(&a); return nil<Integer>(); } if(!mp_isinitialized(&a)) { if(value < FIXNUM_MAX) { Fixnum* result = Fixnum::from(value); if(negative) { return result->neg(state); } else { return result; } } mp_init_set_long(XST, &a, value); } else { shift = base; for(int i = 0; i < count - 1; i++) { shift *= base; } mp_mul_d(XST, &a, shift, &a); mp_add_d(XST, &a, value, &a); } if(negative) { mp_neg(XST, &a, &a); } Integer* result = Bignum::from(state, &a); mp_clear(&a); return result; }