fix .gitignore, Makefile and README.md for 2014/endoh

Unpack src.zip.
This commit is contained in:
Landon Curt Noll committed 2023-03-24 02:57:38 -07:00
1 parent a7163eafa2
commit e18e03bff5
20 files changed
+1616 -96

No files matched your search

+2
View File
@@ -1,3 +1,5 @@
main
main.c
prog
treacle
treacle.c
+212 -77
View File
@@ -1,161 +1,262 @@
#!/usr/bin/env make
#
# 2014 makefile
# IOCCC 2014 winning entry - endoh1
################################################################################
#
# This work by Landon Curt Noll, Simon Cooper, and Leonid A. Broukhis
# is licensed under:
# Regarding individual entries:
#
# 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/
################
# tool locations
################
#############################
# shell used by this Makefile
#############################
#
SHELL= bash
CP= cp
CPP= cpp
GUNZIP= gunzip
LD= ld
MAKE= make
RM= rm
SED= sed
TAR= tar
TRUE= true
#######################
# 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/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/X11R6/include
#X11_INCDIR= /usr/X11/include
X11_INCDIR= /opt/X11/include
# Compiler warnings
# Common C compiler warnings to silence
#
#CWARN=
CWARN= -Wall
#CWARN= -Wall -pedantic
#CSILENCE=
CSILENCE= -Wno-empty-body
# compiler standard
# 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=c11
CSTD= -std=gnu11
# compiler bit architecture
# Compiler bit architecture
#
# Some entries require 32-bitness:
# ARCH= -m32
#
# Some entries require 64-bitess:
# Some entries require 64-bitness:
# ARCH= -m64
#
# By default we assume nothing:
#
ARCH=
# optimization
# Defines that are needed to compile
#
CDEFINE=
#CDEFINE= -DIOCCC
# 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} -I ${X11_INCDIR}/X11
#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= -O
#OPT= -O1
OPT= -O2
#OPT= -O3
#OPT= -O2
OPT= -O3
# Default flags for ANSI C compilation
#
CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT}
# Libraries needed to build
#
LIBS=
#LIBS= -lm
#LIBS= -L ${X11_LIBDIR} -lX11
#LIBS= `${SDL2_CONFIG} --libs`
# default flags for ANSI C compilation
#
CFLAGS= ${CWARN} ${CSTD} ${ARCH} ${OPT} -Wno-empty-body -funsigned-char
# 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.
# C compiler to use
#
CC= cc
#CC=clang
MAY_NEED_GCC= gcc
#CC=gcc
# Compiler add-ons or replacements for clang only
#
ifeq ($(CC),clang)
#
CSILENCE+= -Wno-comma -Wno-missing-variable-declarations \
-Wno-poison-system-directories -Wno-shadow
#
CWARN+= -Weverything
#
endif
# Specific add-ons or replacements for gcc only
#
ifeq ($(CC),gcc)
#
#CSILENCE+=
#
#CWARN+=
#
endif
##############################
# Special flags for this entry
##############################
#
ENTRY= prog main
DATA=
ENTRY= endoh1
PROG= prog
#
OBJ= ${PROG}.o
DATA= Rakefile alpha.rb assemble.rb ast-def.rb gen-prog.rb gen-table.rb link.rb \
machine.rb main.c parse.rb prog-tmpl.c read_me.md register.rb \
serialize.rb src.rb tool.rb vm.c
TARGET= ${PROG} main treacle
#
ALT_OBJ=
ALT_ENTRY=
ALT_TARGET=
#################
# build the entry
#################
#
all: ${ENTRY} ${DATA}
all: data ${TARGET}
@${TRUE}
.PHONY: all alt data everything clean clobber install \
love haste waste maker easter_egg sandwich \
supernova deep_magic magic charon pluto
${PROG}: ${PROG}.c
${CC} ${CFLAGS} $< -o $@ ${LIBS}
main: main.c
${CC} ${CFLAGS} $< -o $@ ${LIBS}
main.c: prog
./prog > $@
prog: prog.c
${CC} ${CFLAGS} $< -o $@ ${LIBS}
${RM} -f $@
./${PROG} > $@
# alternative executable
#
alt: ${ALT_ENTRY}
alt: data ${ALT_TARGET}
@${TRUE}
treacle.c: ${PROG}
${RM} -f $@
./${PROG} < ${PROG}.c > $@
treacle: treacle.c
${CC} ${CFLAGS} $< -o $@ ${LIBS}
# data files
#
data: ${DATA}
@${TRUE}
# both all and alt
#
everything: all alt
###############
# utility rules
###############
#
everything: all alt
clean:
${RM} -f ${ENTRY}.o ${ALT_OBJ} main.o
${RM} -f ${OBJ} ${ALT_OBJ}
${RM} -f main.o
@-if [ -f indent.c ]; then \
echo ${RM} -f indent.c; \
${RM} -f indent.c; \
fi
clobber: clean
${RM} -f ${ENTRY} ${ALT_ENTRY} main.c main
nuke: clobber
@${TRUE}
dist_clean: nuke
@${TRUE}
${RM} -f ${TARGET} ${ALT_TARGET}
@-if [ -e sandwich ]; then \
${RM} -f sandwich; \
echo 'ate sandwich'; \
fi
install:
@echo "Surely we are joking Dr. Science!"
@echo "Dr. Spock says that is not logical!"
@${TRUE}
# backwards compatibility
# you may be able to misunderstand the source by reading indent.c
#
build: all
@${TRUE}
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
##################
@@ -174,35 +275,69 @@ waste:
@echo 'haste'
@${TRUE}
maker:
@echo 'Participate in a maker faire'
@${TRUE}
easter_egg:
@echo you expected to sometimes mis-understand this $${RANDOM} magic
@echo chongo '<was here>' "/\\oo/\\"
@echo Readers shall not be disallowed from failong to be unable to not partly misunderstand this partocular final echo.
@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: nuke
supernova: sandwich
@-if [ -r .code_anal ]; then \
${RM} -f .code_anal_v4; \
${RM} -f .code_anal_v6; \
else \
echo "We are not expected to understand that"; \
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:
deep_magic: supernova
@-if [ -r .code_anal ]; then \
ccode_analysis --deep_magic 2ed3582ec5c094a093fea2a6bcb9f19439892873c9cc45ed1ceaafe08b7d52e1b2c7cd524be3b0b2 --FNV1a_hash 512-bit "${ENTRY}"; \
ccode_analysis --deep_magic 7889066cdbdf09b0408021754586e895be641cc9be29e7eafd3ca65359541f17 \
--FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
else \
echo "You are holding it wrong!"; \
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 --mode 391581 --level 216193 --FNV1a_hash 512-bit "${ENTRY}"; \
ccode_analysis --level 20003 --mode 5717175 --FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
else \
echo "These aren't the primes you're looking for Pauline!"; \
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"
+24 -19
View File
@@ -1,41 +1,47 @@
# Most square (YODA award)
Yusuke Endoh
<mame@ruby-lang.org>
<https://github.com/mame/>
Yusuke Endoh
<mame@ruby-lang.org>
<https://github.com/mame/>
# To build:
## Judges' comments:
### To build:
make
```sh
make
```
### To run:
./prog > foo.c
```sh
./prog > foo.c
```
### Try:
./prog > main.c
```sh
./prog > main.c
make main
./main Hello
make main
./main Hello
```
### Selected Judges Remarks:
## Judges' comments:
Big terminals with tiny fonts have their uses in certain cases.
One of them is:
./prog < prog.c > treacle.c
make treacle
# or instead of make: cc -Wno-empty-body -funsigned-char treacle.c -o treacle
./treacle
```sh
./prog < prog.c > treacle.c
make treacle
./treacle
```
When using a smart phone to read the output, you may need to
step back to see the bigger picture because that picture is worth
more than 1000 words.
## Author's comments:
### Remarks
You may want to use a large display with a very small-font terminal.
@@ -44,11 +50,10 @@ You may want to use a large display with a very small-font terminal.
Decompress `src.zip` if you need explanation.
--------------------------------------------------------------------------------
<!--
## Copyright:
(c) Copyright 1984-2015, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
[judges]: http://www.ioccc.org/judges.html
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
-->
+42
View File
@@ -0,0 +1,42 @@
file "ast.txt" => ["src.rb", "parse.rb", "ast-def.rb"] do |t|
ruby "parse.rb", t.prerequisites[0], t.name
end
file "seq.txt" => ["ast.txt", "serialize.rb"] do |t|
ruby "serialize.rb", t.prerequisites[0], t.name
end
file "alpha.txt" => ["seq.txt", "alpha.rb"] do |t|
ruby "alpha.rb", t.prerequisites[0], t.name
end
file "reg.txt" => ["alpha.txt", "register.rb"] do |t|
ruby "register.rb", t.prerequisites[0], t.name
end
file "raw.txt" => ["reg.txt", "assemble.rb", "machine.rb"] do |t|
ruby "assemble.rb", t.prerequisites[0], t.name
end
file "main.c" => ["raw.txt", "link.rb", "machine.rb", "vm.c"] do |t|
ruby "link.rb", t.prerequisites[0], t.name
end
file "prog.c" => ["main.c", "gen-prog.rb", "prog-tmpl.c"] do |t|
ruby "gen-prog.rb", t.prerequisites[0], t.name
end
file "prog" => "prog.c" do |t|
sh "gcc", "-Wall", "-W", "-Wextra", "-o", t.name, t.prerequisites[0], "-g"
end
task :test => :default do
sh "./prog > m.c && gcc -Wall -W -Wextra -o m m.c"
sh "./m ABCDEabcde0123456789 | ruby qr-png.rb && zbarimg -q t.png"
sh "./m | ruby qr-png.rb && zbarimg -q t.png > t.c"
sh "echo -n QR-Code: > t2.c && cat m.c >> t2.c && diff t.c t2.c && echo && echo OK && echo"
sh "cat prog.c | ./iocccsize -i; wc -c prog.c"
sh "cat m.c | ./iocccsize -i; wc -c m.c"
end
task :default => "prog"
+141
View File
@@ -0,0 +1,141 @@
# alpha conversion (variable renaming)
require_relative "ast-def"
class ID
@@num = 0
def initialize(name)
@unified = nil
@name = "#{ name }:#{ @@num }"
@@num += 1
end
attr_reader :name, :unified
def inspect
if @unified
@unified.inspect
else
"#<#{ self.class }:#{ @name }>"
end
end
def unify(other)
if @unified
@unified.unify(other)
elsif other.term != self
@unified = other
end
end
def term
@unified ? @unified.term : self
end
def ==(other)
term.name == other.term.name
end
alias eql? ==
def hash
if @unified
@unified.hash
else
@name
end
end
end
def merge(vars1, vars2)
vars1.each do |key, var|
if vars2[key] && vars2[key] != var
var.unify(vars2[key])
end
end
vars1.merge(vars2)
end
def check_opt_inc_dec(seq)
seq.each_cons(4) do |insn1, insn2, insn3, insn4|
next unless insn1.is_a?(IRead)
next unless insn2 == IPush[1]
next unless insn3 == IBinary[:+] || insn3 == IBinary[:-]
next unless insn4 == IWrite[insn1.var]
insn1.tmp.unify(insn4.tmp)
end
seq.each do |insn|
case insn
when IBranch, ILoop
check_opt_inc_dec(insn.s1)
check_opt_inc_dec(insn.s2)
end
end
end
def alpha(seq, vars)
nvars = vars.dup
seq.reverse_each do |insn|
case insn
when IRead
if insn.var
if insn.tmp
nvars[insn.var] ||= insn.tmp
nvars[insn.var].unify(insn.tmp)
else
insn.tmp = nvars[insn.var] ||= ID.new(insn.var)
end
end
when IWrite
if insn.var
if insn.tmp
nvars[insn.var] ||= insn.tmp
nvars[insn.var].unify(insn.tmp)
else
insn.tmp = nvars[insn.var]
end
nvars.delete(insn.var)
end
when IExit
nvars.clear
when IBranch
nvars1 = alpha(insn.s1, nvars)
nvars2 = alpha(insn.s2, nvars)
nvars = merge(nvars1, nvars2)
when ILoop
begin
nvars_back = nvars.dup
nvars1 = alpha(insn.s1, nvars)
nvars2 = alpha(insn.s1, alpha(insn.s2, nvars))
nvars = merge(nvars1, nvars2)
merge(nvars, nvars_back)
end until nvars.keys == nvars_back.keys
end
end
nvars
end
def rename(seq)
seq.each do |insn|
case insn
when IRead, IWrite
insn.tmp = ID.new(insn.var) if insn.var == "dummy"
insn.var.replace insn.tmp.term.name if insn.var
when IBranch, ILoop
rename(insn.s1)
rename(insn.s2)
end
end
end
if ARGV.size != 2
puts "usage: #$0 <seq1.txt> <seq2.txt>"
else
seq = eval(File.read(ARGV[0]))
alpha(seq, {})
check_opt_inc_dec(seq)
rename(seq)
File.write(ARGV[1], seq.pretty_inspect)
end
+81
View File
@@ -0,0 +1,81 @@
# dump machine code
require_relative "ast-def"
require_relative "machine"
def dump_seq(seq)
seq.each_cons(2) do |insn1, insn2|
if insn1.is_a?(IPush) && insn2.is_a?(IPush)
p insn1, insn2
raise "consecutive push"
end
end
seq.map do |insn|
case insn
when IRead then (REGISTER_READ + insn.var.ord - ?a.ord).chr if insn.var
when IWrite then (REGISTER_WRITE + insn.var.ord - ?a.ord).chr if insn.var
when IInc then MNEMONIC[:Inc] + (REGISTER_READ + insn.var.ord - ?a.ord).chr
when IDec then MNEMONIC[:Dec] + (REGISTER_READ + insn.var.ord - ?a.ord).chr
when IPush then encode_number(insn.num)
when IBranch
MNEMONIC[:BranchS] + dump_seq(insn.s1) +
MNEMONIC[:BranchM] + dump_seq(insn.s2) +
MNEMONIC[:BranchE]
when ILoop
MNEMONIC[:LoopS] + dump_seq(insn.s1) +
MNEMONIC[:LoopM] + dump_seq(insn.s2) +
MNEMONIC[:LoopE]
when IBinary then MNEMONIC[insn.op]
when ITest then MNEMONIC[:Test]
when IARef then MNEMONIC[:ARef]
when IOut then MNEMONIC[:Out]
when IExit then MNEMONIC[:Exit]
when IASet then MNEMONIC[:ASet]
end
end.join
end
def check_opt_inc_dec(seq, i)
return unless seq[i].is_a?(IRead)
return unless seq[i + 1] == IPush[1]
return unless seq[i + 2] == IBinary[:+] || seq[i + 2] == IBinary[:-]
return unless seq[i + 3] == IWrite[seq[i].var]
insn = (seq[i + 2].op == :+ ? IInc : IDec)[seq[i].var]
flag = seq[i + 4].is_a?(IRead) && seq[i + 4].var == seq[i].var
[insn, seq[i].var, flag]
end
def opt_inc_dec(seq)
while true
modified = false
(seq.size - 3).times do |i|
new_insn, var, flag = check_opt_inc_dec(seq, i)
if new_insn
if flag
seq[i, 5] = [new_insn]
else
seq[i, 4] = [new_insn, IWrite[var]]
end
modified = true
end
end
break if !modified
end
seq.each do |insn|
case insn
when IBranch, ILoop
opt_inc_dec(insn.s1)
opt_inc_dec(insn.s2)
end
end
end
if ARGV.size != 2
puts "usage: #$0 <seq3.txt> <raw.txt>"
else
seq = eval(File.read(ARGV[0]))
opt_inc_dec(seq)
File.write(ARGV[1], dump_seq(seq))
end
+89
View File
@@ -0,0 +1,89 @@
require "pp"
class Node
def ==(other)
self.class == other.class &&
self.class::Keys.all? do |key|
instance_variable_get(:"@#{ key }") == other.send(key)
end
end
attr_accessor :tmp
def self.create(*keys)
klass = Class.new(Node)
klass.const_set(:Keys, keys)
klass.class_eval do
keys.each do |key|
attr_reader key
end
end
klass
end
def self.[](*values)
new(*values)
end
def initialize(*values)
self.class::Keys.zip(values) do |key, value|
instance_variable_set(:"@#{ key }", value)
end
end
def inspect
"#{ self.class }[#{
self.class::Keys.map do |key|
instance_variable_get(:"@#{ key }").inspect
end.join(", ")
}]"
end
def pretty_print(q)
q.group(2, "#{ self.class }[", "]") do
q.seplist(
self.class::Keys.map do |key|
instance_variable_get(:"@#{ key }")
end
) do |v|
q.pp v
end
end
end
end
PRead = Node.create(:var)
PImm = Node.create(:num)
PNop = Node.create
PBinary = Node.create(:op, :e1, :e2)
PIfThenElse = Node.create(:cond, :e1, :e2)
PRShift = Node.create(:val, :shift)
PARef = Node.create(:e1, :e2) # e1[e2]
PSRef = Node.create(:e)
PIRef = Node.create(:e)
PSequence = Node.create(:seq, :e)
PASet = Node.create(:e1, :e2, :e3) # e1[e2] = e3
PAssign = Node.create(:var, :exp)
PLoop = Node.create(:cond, :body)
PExit = Node.create(:e)
POut = Node.create(:e)
PTest = Node.create(:e)
IRead = Node.create(:var)
IWrite = Node.create(:var)
IInc = Node.create(:var)
IDec = Node.create(:var)
INop = Node.create
IPush = Node.create(:num)
IBinary = Node.create(:op)
IARef = Node.create
IASet = Node.create
IExit = Node.create
IOut = Node.create
ITest = Node.create
IBranch = Node.create(:s1, :s2)
ILoop = Node.create(:s1, :s2)
+58
View File
@@ -0,0 +1,58 @@
tmpl = <<END
111111100010101111111
100000101000001000001
101110100001101011101
101110101100101011101
101110101101001011101
100000100110101000001
111111101010101111111
000000001100100000000
010111101010111011010
100101011011110011100
011101111111000101101
001111011010111110100
111001101000111110101
000000001011111110100
111111100010100110110
100000101011001010011
101110101111111100111
101110101100111111100
101110100101110001111
100000101111100010110
111111100110110110100
END
tmpl = "\n" * 8 + tmpl.lines.flat_map do |l|
l = l.chomp.gsub(/./) { $& * 4 } + "\n"
[l, l]
end.join + "\n" * 8
tmpl.gsub!(/^/, "0" * 4 * 4)
tmpl.gsub!(/0+$/, "")
tmpl.gsub!("1\n") { "\\\n" }
tmpl.tr!("10", "# ")
Code = File.read(ARGV[0]).chomp[/"(.*)"/, 1].tr(" ", "Y")
code = File.read("prog-tmpl.c")
code.gsub!('"CODE"') { "\"\\\"" + Code + '"' }
code.gsub!('INDX', (Code.index("#include") - 1 + 1).to_s)
code.gsub!(/^\s+/, "")
code.gsub!("\n", "")
#ode = "#include<stdio.h>/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/" + code
code = "#include<stdio.h>/*IOCCC201420142014IOCCC2014IOCCC2014IOCCC201*/" + code
tmpl[(tmpl =~ /(\\)$/m)] = "#"
count = 0
code = tmpl.gsub(/./) do
if $& == ?# || ($& != " " && count == 3)
c = code.slice!(0, 1)
count += 1 if c == ?"
c
else
$&
end
end
File.write(ARGV[1], code)
+54
View File
@@ -0,0 +1,54 @@
# generate a table of error correction codewords
RS_BLOCK_TABLE = [
[ 26, 1, 26, 19],
[ 44, 1, 44, 34],
[ 70, 1, 70, 55],
[ 100, 1, 100, 80],
[ 134, 1, 134, 108],
[ 172, 2, 86, 68],
[ 196, 2, 98, 78],
[ 242, 2, 121, 97],
[ 292, 2, 146, 116],
[ 346, 2, 86, 68, 2, 87, 69],
[ 404, 4, 101, 81],
[ 466, 2, 116, 92, 2, 117, 93],
[ 532, 4, 133, 107],
[ 581, 3, 145, 115, 1, 146, 116],
[ 655, 5, 109, 87, 1, 110, 88],
[ 733, 5, 122, 98, 1, 123, 99],
[ 815, 1, 135, 107, 5, 136, 108],
[ 901, 5, 150, 120, 1, 151, 121],
[ 991, 3, 141, 113, 4, 142, 114],
[1085, 3, 135, 107, 5, 136, 108],
[1156, 4, 144, 116, 4, 145, 117],
[1258, 2, 139, 111, 7, 140, 112],
[1364, 4, 151, 121, 5, 152, 122],
[1474, 6, 147, 117, 4, 148, 118],
[1588, 8, 132, 106, 4, 133, 107],
[1706, 10, 142, 114, 2, 143, 115],
[1828, 8, 152, 122, 4, 153, 123],
[1921, 3, 147, 117, 10, 148, 118],
[2051, 7, 146, 116, 7, 147, 117],
[2185, 5, 145, 115, 10, 146, 116],
[2323, 13, 145, 115, 3, 146, 116],
[2465, 17, 145, 115],
[2611, 17, 145, 115, 1, 146, 116],
[2761, 13, 145, 115, 6, 146, 116],
[2876, 12, 151, 121, 7, 152, 122],
[3034, 6, 151, 121, 14, 152, 122],
[3196, 17, 152, 122, 4, 153, 123],
[3362, 4, 152, 122, 18, 153, 123],
[3532, 20, 147, 117, 4, 148, 118],
[3706, 19, 148, 118, 6, 149, 119],
]
s = ""
RS_BLOCK_TABLE.each.with_index(1) do |a, v|
size_1 = 4 * v + 16
total_codes, n1, c1, k1, n2, c2, k2 = *a
s << (total_codes * 54 / 265 - (n2 ? n1 + n2 : n1) * (c1 - k1) + 43)
end
p s
+46
View File
@@ -0,0 +1,46 @@
require_relative "machine"
Table = "))*+,**,*)-)/),0).0(6/2+667,(&$###;=@D*0"
def compress(s)
t = "\0".b * s.size
a = [nil] * s.size
n = 0
30.downto(3) do |len|
(s.size - 1).downto(0) do |i|
j = s[0, i + len - 1].rindex(s[i, len])
if s[i, len].size == len && j && i - j < 90 && t[i, len] == "\0".b * len && !s[j..i].include?(?Q)
n += 1
t[i, len] = (i-j).chr * len
a[i] = [i-j, len]
end
end
end
n = 0
s = t.gsub(/(.)\1{,56}/m) do
offset = $`.size
len = $&.size
raise if len > 92 - 34
if $1 == "\0"
(len + 34).chr + s[offset, len]
else
(len + 92).chr + ($1.ord + 36).chr
end
end
end
if ARGV.size != 2
puts "usage: #$0 <raw.txt> <prog.c>"
else
Code = File.read(ARGV[0])
vm_src = File.read("vm.c").lines.drop(1).join.gsub(/^\s*/, "").gsub("\n", "").chomp.gsub(/\s*\/\*.*?\*\//, "")
quine_data = compress("#include<stdio.h>@" + vm_src.gsub('"CODE"', "Q").gsub('"', """'"))
# back patch of absolute address
Code.gsub!(encode_number(39999)) { encode_number((Code ).size + 39901 - 1) }
Code.gsub!(encode_number(97531)) { encode_number((Code + Table ).size + 39901 - 1) }
Code.gsub!(encode_number(50000)) { encode_number((Code + Table + quine_data).size + 39901 + 1) }
File.write(ARGV[1], "#include<stdio.h>\n" + vm_src.sub("CODE") { Code + Table + quine_data } + "\n")
end
+57
View File
@@ -0,0 +1,57 @@
# 33 : not used
# 34 : NG
# 35..55 : read
# 56..76 : write
# 77..85 : binary op
# 86..91 : not used
# 92 : NG
# 93..98 : control (0..5)
# 99..104 : other insns (6..12)
# 105..126 : num (13..32)
MNEMONIC = {
:Nop => "",
:+ => 77.chr,
:- => 78.chr,
:* => 79.chr,
:"&&" => 79.chr,
:^ => 80.chr,
:> => 81.chr,
:/ => 82.chr,
:% => 83.chr,
:">>" => 84.chr, # 6
:"==" => 85.chr,
:BranchS => 93.chr, # 0 -> BranchM
:LoopM => 94.chr, # 1 -> LoopE
:BranchM => 95.chr, # 2 -> BranchE
:LoopE => 96.chr, # 3 -> LoopS (back)
:BranchE => 97.chr, # 4
:LoopS => 98.chr, # 5
:ARef => 99.chr, # 6
:ASet => 100.chr, # 7
:Out => 101.chr, # 8
:Exit => 102.chr, # 9
:Inc => 103.chr, # 10
:Dec => 104.chr, # 11
:Test => 105.chr,
}
REGISTER_READ = 35
REGISTER_WRITE = 56
IMM = 106
def encode_number(n)
if n >= 0
tt = n.to_s(126 - IMM + 1).tr("0-9a-z", [*IMM.chr..126.chr].join)
raise if tt[0] == 126.chr
tt[0, 1] = tt[0].tr([*IMM.chr..125.chr].join, [*(IMM + 1).chr..126.chr].join)
else
raise if n != -1
tt = IMM.chr
end
tt
end
+87
View File
@@ -0,0 +1,87 @@
# parse pseudo Ruby code and generate AST
require_relative "ast-def"
require "ripper"
Subroutines = {}
def expr(e)
case e[0]
when :binary then PBinary[e[2], expr(e[1]), expr(e[3])]
when :var_ref, :vcall
name = e[1][1]
case name
when "nop" then PNop[]
when "minus1" then PImm[-1]
when "Zero" then PImm[0]
else PRead[name]
end
when :aref then PARef[expr(e[1]), expr(e[2][1][0])]
when :assign then PASet[expr(e[1][1]), expr(e[1][2][1][0]), expr(e[2])]
when :@int then PImm[e[1].to_i]
when :paren
PSequence[statements(e[1][0..-2]), expr(e[1][-1])]
when :ifop
PIfThenElse[expr(e[1]), expr(e[2]), expr(e[3])]
when :method_add_arg
case e[1][1][1]
when "inc", "dec"
r = e[2][1][1][0][1][1]
PSequence[
[PAssign[r, PBinary[e[1][1][1] == "inc" ? :+ : :-, PRead[r], PImm[1]]]],
PRead[r]
]
else
pp e
raise e[1][1][1]
end
when Array then expr(e[0])
else
pp e
raise
end
end
def statement(e)
case e[0]
when :assign then PAssign[e[1][1][1], expr(e[2])]
when :method_add_block
if e[1][1][1][1] == "loop"
PLoop[expr(e[1][2][1][1]), statements(e[2][2])]
else
pp e
raise
end
when :command
case e[1][1]
when "call" then Subroutines[e[2][1][0][1][1]]
when "exit" then PExit[expr(e[2][1][0])]
when "out" then POut[expr(e[2][1][0])]
when "test" then PTest[expr(e[2][1][0])]
else
pp e
exit
end
when :void_stmt then PNop[]
else
pp e
raise e[0].to_s
end
end
def statements(s)
s.map {|e| statement(e) }
end
def program(e)
e[1].each do |e|
Subroutines[e[1][1][1][1]] = statements(e[2][2])
end
end
if ARGV.size != 2
puts "usage: #$0 <src.rb> <ast.txt>"
else
program(Ripper.sexp(File.read(ARGV[0])))
File.write(ARGV[1], Subroutines["main"].pretty_inspect)
end
+11
View File
@@ -0,0 +1,11 @@
char*s="CODE",*p,b,d[9338],*q,x,*r=d;
int main(){
;for(p=q=5000+0+d;*s;s++)if(*s>32)*p++=*s-89?*s:32;
for(p=INDX+q;(b=*p++);){
for(d[17]=10;x=*p++,b<92&&34<b--;*r++=x)if(x==9*9)
for(;*q;x=34)*r++=*q++;
for(p-=b<92;b-->4*23;r++)*r=r[36-x];
}
puts(d);
return(0);
}/*IOCCC2014IOCCC2014IOCCC2014IOCCC2014IOCCC*/
+149
View File
@@ -0,0 +1,149 @@
# Q(uine)R code generator
## What is difficult?
As you may know, QR code (specification: ISO/IEC 18004) is quite complex.
It requires a bit stream, Reed-Solomon codes, BCH codes,
data relocation, big (and irregular!) numerical tables,
human-oriented pattern arrangement (difficult to implement by a small program),
etc.
On the other hand, a quine has a big restriction:
it needs to have two copies of the program!
Do you think that it is possible to write QR code encoder in C within *1kB*?
## How did I achieve that?
It is known that we can mitigate the quine restriction by `eval`.
For example, this is a quine written in Ruby:
eval s="print 'eval s='; p s"
Note that `print` appears just once in the program.
To emulate this approach in C, I adopted the following structure.
char s[]="<main code>int main() { <VM code> }";
int main() { <VM code>; }
* `<VM code>` : An own VM implementation (written in C) that executes the main code
* `<main code>` : An object code of quine and QR code encoder (written in the assembly language of the VM)
`<main code>` (about 1100 bytes) appears once
and `<VM code>` (about 450 bytes) appears twice.
So the whole code is about 1100 + 450 * 2 = 2000 bytes. Hooray!
## What sort of VM is that?
The VM is a kind of stack machine which makes the object code compact.
The instruction set is carefully selected
to minimize the sizes of both `<main code>` and `<VM code>`.
* 21 integer registers access
* read, write, inc-and-read, dec-and-read
* Binary operations
* add, sub, mul, div, mod, xor, less than, equal, rshift
* Stack operation
* push (an immediate value)
* Heap access
* load, store
* If-Then-Else
* While-Loop
* Misc.
* putchar, exit
The instructions have variable length,
but many of them are represented as one printable character.
(See `machine.rb` and `assemble.rb` in detail.)
The mapping between instructions and characters is also carefully designed
to minimize `<VM code>`,
which makes the resulted program non-human-readable.
## How did I create the main code?
It is too tedious to write the machine language directly.
So I wrote the source code in Ruby syntax and created a Ruby-to-VM compiler
that involves alpha renaming and automatic register allocation
only for this purpose.
* `src.rb`: the source code of QR code encoder written in Ruby syntax.
* `vm.c`: the source code of the VM.
The compiler tool chain is the following.
src.rb interp.c
| |
| [parse.rb] |
V |
ast.txt |
| |
| [serialize.rb] |
V |
seq.txt |
| |
| [alpha.rb] |
V |
alpha.txt |
| |
| [register.rb] |
V |
reg.txt |
| |
| [assemble.rb] |
V |
raw.txt |
| |
+---------+---------+
|
| [link.rb]
V
main.c
|
| [gen-prog.c]
V
prog.c
* `parse.rb` converts Ruby to AST.
* `serialize.rb` converts AST to intermediate instruction sequence.
* `alpha.rb` applies "alpha-renaming" to the instruction sequence.
* `register.rb` applies "register allocation".
* `assemble.rb` converts the sequence to the final stack-machine instructions.
* `interp.c` is an implementation of the VM.
* `link.rb` generates `main.c` by linking the instructions and interpreter source.
* `gen-prog.c` generates `prog.c` based on `main.c`
You can run the process by `rake` command. `ruby-minisat` gem is needed.
$ gem install ruby-minisat
$ rake
## Tools
I checked that the following QR code readers can read the output of my program.
* Linux: [ZBar bar code reader][1]
* Android: [Barcode Scanner][2]
[1]: http://zbar.sourceforge.net/
[2]: https://play.google.com/store/apps/details?id=com.google.zxing.client.android]
You can use `tool.rb` to convert the output to a png file.
$ ./main Hello | ruby tool.rb > hello.png
$ zbarimg -q hello.png
QR-Code:Hello
$ ruby tool.rb prog.c
$ ruby tool.rb prog.c | ruby tool.rb
## Limitations
* QR code error correction level is fixed to L (Low).
* It supports only byte mode (not numeric, alphanumeric, kanji mode...)
* The mask pattern is fixed to 4.
* The mask is not applied to the remained bits.
+83
View File
@@ -0,0 +1,83 @@
# register allocation (variable renaming again!)
require_relative "ast-def"
require "minisat"
Conflicts = {}
def liveness(seq, vars)
nvars = vars.dup
seq.reverse_each do |insn|
case insn
when IRead, IWrite
nvars[insn.var] = true if insn.var
when IExit
nvars.clear
when IBranch
nvars1 = liveness(insn.s1, nvars)
nvars2 = liveness(insn.s2, nvars)
nvars = nvars1.merge(nvars2)
when ILoop
begin
nvars_back = nvars
nvars1 = liveness(insn.s1, nvars)
nvars2 = liveness(insn.s1, liveness(insn.s2, nvars))
nvars = nvars1.merge(nvars2)
end until nvars.keys == nvars_back.keys
end
nvars.keys.sort.combination(2) do |v1, v2|
Conflicts[[v1, v2].sort] = true
end
nvars.delete(insn.var) if insn.is_a?(IWrite) && insn.var
end
nvars
end
def graph_coloring(liveness, k)
solver = MiniSat::Solver.new
h = {}
liveness.keys.flatten.uniq.each do |var|
vs = (0...k).map { solver.new_var }
solver << vs
vs.combination(2) {|v1, v2| solver << [-v1, -v2] }
h[var] = vs
end
liveness.each_key do |var1, var2|
h[var1].zip(h[var2]) {|v1, v2| solver << [-v1, -v2] }
end
solver.solve
h.each do |var, vs|
h[var] = (vs.find_index {|v| solver[v] } + ?a.ord).chr
end
h
end
def rename(seq, h)
seq.each do |insn|
case insn
when IRead, IWrite
insn.var.replace h[insn.var] if insn.var
when IBranch, ILoop
rename(insn.s1, h)
rename(insn.s2, h)
end
end
end
if ARGV.size != 2
puts "usage: #$0 <seq2.txt> <seq3.txt>"
else
seq = eval(File.read(ARGV[0]))
liveness(seq, {})
rename(seq, graph_coloring(Conflicts, 21))
File.write(ARGV[1], seq.pretty_inspect)
end
+122
View File
@@ -0,0 +1,122 @@
# translate AST into stack-based instruction sequence
require_relative "ast-def"
class PRead
def emit
[IRead[var]]
end
end
class PImm
def emit
[IPush[num]]
end
end
class PNop
def emit
[INop[]]
end
end
class PBinary
def emit
if op == :<
e2.emit + e1.emit + [IBinary[:>]]
else
e1.emit + e2.emit + [IBinary[op]]
end
end
end
class PIfThenElse
def emit
s1 = e1.emit
s2 = e2.emit
# merge the common tails if any
tails = []
s1.reverse.zip(s2.reverse) do |i1, i2|
break if i1 != i2
s1.pop
s2.pop
tails.unshift i1
end
cond.emit + [IBranch[s1, s2]] + tails
end
end
class PARef
def emit
if e1 == PImm[0] && e2.is_a?(PBinary) && e2.op == :+
e2.emit[0..-2] + [IARef[]]
elsif e2 == PImm[0] && e1.is_a?(PBinary) && e1.op == :+
e1.emit[0..-2] + [IARef[]]
else
e1.emit + e2.emit + [IARef[]]
end
end
end
class PASet
def emit
if e1 == PImm[0] && e2.is_a?(PBinary) && e2.op == :+
e3.emit + e2.emit[0..-2] + [IASet[]]
elsif e2 == PImm[0] && e1.is_a?(PBinary) && e1.op == :+
e3.emit + e1.emit[0..-2] + [IASet[]]
else
e3.emit + e2.emit + e1.emit + [IASet[]]
end
end
end
class PAssign
def emit
exp.emit + [IWrite[var != ?_ ? var : nil]]
end
end
class PSequence
def emit
seq.flat_map {|e| e.emit } + e.emit
end
end
class PLoop
def emit
[ILoop[cond.emit, body.flat_map {|e| e.emit }]]
end
end
class PTest
def emit
e.emit + [ITest[]]
end
end
class PExit
def emit
e.emit + [IExit[]]
end
end
class POut
def emit
e.emit + [IOut[]]
end
end
class Array
def emit
flat_map {|e| e.emit }
end
end
if ARGV.size != 2
puts "usage: #$0 <ast.txt> <seq1.txt>"
else
ast = eval(File.read(ARGV[0]))
File.write(ARGV[1], ast.flat_map {|e| e.emit }.pretty_inspect)
end
+280
View File
@@ -0,0 +1,280 @@
# magical calculation methods!
# calculate the total code words for the version given
total_codewords {
n = Version / 7 * 5 + 5
TotalCodes =
Size_1 * Size_1 / 8 - 24 -
(Version > 6 ? n * n / 8 + n : (Version > 1) * 4)
}
# calculate the error correction code words for the version given
error_correction_codewords {
ECCodes = TotalCodes * 54 / 265 - Zero[39999 + Version] + 43
}
# calculate the degree of the generator polynomial
generator_polynomial_degree {
Degree =
Version < 6 ? ECCodes :
Version == 10 ? 18:
(n = 16
loop(ECCodes / 2 % dec(n)) { }
n * 2)
}
other_constants {
N = ECCodes / Degree
M = N - TotalCodes % N
K = (TotalCodes - ECCodes) / N + 1
S = Degree + K
D = K * N - M
}
# GF(2^8)
gen_table {
i = 511
j = 1
loop(i) {
j = ((Forward[Backward[j] = dec(i)] = j) ^ (285 * (j % 2))) / 2
}
}
# calculate the generator polynomial
setup_generator_polynomial {
i = Degree + 1
loop(dec(i)) {
j = Degree + 1
x = 0
loop(j > i) {
y = Coefs[dec(j)]
dummy = Coefs[j] = Backward[x ^ Forward[Degree - i + y]]
x = Forward[y]
}
}
}
# paint fixed cells
setup_fixed_patterns {
# three big markers
i = Size
loop(i) {
i = i - 1
j = Size
loop(j) {
k = i - j * Size
dummy =
Zero[j * Size + i] =
Zero[minus1 - k] =
Zero[Size * Size + k] =
(i < 7 && dec(j) < 7 && (((i - 6) * i + 19 + (j - 6) * j) / 5) ^ 2 < 3) -
((i > 7) + (j > 7) > 0)
}
}
# alignment markers
x = Version / 7 + 2
n = Version - 32 ? (Version + x / 2) * 2 / (x - 1) * 2 : 26
i = x * x
loop(Version > 1 && dec(i) > 1) {
j = 25
loop(i - x && j) {
y = Size_1 - 8 + n
dummy = Zero[
((y - i / x * n) % (y - 4) + j / 5) * Size +
((y - i % x * n) % (y - 4) + j % 5)
] = (33084991 >> dec(j)) % 2
}
}
# BCH for version info
n = Version * 4096
k = n
j = 64
loop(j) {
k = k ^ ((7973 * j) * (k + 1 > 4096 * j))
j = j / 2
}
# (n + k) is the code
# version info and timing
i = 18 * (Version > 6)
loop(i + Size - 16) {
dummy =
Zero[x * Size + y] =
Zero[y * Size + x] =
dec(i) < 0 ?
(x = Size - 8 + i ; y = 6 ; (0 - i) % 2) :
(x = Size - 11 + i % 3; y = i / 3; ((n + k) >> i) % 2)
}
# format info
i = 15
loop(i) {
# BCH for format info (4 + 8) (mask = 4, error correction level = L)
# 26159 is the code
n = dec(i) > 7 ? Size_1 - 14 + i : i + (i > 5)
dummy =
Zero[Size * (Size - 8) + 8] =
Zero[n * Size + 8] =
Zero[9 * Size_1 - n + 8 + (i / 3 == 2)] =
(26159 >> i) % 2
}
}
# data encoding
setup_data {
n = minus1
buff = 64
z = 17
j = 0
loop(inc(n) < D) {
x = Input[j]
_ = x ?
(y = V9 < 0 ? (j = j + 1; x) : (V9 = V9 - 1; V9 ? InputLen % 256 : (InputLen >> 8))
emit = buff + y / 16
buff = (y % 16) * 16
nop) :
(emit = buff
z = z ^ 253
buff = z
nop)
dummy = EncodedData[n] = emit
}
}
# error correction
encode {
x = D
i = N
loop(i) {
y = D + S * i
n = K - (dec(i) < M)
j = n + Degree
loop(j) {
dummy = EncodedData[dec(y)] = dec(j) < n ? EncodedData[dec(x)] : 0
}
loop(j < n * Degree) {
t = j % Degree + 1
idx = EncodedData + y + j / Degree
dummy = idx[t] = idx[t] ^ ((idx[0] > 0) * Forward[Coefs[t] + Backward[idx[0]]])
j = j + 1
}
}
}
# make the pattern by placing the data
place {
x = Size_1
y = Size_1
j = minus1
loop(inc(j) < S) {
i = M * (j == K - 1)
loop(i < N) {
t = 8
loop(t) {
xx = x + (x > 5)
_ = Zero[y * Size + xx] < 0 ?
(dummy = Zero[y * Size + xx] =
((EncodedData[j + (j < K ? i * K - (i < M ? i : M) : D + i * S)] >> dec(t)) ^
(y / 2 + xx / 3)) % 2 == 0;
nop) : nop
x = x -
(x % 2 ? 1 :
(x / 2 % 2 ?
(y ? (y = y - 1; minus1) : 1) :
(y - Size_1 ? (y = y + 1; minus1) : 1)))
}
i = i + 1
}
}
}
# print the pattern to stdout
output {
k = Size + 4
y = Size_1 - k
loop(inc(y) < k) {
x = Size_1 - k
loop(inc(x) < k) {
out 27; out 91; out 52
out 55 -
(x > minus1 && x < Size && y > minus1 && y < Size) *
(x + y * Size < 0 ? 0 : (Zero[x + y * Size] > 0)) * 7
out 109; out 32; out 32
}
out 27; out 91; out 48; out 109; out 10
}
}
# generate the code itself by decompression
decompress {
i = 97531 # a placeholder value that will be replaced by assemble.rb
InputLen = minus1
# decompress
loop((b = Zero[inc(i)]; b)) {
_ = b < 92 ?
(loop(dec(b) > 33) {
x = Zero[inc(i)]
_ = x - 81 ? nop :
(k = 39900 # quine
x = Input[inc(InputLen)] = 34
loop((z = Zero[inc(k)]; z)) {
dummy = Input[inc(InputLen)] = z
}
nop
)
dummy = Input[inc(InputLen)] = x
}
nop) :
(i = i + 1
loop(dec(b) > 91) {
dummy = Input[InputLen] = Input[inc(InputLen) - i[0] + 36]
}
nop)
}
InputLen = InputLen + 1
dummy = 17[Input] = 10 # newline after "#include<stdio.h>"
}
main {
# memory layout
# 0...31329: Pattern data
# 32000...32512: Forward translation of GF(2^8)
# 32512...32768: Backward translation of GF(2^8)
# 32800...33000: Coefficients of generator polynomial
# 33000...37000: EncodedData
# 40000... : Code + nul + Input
Forward = 32000
Backward = 32512
Coefs = 32800
EncodedData = 33000
Input = 50000 # a placeholder
loop(Input[InputLen]) { InputLen = InputLen + 1 }
_ = InputLen ? nop : (call decompress; nop)
Version = 0
loop(Version < 40) {
Size_1 = 4 * inc(Version) + 16
Size = Size_1 + 1
call total_codewords
call error_correction_codewords
call generator_polynomial_degree
call other_constants
V9 = Version > 9
_ = D - 1 - V9 > InputLen ? (
call gen_table
call setup_generator_polynomial
call setup_fixed_patterns
call setup_data
call encode
call place
call output
exit 0
nop
) : nop
}
exit 1
}
Binary file not shown.
+49
View File
@@ -0,0 +1,49 @@
require "zlib"
lines = [*$<]
if lines.join.gsub(/\e\[\d*m/, "").chars.uniq.sort == ["\n", " "]
matrix = lines.map do |line|
row = []
cell = nil
line.chomp.scan(/\G(?:\e\[(\d+)m|..)/) do
if $1
cell = $1 == "40" ? :black : :white
else
row << cell
end
end
row
end
size = matrix.size
if size != matrix.first.size
raise "not square: #{ matrix.first.size } x #{ matrix.size }"
end
$stderr.puts "Version: #{ (size - 25) / 4 }"
scale = 400 / size
data = (0 ... scale * size).flat_map do |y|
[0] + (0 ... scale * size).flat_map do |x|
matrix[y / scale][x / scale] == :black ? [0, 0, 0] : [255, 255, 255]
end
end
print "\x89PNG\r\n\x1a\n"
[
["IHDR", [scale * size, scale * size, 8, 2, 0, 0, 0].pack("NNCCCCC")],
["IDAT", Zlib::Deflate.deflate(data.pack("C*"))],
["IEND", ""],
].each do |type, data|
print [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N")
end
else
width = lines.map {|line| line.chomp.size + 4 * 4 }.max
lines.each do |line|
puts line.chomp.ljust(width).gsub(/../) { "\e[#{ $& == " " ? 47 : 40 }m " }
end
end
+29
View File
@@ -0,0 +1,29 @@
#include<stdio.h>
int H[99999],*s,*c,d,t;
int main(int n,char*v[]){
for(s=c=H+39999;(*++s="CODE"[d++]););
for(;n>1&&(*++s=v[1][t++]););
/*for(;n=getchar(),n>0;)*++s=n;*/
for(s=H;d=*c++%93,d-9;){
int v=*s,g[]={n+v,v-n,n*v,v^n,n<v,n?v/n:0,n?v%n:0,v>>n,v==n};
for(t=0;d<4&&d>=2*!!n&&(c-=d/3*2,v=3+*c++,t||v!=98+d);)
t+=v++/6-16?0:v/2%3-1;
for(t=d-14;t>0&&d<34&&105<*c;)t=t*21+*c++-106;
n=
/*d==12?fprintf(stderr,"test:%d\n",n),*s--:*/
d>76?s--,g[d-77]: /* operation */
d>55?H[d-21]=n,*s--: /* reg write */
d>9?
*++s=n,
12>d?H[*c++]+=21-d*2: /* inc / dec */
d<34?t:H[d] /* imm push / reg read */
:
d>5?
s--,
7<d?putchar(n),v: /* out */
6<d?H[*g+99]=*s--: /* heap write */
H[*g+99]: /* heap read */
2>d?*s--:n; /* control */
}
return n;
}