1996 fixes and updates
Update the README.md files - at least with those not in a previous commit. The updates mostly include formatting (make them markdown) but probably also typos here and there. More probably has to be done with these files. The 1996/august/.gitignore was updated as well with a file that is generated by one of the suggested commands.
This commit is contained in:
1 parent
2d6d05e053
commit
8870040156
9 files changed
+333
-314
No files matched your search
@@ -1,2 +1,3 @@
|
||||
august
|
||||
august.oc
|
||||
test.oo
|
||||
+87
-86
@@ -10,7 +10,7 @@
|
||||
|
||||
make all
|
||||
|
||||
### To run
|
||||
## To run:
|
||||
|
||||
cat august.c test.oc | ./august > test.oo
|
||||
./august < test.oo
|
||||
@@ -19,158 +19,159 @@
|
||||
If the above cat line does not execute in a very short amount
|
||||
of time, then you may need to fix your compiler or use gcc.
|
||||
|
||||
## Judges' comments
|
||||
## Try:
|
||||
|
||||
Try:
|
||||
cat august.c fac.oc | ./august > fac.oo
|
||||
./august < fac.oo
|
||||
|
||||
Or try:
|
||||
cat august.c fac.oc | ./august | ./august
|
||||
|
||||
And try:
|
||||
cat august.c parse.oc | ./august > parse.oo
|
||||
cat parse.oo fac.oc | ./august | ./august
|
||||
|
||||
This entry can feed on itself. If you C preprocess the source,
|
||||
you compile the interpreter:
|
||||
|
||||
## Judges' comments:
|
||||
|
||||
This entry can feed on itself. If you C preprocess the source,
|
||||
you compile the interpreter:
|
||||
|
||||
make august.oc
|
||||
|
||||
We can now compile the interpreter as follows:
|
||||
We can now compile the interpreter as follows:
|
||||
|
||||
cat august.c august.oc | ./august > august.oo
|
||||
|
||||
and use the compiled interpreter to execute some previously
|
||||
compiled code:
|
||||
and use the compiled interpreter to execute some previously
|
||||
compiled code:
|
||||
|
||||
cat august.oo test.oo | ./august
|
||||
|
||||
And we can have the compiled interpreter interpret itself which
|
||||
inturn compiles the test program:
|
||||
And we can have the compiled interpreter interpret itself which
|
||||
in turn compiles the test program:
|
||||
|
||||
cat august.oo august.oo fac.oo | ./august
|
||||
|
||||
And if you have lots of spare time, you can recurse one level deeper:
|
||||
And if you have lots of spare time, you can recurse one level deeper:
|
||||
|
||||
cat august.oo august.oo august.oo fac.oo | ./august
|
||||
|
||||
We (the judges) recommend that you spend some time studying this
|
||||
entry as it surely will make the 'best of the IOCCC' list. It was
|
||||
very well received by those who attended the IOCCC BOF.
|
||||
We (the judges) recommend that you spend some time studying this
|
||||
entry as it surely will make the 'best of the IOCCC' list. It was
|
||||
very well received by those who attended the IOCCC BOF.
|
||||
|
||||
## Author's comments
|
||||
|
||||
This program is a bytecode interpreter. This fact is not
|
||||
particularly obfuscated; any experienced C programmer can see that
|
||||
in about a minute.
|
||||
This program is a bytecode interpreter. This fact is not
|
||||
particularly obfuscated; any experienced C programmer can see that
|
||||
in about a minute.
|
||||
|
||||
The interpreter reads the bytecode from the standard input. What
|
||||
remains of the standard input after this is left as standard input for
|
||||
the interpreted program.
|
||||
The interpreter reads the bytecode from the standard input. What
|
||||
remains of the standard input after this is left as standard input for
|
||||
the interpreted program.
|
||||
|
||||
Having an interpreter but no code isn't any fun. But within a comment
|
||||
in the beginning of the interpreter is a sequence of bytes that is
|
||||
code that the interpreter understands. It is in fact the code for a
|
||||
compiler that compiles from OC (Obfuscated C, a subset of C) into the
|
||||
byte code that the interpreter understands.
|
||||
Having an interpreter but no code isn't any fun. But within a comment
|
||||
in the beginning of the interpreter is a sequence of bytes that is
|
||||
code that the interpreter understands. It is in fact the code for a
|
||||
compiler that compiles from OC (Obfuscated C, a subset of C) into the
|
||||
byte code that the interpreter understands.
|
||||
|
||||
The language the compiler understands is a subset of C (the grammar
|
||||
is in grammar.small) with the functions getchar() and putchar()
|
||||
available. The subset has the types `int', `char', and pointers to
|
||||
those (it has function pointers too, but not with the proper
|
||||
syntax). Arrays can only be global. There is a rather limited set
|
||||
of operators, and control constructs.
|
||||
The language the compiler understands is a subset of C (the grammar
|
||||
is in grammar.small) with the functions getchar() and putchar()
|
||||
available. The subset has the types `int', `char', and pointers to
|
||||
those (it has function pointers too, but not with the proper
|
||||
syntax). Arrays can only be global. There is a rather limited set
|
||||
of operators, and control constructs.
|
||||
|
||||
The compiler has absolutely no error checking, so things
|
||||
can go wrong. But this is what seasoned C progammers are used to.
|
||||
The compiler has absolutely no error checking, so things
|
||||
can go wrong. But this is what seasoned C programmers are used to.
|
||||
|
||||
Assume that the interpreter has been compiled (source in prog.c and
|
||||
binary in prog) and that we have this in a file named test.oc:
|
||||
Assume that the interpreter has been compiled (source in prog.c and
|
||||
binary in prog) and that we have this in a file named test.oc:
|
||||
|
||||
main() { putchar('!'); putchar('\n'); exit(0); }
|
||||
|
||||
We can then compile by
|
||||
We can then compile by
|
||||
|
||||
cat august.c test.oc | ./august > test.oo
|
||||
|
||||
And run the compiled program by
|
||||
And run the compiled program by
|
||||
|
||||
./august < test.oo
|
||||
|
||||
Or even simpler:
|
||||
Or even simpler:
|
||||
|
||||
cat august.c test.oc | ./august | ./august
|
||||
|
||||
A larger example is included fac.oc.
|
||||
A larger example is included fac.oc.
|
||||
|
||||
The compiler for OC is naturally written in OC (where did you think
|
||||
the byte code came from?). It is in the file parse.oc.
|
||||
The compiler for OC is naturally written in OC (where did you think
|
||||
the byte code came from?). It is in the file parse.oc.
|
||||
|
||||
Hmm... So now we have an interpreter written in OC and a compiler
|
||||
for OC, can we compile the interpreter? Yes, we can! The OC
|
||||
compiler does not have a preprocessor (hey, what do you expect from
|
||||
a program that is this small), so we need to use an external one.
|
||||
To preprocess august.c use the command from the build file, but add
|
||||
the -E flag and change 60000 to 40000, i.e.:
|
||||
Hmm... So now we have an interpreter written in OC and a compiler
|
||||
for OC, can we compile the interpreter? Yes, we can! The OC
|
||||
compiler does not have a preprocessor (hey, what do you expect from
|
||||
a program that is this small), so we need to use an external one.
|
||||
To preprocess august.c use the command from the build file, but add
|
||||
the -E flag and change 60000 to 40000, i.e.:
|
||||
|
||||
cc -E -DZ=40000 .... august.c > prog.oc
|
||||
|
||||
If prog.oc contain some junk line starting with a `#' (most likely
|
||||
it does) then remove it.
|
||||
If prog.oc contain some junk line starting with a `#' (most likely
|
||||
it does) then remove it.
|
||||
|
||||
OK, we can now compile the interpreter:
|
||||
OK, we can now compile the interpreter:
|
||||
|
||||
cat august.c prog.oc | ./august > prog.oo
|
||||
|
||||
And we can run it:
|
||||
And we can run it:
|
||||
|
||||
cat prog.oo test.oo | ./august
|
||||
|
||||
Here we have the interpreter interpreting another interpreter that runs
|
||||
the program. Did you think that was too fast? Just throw in another
|
||||
level of interpretation:
|
||||
Here we have the interpreter interpreting another interpreter that runs
|
||||
the program. Did you think that was too fast? Just throw in another
|
||||
level of interpretation:
|
||||
|
||||
cat prog.oo prog.oo test.oo | ./august
|
||||
|
||||
And another...
|
||||
And another...
|
||||
|
||||
cat prog.oo prog.oo prog.oo test.oo | ./august
|
||||
|
||||
*****
|
||||
*****
|
||||
|
||||
Fitting the byte code for the compiler into the initial comment
|
||||
was a challenge. I had to strip the compiler/interpreter of
|
||||
a lot of functionality to bring down their sizes. The initial
|
||||
version had a full set of binary operators as well as prefix &.
|
||||
Given a handful more bytes it would be easy to put them back.
|
||||
Fitting the byte code for the compiler into the initial comment
|
||||
was a challenge. I had to strip the compiler/interpreter of
|
||||
a lot of functionality to bring down their sizes. The initial
|
||||
version had a full set of binary operators as well as prefix &.
|
||||
Given a handful more bytes it would be easy to put them back.
|
||||
|
||||
Anyway, the bytecode from the compiler is first optimized in
|
||||
an external optimizer (not supplied here); this brings down the
|
||||
size of it by about 30%. The remaining code is then LZ-compressed
|
||||
(roughly) with a sliding window of 255 bytes. The most common
|
||||
occuring bytes are then encoded using " \n\t;{}" to avoid having
|
||||
them counted as significant. About 200 bytes of the interpreter
|
||||
handles the unpacking of the byte code (this is a lot, but it pays
|
||||
off).
|
||||
Anyway, the bytecode from the compiler is first optimized in
|
||||
an external optimizer (not supplied here); this brings down the
|
||||
size of it by about 30%. The remaining code is then LZ-compressed
|
||||
(roughly) with a sliding window of 255 bytes. The most common
|
||||
occuring bytes are then encoded using " \n\t;{}" to avoid having
|
||||
them counted as significant. About 200 bytes of the interpreter
|
||||
handles the unpacking of the byte code (this is a lot, but it pays
|
||||
off).
|
||||
|
||||
Despite all this work I still had to put a lot of code in the
|
||||
compilation command. This disturbs me. It is possible to compress
|
||||
the code further with a better entropy coder (e.g. an arithmetic
|
||||
coder), but decompression then takes up too much code. Maybe this
|
||||
would be a way to fit it all into the 1536 bytes, but I'm don't have
|
||||
time to try it.
|
||||
Despite all this work I still had to put a lot of code in the
|
||||
compilation command. This disturbs me. It is possible to compress
|
||||
the code further with a better entropy coder (e.g. an arithmetic
|
||||
coder), but decompression then takes up too much code. Maybe this
|
||||
would be a way to fit it all into the 1536 bytes, but I'm don't have
|
||||
time to try it.
|
||||
|
||||
*****
|
||||
*****
|
||||
|
||||
Execution starts at main(). Nothing can be used before it is
|
||||
defined. getchar() and putchar() are predefined functions.
|
||||
Execution starts at main(). Nothing can be used before it is
|
||||
defined. getchar() and putchar() are predefined functions.
|
||||
|
||||
The following is a description of the OC grammar:
|
||||
The following is a description of the OC grammar:
|
||||
|
||||
OC grammar
|
||||
==========
|
||||
Terminals are in quotes, () is used for bracketing.
|
||||
OC grammar
|
||||
==========
|
||||
|
||||
Terminals are in quotes, () is used for bracketing.
|
||||
|
||||
program: decl*
|
||||
|
||||
@@ -228,6 +229,6 @@
|
||||
|
||||
stars: "*"*
|
||||
|
||||
*****
|
||||
*****
|
||||
|
||||
Well, that is enough ranting for now.
|
||||
Well, that is enough ranting for now.
|
||||
@@ -51,7 +51,7 @@ We (the judges) recommend that you take the time needed to understand
|
||||
how this program works. The source is small enough to make the effort
|
||||
reasonable, and complex enough to make it interesting.
|
||||
|
||||
## Author's comments
|
||||
## Author's comments:
|
||||
|
||||
This program assumes that your terminal wraps lines automatically.
|
||||
If this is not the case, you may need to change the space in the
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
|
||||
./eldby
|
||||
|
||||
WARNING: on modern systems this is very fast and could be a problem for those
|
||||
susceptible to photosensitivity. Please try this one with caution if this
|
||||
applies to you! The same goes for people who can be overstimulated.
|
||||
|
||||
## Judges' comments
|
||||
|
||||
We were impressed by the author's ability to render spheres in 3D
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
echo 'Huffman Decoding' | ./huffman
|
||||
|
||||
NOTE: this program uses `gets()` so you will likely get a warning when compiling
|
||||
and/or running on modern systems.
|
||||
|
||||
## Try:
|
||||
|
||||
echo 'seeing or feeling is believing' | ./huffman
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
make all
|
||||
|
||||
### To run
|
||||
### To run:
|
||||
|
||||
./jonth
|
||||
|
||||
@@ -20,18 +20,18 @@
|
||||
|
||||
./jonth host1:0 host2:0
|
||||
|
||||
## Judges' comments
|
||||
## Judges' comments:
|
||||
|
||||
|
||||
For extra credit, figure out how you can cheat by taking over
|
||||
a square already occupied by an opponent.
|
||||
For extra credit, figure out how you can cheat by taking over
|
||||
a square already occupied by an opponent.
|
||||
|
||||
## Author's comments
|
||||
## Author's comments:
|
||||
|
||||
This is multi-user tic-tac-toe for X Windows. You may give one or two X
|
||||
displays as arguments to this game.
|
||||
|
||||
Known bugs:
|
||||
### Known bugs:
|
||||
|
||||
Sometimes on Solaris X gets confused and this program core dumps,
|
||||
solution: give two display arguments (prog host:0 host:0)
|
||||
|
||||
+29
-29
@@ -20,50 +20,50 @@ For more information try:
|
||||
./rcm < rfc1952.gz
|
||||
|
||||
|
||||
## Judges' comments
|
||||
## Judges' comments:
|
||||
|
||||
For a good noop try:
|
||||
|
||||
gzip -c < rcm.c | ./rcm
|
||||
|
||||
## Author's comments
|
||||
## Author's comments:
|
||||
|
||||
Except for some silly requirements regarding input validation, CRC
|
||||
checking, and similar unimportant fluff, this program is a fully
|
||||
compliant RFC1951/RFC1952 (GNU Gzip) file uncompressor. Feed it a
|
||||
"gzip" compressed file on standard input, and it will write the fully
|
||||
uncompressed original file to standard output.
|
||||
Except for some silly requirements regarding input validation, CRC
|
||||
checking, and similar unimportant fluff, this program is a fully
|
||||
compliant RFC1951/RFC1952 (GNU Gzip) file uncompressor. Feed it a
|
||||
"gzip" compressed file on standard input, and it will write the fully
|
||||
uncompressed original file to standard output.
|
||||
|
||||
ObJustifications for this entry:
|
||||
### ObJustifications for this entry:
|
||||
|
||||
The Design Trade-off Argument
|
||||
#### The Design Trade-off Argument:
|
||||
|
||||
"The 184 characters worth of defines in the build file have been
|
||||
carefully offset by 184 redundant and unecessary '{', '}', and ';'
|
||||
characters in the source file."
|
||||
"The 184 characters worth of defines in the build file have been
|
||||
carefully offset by 184 redundant and unnecessary '{', '}', and ';'
|
||||
characters in the source file."
|
||||
|
||||
The Design Review Argument
|
||||
#### The Design Review Argument:
|
||||
|
||||
"Using only 216 characters out of a maximum of 256, the build file
|
||||
meets and actually exceeds specification."
|
||||
"Using only 216 characters out of a maximum of 256, the build file
|
||||
meets and actually exceeds specification."
|
||||
|
||||
The Program Metrics Argument
|
||||
#### The Program Metrics Argument:
|
||||
|
||||
"The program source is less than 3,100 characters in length and
|
||||
uncompresses file `emacs-19.34b.tar.gz' in about 130 seconds on
|
||||
my HP 9000/735. The now obsolete source file `inflate.c' from
|
||||
the GNU gzip source tree is 31,613 characters in length and
|
||||
uncompresses the emacs distribution file in slightly under 25
|
||||
seconds. Thus:
|
||||
"The program source is less than 3,100 characters in length and
|
||||
uncompresses file `emacs-19.34b.tar.gz' in about 130 seconds on
|
||||
my HP 9000/735. The now obsolete source file `inflate.c' from
|
||||
the GNU gzip source tree is 31,613 characters in length and
|
||||
uncompresses the emacs distribution file in slightly under 25
|
||||
seconds. Thus:
|
||||
|
||||
3100 / 130 = 23.85 -vs- 31613 / 25 = 1264.52
|
||||
|
||||
which represents a better than 530% improvement in the ratio of
|
||||
source file size to execution time."
|
||||
which represents a better than 530% improvement in the ratio of
|
||||
source file size to execution time."
|
||||
|
||||
The Whining Programmer's Argument
|
||||
#### The Whining Programmer's Argument:
|
||||
|
||||
"The inflate algorithm requires that over 400 characters worth
|
||||
of constants and tables be defined. Which means the program
|
||||
itself had to be squeezed down to about 1,100 IOCCC countable
|
||||
bytes."
|
||||
"The inflate algorithm requires that over 400 characters worth
|
||||
of constants and tables be defined. Which means the program
|
||||
itself had to be squeezed down to about 1,100 IOCCC countable
|
||||
bytes."
|
||||
+98
-86
@@ -12,119 +12,131 @@
|
||||
|
||||
make all
|
||||
|
||||
### To run
|
||||
## To run:
|
||||
|
||||
./schweikh2
|
||||
|
||||
## Judges' comments
|
||||
The program accepts up to 4 parameters:
|
||||
|
||||
Also try:
|
||||
0. The number of operators in the expression [default: 9]
|
||||
|
||||
1. The value of the first operand [default: 1]
|
||||
|
||||
2. The (possibly negative) increment to the next operand [default: 1]
|
||||
|
||||
3. The goal value or 0/0 [default: 42]
|
||||
|
||||
|
||||
## Try:
|
||||
|
||||
./schweikh2 15 1 0 0/0
|
||||
|
||||
The program accepts up to 4 parameters:
|
||||
What happens if you pass only three parameters like the below?
|
||||
|
||||
1) The number of operators in the expression [default: 9]
|
||||
./schweikh2 15 1 0
|
||||
|
||||
2) The value of the first operand [default: 1]
|
||||
## Judges' comments:
|
||||
|
||||
3) The (possibly negative) increment to the next operand [default: 1]
|
||||
|
||||
4) The goal value or 0/0 [default: 42]
|
||||
|
||||
The original program, with only slight obfuscations, may be found
|
||||
in hunni.c. The perl script, obfuscates it appropriately:
|
||||
The original program, with only slight obfuscations, may be found
|
||||
in `hunni.c`. The perl script, obfuscates it appropriately:
|
||||
|
||||
perl ./hunni.pl < hunni.c
|
||||
|
||||
This entry was another crowd pleaser at the IOCCC BOF.
|
||||
This entry was another crowd pleaser at the IOCCC BOF.
|
||||
|
||||
## Author's comments
|
||||
## Author's comments:
|
||||
|
||||
Note: the source must be viewed with tabstop 8. To appreciate the
|
||||
beauty have your eyes half closed.
|
||||
Note: the source must be viewed with tabstop 8. To appreciate the
|
||||
beauty have your eyes half closed.
|
||||
|
||||
Why I think this program is obfuscated:
|
||||
### Why I think this program is obfuscated:
|
||||
|
||||
C is a free format language. It is therefore a question of style how
|
||||
variables, operators, declarations, functions and syntactic elements
|
||||
like braces and parentheses are grouped together. As a rule of thumb,
|
||||
I put spaces around operators, after opening braces and before closing
|
||||
braces. I have generalized this rule by putting whitespace after *any*
|
||||
non-white space character (with the exception of preprocessor directives).
|
||||
There are no recognizable keywords anymore. There are no recognizable
|
||||
string literals any more. The main function is invisible. All operators
|
||||
longer than one character have gone. They have been replaced by macro
|
||||
calls with quite a number of arguments. The keys are the token pasting
|
||||
and stringizing macros e and O. The "arcane rules of token pasting"
|
||||
[K&R2] are used up to three levels, H(O,r)R(O,r)! which is needed to make
|
||||
one 8-character token from 8 characters ('unsigned'). BTW, C has no 9 or
|
||||
more character reserved words, so a fourth level is only required if
|
||||
you intend to generate object names from 9 or more components. The final
|
||||
round even swaps the tokens, so all keywords must be spelled backwards
|
||||
(sort of, can you spot printf in the P macro?).
|
||||
C is a free format language. It is therefore a question of style how
|
||||
variables, operators, declarations, functions and syntactic elements
|
||||
like braces and parentheses are grouped together. As a rule of thumb,
|
||||
I put spaces around operators, after opening braces and before closing
|
||||
braces. I have generalized this rule by putting whitespace after *any*
|
||||
non-white space character (with the exception of preprocessor directives).
|
||||
There are no recognizable keywords anymore. There are no recognizable
|
||||
string literals any more. The main function is invisible. All operators
|
||||
longer than one character have gone. They have been replaced by macro
|
||||
calls with quite a number of arguments. The keys are the token pasting
|
||||
and stringizing macros e and O. The "arcane rules of token pasting"
|
||||
[K&R2] are used up to three levels, `H(O,r)R(O,r)` (!) which is needed to make
|
||||
one 8-character token from 8 characters ('unsigned'). BTW, C has no 9 or
|
||||
more character reserved words, so a fourth level is only required if
|
||||
you intend to generate object names from 9 or more components. The final
|
||||
round even swaps the tokens, so all keywords must be spelled backwards
|
||||
(sort of, can you spot printf in the P macro?).
|
||||
|
||||
Try to indent the source. The unbalanced parentheses confuse Solaris'
|
||||
indent to the max (although the result does compile):
|
||||
Try to indent the source. The unbalanced parentheses confuse Solaris'
|
||||
indent to the max (although the result does compile):
|
||||
|
||||
Looking at the indented source is even *less* fun, because the nice
|
||||
diamond pattern has been replaced by something that looks like, uhm,
|
||||
modem noise.
|
||||
Looking at the indented source is even *less* fun, because the nice
|
||||
diamond pattern has been replaced by something that looks like, uhm,
|
||||
modem noise.
|
||||
|
||||
Lint the source. It's clean. How obfuscated...
|
||||
Lint the source. It's clean. How obfuscated...
|
||||
|
||||
What's the exit status? The program exits with J - 1 where J is the
|
||||
return value from several non-void functions (needed to shut up
|
||||
lint). The last assignment is J = printf ("%c", 10); so the exit
|
||||
status is 0 unless the last printf failed, which is even a *useful*
|
||||
status. A further obfuscation is the use of 10 instead of '\n'.
|
||||
'\n' can not be constructed by token pasting, AFAIK. That's why schweikh2
|
||||
is likely to produce occasional garbage on non-ASCII execution character
|
||||
set systems.
|
||||
What's the exit status? The program exits with J - 1 where J is the
|
||||
return value from several non-void functions (needed to shut up
|
||||
lint). The last assignment is J = printf ("%c", 10); so the exit
|
||||
status is 0 unless the last printf failed, which is even a *useful*
|
||||
status. A further obfuscation is the use of 10 instead of '\n'.
|
||||
'\n' can not be constructed by token pasting, AFAIK. That's why `schweikh2`
|
||||
is likely to produce occasional garbage on non-ASCII execution character
|
||||
set systems.
|
||||
|
||||
What this program does:
|
||||
-----------------------
|
||||
|
||||
The program is an expression evaluator for a limited class of
|
||||
expressions. Known operators are + - * /, operands are real numbers
|
||||
(integers or fractions). The operands are restricted in that they must
|
||||
differ by a constant value, for example 1 + 2 * 3 / 4 - 5. For any
|
||||
combination of operators the expression is evaluated and compared to a
|
||||
goal value. If they compare equal the expression is printed. If the goal
|
||||
value is specified as 0/0 all expressions and results are printed. For
|
||||
any fraction, the numerator and denominator are divided by their
|
||||
greatest common divisor.
|
||||
What this program does:
|
||||
-----------------------
|
||||
|
||||
The program accepts up to 4 parameters:
|
||||
1) The number of operators in the expression [default: 9]
|
||||
2) The value of the first operand [1]
|
||||
3) The (possibly negative) increment to the next operand [1]
|
||||
4) The goal value or 0/0 [42]
|
||||
The program is an expression evaluator for a limited class of
|
||||
expressions. Known operators are + - * /, operands are real numbers
|
||||
(integers or fractions). The operands are restricted in that they must
|
||||
differ by a constant value, for example 1 + 2 * 3 / 4 - 5. For any
|
||||
combination of operators the expression is evaluated and compared to a
|
||||
goal value. If they compare equal the expression is printed. If the goal
|
||||
value is specified as 0/0 all expressions and results are printed. For
|
||||
any fraction, the numerator and denominator are divided by their
|
||||
greatest common divisor.
|
||||
|
||||
On ANSI/ISO C conformant systems (where long is at least 32 bits)
|
||||
overflow will occur in the default case (first operand 1, increment 1)
|
||||
only for more than 11 operators. Because a bit mask is used that needs
|
||||
twice the number of operands in bits (plus a sentinel value) on those
|
||||
systems the first parameter must not exceed 15.
|
||||
The program accepts up to 4 parameters:
|
||||
1) The number of operators in the expression [default: 9]
|
||||
2) The value of the first operand [1]
|
||||
3) The (possibly negative) increment to the next operand [1]
|
||||
4) The goal value or 0/0 [42]
|
||||
|
||||
If long is at least 64 bits overflow will occur for more than 19
|
||||
operators but you will probably wait quite some time for every of the
|
||||
4^19 = 274,877,906,944 combinations to be evaluated... While I'm at it,
|
||||
on a 50 MHz sparc, 4^11 expressions are evaluated in about 3 cpu
|
||||
minutes (gcc -O3).
|
||||
On ANSI/ISO C conformant systems (where long is at least 32 bits)
|
||||
overflow will occur in the default case (first operand 1, increment 1)
|
||||
only for more than 11 operators. Because a bit mask is used that needs
|
||||
twice the number of operands in bits (plus a sentinel value) on those
|
||||
systems the first parameter must not exceed 15.
|
||||
|
||||
Sample arguments:
|
||||
-----------------
|
||||
$ schweikh2 6
|
||||
$ schweikh2 7 0
|
||||
$ schweikh2 7 7 0
|
||||
$ schweikh2 8 9 -1 -67/21
|
||||
$ schweikh2 8 2 2/5 -98/25
|
||||
$ schweikh2 2 1/0 1 1/0
|
||||
If long is at least 64 bits overflow will occur for more than 19
|
||||
operators but you will probably wait quite some time for every of the
|
||||
4^19 = 274,877,906,944 combinations to be evaluated... While I'm at it,
|
||||
on a 50 MHz sparc, 4^11 expressions are evaluated in about 3 cpu
|
||||
minutes (gcc -O3).
|
||||
|
||||
Sample arguments:
|
||||
-----------------
|
||||
|
||||
$ ./schweikh2 6
|
||||
$ ./schweikh2 7 0
|
||||
$ ./schweikh2 7 7 0
|
||||
$ ./schweikh2 8 9 -1 -67/21
|
||||
$ ./schweikh2 8 2 2/5 -98/25
|
||||
$ ./schweikh2 2 1/0 1 1/0
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
The diamond pattern can be continued without a break.
|
||||
For a nice printout try this on your Bourne compatible shell:
|
||||
|
||||
|
||||
% while :; do grep -v '#' schweikh2.c; done
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
The diamond pattern can be continued without a break.
|
||||
For a nice printout try this on your Bourne compatible shell
|
||||
% while :; do grep -v '#' schweikh2.c; done
|
||||
+104
-106
@@ -12,57 +12,58 @@
|
||||
|
||||
make all
|
||||
|
||||
### To run
|
||||
## To run:
|
||||
|
||||
WARNING: Do not run this program without reading this text down to the end.
|
||||
It may render your system unusable for a limited amount of time
|
||||
or force you to reboot using the Big Red Button! This is not a joke.
|
||||
You have been warned.
|
||||
|
||||
./schweikh3
|
||||
|
||||
## Judges' comments
|
||||
|
||||
The source fails to compile on compilers that recognize the inline
|
||||
keyword by default. For gcc, you may need to use -ansi.
|
||||
|
||||
See the file, schweikh3.info for example output from various systems.
|
||||
|
||||
But still it is a useful utility. To use try:
|
||||
**WARNING**: Do not run this program without reading this text down to the end!
|
||||
It may render your system unusable for a limited amount of time
|
||||
or force you to reboot using the Big Red Button! This is **NOT** a joke.
|
||||
You have been warned.
|
||||
|
||||
./schweikh3
|
||||
|
||||
and thanks for the virtual memories! :-)
|
||||
|
||||
NOTE: On some systems such as SunOS, one may need to compile with:
|
||||
## Judges' comments:
|
||||
|
||||
${CC} ${CFLAGS} \
|
||||
-I. -D_POSIX_SOURCE '-Ddifftime(a,b)=(double)(b-a)' \
|
||||
schweikh3.c -o schweikh3
|
||||
The source fails to compile on compilers that recognize the inline
|
||||
keyword by default. For gcc, you may need to use -ansi.
|
||||
|
||||
## Author's comments
|
||||
See the file, `schweikh3.info` for example output from various systems.
|
||||
|
||||
Why I think my entry is obfuscated
|
||||
----------------------------------
|
||||
But still it is a useful utility. To use try:
|
||||
|
||||
There are some layout obfuscations that hinder readability, like
|
||||
some (but not all) one character tokens in column 1. Includes,
|
||||
defines and declarations of data and functions appear intermixed.
|
||||
iso646.h is even included in the middle of an expression, how
|
||||
disgusting. Besides, requiring iso646.h will render the program
|
||||
uncompilable on many systems that have not yet incorporated
|
||||
Amendment One to The Standard. If you, dear reader, suffer from
|
||||
this disease, just read iso646.h.
|
||||
./schweikh3
|
||||
|
||||
There are no `if' statements. They have been replaced by
|
||||
"switch () case 1:" or "switch () case 0:" etc. At least one
|
||||
switch has a declaration after the opening brace, booh!
|
||||
Identifiers are named after random programming languages, Basic,
|
||||
Cobol, F77, awk etc.
|
||||
and thanks for the virtual memories! :-)
|
||||
|
||||
You have to select maximum ANSI C conformance of your
|
||||
compiler. The source fails to compile under a C++ compiler due to
|
||||
the `new' identifier which is a C++ reserved word:
|
||||
NOTE: On some systems such as SunOS, one may need to compile with:
|
||||
|
||||
${CC} ${CFLAGS} \
|
||||
-I. -D_POSIX_SOURCE '-Ddifftime(a,b)=(double)(b-a)' \
|
||||
schweikh3.c -o schweikh3
|
||||
|
||||
## Author's comments:
|
||||
|
||||
Why I think my entry is obfuscated
|
||||
----------------------------------
|
||||
|
||||
There are some layout obfuscations that hinder readability, like
|
||||
some (but not all) one character tokens in column 1. Includes,
|
||||
defines and declarations of data and functions appear intermixed.
|
||||
iso646.h is even included in the middle of an expression, how
|
||||
disgusting. Besides, requiring iso646.h will render the program
|
||||
uncompilable on many systems that have not yet incorporated
|
||||
Amendment One to The Standard. If you, dear reader, suffer from
|
||||
this disease, just read iso646.h.
|
||||
|
||||
There are no `if' statements. They have been replaced by
|
||||
"switch () case 1:" or "switch () case 0:" etc. At least one
|
||||
switch has a declaration after the opening brace, booh!
|
||||
Identifiers are named after random programming languages, Basic,
|
||||
Cobol, F77, awk etc.
|
||||
|
||||
You have to select maximum ANSI C conformance of your
|
||||
compiler. The source fails to compile under a C++ compiler due to
|
||||
the `new' identifier which is a C++ reserved word:
|
||||
|
||||
$ g++ prog.c
|
||||
prog.c:9: parse error before `new'
|
||||
@@ -73,8 +74,8 @@
|
||||
[...]
|
||||
prog.c:91: confused by earlier errors, bailing out
|
||||
|
||||
The source fails to compile on compilers that recognize the inline
|
||||
keyword by default, e.g. gcc:
|
||||
The source fails to compile on compilers that recognize the inline
|
||||
keyword by default, e.g. gcc:
|
||||
|
||||
$ gcc prog.c
|
||||
prog.c:46: parse error before `void'
|
||||
@@ -83,84 +84,81 @@
|
||||
prog.c: At top level:
|
||||
prog.c:97: parse error before `void'
|
||||
|
||||
The -ansi option to gcc fixes this.
|
||||
The `-ansi` option to gcc fixes this.
|
||||
|
||||
The source fails to compile on obsolete systems with obsolete
|
||||
memory models due to the identifier `far' (to be honest: this is
|
||||
untested :-).
|
||||
The source fails to compile on obsolete systems with obsolete
|
||||
memory models due to the identifier `far' (to be honest: this is
|
||||
untested :-).
|
||||
|
||||
Does your indent cope with backslash/newline pairs inbetween
|
||||
keywords like in "re\<newline>turn"? Solaris' indent inserts spaces
|
||||
and thereby renders the source uncompilable...
|
||||
Does your indent cope with backslash/newline pairs inbetween
|
||||
keywords like in "re\<newline>turn"? Solaris' indent inserts spaces
|
||||
and thereby renders the source uncompilable...
|
||||
|
||||
The indented source will exhibit different semantics when indenting
|
||||
changes the number of lines (due to using the __LINE__ macro in an
|
||||
expression).
|
||||
The indented source will exhibit different semantics when indenting
|
||||
changes the number of lines (due to using the __LINE__ macro in an
|
||||
expression).
|
||||
|
||||
And what about sizeof __TIME__? This one's for the philosophers
|
||||
and physicists among us programmers. What is it? sizeof (char *) -- no.
|
||||
Strlen ("hh:mm:ss")? -- no. It's the same as sizeof __DATE__ -
|
||||
sizeof "no" :-)
|
||||
And what about sizeof __TIME__? This one's for the philosophers
|
||||
and physicists among us programmers. What is it? sizeof (char *)? -- no.
|
||||
strlen ("hh:mm:ss")? -- no. It's the same as sizeof __DATE__ -
|
||||
sizeof "no" :-)
|
||||
|
||||
The program is POSIX conformant and lints without warning under
|
||||
Solaris' Sunpro lint. Allocated memory is correctly freed on all
|
||||
possible execution paths.
|
||||
The program is POSIX conformant and lints without warning under
|
||||
Solaris' Sunpro lint. Allocated memory is correctly freed on all
|
||||
possible execution paths.
|
||||
|
||||
What this program does
|
||||
----------------------
|
||||
What this program does:
|
||||
----------------------
|
||||
|
||||
This program tests your operating system's allocation honesty. It
|
||||
forks a child that will allocate as much as possible with byte
|
||||
granularity and then write to every 1024th byte, at the same time
|
||||
writing a status report on stdout. This way each page of the
|
||||
allocated virtual memory will eventually be referenced. Without
|
||||
any arguments, malloc() will be used to allocate memory. If the
|
||||
first arg starts with c, calloc() will be used instead.
|
||||
This program tests your operating system's allocation honesty. It
|
||||
forks a child that will allocate as much as possible with byte
|
||||
granularity and then write to every 1024th byte, at the same time
|
||||
writing a status report on stdout. This way each page of the
|
||||
allocated virtual memory will eventually be referenced. Without
|
||||
any arguments, malloc() will be used to allocate memory. If the
|
||||
first arg starts with c, calloc() will be used instead.
|
||||
|
||||
I distinguish three different behaviours:
|
||||
I distinguish three different behaviours:
|
||||
|
||||
1) OS without memory overcommit:
|
||||
malloc does not lie -- you can use all memory the OS granted to you.
|
||||
1. OS without memory overcommit: malloc does not lie -- you can use all memory
|
||||
the OS granted to you.
|
||||
|
||||
2) OS with memory overcommit:
|
||||
malloc lies -- any program may get killed at random when the
|
||||
OS can not acquire a free page.
|
||||
2. OS with memory overcommit: malloc lies -- any program may get killed at
|
||||
random when the OS can not acquire a free page.
|
||||
|
||||
3) OS with pseudo memory overcommit:
|
||||
malloc lies -- The OS puts processes to sleep for which it can
|
||||
not acquire a free page. Because other processes are also
|
||||
affected this way, the system slows down and quickly becomes
|
||||
totally unusable (also known as "thrashing"). Because more and
|
||||
more processes wait for memory returned by other exiting processes
|
||||
the system eventually enters a deadlock situation and effectively
|
||||
halts.
|
||||
3. OS with pseudo memory overcommit: malloc lies -- The OS puts processes to
|
||||
sleep for which it can not acquire a free page. Because other processes are also
|
||||
affected this way, the system slows down and quickly becomes totally unusable
|
||||
(also known as "thrashing"). Because more and more processes wait for memory
|
||||
returned by other exiting processes the system eventually enters a deadlock
|
||||
situation and effectively halts.
|
||||
|
||||
I had the pleasure and pain to find all three behaviours on the
|
||||
platforms I use regularly. What follows are the outputs for
|
||||
Solaris, FreeBSD and Linux. The Linux case is interesting in that
|
||||
the behaviour changes when calloc is used instead of malloc.
|
||||
For FreeBSD, the last two lines are not messages stemming from
|
||||
my program but issued by the syslog daemon.
|
||||
I had the pleasure and pain to find all three behaviours on the
|
||||
platforms I use regularly. What follows are the outputs for
|
||||
Solaris, FreeBSD and Linux. The Linux case is interesting in that
|
||||
the behaviour changes when calloc is used instead of malloc.
|
||||
For FreeBSD, the last two lines are not messages stemming from
|
||||
my program but issued by the syslog daemon.
|
||||
|
||||
Implementation remarks
|
||||
----------------------
|
||||
Implementation remarks
|
||||
----------------------
|
||||
|
||||
How can we distinguish the three different OS types from the
|
||||
table above?
|
||||
How can we distinguish the three different OS types from the
|
||||
table above?
|
||||
|
||||
Obviously, type 1), no overcommit, is easy.
|
||||
Obviously, type 1), no overcommit, is easy.
|
||||
|
||||
For type 2), random kill, we need to fork a child that can be killed
|
||||
and whose exit status the parent collects and decodes. Here's
|
||||
where POSIX enters the scene.
|
||||
For type 2), random kill, we need to fork a child that can be killed
|
||||
and whose exit status the parent collects and decodes. Here's
|
||||
where POSIX enters the scene.
|
||||
|
||||
Type 3), thrashing, was the hardest part. The first attempt was to
|
||||
measure the elapsed processor time it takes to write a single byte
|
||||
(using clock()). If a certain threshold is reached I assume the
|
||||
system is thrashing. When thrashing, however, the process does not
|
||||
consume processor time in a noticeable amount any longer and I lose.
|
||||
So I switched to measuring wall clock time using time(). On the one
|
||||
system that I observe thrashing (Linux) this works (although the
|
||||
value returned by time seems to 'hang' sometimes when the OS thrashes.
|
||||
Exceeding a threshold of 5 seconds may be detected only after half a
|
||||
minute or so.
|
||||
Type 3), thrashing, was the hardest part. The first attempt was to
|
||||
measure the elapsed processor time it takes to write a single byte
|
||||
(using clock()). If a certain threshold is reached I assume the
|
||||
system is thrashing. When thrashing, however, the process does not
|
||||
consume processor time in a noticeable amount any longer and I lose.
|
||||
So I switched to measuring wall clock time using time(). On the one
|
||||
system that I observe thrashing (Linux) this works (although the
|
||||
value returned by time seems to 'hang' sometimes when the OS thrashes.
|
||||
Exceeding a threshold of 5 seconds may be detected only after half a
|
||||
minute or so.
|
||||
Reference in new issue
Block a user