fix Makefile and README.md for 2000/bmeyer

This commit is contained in:
Landon Curt Noll committed 2023-03-14 01:28:57 -07:00
1 parent 8755f28add
commit a5cb13ee8f
2 files changed
+657 -496

No files matched your search

+300 -186
View File
@@ -1,215 +1,329 @@
#!/bin/make #!/usr/bin/env make
# 2000 makefile
# #
# Copyright (C) Simon Cooper, Landon Curt Noll, Peter Seebach # IOCCC 2000 winning entry - bmeyer
# and Leonid A. Broukhis, 2000.
# All Rights Reserved. Permission for personal, educational or non-profit
# use is granted provided this this copyright and notice are included in its
# entirety and remains unaltered. All other uses must receive prior permission
# from the contest judges.
SHELL= /bin/sh ################################################################################
CAT= cat
RM= rm
SED= sed
TRUE= true
#CINCLUDE= -I ${HOMEBREW_PREFIX}/include/SDL2
# optimization
# #
# Most compiles will safely use -O2. Some can use only -O. # Regarding individual entries:
# A few compilers have broken optimizers and thus you may #
# not want anything. # IOCCC winning entry code may not work on your system. Even with standards,
# conditions and build environments change, especially when it comes to more
# extreme code such you might find in an IOCCC winner. Moreover, what was
# allowed and liked in one IOCCC contest may no longer be liked/allowed in
# later IOCCC years.
#
# Corrections on winning author and fixing typos in remarks are VERY WELCOME.
#
# GitHub pull requests are welcome:
#
# If you have a patch that might make running an entry work in modern Unix-like
# environments, please create a GitHub pull request for the IOCCC judges to
# consider.
#
# Suggestions and corrections regarding Makefiles, winning author information,
# fixing typos in remarks, and code patches are welcome via opening a GitHub
# issue or better yet, by creating a GitHub pull request for the IOCCC judges
# to consider. To form a GitHub issue or GitHub pull, please visit:
#
# https://github.com/ioccc-src/winner
#
################################################################################
# This work by Landon Curt Noll and Leonid A. Broukhis is licensed under:
#
# Creative Commons Attribution-ShareAlike 3.0 Unported License.
#
# See: http://creativecommons.org/licenses/by-sa/3.0/
#############################
# shell used by this Makefile
#############################
#
SHELL= bash
#######################
# common tool locations
#######################
#
include ../../var.mk
# Set X11_LIBDIR to the directory where the X11 library resides
#
#X11_LIBDIR= /usr/X11R6/lib
#X11_LIBDIR= /usr/X11/lib
X11_LIBDIR= /opt/X11/lib
# Set X11_INCLUDEDIR to the directory where the X11 include files reside
#
#X11_INCDIR= /usr/X11R6/include
#X11_INCDIR= /usr/X11/include
X11_INCDIR= /opt/X11/include
# Common C compiler warnings to silence
#
#CSILENCE=
CSILENCE= -Wno-format -Wno-parentheses -Wno-string-plus-int
# Common C compiler warning flags
#
#CWARN= ${CSILENCE}
#CWARN= -Wall ${CSILENCE}
#CWARN= -Wall -Wextra ${CSILENCE}
CWARN= -Wall -Wextra -pedantic ${CSILENCE}
# Compiler standard
#
#CSTD=
#CSTD= -ansi
CSTD= -std=gnu99
# Compiler bit architecture
#
# Some entries require 32-bitness:
# ARCH= -m32
#
# Some entries require 64-bitness:
# ARCH= -m64
#
# By default we assume nothing:
#
ARCH=
# Defines that are needed to compile
#
#CDEFINE=
CDEFINE= -DY=
# Include files that are needed to compile
#
CINCLUDE=
#CINCLUDE= -include stdlib.h
#CINCLUDE= -include stdio.h
#CINCLUDE= -include stdlib.h -include stdio.h
#CINCLUDE= -I ${X11_INCDIR}
#CINCLUDE= `${SDL2_CONFIG} --cflags`
# Optimization
#
# Most compiles will safely use -O2. Some can use only -O1 or -O.
# A few compilers have broken optimizers or this entry make break
# under those buggy optimizers and thus you may not want anything.
# Reasonable compilers will allow for -O3.
# #
#OPT= #OPT=
#OPT= -O #OPT= -O
OPT= -O2 #OPT= -O1
#OPT= -O2
OPT= -O3
# flags for ANSI compiles # Default flags for ANSI C compilation
# #
# NOTE: Some ANSI compilers make use of -Xa to turn on ANSI mode, CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT}
# others such as gcc may want -ansi, others (Compaq Tru64 Unix)
# want -std1, and others may want nothing at all.
# Adjust the CFLAGS line as needed.
#
# NOTE: Some compilers cannot optimize, or optimize some entries
# incorrectly. You might want to turn on -O to see if your
# compiler can handle them.
#
#
#CFLAGS= -Xa ${OPT}
#CFLAGS= -std1 $(OPT)
CFLAGS= -ansi ${OPT}
NO_ANSI_CFLAGS= # Libraries needed to build
#NO_ANSI_CFLAGS= #
#LIBS=
LIBS= -lm
#LIBS= -L ${X11_LIBDIR} -lX11
#LIBS= `${SDL2_CONFIG} --libs`
# NOTE: On some systems, the library path does not include the location # C compiler to use
# of the X11 libraries. You might need to use a -L/path/to/X11/lib/dir
# in order to compile with these libs.
#
X11CCFLAGS= -I/usr/X11R6/include -I/usr/X11R6/include/X11 -I/usr/include/X11
#X11CCFLAGS=
X11LDFLAGS= -L/usr/X11R6/lib
#X11LDFLAGS=
# Perl
#
PERL= perl
#PERL= perl5
# ANSI compiler
#
# Set CC to the name of your ANSI compiler.
#
# Some entries seem to need gcc. If you have gcc, set
# both CC and MAY_NEED_GCC to gcc.
#
# If you do not have gcc, set CC to the name of your ANSI compiler, and
# set MAY_NEED_GCC to either ${CC} (and hope for the best) or to just :
# to disable such programs.
# #
CC= cc CC= cc
MAY_NEED_GCC= gcc #CC=clang
#CC=gcc
# winners that compile under ANSI C # Compiler add-ons or replacements for clang only
# #
WINNERS= anderson schneiderwent primenum bellard bmeyer briddlebane dhyang \ ifeq ($(CC),clang)
dmy2jd jarijyrki dlowe natori rince robison thadgavin tomx
ALT_NAMES= schneiderwent.orig briddlebane.orig thadgavin.orig
DATA_FILES= schneiderwent_files bmeyer_files jarijyrki_files
all: ${WINNERS}
# Best use of flags
# #
anderson: anderson.c CSILENCE+= -Wno-comma -Wno-disabled-macro-expansion -Wno-float-conversion \
${CC} ${CFLAGS} $? -o $@ -Wno-format-nonliteral -Wno-missing-prototypes \
-Wno-missing-variable-declarations -Wno-poison-system-directories \
# Most Timely Output -Wno-shadow -Wno-shorten-64-to-32 -Wno-sign-conversion \
-Wno-strict-prototypes
# #
schneiderwent: schneiderwent.c CWARN+= -Weverything
${CC} ${CFLAGS} $? -o $@
schneiderwent_files: schneiderwent.data
schneiderwent.orig: schneiderwent.orig.c
${CC} ${CFLAGS} $? -o $@
# Best Abuse of CPP
# #
primenum: primenum.c endif
${CC} ${CFLAGS} $? -o $@
# Most Specific Output # Specific add-ons or replacements for gcc only
# #
bellard: bellard.c ifeq ($(CC),gcc)
${CC} ${CFLAGS} $? -o $@ #
#CSILENCE+=
#
#CWARN+=
#
endif
# Best Utility
#
# machines operating in 64 bit mode:
#
bmeyer: bmeyer.c
${CC} -DY= -O6 $? -o $@ -lm
#
# linux/x86, glibc 2.0, libc4/5:
#
# bmeyer: bmeyer.c
# ${CC} -DY="__setfpucw(0x127f)" -O6 $? -o $@ -lm
#
# linux/x86, glibc 2.1:
#
# bmeyer: bmeyer.c
# ${MAY_NEED_GCC} -DY='int x=0x127f; __asm__ ("fldcw %0" : : "m" (*&x))' \
# -O6 $? -o $@ -lm
bmeyer_files: lavabus.pgm lenna.glic michael.pgm ##############################
# Special flags for this entry
# Best Abuse of User ##############################
# #
briddlebane: briddlebane.c ENTRY= bmeyer
${CC} ${CFLAGS} $? -o $@ -lm PROG= ${ENTRY}
briddlebane.orig: briddlebane.orig.c
${CC} ${CFLAGS} $? -o $@ -lm
# Best Layout
# #
dhyang: dhyang.c OBJ= ${PROG}.o
${CC} ${CFLAGS} $? -o $@ DATA= lavabus.pgm lenna.glic michael.pgm
TARGET= ${PROG}
dmy2jd: dmy2jd.c
${CC} ${CFLAGS} $? -o $@
# Best of Show
# #
jarijyrki: jarijyrki.c ALT_OBJ=
${CC} ${X11CCFLAGS} ${CFLAGS} -DNeedFunctionPrototypes \ ALT_TARGET=
-DU=40 -DT=98 '-Dz=(T+1)*U*U' -DQ=80 -DS=20 -DN=10 -DB=5 -DG=23 \
-Dp=7 '-DM=((p+1)*Q)+S' '-DH=(G*S)+S+S' -DC=XK_Up -DL=XK_Down \
-DO=XK_Left -DV=XK_Right -DR=XK_Escape -D_=XK_BackSpace \
$? -o $@ ${X11LDFLAGS} -lX11
jarijyrki_files: sheet1.info
# Worst Abuse of the Rules #################
# build the entry
#################
# #
# NOTE: You will need to adjust this path to the place where Perl has been all: data ${TARGET}
# installed on your system. Running "perl -V" may help you locate
# this place.
#
# The following bit of perl may help determining the values you need:
#
# perl -MConfig -e 'print "$Config{archlibexp}/CORE\n"'
#
#
dlowe: dlowe.c
${CC} ${NO_ANSI_CFLAGS} -DPERL_POLLUTE \
`${PERL} -MExtUtils::Embed -e ccopts` $? -o $@ \
`${PERL} -MExtUtils::Embed -e ldopts`
# Best Small Program
#
natori: natori.c
${CC} ${CFLAGS} $? -o $@ -lm
# Astronomically Obfuscated
#
rince: rince.c
${CC} ${X11CCFLAGS} ${CFLAGS} -DM_PI=3.14159265358979323846 \
$? -o $@ -lm ${X11LDFLAGS} -lX11
# Best game
#
robison: robison.c
${CC} ${CFLAGS} $? -o $@
# Most Portable Output
#
thadgavin: thadgavin.c
${CC} ${CFLAGS} $? -o $@ -lcurses -lm
thadgavin.orig: thadgavin.orig.c
${CC} ${CFLAGS} $? -o $@ -lncurses -lm
# Most Complete Program
#
tomx: tomx.c
${CC} ${CFLAGS} $? -o $@
everything: ${WINNERS} ${ALT_NAMES} ${DATA_FILES}
clean:
${RM} -f *.o a.out core ${WINNERS}
clobber: clean
${RM} -f ${WINNERS}
${RM} -f ${ALT_NAMES}
nuke: clobber
@${TRUE} @${TRUE}
install: all .PHONY: all alt data everything clean clobber install \
${CAT} ${WINNERS} ${ALT_NAMES} > /dev/null love haste waste maker easter_egg sandwich \
supernova deep_magic magic charon pluto
${PROG}: ${PROG}.c
${CC} ${CFLAGS} $< -o $@ ${LIBS}
# alternative executable
#
alt: data ${ALT_TARGET}
@${TRUE}
# data files
#
data: ${DATA}
@${TRUE}
# both all and alt
#
everything: all alt
###############
# utility rules
###############
#
clean:
${RM} -f ${OBJ} ${ALT_OBJ}
@-if [ -f indent.c ]; then \
echo ${RM} -f indent.c; \
${RM} -f indent.c; \
fi
clobber: clean
${RM} -f ${TARGET} ${ALT_TARGET}
@-if [ -e sandwich ]; then \
${RM} -f sandwich; \
echo 'ate sandwich'; \
fi
install:
@echo "Dr. Spock says that is not logical!"
@${TRUE}
# you may be able to misunderstand the source by reading indent.c
#
indent.c: ${PROG}.c
@if which "${GINDENT}" >/dev/null 2>&1; then \
echo ${RM} -f $@; \
${RM} -f $@; \
echo "${GINDENT} < $< > $@"; \
${GINDENT} < $< > $@; \
elif which "${INDENT}" >/dev/null 2>&1; then \
echo ${RM} -f $@; \
${RM} -f $@; \
echo "${INDENT} < $< > $@"; \
${INDENT} < $< > $@; \
else \
echo "no intent tool found, ident $< yourself, sorry"; \
echo "exit 1"; \
exit 1; \
fi
##################
# 133t hacker rulz
##################
#
love:
@echo 'not war?'
@${TRUE}
haste:
$(MAKE) waste
@${TRUE}
waste:
@echo 'haste'
@${TRUE}
maker:
@echo 'Participate in a maker faire'
@${TRUE}
easter_egg:
@echo "you expected to often misunderstand this $${RANDOM} magic"
@echo "chongo '<was here>' /\\oo/\\"
@echo "Eggy"
sandwich: easter_egg
@if [ `id -u` -eq 0 ]; then \
echo 'Okay.'; \
echo "$${RANDOM}`date +%s`$${RANDOM}" > $@; \
else \
echo 'What? Make it yourself.'; \
exit 1; \
fi
# Understand the history of the Homebrew Computer Club
# as well as the West Coast Computer Faire and
# you might be confused different. :-)
#
supernova: sandwich
@-if [ -r .code_anal ]; then \
${RM} -f .code_anal_v6; \
else \
echo "planet deniers, like some members of the IAU, are so cute when they try to defend their logic"; \
fi
@echo "A $@ helps ${MAKE} the elements that help form planets"
@${TRUE}
deep_magic: supernova
@-if [ -r .code_anal ]; then \
ccode_analysis --deep_magic 7889066cdbdf09b0408021754586e895be641cc9be29e7eafd3ca65359541f17 \
--FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
else \
echo "Wrong! Do it again!"; \
sleep 2; \
echo "Wrong! Do it again!"; \
sleep 2; \
echo "Wrong! Do it again!"; \
fi
@${TRUE}
magic: deep_magic
@-if [ -r .code_anal ]; then \
ccode_analysis --level 20003 --mode 5717175 --FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
else \
echo "If you don't eat yer meat, you can't have any pudding!"; \
echo "How can you have any pudding if you don't eat yer meat?!"; \
fi
@${TRUE}
# The IOCCC resident astronomer states
#
charon: magic
@echo "$@ is a dwarf planet"
@echo "dwarf is a type of planet"
@echo "therefore IAU, $@ is a planet"
pluto: charon
${MAKE} charon
@echo "$@ is a dwarf planet"
@echo "dwarf is a type of planet"
@echo "therefore, $@ is a planet"
@echo "having lots of planets is good feature because supernovae can help ${MAKE} a lot of them"
+357 -310
View File
@@ -1,351 +1,398 @@
Best Utility: # Best Utility:
Bernd Meyer Bernd Meyer
Monash University, Melbourne, Australia Monash University, Melbourne, Australia
1/832 Blackburn Rd 1/832 Blackburn Rd
Clayton, VIC 3168 Clayton, VIC 3168
Australia Australia
http://byron.csse.monash.edu.au/ http://byron.csse.monash.edu.au/
# To build:
Judges' Comments: ```sh
make
```
To build: ### To run:
make bmeyer ```sh
./prog
```
Compile with the maximum possible optimization on your system. ### Try:
If your system has 80-bit internal representation of floating
point values (x86, some Motorola processors) and you want the
compressed format to be portable across platforms, you need
to set your FPU to use 64 bit mode. See the extra build lines
in the Makefile, and read the author's comments below.
If you cannot work out hwo to get into 64 bit mode, you can In a video window (white text on black background):
try using -ffloat-store if you compile with GCC (this may have
a performance penalty). The resulting binary will have a good
chance to produce portable files, but due to the resulting
double-rounding (first internally to 80 bit, then to 64 bit
on writing the value to memory), there is a small but very real
chance any given file produced might not be portable.
To run: ```sh
./bmeyer < michael.pgm > michael.glic
```
In a video window (white text on black background): Compare the michael.pgm to the screen output.
./bmeyer < michael.pgm > michael.glic To test that you are compressing and decompressing the same way the
author was, try:
Compare the michael.pgm to the screen output. ```sh
./bmeyer < lenna.glic
```
To test that you are compressing and decompressing the same way the Also try in a reverse video window:
author was, try
./bmeyer < lenna.glic ```sh
./bmeyer < michael.pgm 2>/dev/null | ./bmeyer > new.pgm
```
Also try in a reverse video window: Compare the michael.pgm to the screen output. Compare the new.pgm file
to the michael.pgm file.
./bmeyer < michael.pgm 2>/dev/null | ./bmeyer > new.pgm Create a large reverse video xterm, say 200 columns wide and try:
Compare the michael.pgm to the screen output. Compare the new.pgm file ```sh
to the michael.pgm file. ./bmeyer 200 < lavabus.pgm > lavabus.glic
```
Create a large reverse video xterm, say 200 columns wide and try: Produce an even smaller output:
./bmeyer 200 < lavabus.pgm > lavabus.glic ```sh
./bmeyer -2 200 < lavabus.pgm > lavabus.glic2
```
Produce an even smaller output: Decompress that smaller glic-file:
./bmeyer -2 200 < lavabus.pgm > lavabus.glic2 ```sh
./bmeyer -2 200 < lavabus.glic2 > new.pgm
```
Decompress that smaller glic-file: Compare the new.pgm file to the lavabus.pgm file by viewing them.
./bmeyer -2 200 < lavabus.glic2 > new.pgm ## Judges' comments:
Compare the new.pgm file to the lavabus.pgm file by viewing Don't even think of supplying different compression factors (the
them. dashed argument) for compression and decompression! You've been warned.
Don't even think of supplying different compression factors (the The author recommended on linux/x86, glibc 2.0, libc4/5:
dashed argument) for compression and decompression! You've been warned.
```make
bmeyer: bmeyer.c
CC -DY="__setfpucw(0x127f)" -O6 $? -o $@ -lm
```
Selected Author's Comments: However many compilers use -O3 as the maximum level for -O optimization.
GLICBAWLS And on linux/x86, glibc 2.1:
A) Function ```make
=========== bmeyer: bmeyer.c
gcc -DY='int x=0x127f; __asm__ ("fldcw %0" : : "m" (*&x))' $? -o $@ -lm
```
"GLICBAWLS" stands for: Compile with the maximum possible optimization on your system.
If your system has 80-bit internal representation of floating
point values (x86, some Motorola processors) and you want the
compressed format to be portable across platforms, you need
to set your FPU to use 64 bit mode. See the extra build lines
in the Makefile, and read the author's comments below.
"Grey Level Image Compression By Adaptive Weighted Least Squares". If you cannot work out how to get into 64 bit mode, you can
try using -ffloat-store if you compile with GCC (this may have
a performance penalty). The resulting binary will have a good
chance to produce portable files, but due to the resulting
double-rounding (first internally to 80 bit, then to 64 bit
on writing the value to memory), there is a small but very real
chance any given file produced might not be portable.
And that's exactly what this program does --- feed it a .PGM file The makefile defaults to -O3 and no `asm()` call.
(either raw [i.e. P5] or ascii [i.e. P2]) on stdin, and it will
output a compressed version to stdout. Feed it a compressed file
on stdin, and it will decompress it to stdout --- as either raw or
ascii .PGM, depending on what format it was compressed from. For
example:
./bmeyer < michael.pgm > michael.glic ## Author's comments:
and then GLICBAWLS
./bmeyer < michael.glic > michael.decoded.pgm A) Function
===========
As it turns out, glicbawls compresses greyscale images (or, more "GLICBAWLS" stands for:
precisely, "natural" greyscale images) better than any other
program I am aware of (with the exception of the not-yet-published
program developed for my PhD thesis). Of course, this might take
some time, so a progress indicator is provided. To make things
more interesting, instead of a boring bar or percentage display,
an ASCII rendition of the image being compressed/decompressed is
output to stderr (assuming white text on black background). If you
have trouble recognizing the image, try squinting at it ;-).
If you still have trouble, simply use a terminal or xterm with "Grey Level Image Compression By Adaptive Weighted Least Squares".
more than 80 columns, and tell glicbawls about it by giving the
number of columns as an argument. For example
./bmeyer 260 <lavabus.pgm >lavabus.glic And that's exactly what this program does --- feed it a .PGM file
(either raw [i.e. P5] or ascii [i.e. P2]) on stdin, and it will
output a compressed version to stdout. Feed it a compressed file
on stdin, and it will decompress it to stdout --- as either raw or
ascii .PGM, depending on what format it was compressed from. For
example:
will allow glicbawls to use up to 260 columns, which makes things ```sh
much more recognizable. ./bmeyer < michael.pgm > michael.glic
```
Still running out of space? Need to compress those images just a and then
tad more? And maybe you can handle some loss due to the
compression? If so, you can use glicbawls' "near lossless"
compression mode. In that mode, the decoded pixel values are
allowed to differ from tbe originals, but there is an upper limit
on the magnitude of the difference. For example, if a maximum
error of 2 is allowed, an original pixel value of 37 could be
decoded as 35,36,37, 38 or 39. In order to use this mode, simply
give the maximum allowed error as an argument, with a dash ('-')
before it. For example
./bmeyer -2 < graph.pgm > graph.2.glic ```sh
./bmeyer < michael.glic > michael.decoded.pgm
will compress in a way that allows decompression with a maximum ```
error of two. You can combine both types of arguments, i.e.
As it turns out, glicbawls compresses greyscale images (or, more
./bmeyer -3 180 < graph.pgm >graph.3.glic precisely, "natural" greyscale images) better than any other
program I am aware of (with the exception of the not-yet-published
will compress with a maximum error of 3, using a 180 column program developed for my PhD thesis). Of course, this might take
terminal for the progress display, and some time, so a progress indicator is provided. To make things
more interesting, instead of a boring bar or percentage display,
./bmeyer -3 180 < graph.3.glic > graph.3maxerr.pgm an ASCII rendition of the image being compressed/decompressed is
output to stderr (assuming white text on black background). If you
will decompress the result on such a terminal. have trouble recognizing the image, try squinting at it ;-).
But wait, there is more --- you can also feed glicbawls .PPM files If you still have trouble, simply use a terminal or xterm with
(i.e. colour images), raw or ascii, your choice. Glicbawls will more than 80 columns, and tell glicbawls about it by giving the
compress and decompress them just fine. However, it only does number of columns as an argument. For example
"reasonably well" on them; It beats PNG easily, but there are
certainly programs out there that can compress colour images ```sh
considerably better. And don't even *think* about using it for ./bmeyer 260 <lavabus.pgm >lavabus.glic
so-called "artistic" images (i.e. anything that wasn't scanned ```
from a photo or camera)....
will allow glicbawls to use up to 260 columns, which makes things
***************************************************************** much more recognizable.
* *
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING * Still running out of space? Need to compress those images just a
* * tad more? And maybe you can handle some loss due to the
* * compression? If so, you can use glicbawls' "near lossless"
* Glicball is rather CPU and FPU intensive. Don't * compression mode. In that mode, the decoded pixel values are
* run it on anything slow. Things start to be * allowed to differ from tbe originals, but there is an upper limit
* usable around 300+MHz, with 600+ being highly * on the magnitude of the difference. For example, if a maximum
* desirable. * error of 2 is allowed, an original pixel value of 37 could be
* * decoded as 35,36,37, 38 or 39. In order to use this mode, simply
***************************************************************** give the maximum allowed error as an argument, with a dash ('-')
before it. For example
```sh
B) Compile Instructions ./bmeyer -2 < graph.pgm > graph.2.glic
======================= ```
Unlike most compression program, glicbawls makes heavy use of will compress in a way that allows decompression with a maximum
floating point maths --- and not all FPUs are created equal. Most error of two. You can combine both types of arguments, i.e.
importantly, the x86 and 68k FPUs use 80 bit floating point
formats internally, while most other processors use IEEE 64 bit ```sh
formats by default. If not taken care of, this means that a ./bmeyer -3 180 < graph.pgm >graph.3.glic
glicbawls file compressed on one CPU cannot generally be ```
decompressed on another.
will compress with a maximum error of 3, using a 180 column
Fortunately, just about all current FPUs can be put into IEEE 64 terminal for the progress display, and
bit mode (or at least something close enough for
glicbawls). Unfortunately, there is no standard way of doing ```sh
so. For this reason, the build file contains *three* compile ./bmeyer -3 180 < graph.3.glic > graph.3maxerr.pgm
lines; One for machines that already use 64 bit mode by default ```
(most RISC processors), one for linux/x86 machines using libc4,
libc5 or glibc2.0, and one for linux/x86 machines using glibc2.1 will decompress the result on such a terminal.
(glibc2.1 no longer exports the __setfpucw function, necessitating
the use of gcc inline assembly. *Grumble*). But wait, there is more --- you can also feed glicbawls .PPM files
(i.e. colour images), raw or ascii, your choice. Glicbawls will
So, if you are building on a machine that does not, by default, compress and decompress them just fine. However, it only does
use IEEE 64 bit floating point doubles, you'll have to define Y to "reasonably well" on them; It beats PNG easily, but there are
be a command that switches it into that mode. If you fail to do certainly programs out there that can compress colour images
so, your glicbawls executable will still be able to decompress its considerably better. And don't even *think* about using it for
own compressed files, but you may be unable to exchange files with so-called "artistic" images (i.e. anything that wasn't scanned
glicbawls on other machines. from a photo or camera)....
*****************************************************************
C) Obfuscation * *
============== * WARNING WARNING WARNING WARNING WARNING WARNING WARNING *
* *
Most of the obfuscation of this program was driven by the * *
desperate struggle to fit all of glicbawls' features into code * Glicball is rather CPU and FPU intensive. Don't *
that fits the IOCCC's size limits. All identifiers names are * run it on anything slow. Things start to be *
single-character, which was only possible by ruthless exploitation * usable around 300+MHz, with 600+ being highly *
of scope. There is a function M() that handles nearly all input * desirable. *
from stdin, handles all output to stdout, and also recursively * *
calculates one of the mathematical expressions needed for error *****************************************************************
modelling. There is a function that calculates (A^(-1)*b)*x with A
a matrix and b and x being vectors, and does so in a very
efficient way --- but one would be hard pressed to find it. There
is an arithmetic coder that is even less recognizable. A lot of B) Compile Instructions
loop variables don't get initialized, but instead the code is =======================
arranged in such a way that they just happen to have the right
value when control flow reaches the loops. Lots of functions use Unlike most compression program, glicbawls makes heavy use of
the same two global scratch variables. Variables change their floating point maths --- and not all FPUs are created equal. Most
meaning all the time, and the differences between compressing and importantly, the x86 and 68k FPUs use 80 bit floating point
decompressing are handled in rather subtle ways all over the formats internally, while most other processors use IEEE 64 bit
program. The string that holds the ascii values for displaying the formats by default. If not taken care of, this means that a
progress image doubles as a description on how to find a pixel's glicbawls file compressed on one CPU cannot generally be
causal neighbours. And then there are a few #define's that really decompressed on another.
are just in there to save a few characters, but as an added
"benefit" make the source ever more unreadable. Fortunately, just about all current FPUs can be put into IEEE 64
bit mode (or at least something close enough for
In short --- running it through the preprocessor and the glicbawls). Unfortunately, there is no standard way of doing
pretty-printer will give you something that looks slightly less so. For this reason, the build file contains *three* compile
like line noise and slightly more like a C program, but unless you lines; One for machines that already use 64 bit mode by default
are a true wizard, it is unlikely to gain you any insights into (most RISC processors), one for linux/x86 machines using libc4,
what is actually going on....[0] libc5 or glibc2.0, and one for linux/x86 machines using glibc2.1
(glibc2.1 no longer exports the __setfpucw function, necessitating
the use of gcc inline assembly. *Grumble*).
D) Bugs, Assumptions and TODOs
============================== So, if you are building on a machine that does not, by default,
use IEEE 64 bit floating point doubles, you'll have to define Y to
Glicbawls assumes that characters are 8 bit wide, and that the be a command that switches it into that mode. If you fail to do
character set used is ASCII. If one of these isn't true, all hell so, your glicbawls executable will still be able to decompress its
will break loose. own compressed files, but you may be unable to exchange files with
glicbawls on other machines.
The .PGM headers are not checked very thoroughly --- glicbawls
will happily try to encode lots of files that it shouldn't
touch. The old adage of "garbage in, garbage out" holds in that C) Obfuscation
case. Furthermore, even if glicbawls detects an incorrect input, ==============
it fails to provide a meaningful error message, and instead
simply aborts. Most of the obfuscation of this program was driven by the
desperate struggle to fit all of glicbawls' features into code
When decompressing a near-losslessy encoded image, the maximum that fits the IOCCC's size limits. All identifiers names are
allowed error is required as a command line parameter. This should single-character, which was only possible by ruthless exploitation
really be saved in the compressed file! of scope. There is a function M() that handles nearly all input
from stdin, handles all output to stdout, and also recursively
Glicbawls relies on the decimal representation of all integers calculates one of the mathematical expressions needed for error
having less than 255 digits. While ANSI C does not guarantee this, modelling. There is a function that calculates (A^(-1)*b)*x with A
it seems rather unlikely that anyone in the foreseeable future a matrix and b and x being vectors, and does so in a very
would use images with 10^255 rows, columns or different levels of efficient way --- but one would be hard pressed to find it. There
grey. Also, it is assumed that any integer used can be cast to a is an arithmetic coder that is even less recognizable. A lot of
double and back without loss of precision. This limits the number loop variables don't get initialized, but instead the code is
of rows, columns and grey levels to 9 quintillion, which should arranged in such a way that they just happen to have the right
also be sufficient for the next few years. value when control flow reaches the loops. Lots of functions use
the same two global scratch variables. Variables change their
There is a theoretical chance that you might get a division by meaning all the time, and the differences between compressing and
zero error. You have a MUCH better chance of getting hit by decompressing are handled in rather subtle ways all over the
lightning the very second you realize that you just won a few program. The string that holds the ascii values for displaying the
million in a lottery. But if it happens to you, don't say I progress image doubles as a description on how to find a pixel's
didn't warn you! Instead, go out and buy a lightning rod and a causal neighbours. And then there are a few #define's that really
lottery ticket ;-) are just in there to save a few characters, but as an added
"benefit" make the source ever more unreadable.
At one point, a pointer to int is used as the target of a "%1[#]"
conversion in a scanf, which makes gcc rather unhappy, but In short --- running it through the preprocessor and the
shouldn't really cause anyone any trouble. The same goes for a pretty-printer will give you something that looks slightly less
couple of assignments which are used as truth values. like line noise and slightly more like a C program, but unless you
are a true wizard, it is unlikely to gain you any insights into
what is actually going on....[0]
E) Algorithm
============
D) Bugs, Assumptions and TODOs
An exhaustive explanation of the algorithm used would go beyond ==============================
the scope of an info file, and would also be quite difficult in
pure ASCII, so the following is just a rough outline, and contains Glicbawls assumes that characters are 8 bit wide, and that the
some compression-speak... character set used is ASCII. If one of these isn't true, all hell
will break loose.
The image is handled in scanline ordering. For each pixel, an
expected value is calculated by creating a least squares predictor The .PGM headers are not checked very thoroughly --- glicbawls
of order 12 based on all of the previously encoded/decoded will happily try to encode lots of files that it shouldn't
pixels. However, the contribution of each pixel is weighted touch. The old adage of "garbage in, garbage out" holds in that
according to its Manhattan-distance from the current pixel, case. Furthermore, even if glicbawls detects an incorrect input,
D. That weight used is pow(0.8,D). [1][2][3] it fails to provide a meaningful error message, and instead
simply aborts.
In a similar way, the prediction *errors* for all previous pixels When decompressing a near-losslessy encoded image, the maximum
are combined --- a weighted average (with weight pow(0.7,D)) of allowed error is required as a command line parameter. This should
the squares of all previous prediction errors is taken, and the really be saved in the compressed file!
square root of the (appropriately scaled) result is called sigma.
Glicbawls relies on the decimal representation of all integers
The predicted value and sigma are then used as the parameters of a having less than 255 digits. While ANSI C does not guarantee this,
Student or t-distribution (which is similar to a normal it seems rather unlikely that anyone in the foreseeable future
distribution, but has more weight in the tails), providing a would use images with 10^255 rows, columns or different levels of
probability distribution for the current pixel. grey. Also, it is assumed that any integer used can be cast to a
double and back without loss of precision. This limits the number
Next, the possible interval of values for the current pixel is of rows, columns and grey levels to 9 quintillion, which should
halved, and through integration of the probability distribution, a also be sufficient for the next few years.
probability is calculated for the pixel value being in the lower
half. During encoding, the actual value is examined, and the There is a theoretical chance that you might get a division by
result encoded using an arithmetic coder. During decoding, the zero error. You have a MUCH better chance of getting hit by
arithmetic decoder provides the information which half the actual lightning the very second you realize that you just won a few
value is in. In both cases, the interval borders are adjusted million in a lottery. But if it happens to you, don't say I
accordingly, the arithmetic coder/decoder updated, and the didn't warn you! Instead, go out and buy a lightning rod and a
interval halving repeated until the precise value has been lottery ticket ;-)
en/decoded[4].
At one point, a pointer to int is used as the target of a "%1[#]"
conversion in a scanf, which makes gcc rather unhappy, but
[0] Yes, this could be seen as a challenge.... shouldn't really cause anyone any trouble. The same goes for a
[1] Actually, the weight is pow(0.8,D)/s, where s is the sigma couple of assignments which are used as truth values.
used for en/decoding the particular previous pixel. Dividing
by s is a rather arbitrary action justified only by the
improvement in results. Mathematically, I can only justify E) Algorithm
dividing by s^2, or not dividing at all.... ============
[2] Obviously, these least squares predictors are not calculated
from scratch for each pixel. Some clever reuse of earlier An exhaustive explanation of the algorithm used would go beyond
results allows O(1) calculation, rather than the the scope of an info file, and would also be quite difficult in
O(previous_rows*columns) this description would suggest. pure ASCII, so the following is just a rough outline, and contains
[3] As anyone who has ever tried local least squares knows from some compression-speak...
bitter experience, there is a lot of numerical instability
associated with them. So glicbawls adds a bias towards an The image is handled in scanline ordering. For each pixel, an
averaging predictor to the equation system the least squares expected value is calculated by creating a least squares predictor
predictor is calculated from. The weight that is given to of order 12 based on all of the previously encoded/decoded
this bias is constantly adjusted throughout coding/decoding, pixels. However, the contribution of each pixel is weighted
thus justifying the 'a' in "glicbawls". according to its Manhattan-distance from the current pixel,
[4] In the case of near-lossless coding, the interval is not D. That weight used is pow(0.8,D). [1][2][3]
necessarily *halved*, but rather split at a convenient point
(the split points are chosen in such a way as to minimise the
expected number of bits needed for coding). Also, the In a similar way, the prediction *errors* for all previous pixels
iteration ends as soon as the interval is small enough to are combined --- a weighted average (with weight pow(0.7,D)) of
guarantee decoding within the requested maximum error. the squares of all previous prediction errors is taken, and the
square root of the (appropriately scaled) result is called sigma.
####################################################################
The predicted value and sigma are then used as the parameters of a
Photo Credit: Student or t-distribution (which is similar to a normal
distribution, but has more weight in the tails), providing a
Lavabus was taken by the judge, Landon Curt Noll. Michael was probability distribution for the current pixel.
provided by the author. Lenna is a 1972 Playboy centerfold.
Next, the possible interval of values for the current pixel is
michael.pgm halved, and through integration of the probability distribution, a
probability is calculated for the pixel value being in the lower
The author's Godchild and nephew. half. During encoding, the actual value is examined, and the
result encoded using an arithmetic coder. During decoding, the
lavabus.pgm arithmetic decoder provides the information which half the actual
value is in. In both cases, the interval borders are adjusted
A bus that was trapped by lava from Kilauea volcano in accordingly, the arithmetic coder/decoder updated, and the
Hawaii, USA. Nobody was in the bus at the time, BTW. interval halving repeated until the precise value has been
Photo date: 1981. en/decoded[4].
lenna.glic
[0] Yes, this could be seen as a challenge....
A November 1972 Playboy centerfold was scanned in long ago and [1] Actually, the weight is pow(0.8,D)/s, where s is the sigma
has been used for image compression research since then. Playboy used for en/decoding the particular previous pixel. Dividing
originally threatened to prosecute unauthorized use, but by s is a rather arbitrary action justified only by the
eventually granted that, having been in use for image compression improvement in results. Mathematically, I can only justify
testing for so long, it was now a useful benchmark. (Note: This dividing by s^2, or not dividing at all....
picture doesn't show very much.) [2] Obviously, these least squares predictors are not calculated
from scratch for each pixel. Some clever reuse of earlier
results allows O(1) calculation, rather than the
O(previous_rows*columns) this description would suggest.
[3] As anyone who has ever tried local least squares knows from
bitter experience, there is a lot of numerical instability
associated with them. So glicbawls adds a bias towards an
averaging predictor to the equation system the least squares
predictor is calculated from. The weight that is given to
this bias is constantly adjusted throughout coding/decoding,
thus justifying the 'a' in "glicbawls".
[4] In the case of near-lossless coding, the interval is not
necessarily *halved*, but rather split at a convenient point
(the split points are chosen in such a way as to minimise the
expected number of bits needed for coding). Also, the
iteration ends as soon as the interval is small enough to
guarantee decoding within the requested maximum error.
####################################################################
Photo Credit:
Lavabus was taken by the judge, Landon Curt Noll. Michael was
provided by the author. Lenna is a 1972 Playboy centerfold.
michael.pgm
The author's Godchild and nephew.
lavabus.pgm
A bus that was trapped by lava from Kilauea volcano in
Hawaii, USA. Nobody was in the bus at the time, BTW.
Photo date: 1981.
lenna.glic
A November 1972 Playboy centerfold was scanned in long ago and
has been used for image compression research since then. Playboy
originally threatened to prosecute unauthorized use, but
eventually granted that, having been in use for image compression
testing for so long, it was now a useful benchmark. (Note: This
picture doesn't show very much.)