build_dir = build
target = $(build_dir)/zoltraak.exe
cxx_target = $(build_dir)/zoltraak_cxx.exe
source = zoltraak40.c
#source = spam4.c

cc = gcc
cxx = g++
cflags = -O2 -Wall -Werror -pedantic -march=native

# ................................................................
# Executables.

$(target): $(source) | make_build_dir
	$(cc) $(cflags) $< -o $@

$(cxx_target): $(build_dir)/encoder.cc
	$(cxx) $(cflags) $< -o $@

$(build_dir)/encoder.cc: $(source) | make_build_dir
	cp -f $< $@

$(build_dir)/repeated_substrings.exe: repeated_substrings.cc | make_build_dir
	$(cxx) $(cflags) -std=c++20 $< -o $@

$(build_dir)/fast_zoltraak.exe: fast_zoltraak.c | make_build_dir
	$(cc) $(cflags) $< -o $@

$(build_dir)/crc32.exe: crc32.c | make_build_dir
	$(cc) $(cflags) $< -o $@

$(build_dir)/force_crc.exe: force_crc.cc | make_build_dir
	$(cxx) $(cflags) $< -o $@

$(build_dir)/force_crc_list_variations.exe: force_crc.cc | make_build_dir
	$(cxx) $(cflags) -DLIST_ALL_VARIATIONS $< -o $@


# ................................................................
# Tests.
test: \
	$(build_dir)/compile_source.test_passed \
	$(build_dir)/compile_output.test_passed \
	$(build_dir)/encode_empty.test_passed \
	$(build_dir)/encode_one_byte.test_passed \
	$(build_dir)/encode_one_line.test_passed \
	$(build_dir)/encode_binary.test_passed \
	$(build_dir)/encode_random_bits.test_passed \
	$(build_dir)/encode_random_bytes.test_passed \
	$(build_dir)/encode_repeat_one_byte.test_passed \
	$(build_dir)/encode_repeat_two_bytes.test_passed \
	$(build_dir)/encode_repeat_with_prefix.test_passed \
	$(build_dir)/encode_repeat_multiple_groups.test_passed \
	$(build_dir)/encode_12_days_of_christmas.test_passed \
	$(build_dir)/encode_99_bottles_of_beer.test_passed \
	$(build_dir)/encode_around_the_world.test_passed \
	$(build_dir)/encode_template_specialization.test_passed \
	$(build_dir)/encode_tetris_bags.test_passed \
	$(build_dir)/encode_large_repeat1.test_passed \
	$(build_dir)/encode_large_repeat2.test_passed \
	$(build_dir)/encode_extra_large0.test_passed \
	$(build_dir)/encode_extra_large1.test_passed \
	$(build_dir)/encode_extra_large2.test_passed \
	$(build_dir)/encode_extra_large3.test_passed \
	$(build_dir)/encode_extra_large4.test_passed \
	$(build_dir)/encode_huge1.test_passed \
	$(build_dir)/encode_huge2.test_passed \
	$(build_dir)/encode_huge3.test_passed \
	$(build_dir)/concat_empty_empty.test_passed \
	$(build_dir)/concat_empty_one.test_passed \
	$(build_dir)/concat_two_empty.test_passed \
	$(build_dir)/concat_one_two.test_passed \
	$(build_dir)/concat_one_two_empty_one.test_passed \
	$(build_dir)/concat_around_12_template.test_passed \
	$(build_dir)/cxx_empty.test_passed \
	$(build_dir)/cxx_12_days_of_christmas.test_passed \
	$(build_dir)/cxx_around_the_world.test_passed \
	$(build_dir)/cxx_template_specialization.test_passed \
	$(build_dir)/cxx_tetris_bags.test_passed \
	$(build_dir)/embedded_crc.test_passed \
	$(build_dir)/run_zoltraak.test_passed \
	$(build_dir)/fast_zoltraak.test_passed \
	$(build_dir)/crc32.test_passed \
	$(build_dir)/force_crc.test_passed \
	$(build_dir)/repeated_substrings.test_passed

# Verify that input source is compatible with various compilers and C standards.
$(build_dir)/compile_source.test_passed: $(source) test_compile_all.sh | make_build_dir
	bash test_compile_all.sh $< && touch $@

# Verify that output sources are compatible with various compilers and
# C standards.  encode_random* and encode_large* are excluded to save time.
$(build_dir)/compile_output.test_passed: \
	$(build_dir)/encode_empty.c \
	$(build_dir)/encode_one_byte.c \
	$(build_dir)/encode_one_line.c \
	$(build_dir)/encode_binary.c \
	$(build_dir)/encode_repeat_one_byte.c \
	$(build_dir)/encode_repeat_two_bytes.c \
	$(build_dir)/encode_repeat_with_prefix.c \
	$(build_dir)/encode_repeat_multiple_groups.c \
	$(build_dir)/encode_12_days_of_christmas.c \
	$(build_dir)/encode_99_bottles_of_beer.c \
	$(build_dir)/encode_around_the_world.c \
	$(build_dir)/encode_template_specialization.c \
	$(build_dir)/encode_tetris_bags.c \
	$(build_dir)/concat_empty_empty.c \
	$(build_dir)/concat_empty_one.c \
	$(build_dir)/concat_two_empty.c \
	$(build_dir)/concat_one_two.c \
	$(build_dir)/concat_one_two_empty_one.c \
	$(build_dir)/concat_around_12_template.c
	bash test_compile_all.sh $^ && touch $@

