
QUIET ?= @

####################################################################

ALL_PROGS = gen_9j gen_3j combine_js unique_js hash_js
ALL_PROGS_HASH = info_js lookuptest \
	clookup

all: lib/libfastwigxj.a \
	$(addprefix bin/,$(ALL_PROGS) $(ALL_PROGS_HASH))

####################################################################

UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Darwin)
    SO_EXT = dylib
else
    SO_EXT = so
endif

shared: lib/libfastwigxj_shared.$(SO_EXT)

####################################################################

HEADERS = gen/fastwigxj_auto_config.h \
	cfg/fastwigxj_config.h

LIB_OBJS = fastwigxj.o canonicalise.o fortran_wrap.o

LIB_OBJECTS = $(LIB_OBJS:%.o=obj/%.o)

OBJS_LIB = fastwigxj_cc.o

OBJS  = $(addsuffix .o,$(ALL_PROGS)) \
	$(addsuffix .o,$(ALL_PROGS_HASH)) \
	$(OBJS_LIB) gen_buf.o gen_header.o

BUILD_OBJS = $(addprefix obj/,$(OBJS))

SRC_DIRS = . src wrap test example

AUTO_DEPS = $(BUILD_OBJS:%.o=%.d)
LIB_AUTO_DEPS = $(LIB_OBJECTS:%.o=%.d)

####################################################################

AR = ar
RLIB = ranlib

FCFLAGS += -O3 -I mod/
CFLAGS  += -O3 -fPIC -g -I inc/ -I cfg/ -I gen/ -I src/ \
	-Wall -Wextra -Wconversion -Wcast-qual
CFLAGS  += -march=native
CXXFLAGS = $(CFLAGS)
ARFLAGS = -cr

LDFLAGS  = -lm -O3

# CFLAGS  += -DWIGNER9J_NO_TRIVIAL0_CHECK=1 -DWIGNER9J_NO_OVERFLOW_CHECK=1

LDFLAGS += -Llib/
LDLIBS += -lfastwigxj

####################################################################

WIGXJPFDIR = $(shell ls ../wigxjpf*/inc/wigxjpf.h | \
	sed -e  "s,/inc/wigxjpf.h,," | sort | tail -n 1)

ifeq ($(WIGXJPFDIR),)
$(error Cannot find any WIGXJPF directory: ../wigxjpf*)
endif

FCFLAGS += -I$(WIGXJPFDIR)/mod
CFLAGS += -I$(WIGXJPFDIR)/inc
LDFLAGS += -L$(WIGXJPFDIR)/lib
LDLIBS += -lwigxjpf

HEADERS += $(WIGXJPFDIR)/inc/wigxjpf.h $(WIGXJPFDIR)/inc/wigxjpf_quadmath.h

####################################################################

# HAVE_QUADMATH := $(shell $(CC) -DTEST_FLOAT128=1 src/test_cc_dbl.c \
# 	-lquadmath -o /dev/null 2> /dev/null && echo 1)

ifeq ($(HAVE_QUADMATH),1)
LDLIBS += -lwigxjpf_quadmath
LDLIBS += -lquadmath
TESTLDLIBS += -lquadmath
else
NO_FLOAT128_FLAG = --no-float128
endif

####################################################################

AUTO_CONFIG_TESTS = TEST_LONG_DOUBLE TEST_FLOAT128 \
	TEST_THREAD TEST_THREAD_MUTEX TEST_LSFENCE \
	TEST_SSE4_1 TEST_AVX2

gen/fastwigxj_auto_config.h.%: src/test_cc_dbl.c Makefile
	@echo "  AUTO   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)rm -f $@
	$(QUIET)echo "/* Autogenerated: test $* - editing is useless! */" > $@
	$(QUIET)$(CC) -march=native -D$*=1 $< -o $@.exe $(TESTLDLIBS) -Werror \
	  > $@.cc.out 2> $@.cc.err && \
	  ./$@.exe \
	  > $@.run.out 2> $@.run.err && \
	  grep define $@.run.out >> $@ || true

gen/fastwigxj_auto_config.h: \
	  $(addprefix gen/fastwigxj_auto_config.h.,$(AUTO_CONFIG_TESTS))
	@echo "  AUTO   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)cat $^ > $@

####################################################################

####################################################################

-include $(AUTO_DEPS) $(LIB_AUTO_DEPS) # dependency files (.d)

# In such templates, all $ must be replaced by $$, to avoid evaluation
# at instantiation
define COMPILE_FROM_DIR_template
obj/%.o: $(1)/%.cc obj/%.d $(HEADERS)
	@echo "   CXX   $$@"
	@mkdir -p $$(dir $$@)
	$$(QUIET)$$(CXX) $$(CXXFLAGS) $$< -c -o $$@

