The task:
A set of functions used to read/write integers from/to stdin/stdout as efficiently as possible.
Rationale:
printf
/scanf
fail to offer best possible performance. For example, it is possible to write a solution for UVA problem 10055 "Hashmat the Brave Warrior" that runs in below 0.01 sec, however a straightforward solution with printf
/ scanf
typically runs in around 0.02 sec. Digit by digit input/output of numbers using getchar_unlocked
and putchar_unlocked
is the only way I'm aware of to make it run as fast as possible.
Result:
- Functions
rd*
read integers, and functionswr*
write integers. - The next few letters in the function's name describe the type the function operates on, with similar naming conventions to
printf
/scanf
format specifiers. So for examplerdllu
reads anunsigned long long int
, whilewrhhi
writes the numerical value of asigned char
. (It is possible to use a function operating on unsigned integers to read / write a signed integer, this will simply skip checking if the integer is negative). - In addition, every
rd*
function has a counterpart ending witheof
, meaning that it checks for EndOfFile and returns0
in that case. Functions not checking for EOF always return1
. This is to enable convenient reading a few integers in a loop likewhile(rdllueof(&a) && rdllu(&b)) {do_something;}
. - You might say that these names are cryptic and unreadable. Honestly though, I think they're not much worse than standard library functions like
strncmpy
. And definitely much better than the alternative, that isread_unsigned_long_long_int_while_checking_for_eof
. If I am to avoid such long names I'm not sure what else can I devise.
Approach:
Sorry for using macros to generate these functions, that's another point I suppose you might think is wrong. But otherwise I'd have to write 30 repetitive functions, which would violate DRY pretty hard.
The code:
#include <stdio.h> int fisdigit(int ch) { return ch >= '0' && ch <= '9'; } #define rdi_funcname_sign_1_eof_1(abbrev) rd##abbrev##ieof #define rdi_funcname_sign_1_eof_0(abbrev) rd##abbrev##i #define rdi_funcname_sign_0_eof_1(abbrev) rd##abbrev##ueof #define rdi_funcname_sign_0_eof_0(abbrev) rd##abbrev##u #define wri_funcname_sign_1(abbrev) wr##abbrev##i #define wri_funcname_sign_0(abbrev) wr##abbrev##u #define rwi_argtype_sign_1(type) unsigned type #define rwi_argtype_sign_0(type) signed type #define read_integer(type, abbrev, check_sign, check_eof) \ int \ rdi_funcname_sign_##check_sign##_eof_##check_eof(abbrev) \ (rwi_argtype_sign_##check_sign(type)* n) \ { \ *n = 0; \ int ch; \ int is_neg = 0; \ \ do { \ ch = getchar_unlocked(); \ if(check_eof) { \ if(ch == EOF) \ return 0; \ } \ if(check_sign) { \ if(ch == '-') \ is_neg = 1; \ }\ } while(!fisdigit(ch)); \ \ do { \ *n *= 10; \ *n += ch-'0'; \ ch = getchar_unlocked(); \ } while(fisdigit(ch)); \ if(check_sign) { \ if(is_neg) \ *n *= -1; \ } \ return 1; \ } #define write_integer(type, abbrev, check_sign, max_digits) \ void \ wri_funcname_sign_##check_sign(abbrev) \ (rwi_argtype_sign_##check_sign(type) n) \ { \ size_t const buffsiz = \ (check_sign ? max_digits+1 : max_digits); \ char buff[max_digits]; \ \ size_t i = 0; \ if(check_sign) { \ if(n < 0) \ buff[i++] = '-'; \ } \ while(n != 0) { \ buff[i++] = n%10; \ n /= 10; \ } \ \ if(i == 0) \ putchar_unlocked('0'); \ else \ while(i-- != 0) \ putchar_unlocked(buff[i] + '0'); \ \ return; \ } #define rwi_variations(type, abbrev, max_digits_i, max_digits_u) \ read_integer(type, abbrev, 1, 1) \ read_integer(type, abbrev, 1, 0) \ read_integer(type, abbrev, 0, 1) \ read_integer(type, abbrev, 0, 0) \ write_integer(type, abbrev, 1, max_digits_i) \ write_integer(type, abbrev, 0, max_digits_u) rwi_variations(long long int, ll, 19, 20) rwi_variations(long int, l, 10, 10) rwi_variations(int, , 5, 5) rwi_variations(short int, h, 5, 5) rwi_variations(char, hh, 3, 3) #undef rdi_funcname_sign_1_eof_1 #undef rdi_funcname_sign_0_eof_1 #undef rdi_funcname_sign_1_eof_0 #undef rdi_funcname_sign_0_eof_0 #undef wri_funcname_sign_1 #undef wri_funcname_sign_0 #undef rwi_argtype_sign_1 #undef rwi_argtype_sign_0 #undef rwi_variations #undef read_integer #undef write_integer
Restriction:
Must compile with ANSI C 5.3.0 - GNU C Compiler with options: -lm -lcrypt -O2 -pipe -ansi -DONLINE_JUDGE
Use case:
The solution for the aforementioned UVA Problem 10055: "Hashmat the Brave Warrior":
void wrc(char c) { putchar_unlocked(c); } int main() { unsigned long long int a, b; while(rdllueof(&a) && rdllu(&b)) { if(a > b) wrllu(a-b); else wrllu(b-a); wrc('\n'); } return 0; }
I added the wrc
function just for consistency with the naming convention of wrllu
et al. I think it's better to print everything with wr*
rather than print integers with wr*
and characters with putchar_unlocked
.
PS
Any way to optimize this even more?
write_integer(type, abbrev, 1, max_digits_i) \ write_integer(type, abbrev, 0, max_digits_u)
. How does the receiving side separate the 2 integers as would they not be textually concatenated?\$\endgroup\$wrhhu(5); wrhhu(6);
but ratherwrhhu(5); wrc(' '); wrhhu(6)
Not sure why should it be any other way, C++iostream
facilities work in a similar way wrt this, ideone.com/XwFCnH\$\endgroup\$