# Test run_zoltraak.pl script.
$(build_dir)/run_zoltraak.test_passed: run_zoltraak.pl run_zoltraak_test.sh | make_build_dir
	bash run_zoltraak_test.sh $< && touch $@

# Test fast_zoltraak tool.
$(build_dir)/fast_zoltraak.test_passed: $(build_dir)/fast_zoltraak.exe fast_zoltraak_test.sh
	bash fast_zoltraak_test.sh $< && touch $@

# Test crc32 tool.
$(build_dir)/crc32.test_passed: $(build_dir)/crc32.exe crc32_test.sh
	bash crc32_test.sh $< && touch $@

# Test force_crc tool.
$(build_dir)/force_crc.test_passed: $(build_dir)/force_crc.exe $(build_dir)/crc32.exe force_crc_test.sh
	bash force_crc_test.sh $< $(build_dir)/crc32.exe && touch $@

# Test repeated_substrings tool.
$(build_dir)/repeated_substrings.test_passed: $(build_dir)/repeated_substrings.exe repeated_substrings_test.sh
	bash repeated_substrings_test.sh $< && touch $@

# Verify that CRC of source code is embedded in the source code.
$(build_dir)/embedded_crc.test_passed: $(build_dir)/crc32.exe $(source)
	grep -qF `$(build_dir)/crc32.exe $(source)` $(source) && touch $@

# Template rules for checking that input->c->exe->output roundtrip reproduces
# the original input.
%.test_passed: %.input %.output
	cmp $^ && touch $@

%.output: %.exe
	./$< > $@

%.exe: %.c %.size_ok %.run_ok
	$(cc) -I. -O0 -Wall -Werror -pedantic $< -o $@

%.size_ok: %.c
	perl -e '$$s=0; while(<>){ die "C code too large\n" if ++$$s > 9999; }' $< && touch $@

%.run_ok: %.input %.run_output
	cmp $^ && touch $@

%.run_output : %.c run_zoltraak.pl
	perl run_zoltraak.pl $< > $@

%.c: %.input $(target)
	./$(target) < $< > $@

# Template rules for checking that xl_input->c->pl->xl_output roundtrip
# reproduces the original input.
%.test_passed: %.xl_input %.xl_output
	cmp $^ && touch $@

%.xl_output: %.c run_zoltraak.pl
	perl run_zoltraak.pl $< > $@

%.c: %.xl_input $(target)
	./$(target) < $< > $@

# Template rules for checking that cxx_input->cc->exe->cxx_output roundtrip
# reproduces the original input.
%.test_passed: %.cxx_input %.cxx_output
	cmp $^ && touch $@

%.cxx_output: %.exe
	./$< > $@

%.exe: %.cc
	$(cxx) -I. -O0 -Wall -Werror -pedantic $< -o $@

%.cc: %.cxx_input $(cxx_target)
	./$(cxx_target) < $< > $@

# Template rules for checking that xxl_input->xxl_output roundtrip
# reproduces the original input.
%.test_passed: %.xxl_input $(target) $(build_dir)/fast_zoltraak.exe
	./$(target) < $< | $(build_dir)/fast_zoltraak.exe | cmp $< - && touch $@

# Test inputs for encoder tests.
$(build_dir)/encode_empty.input: | make_build_dir
	touch $@

$(build_dir)/encode_one_byte.input: | make_build_dir
	echo -n "!" > $@

$(build_dir)/encode_one_line.input: | make_build_dir
	echo "333" > $@

$(build_dir)/encode_binary.input: | make_build_dir
	perl -e 'for($$i=0;$$i<256;$$i++){print chr($$i);}' > $@

$(build_dir)/encode_random_bits.input: | make_build_dir
	perl -e 'srand(1);for($$i=0;$$i<2048;$$i++){print chr(48+int(rand(2)));}' > $@

$(build_dir)/encode_random_bytes.input: | make_build_dir
	perl -e 'srand(8);for($$i=0;$$i<1024;$$i++){print chr(int(rand(256)));}' > $@

$(build_dir)/encode_repeat_one_byte.input: | make_build_dir
	echo "4444" > $@

$(build_dir)/encode_repeat_two_bytes.input: | make_build_dir
	echo "12121212" > $@

$(build_dir)/encode_repeat_with_prefix.input: | make_build_dir
	echo "111abcabcabcabcabcabc" > $@

$(build_dir)/encode_repeat_multiple_groups.input: | make_build_dir
	echo "111122221111222233333111122221111222233333" > $@

$(build_dir)/encode_12_days_of_christmas.input: sample_input_12_days.txt | make_build_dir
	cp -f $< $@

