8
\$\begingroup\$

Goal is to write the shortest possible C89 and C99-compliant single-module C program which will compute and print out a single-line string whose sort order will correspond with the date given by the predefined __DATE__ macro (in other words, later dates will yield later-sorting strings). The programmer is free to arbitrarily select the mapping between dates and strings, but every entry should specify the mapping and it should be obvious that it will sort correctly (e.g. a programmer could decide to compute (day + month*73 + year*4129) and output that as a number, though it's likely that particular encoding would probably require a longer program than some others).

The program should yield identical results on any standards-compliant compiler on which 'int' is 32 bits or larger and both source and target character sets are 7-bit ASCII, and should not rely upon any implementation-defined or undefined behavior nor print any characters outside the 32-126 range except for a single newline at the end. The program should contain the following aspects indicated below (replacing «CODE» with anything desired):

♯include <stdio.h>
«CODE»int main(void){«CODE»}

All output shall be produced by the printf at the end (i.e. the correct value will be in an int called z). The required elements will be included in the character total for each entry.

Code should operate correctly for all future dates through Dec 31 9999. Libraries which are standard in both C89 and C99 may be used, provided that appropriate headers are included. Note that standard date libraries may not be assumed to operate beyond the Unix limits.

Note: Code is permitted to perform Undefined Behavior if and only if the __DATE__ macro expands to a macro other than a date between Feb 11 2012 and Dec 31 9999 (expressed in that format, using C-standard abbreviated English month names Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)

Note 2: For compliance with C99 standards, 'main' should return an arbitrary, but defined value, and the newline is required. The first requirement, btw, adds 7 characters to my best effort; the single new-line adds 5.

\$\endgroup\$
16
  • 4
    \$\begingroup\$ This seems kind of pointless because there is already a UNIX timestamp that you can perform operations on to determine the date, and is constantly increasing. also, is this code golf? you should tag it as such \$\endgroup\$
    – Blazer
    Feb 10, 2012 at 23:38
  • \$\begingroup\$ Unix date does not match criteria to reach 2099: LC_ALL=C date -d "2060/02/13" date: invalid date 2060/02/13'` \$\endgroup\$ Feb 11, 2012 at 0:02
  • \$\begingroup\$ @Blazer: I would consider the use of a Unix timestamp to violate the prohibition against date-parting routines. The primary goal is to figure out an efficient way of converting "Jan/Feb/Mar/etc" into a sortable number. \$\endgroup\$
    – supercat
    Feb 11, 2012 at 0:17
  • \$\begingroup\$ @userunknown: Depends on the unix. 32 bit unix time runs out in 2038, I think, but 64 bit unix time...Scientific Linux 5.3 gives $ LC_ALL=C date -d "2060/02/13" Fri Feb 13 00:00:00 CST 2060 on x86_64. \$\endgroup\$ Feb 11, 2012 at 0:47
  • 1
    \$\begingroup\$ That said, this question need to be formed as a CodeGolf.Se compliant puzzle (with the winning condition specified and the like). Supercat, please feel welcome here, but also read the FAQ and examine some of the other questions here. I'll be happy to reopen this when you've tuned it up a little. Just submit a flag for help. \$\endgroup\$ Feb 11, 2012 at 0:49

4 Answers 4

3
\$\begingroup\$

C, 137 184 184 140 120 106 103 characters

Replaced the month name lookup with a magic formula.
The formula (m[1]*4388^m[2]*7)%252 is ascending for month names.
Changed it to nicely return 0, at no cost.
It no longer prints a number. Instead it prints a string, which should sort right.
Implemented supercat's %*s idea, which inserts more spaces for earlier months, along with a function that's descending for month names - (m[1]*29^m[2]+405)%49.

#include<stdio.h>
int main(void){
    char*m=__DATE__"%*.6s\n"+1;
    return!printf(m+6,(*m*29^m[1]+405)%49,m);
}

I thought single digit days are represented as Jan_1_2012 (_ being a space), when in fact it's Jan__1_2012 (extra space). This complicated things, so my previous versions were more complicated:

#include<stdio.h>
int main(void){
    char*m=__DATE__+1,*t=m+m[4]/16;
    return!printf("%s%3d%s\n",t+3,(*m*4388^m[1]*7)%252,t);
}
\$\endgroup\$
33
  • \$\begingroup\$ I hate to be nit-picky, but is the use of an unprototyped strstr function C99 compliant? Also, my original plan for the puzzle was to compute a single integer, but given the present requirements if you can save a few characters by assembling an output string you should do so. \$\endgroup\$
    – supercat
    Feb 12, 2012 at 17:51
  • \$\begingroup\$ Added #include<string.h>, sacrificing some more characters. I don't know if it's required by c99, but considering 64bit, where assuming strstr returns int can end badly, it's better like this. So now I'm barely ahead of the competition (but being ahead thanks to omitted includes isn't such a big deal). \$\endgroup\$
    – ugoren
    Feb 12, 2012 at 19:46
  • \$\begingroup\$ That sort of formula is the type of thing I was hoping to see in answers. I spent a lot of time crunching mine, but it still has an 11-character string literal in it. I think your program relies upon Undefined Behavior on a 32-bit machine when the year exceeds 8191, though. \$\endgroup\$
    – supercat
    Feb 14, 2012 at 14:26
  • \$\begingroup\$ Indeed, there's an integer overflow, which is harmless on normal compilers. y*252 keeps the numbers small, but is two characters more. Perhaps a U on one of the constants will promote everything to unsigned (but how to test it, given that it works as is in all compilers I know?) \$\endgroup\$
    – ugoren
    Feb 14, 2012 at 14:49
  • \$\begingroup\$ Adding a "u" to the constant 4388, 7, or 252 would I believe eliminate any Undefined Behavior, but compare the strings output by your program on "Jan 01 6000" with those for "Feb 14 2012". \$\endgroup\$
    – supercat
    Feb 14, 2012 at 16:01
1
\$\begingroup\$

C, 194 characters

#include <stdio.h>
#include <string.h>
d,y,n[3];
int main(void)
{
sscanf(__DATE__,"%s %d %d",n,&d,&y);
return printf("%d%02d%02d",y,13-strlen(strstr("JanFebMarAprMayJunJulAugSepOctNovDec",n))/3,d);
}

I think most of the newlines are unnecessary, but I've left them in for readability.

Not sure what your feelings are about compiler warnings - this throws a few but runs fine. Also not sure whether the declarations with no type are valid C89 or not.

\$\endgroup\$
2
  • \$\begingroup\$ I don't believe C99 allows for implicit declarations of variable type. Even if such declarations were permitted for variables of type int, your substitution of an array of int for an array of char is almost certainly illegitimate. I'll also suggest that trying to convert the month into a nice number from 1 to 12 isn't necessary. As a simplification, arrange the months in the other order and you could drop the "13-" and the "/3". December would yield 36 and January would yield 3. That would save you five characters right there. Also, you if you append the declaration of a char*... \$\endgroup\$
    – supercat
    Feb 11, 2012 at 23:33
  • \$\begingroup\$ ..with your declaration of n[], it might be profitable to define a variable for your "JanFeb"etc. string so you can subtract the strstr result from its base address. I don't think the strstr approach will get as short as my solution, no matter what you do, but it would be interesting to see how close it gets. \$\endgroup\$
    – supercat
    Feb 11, 2012 at 23:36
1
\$\begingroup\$

Here are the solutions that I had come up with (for brevity, I'm just writing the #include line once--copy and paste as needed to assemble a testable program).

(my entries)
#include <stdio.h>
int main(void){char*d=__DATE__"%s%sDFCwu-vBxE-t%c"+1;return printf(d+6,d+(*d+d[1])%17+9,d,10);}
int main(void){char*d="h-elbj-ikcfdga-"__DATE__+16;return printf("%s%s\n",d+6,d-(*d+d[1])%17);}
int main(void){char*d=__DATE__+1;return printf("%s%x%s\n",d+6,3**d+"![ WT -#[ 8"[d[1]%12],d);}
int main(void){char*d=__DATE__"%x%.5s\n"+1;return printf(d+6,3**d+"![ WT -#[ 8"[d[1]%12],d);}
int main(void){char*d=__DATE__"%x%.5s\n";return printf(d+7,d[1]+"1C0EB042E0:"[d[2]%12],d);}
(ugoren's entry, golfed, for comparison, and adding .5 to the %s format specifier)
int main(void){char*m=__DATE__"%3d%.5s\n"+1;return!printf(m+6,(*m*803^m[1]*95)%94,m);}

My initial approaches made use of a month-to-number approach which can turn each month into an arbitrary character. If in a production embedded-systems environment one had to turn an alphabetic month into a number 1-12, the approach might actually be an efficient algorithm to do so (perhaps using repeated subtract-17 instead of mod, if no other divisions are required).

Allowing the computation to return values which were sortable but not consecutive made it possible to use a smaller table indexed using the third character of the month, and add to that character the second. A further savings was achieved by realizing that the multiplication by three I'd used to facilitate "tiebreaking" was in fact not needed, since the months which would otherwise yield matching hash values were correctly sortable by their first character.

I tried quite a few variations, but nothing came close to the operator combination that ugoren found, which don't require any table. I was also impressed by his entry which would have accommodated single-digit dates without padding, which would have been a challenge in its own right, and one which I doubt that I could have handled as nicely.

\$\endgroup\$
1
  • \$\begingroup\$ Certainly nice solutions. All based on converting the month to a unique number, then using a table to make it sort right. Using just one character with the table, while breaking the ties using the other is also a nice idea. \$\endgroup\$
    – ugoren
    Feb 24, 2012 at 21:54
0
\$\begingroup\$

C, 163 characters

A different approach from my other solution.
I can save 13 characters by making t an int array, and relying on the internal layout of struct tm. But I guess it violates the rules.

#define _XOPEN_SOURCE
#include<time.h>
#include<stdio.h>
int main(void){
    struct tm t;
    strptime(__DATE__,"%b%d%Y",&t);
    return printf("%d\n",t.tm_year*366+t.tm_yday);
}
\$\endgroup\$
2
  • \$\begingroup\$ I should have been clearer in my wording, as I meant to imply that code should operate correctly in any standards-conforming C89 compiler and <i>also</i> work correctly with any standards-conforming C99 compiler. Still, since I was unclear about that, that wouldn't be a disqualifying factor if the C99 standard in fact states that the libraries will work all the way through Dec 31, 9999. Does the spec in fact promise that? \$\endgroup\$
    – supercat
    Feb 14, 2012 at 5:01
  • \$\begingroup\$ I actually have no idea. But since my other answer is much shorter, and makes no such assumptions, I don't think I'll bother checking it. \$\endgroup\$
    – ugoren
    Feb 14, 2012 at 7:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.