obj/%.d: $(1)/%.cc $(HEADERS)
	@echo "  DEPS   $$@"
	@mkdir -p $$(dir $$@)
	$$(QUIET)$$(CXX) $$(CXXFLAGS) -MM -MG $$< | \
	  sed -e 's,\($$(*F)\)\.o[ :]*,$$(dir $$@)$$*.o $$@ : ,g' \
	> $$@

obj/%.o: $(1)/%.c obj/%.d $(HEADERS)
	@echo "   CC    $$@"
	@mkdir -p $$(dir $$@)
	$$(QUIET)$$(CC) $$(CFLAGS) $$< -c -o $$@

obj/%.d: $(1)/%.c $(HEADERS)
	@echo "  DEPS   $$@"
	@mkdir -p $$(dir $$@)
	$$(QUIET)$$(CC) $$(CFLAGS) -MM -MG $$< | \
	  sed -e 's,\($$(*F)\)\.o[ :]*,$$(dir $$@)$$*.o $$@ : ,g' \
	> $$@

obj/%.o: $(1)/%.f
	@echo "   FC    $$@"
	@mkdir -p $$(dir $$@)
	$$(QUIET)$$(FC) $$(FCFLAGS) $$< -c -o $$@
endef

$(foreach dir,$(SRC_DIRS),$(eval $(call COMPILE_FROM_DIR_template,$(dir),)))

####################################################################

lib/libfastwigxj.a: $(LIB_OBJECTS) $(LIB_AUTO_DEPS)
	@echo "   AR    $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)$(AR) $(ARFLAGS) $@ $(LIB_OBJECTS)
	$(QUIET)$(RLIB) $@

lib/libfastwigxj_shared.so: lib/libfastwigxj.a
	@echo "  LINK   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)$(CC) -shared -Wl,-whole-archive $< -Wl,-no-whole-archive -o $@

lib/libfastwigxj_shared.dylib: lib/libfastwigxj.a
	@echo "  LINK   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)$(CC) -dynamiclib -Wl,-force_load $< -o $@

####################################################################

define LINK_PROG_template
bin/$(1): obj/$(1).o $(addprefix obj/,$(2)) lib/libfastwigxj.a
	@echo "  LINK   $$@"
	@mkdir -p $$(dir $$@)
	$(QUIET)$(CXX) $(CXXFLAGS) -o $$@ \
	  obj/$(1).o $(addprefix obj/,$(2)) $(LDFLAGS) $(LDLIBS) $(3)
endef

$(foreach prog,$(ALL_PROGS),\
	$(eval $(call LINK_PROG_template,$(prog),gen_buf.o gen_header.o,)))

$(foreach prog,$(ALL_PROGS_HASH),\
	$(eval $(call LINK_PROG_template,$(prog),$(OBJS_LIB),)))

####################################################################

mod/ffastwigxj.mod: src/ffastwigxj.f
	@echo "   MOD   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)cd $(dir $@) ; $(FC) -c ../$<

obj/flookup.o: mod/ffastwigxj.mod

bin/flookup: obj/flookup.o lib/libfastwigxj.a
	@echo "  LINK   $@"
	$(QUIET)mkdir -p $(dir $@)
	$(QUIET)$(FC) $(LDFLAGS) -o $@ $< $(LDLIBS)

####################################################################

# Simple test to make sure we have compiled and are not obviously wrong

ALL_TEST_PROGS_DEP = $(addprefix bin/,$(ALL_PROGS) $(ALL_PROGS_HASH))

test.9j.mark: $(ALL_TEST_PROGS_DEP)
	@echo "  TEST   $@"
	$(QUIET)(bin/gen_9j --lim=3 | bin/combine_js | \
	  bin/unique_js ./test_comb_3_6_12.9j) \
	  > gen/test.9j.out 2> gen/test.9j.err
	$(QUIET)bin/hash_js ./test_comb_3_6_12.9j ./test_hashed_3_6_12.9j \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/info_js ./test_hashed_3_6_12.9j \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/lookuptest --lim=2 ./test_hashed_3_6_12.9j \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/lookuptest --lim=3 ./test_hashed_3_6_12.9j \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/lookuptest --lim=4 ./test_hashed_3_6_12.9j \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/lookuptest --lim=5 ./test_hashed_3_6_12.9j --dyn-9j=100 \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)bin/lookuptest --lim=5 ./test_hashed_3_6_12.9j --dyn-9j=1000 \
	  >> gen/test.9j.out 2>> gen/test.9j.err
	$(QUIET)diff -u test/test.9j.good gen/test.9j.out
	$(QUIET)touch $@