$(build_dir)/encode_99_bottles_of_beer.input: 99_bottles.rb | make_build_dir
	ruby $< > $@

$(build_dir)/encode_around_the_world.input: | make_build_dir
	perl -e 'print "Around the world.\n" x 144' > $@

$(build_dir)/encode_template_specialization.input: sample_input_templates.txt | make_build_dir
	cp -f $< $@

$(build_dir)/encode_tetris_bags.input: | make_build_dir
	ruby -e 'srand(7);64.times{print "IOTSZJL".chars.shuffle * ""}' > $@

$(build_dir)/encode_large_repeat1.input: | make_build_dir
	perl -e 'print "!" x 0x20000' > $@

$(build_dir)/encode_large_repeat2.input: | make_build_dir
	perl -e 'print "xxxxyyyyyzzzz" x 0x2800' > $@

$(build_dir)/encode_extra_large0.xl_input: run_zoltraak.pl | make_build_dir
	cp -f $< $@

$(build_dir)/encode_extra_large1.xl_input: | make_build_dir
	perl -e 'print "~" x 0x100000, "\n"' > $@

$(build_dir)/encode_extra_large2.xl_input: | make_build_dir
	perl -e 'srand(12);for($$i=0;$$i<0x100001;$$i++){print chr(48+int(rand(4)));}print "\n";' > $@

$(build_dir)/encode_extra_large3.xl_input: | make_build_dir
	perl -e 'srand(13);for($$i=0;$$i<0x100002;$$i++){print chr(int(rand(256)));}' > $@

$(build_dir)/encode_extra_large4.xl_input: generate_permuted_blocks.rb | make_build_dir
	ruby generate_permuted_blocks.rb > $@

$(build_dir)/encode_huge1.xxl_input: | make_build_dir
	perl -e 'srand(101);for($$i=0;$$i<0x400001;$$i++){print chr(65+int(rand(4)));}print "\n";' > $@

$(build_dir)/encode_huge2.xxl_input: | make_build_dir
	perl -e 'srand(102);for($$i=0;$$i<0x400002;$$i++){print chr(int(rand(256)));}' > $@

$(build_dir)/encode_huge3.xxl_input: $(target)
	./$< < $< > $@

# Concatenation tests.
$(build_dir)/concat_empty_empty.c: $(build_dir)/encode_empty.c
	cat $< $< > $@

$(build_dir)/concat_empty_one.c: $(build_dir)/encode_empty.c $(build_dir)/encode_repeat_one_byte.c
	cat $^ > $@

$(build_dir)/concat_two_empty.c: $(build_dir)/encode_repeat_two_bytes.c $(build_dir)/encode_empty.c
	cat $^ > $@

$(build_dir)/concat_one_two.c: $(build_dir)/encode_repeat_one_byte.c $(build_dir)/encode_repeat_two_bytes.c
	cat $^ > $@

$(build_dir)/concat_one_two_empty_one.c: $(build_dir)/concat_one_two.c $(build_dir)/concat_empty_one.c
	cat $^ > $@

$(build_dir)/concat_around_12_template.c: $(build_dir)/encode_around_the_world.c $(build_dir)/encode_12_days_of_christmas.c $(build_dir)/encode_template_specialization.c
	cat $^ > $@

$(build_dir)/concat_empty_empty.input: | make_build_dir
	touch $@

$(build_dir)/concat_empty_one.input: $(build_dir)/encode_repeat_one_byte.input
	cp -f $< $@

$(build_dir)/concat_two_empty.input: $(build_dir)/encode_repeat_two_bytes.input
	cp -f $< $@

$(build_dir)/concat_one_two.input: $(build_dir)/encode_repeat_one_byte.input $(build_dir)/encode_repeat_two_bytes.input
	cat $^ > $@

$(build_dir)/concat_one_two_empty_one.input: $(build_dir)/concat_one_two.input $(build_dir)/concat_empty_one.input
	cat $^ > $@

$(build_dir)/concat_around_12_template.input: $(build_dir)/encode_around_the_world.input $(build_dir)/encode_12_days_of_christmas.input $(build_dir)/encode_template_specialization.input
	cat $^ > $@

# C++ tests.
$(build_dir)/cxx_empty.cxx_input: | make_build_dir
	touch $@

$(build_dir)/cxx_12_days_of_christmas.cxx_input: sample_input_12_days.txt | make_build_dir
	cp -f $< $@

$(build_dir)/cxx_around_the_world.cxx_input: $(build_dir)/encode_around_the_world.input
	cp -f $< $@

$(build_dir)/cxx_template_specialization.cxx_input: sample_input_templates.txt | make_build_dir
	cp -f $< $@

$(build_dir)/cxx_tetris_bags.cxx_input: $(build_dir)/encode_tetris_bags.input
	cp -f $< $@

# ................................................................
# Maintenance rules.
make_build_dir: $(build_dir)

$(build_dir):
	mkdir -p $@

clean:
	-rm -rf $(build_dir)