test_by6j.9j.mark: $(ALL_TEST_PROGS_DEP) test.9j.mark test_float128.6j.mark
	@echo "  TEST   $@"
	$(QUIET)bin/info_js ./test_hashed_3_6_12.9j \
	  > gen/test_by6j.9j.out 2> gen/test_by6j.9j.err
	$(QUIET)bin/info_js ./test_table_8_float128.6j \
	  >> gen/test_by6j.9j.out 2>> gen/test_by6j.9j.err
	$(QUIET)bin/lookuptest --lim=2 \
	  ./test_hashed_3_6_12.9j ./test_table_8_float128.6j \
	  >> gen/test_by6j.9j.out 2>> gen/test_by6j.9j.err
	$(QUIET)bin/lookuptest --lim=3 \
	  ./test_hashed_3_6_12.9j ./test_table_8_float128.6j \
	  >> gen/test_by6j.9j.out 2>> gen/test_by6j.9j.err
	$(QUIET)bin/lookuptest --lim=4 \
	  ./test_hashed_3_6_12.9j ./test_table_8_float128.6j \
	  >> gen/test_by6j.9j.out 2>> gen/test_by6j.9j.err
	$(QUIET)bin/lookuptest --lim=5 \
	  ./test_hashed_3_6_12.9j ./test_table_8_float128.6j \
	  >> gen/test_by6j.9j.out 2>> gen/test_by6j.9j.err
	$(QUIET)diff -u test/test_by6j.9j.good gen/test_by6j.9j.out
	$(QUIET)touch $@

test.6j.mark: $(ALL_TEST_PROGS_DEP)
	@echo "  TEST   $@"
	$(QUIET)bin/hash_js --max-E-6j=8 /dev/null ./test_table_8.6j \
	  > gen/test.6j.out 2> gen/test.6j.err
	$(QUIET)bin/info_js ./test_table_8.6j \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)bin/lookuptest --lim=6 ./test_table_8.6j \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)bin/lookuptest --lim=8 ./test_table_8.6j \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)bin/lookuptest --lim=11 ./test_table_8.6j \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)bin/lookuptest --lim=11 ./test_table_8.6j --dyn-6j=100 \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)bin/lookuptest --lim=11 ./test_table_8.6j --dyn-6j=1000 \
	  >> gen/test.6j.out 2>> gen/test.6j.err
	$(QUIET)diff -u test/test.6j.good gen/test.6j.out
	$(QUIET)touch $@

test_float128.6j.mark: $(ALL_TEST_PROGS_DEP)
	@echo "  TEST   $@"
	$(QUIET)bin/hash_js --max-E-6j=8 --float128 \
	  /dev/null ./test_table_8_float128.6j \
	  > gen/test_float128.6j.out 2> gen/test_float128.6j.err
	$(QUIET)bin/info_js ./test_table_8_float128.6j \
	  >> gen/test_float128.6j.out 2>> gen/test_float128.6j.err
	$(QUIET)bin/lookuptest --lim=8 ./test_table_8_float128.6j \
	  >> gen/test_float128.6j.out 2>> gen/test_float128.6j.err
	$(QUIET)touch $@

test.3j.mark: $(ALL_TEST_PROGS_DEP)
	@echo "  TEST   $@"
	$(QUIET)bin/hash_js --max-E-3j=18 /dev/null ./test_table_18.3j \
	  > gen/test.3j.out 2> gen/test.3j.err
	$(QUIET)bin/info_js ./test_table_18.3j \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)bin/lookuptest --lim=12 ./test_table_18.3j \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)bin/lookuptest --lim=18 ./test_table_18.3j \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)bin/lookuptest --lim=22 ./test_table_18.3j \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)bin/lookuptest --lim=22 ./test_table_18.3j --dyn-3j=1000 \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)bin/lookuptest --lim=22 ./test_table_18.3j --dyn-3j=10000 \
	  >> gen/test.3j.out 2>> gen/test.3j.err
	$(QUIET)diff -u test/test.3j.good gen/test.3j.out
	$(QUIET)touch $@

####################################################################

.PHONY: lookuptest.prereq
lookuptest.prereq: test.3j.mark test.6j.mark test.9j.mark

%lookup.test: bin/%lookup lookuptest.prereq
	@echo "  CHECK  $@"
	$(QUIET)mkdir -p gen/
	$(QUIET)./$< $(NO_FLOAT128_FLAG) > gen/$*lookup.out
	$(QUIET)diff -u \
	  $(or $(wildcard example/$*lookup$(NO_FLOAT128_FLAG).good), \
	       example/$*lookup.good)\
	  gen/$*lookup.out
	$(QUIET)#touch $@ # Do not stamp, so test is re-run.

####################################################################

test: test.9j.mark test.6j.mark test.3j.mark clookup.test
ifeq ($(HAVE_QUADMATH),1)
lookuptest.prereq: test_float128.6j.mark
test: test_float128.6j.mark test_by6j.9j.mark
endif

####################################################################

clean:
	rm -rf gen/
	rm -rf obj/
	rm -rf lib/
	rm -rf bin/
	rm -rf mod/
	rm -f test.3j.mark test.6j.mark test.9j.mark test_float128.6j.mark
