# crmvec: libmvec.so.1 (the library), crtest (native checks) and libcrref.so
# (CORE-MATH as a host reference for check-pocl.py). gcc 13 on x86-64.
# -ffp-contract=off everywhere: the vector code transcribes CORE-MATH's
# operation order, fma exactly where CORE-MATH writes one, and the scalar
# fallbacks must round the same way. -frounding-math (2026-09-27): gcc may
# otherwise fold floating-point constants assuming round-to-nearest, and
# CORE-MATH is correctly rounded in all four modes only without that (its
# own gcc builds use the flag); 33 of its 72 files compile differently.
# make's built-in CC is cc, so "CC ?= gcc" never applied (until 2026-09-29);
# a CC from the command line or the environment still wins
ifeq ($(origin CC),default)
CC      := gcc
endif
.DEFAULT_GOAL := all
VERSION := 0.12.2
# the fast mode's kernel version: its results are those fast/bits-v$(FAST_KERNELS).txt records; a release that
# changes any of them bumps this and says so (docs/fast-mode.md, "Versions")
FAST_KERNELS := 2
# install locations (make install PREFIX=... DESTDIR=...): the libraries go
# to a directory of their own, so that nothing replaces the system's
# libmvec.so.1 until a program asks for it (crmvec-run, or the rpath that
# pkg-config crmvec gives)
PREFIX  ?= /usr/local
LIBDIR  ?= $(PREFIX)/lib
INCDIR  ?= $(PREFIX)/include
BINDIR  ?= $(PREFIX)/bin
PKGDIR  ?= $(LIBDIR)/pkgconfig
CRMDIR  := $(LIBDIR)/crmvec
# the library path crmvec-run sets: CRMDIR, or a list of directories (the
# Debian package names both architectures' so that crmvec-run is the same
# file in each, as Multi-Arch: same needs; the dynamic linker skips a
# library built for another architecture)
CRMRUN  ?= $(CRMDIR)
HOSTARCH := $(shell $(CC) -dumpmachine | cut -d- -f1)
# the aarch64 build directory (defined here: rules below name it before the
# aarch64 section, and make expands a rule's prerequisites as it reads it)
A64     := build-aarch64
CFLAGS  ?= -O2
# CPPFLAGS and LDFLAGS (a distribution's hardening flags) go into the
# libraries' compiles and links (from 2026-09-29; before, only CFLAGS did)
FP      := -ffp-contract=off -frounding-math
# crmvec.c's vector code runs only in round-to-nearest (crm_rn sends the
# other modes to CORE-MATH), so it is built without -frounding-math, which
# cost the float exp and log family 3-15% (2026-09-27); CORE-MATH and
# everything else that runs in every mode keep it
FPV     := -ffp-contract=off
# crmvec.c's #if tests read option macros (LOGF_FL, CBRTF_FL, ...) whose defaults it defines itself: an undefined one
# evaluates to 0 without a word, and the code it guards silently falls back (2026-10-02: an edit deleted CBRTF_FL's
# default, and cbrtf went back to its old path with every check still passing). -Werror=undef refuses that.
UNDEF   := -Werror=undef
CR      := expf.c exp2f.c exp10f.c logf.c log2f.c log10f.c sinf.c cosf.c tanf.c powf.c \
           exp.c sin.c cos.c tan.c log/log.c pow/pow.c \
           acosf.c acoshf.c asinf.c asinhf.c atanf.c atan2f.c atanhf.c cbrtf.c coshf.c \
           erff.c erfcf.c expm1f.c hypotf.c log1pf.c sinhf.c tanhf.c \
           acos.c acosh.c asin.c asinh.c atan.c atanh.c cbrt.c cosh.c erf.c erfc.c \
           exp10.c exp2.c expm1.c hypot.c log1p.c log2.c sinh.c tanh.c atan2/atan2.c log10/log10.c \
           sinpi.c cospi.c tanpi.c asinpi.c acospi.c atanpi.c atan2pi/atan2pi.c lgamma.c tgamma.c \
           rsqrt.c sincos.c sinpif.c cospif.c tanpif.c asinpif.c acospif.c atanpif.c atan2pif.c \
           lgammaf.c tgammaf.c rsqrtf.c sincosf.c
HDR     := $(wildcard crmvec-*.h)
# Every shared library here is linked with -z defs (an undefined symbol is
# a link error, not a failure to load) and -Bsymbolic-functions: a call from
# one of its functions to another it exports (the unmasked SVE names call
# the masked ones, the 2-lane AdvSIMD forms the 4-lane ones) binds to its
# own, where through the PLT another library's copy could take it (glibc's
# libmvec loaded first made crmvec's _ZGVsNxv_sin run glibc's sin;
# 2026-09-29)
# the library's own sources, beside CORE-MATH's
LIB     := crmvec.c crmvec-scalar.c crmvec-f16.c
LIBC    := crmvec-scalar.c crmvec-f16.c crmvec-roundeven.c
# the libraries' calls to CORE-MATH go through crmvec-fpenv.c, which runs
# them with flush-to-zero off (2026-09-29): linked with --wrap=cr_<name> for
# every name listed there (the checks link CORE-MATH directly)
comma   := ,
CRWRAP  := crmvec-fpenv.c $(foreach n,$(shell grep -o 'CRW_[A-Z0-9]*([a-z0-9]*)' crmvec-fpenv.c | grep -v '(n)' | sed 's/.*(//; s/)//'),-Wl$(comma)--wrap=cr_$(n))
# CORE-MATH's half and bfloat16 functions (crmvec-f16.c), built with hidden
# visibility into an archive: each file also defines a stand-in under the
# bare name (sinf16) that is not correctly rounded and must not be exported.
# Each stand-in gets its own section, so --gc-sections drops them (they were
# the source of the library's libm imports). Until 0.11.1, cr_cbrtf16 returned
# cbrtf((float)x), the C library's cbrtf, and crmvec-cbrtf16.h pointed it at
# CORE-MATH's (found 2026-09-30 by repro-scan); CORE-MATH's file has needed no
# C library function since 398b235 (2026-10-05).
F16SRC  := $(wildcard f16/*.c) $(wildcard bf16/*.c)
F16FLAGS = -ffunction-sections
# The compiler must have __bf16 arithmetic (gcc 13 on x86-64), _Float16 (gcc 12) and __builtin_convertvector (gcc 9):
# checked once, before the first object, so that an older gcc stops with this message, not pages of errors
# (2026-10-07: RHEL 8's gcc 8.5, RHEL 9's and Ubuntu 22.04's 11, openSUSE Leap 15's 7.5, Debian 12's 12.2)
cc-check:
	@printf 'typedef float v4 __attribute__((vector_size(16)));\ntypedef int i4 __attribute__((vector_size(16)));\n_Float16 h;\n__bf16 g(__bf16 a) { return (float)a * 2.0f; }\ni4 f(v4 a) { return __builtin_convertvector(a, i4); }\n' \
	  | $(CC) -x c -c -o /dev/null - 2>/dev/null || { echo "crmvec needs gcc 13 or newer: $(CC) $$($(CC) -dumpfullversion 2>/dev/null || $(CC) -dumpversion) lacks __bf16, _Float16 or __builtin_convertvector. RHEL 8 and 9: . /opt/rh/gcc-toolset-14/enable first; openSUSE Leap 15: make CC=gcc-13" >&2; exit 1; }
# bf16/*.c get crmvec-gcc13-bf16.h: GCC 13 with AArch64's fp16 miscompiles __bf16 constants (GCC PR111867)
BF16FIX = case $$f in bf16/*) x="-include crmvec-gcc13-bf16.h";; *) x=;; esac
libcrf16.a: $(F16SRC) crmvec-gcc13-bf16.h | cc-check
	rm -rf build-f16 && mkdir -p build-f16
	for f in $(F16SRC); do $(BF16FIX); $(CC) $(CFLAGS) $(CPPFLAGS) $(FP) $(F16FLAGS) $$x -fPIC -fvisibility=hidden -c -o build-f16/$$(echo $$f | tr / -).o $$f || exit 1; done
	rm -f $@ && ar rcs $@ build-f16/*.o

# mpfrcheck and pownf-search call MPFR 4.2's functions (mpfr_pown,
# mpfr_powr, mpfr_sinpi, ...): with an older MPFR (openSUSE 15.6 has 4.1)
# they are left out of all and check, with a message, instead of stopping
# the build (f16check, on both architectures, falls back to its output hash); an MPFR elsewhere goes in through CC or CFLAGS (-I, -L)
# (the probe writes '#' as printf's \043: make 4.3 and later keep the
# backslash of a \# inside $(shell), so the directives never reached the
# compiler and every MPFR passed; a header saying 4.1 caught it, 2026-10-01)
MPFR42  := $(shell printf '\043include <mpfr.h>\n\043if MPFR_VERSION < MPFR_VERSION_NUM(4,2,0)\n\043error\n\043endif\n' | $(CC) $(CFLAGS) -x c -E - > /dev/null 2>&1 && echo yes)
ifeq ($(HOSTARCH),aarch64)
# a native aarch64 build: the aarch64 rules below with this compiler; the
# x86 checks do not build here
all: lib $(A64)/aarch64-check $(A64)/aarch64-check-advsimd $(A64)/nbench
lib: $(A64)/libmvec.so.1 $(A64)/libsleefgnuabi.so.3 crpreload/libcrpreload.so
LIBS_BUILT = $(A64)/libmvec.so.1 $(A64)/libsleefgnuabi.so.3 crpreload/libcrpreload.so
else
ifeq ($(MPFR42),)
$(info mpfrcheck, pownf-search: left out, no MPFR 4.2 or later found (they call mpfr_pown, mpfr_sinpi, ...))
endif
all: libmvec.so.1 crtest libcrref.so bcheck cecheck lcheck hypot-midpoints hypotf-midpoints tan-poles bbench ebench $(if $(MPFR42),mpfrcheck pownf-search) f16check headercheck roundeven-check
lib: libmvec.so.1 crpreload/libcrpreload.so fast/libmvec.so.1
LIBS_BUILT = libmvec.so.1 crpreload/libcrpreload.so
endif

# crpreload (x86-64 since 0.9.0, aarch64 since 0.11.0): CORE-MATH's 76 elementary functions under their C
# library names, as a second library beside libmvec.so.1, for programs whose scalar libm calls should be correctly
# rounded too (LD_PRELOAD, or crmvec-run --libm). Built in crpreload/ from this directory's CORE-MATH copies (its
# Makefile reads print-sources); on aarch64 a single build, every call guarded against FPCR's flush bits.
CRP_DEPS := $(wildcard crpreload/*.c crpreload/*.h) crpreload/mkdispatch.sh crpreload/Makefile crmvec-fpenv.h
crpreload/libcrpreload.so: $(CRP_DEPS) $(CR)
	+$(MAKE) -C crpreload libcrpreload.so CC="$(CC)" CFLAGS="$(CFLAGS)"
crpreload/crpreload-check: crpreload/crpreload-check.c crpreload/crpreload-list.h
	+$(MAKE) -C crpreload crpreload-check CC="$(CC)"

install: lib crmvec.h crmvec-simd.h crmvec.pc.in crmvec-run.in crmvec-stamp.in
	install -d $(DESTDIR)$(CRMDIR) $(DESTDIR)$(INCDIR) $(DESTDIR)$(PKGDIR) $(DESTDIR)$(BINDIR)
	install -m 755 $(LIBS_BUILT) $(DESTDIR)$(CRMDIR)/
	ln -sf libmvec.so.1 $(DESTDIR)$(CRMDIR)/libmvec.so
	[ ! -f $(DESTDIR)$(CRMDIR)/libsleefgnuabi.so.3 ] || ln -sf libsleefgnuabi.so.3 $(DESTDIR)$(CRMDIR)/libsleefgnuabi.so
	[ ! -f fast/libmvec.so.1 ] || { install -d $(DESTDIR)$(CRMDIR)/fast && install -m 755 fast/libmvec.so.1 $(DESTDIR)$(CRMDIR)/fast/ && ln -sf libmvec.so.1 $(DESTDIR)$(CRMDIR)/fast/libmvec.so; }
	install -m 644 crmvec.h $(DESTDIR)$(INCDIR)/crmvec.h
	install -m 644 crmvec-simd.h $(DESTDIR)$(INCDIR)/crmvec-simd.h
	sed -e 's|@CRMDIR@|$(CRMDIR)|g' -e 's|@INCDIR@|$(INCDIR)|g' -e 's|@VERSION@|$(VERSION)|g' crmvec.pc.in > $(DESTDIR)$(PKGDIR)/crmvec.pc
	sed -e 's|@CRMDIR@|$(CRMRUN)|g' -e 's|@VERSION@|$(VERSION)|g' crmvec-run.in > $(DESTDIR)$(BINDIR)/crmvec-run
	chmod 755 $(DESTDIR)$(BINDIR)/crmvec-run
	sed -e 's|@VERSION@|$(VERSION)|g' -e 's|@FASTKERNELS@|$(FAST_KERNELS)|g' crmvec-stamp.in > $(DESTDIR)$(BINDIR)/crmvec-stamp
	chmod 755 $(DESTDIR)$(BINDIR)/crmvec-stamp

# crmvec.h declares what the library defines: compile the definitions with it
headercheck: crmvec.h crmvec-f16.c crmvec-scalar.c
	$(CC) -fsyntax-only -Wall -include crmvec.h crmvec-f16.c
	$(CC) -fsyntax-only -Wall -include crmvec.h crmvec-scalar.c
	@grep -q '"$(VERSION)"' crmvec.h || { echo "crmvec.h's CRMVEC_VERSION is not $(VERSION)"; exit 1; }

# PORT: all 52 functions from the portable core in port/ (35 in 0.3.0, all
# from 0.4.0) instead of the SIMDe route (aarch64) or crmvec.c's intrinsics
# (x86). The default differs by target (from crmvec 0.3.0):
#   aarch64: on. The portable NEON code is 2.3 to 19 times faster than the
#            SIMDe route on a Neoverse N2, and checked (every float on all
#            2^32 inputs natively, make check, aarch64-check at three SVE
#            lengths in all four rounding modes);
#   x86:     off. On Zen 3 the intrinsics are as fast or faster; on
#            Cascade Lake the core built by clang takes 0.94 times their
#            time at the median (2026-09-29, docs/platforms.md "One portable source").
# PORT=0 or PORT=1 on the command line sets both. The x86 file is built with
# PORTCC (gcc or clang). Switching needs `make clean`: the objects do not
# record which way they were built.
PORT    ?=
# only 0 or 1: any other value built a library with 52 undefined symbols,
# and make succeeded (2026-09-29). A PORT from the environment that is
# neither (containers often set PORT=8080 for a web server) is ignored with
# a warning; one given to make is an error
ifneq ($(filter-out 0 1,$(PORT)),)
ifeq ($(origin PORT),environment)
$(warning ignoring PORT=$(PORT) from the environment: crmvec's PORT is 0 or 1)
PORT    :=
else
$(error PORT must be 0 or 1, not "$(PORT)")
endif
endif
X86PORT := $(if $(PORT),$(PORT),0)
A64PORT := $(if $(PORT),$(PORT),1)
PORTCC  ?= $(CC)
PORTDEFS ?=   # extra -D flags for the port object only (speed experiments)
PORTOBJ := $(if $(filter 1,$(X86PORT)),crmvec-port.o)
crmvec-port.o: | cc-check
crmvec-port.o: port/crmvec-port.c port/portable.h port/port-log.h port/port-exp.h port/port-expf.h port/port-sincos.h port/port-sinf.h port/port-hypf.h port/port-erff.h port/port-logf.h port/port-powf.h port/port-log1pf.h port/port-atanf.h port/port-tanf.h port/port-dfast.h port/port-erf.h port/port-tanh.h port/port-pow.h port/port-expm1.h port/port-sinhcosh.h port/port-asinh.h port/port-atanh.h port/port-atan.h port/port-asin.h port/port-atan2.h port/port-cbrt.h crmvec-powf-tab.h crmvec-atan2-tab.h crmvec-asin-tab.h crmvec-atan-tab.h crmvec-erf-tab.h crmvec-pow-tab.h crmvec-erff-tab.h crmvec-erfcf-tab.h crmvec-rows-tab.h crmvec-exp-tab.h crmvec-sin-tab.h
	$(PORTCC) $(CPPFLAGS) -O3 -ffp-contract=off -fno-math-errno -mavx2 -mfma -fPIC $(PORTDEFS) -c -o $@ port/crmvec-port.c

# E512 (x86, from 2026-09-29): the AVX-512 entry points run the portable
# core built for 512-bit vectors (port/crmvec-port-e.c) where the CPU has
# AVX512F and AVX512DQ; E512=0 keeps the AVX2 code on each half. Built with
# PORTCC, like crmvec-port.o. Switching needs `make clean`.
E512    ?= 1
ifneq ($(filter-out 0 1,$(E512)),)
$(error E512 must be 0 or 1, not "$(E512)")
endif
EOBJ    := $(if $(filter 1,$(E512)),crmvec-port-e.o)
crmvec-port-e.o: port/crmvec-port-e.c port/portable.h $(wildcard port/port-*.h) $(HDR) crmvec-functions.h | cc-check
	$(PORTCC) $(CPPFLAGS) -O3 -ffp-contract=off -fno-math-errno -mavx512f -mavx512dq -mfma -fPIC $(PORTDEFS) -c -o $@ port/crmvec-port-e.c
PORTOBJ += $(EOBJ)

crmvec.o: crmvec.c $(HDR) $(PORTOBJ)
	$(CC) $(CFLAGS) $(CPPFLAGS) $(FPV) $(UNDEF) -DCRMVEC_PORT=$(X86PORT) -DCRMVEC_E512=$(E512) -fPIC -c -o $@ crmvec.c

# rsqrt-vcheck (the vector double rsqrt against CORE-MATH) and its control:
# the same library with the transcribed rounding test off, which must differ
rsqrt-vcheck: rsqrt-vcheck.c
	$(CC) $(CFLAGS) -fopenmp -o $@ rsqrt-vcheck.c -ldl -lm
rsqrt-plant/libmvec.so.1: crmvec.c $(HDR) $(PORTOBJ) $(LIBC) crmvec-fpenv.c $(CR) libcrf16.a crmvec-exports.map
	@mkdir -p rsqrt-plant
	$(CC) $(CFLAGS) $(CPPFLAGS) $(FPV) $(UNDEF) -DCRMVEC_PORT=$(X86PORT) -DCRMVEC_E512=$(E512) -DCRMVEC_RSQRT_PLANT -fPIC -c -o rsqrt-plant/crmvec.o crmvec.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $(FP) $(LDFLAGS) -fPIC -shared -Wl,-z,defs -Wl,-Bsymbolic-functions -Wl,--gc-sections -Wl,-soname,libmvec.so.1 -Wl,--version-script=crmvec-exports.map -o $@ rsqrt-plant/crmvec.o $(PORTOBJ) $(LIBC) $(CR) $(CRWRAP) libcrf16.a -lm

# for the checks built with -mavx2 (crtest, hypot-midpoints), as before
crmvec-avx2.o: crmvec.c $(HDR) $(PORTOBJ)
	$(CC) $(CFLAGS) $(FPV) $(UNDEF) -DCRMVEC_PORT=$(X86PORT) -DCRMVEC_E512=$(E512) -mavx2 -mfma -c -o $@ crmvec.c

libmvec.so.1: crmvec.o $(LIBC) crmvec-fpenv.c $(HDR) $(CR) libcrf16.a crmvec-exports.map
	$(CC) $(CFLAGS) $(CPPFLAGS) $(FP) $(LDFLAGS) -fPIC -shared -Wl,-z,defs -Wl,-Bsymbolic-functions -Wl,--gc-sections -Wl,-soname,libmvec.so.1 -Wl,--version-script=crmvec-exports.map -o $@ crmvec.o $(PORTOBJ) $(LIBC) $(CR) $(CRWRAP) libcrf16.a -lm

ifneq ($(HOSTARCH),aarch64)
include fast/fast.mk
endif

crtest: crtest.c crtest-hard.h port/pow-parity.h crmvec-avx2.o $(LIBC) crmvec-fpenv.c $(HDR) $(CR) libcrf16.a
	$(CC) $(CFLAGS) $(FP) -mavx2 -mfma -fopenmp -o $@ crtest.c crmvec-avx2.o $(PORTOBJ) $(LIBC) $(CR) $(CRWRAP) libcrf16.a -lm -ldl

# CRREF_OMP= builds it without OpenMP (crref.c's array loops then run serially), and with crmvec-roundeven.c it
# needs nothing newer than glibc 2.17: so built in manylinux2014 it loads in any distribution (docs/distros.md)
CRREF_OMP ?= -fopenmp
libcrref.so: crref.c crmvec-scalar.c crmvec-roundeven.c crmvec-pownf-tab.h $(CR)
	$(CC) $(CFLAGS) $(FP) -fPIC -shared -Wl,-z,defs $(CRREF_OMP) -o $@ crref.c crmvec-scalar.c crmvec-roundeven.c $(CR) -lm

# how far the C library's libm is from correct rounding, against libcrref.so (docs/distros.md); not part of `all`
craccuracy: craccuracy.c
	$(CC) $(CFLAGS) -fno-builtin -pthread -o $@ craccuracy.c -ldl -lm

# craccuracy's other half: results where both are zero with opposite signs (docs/distros.md); not part of `all`
craccuracy-zsign: craccuracy-zsign.c
	$(CC) $(CFLAGS) -fno-builtin -pthread -o $@ craccuracy-zsign.c -ldl -lm

# whether the C library's libm gives other bits with AVX2 and FMA hidden (docs/distros.md); not part of `all`
cpupath: cpupath.c
	$(CC) $(CFLAGS) -fno-builtin -pthread -o $@ cpupath.c -lm

# baseline x86-64 on purpose (no -mavx): the SSE2 entry points' check must run on a CPU without AVX
bcheck: bcheck.c crtest-ftz.h
	$(CC) $(CFLAGS) -o $@ bcheck.c -ldl -lm

hypot-midpoints: hypot-midpoints.c crmvec-avx2.o $(LIBC) crmvec-fpenv.c $(HDR) $(CR) libcrf16.a
	$(CC) $(CFLAGS) $(FP) -mavx2 -mfma -o $@ hypot-midpoints.c crmvec-avx2.o $(PORTOBJ) $(LIBC) $(CR) $(CRWRAP) libcrf16.a -lm

# hypotf on float pairs within 2^-50 of a midpoint, found by search (random
# pairs never get there); a build with -DFBR_SCALE=0 must differ
hypotf-midpoints: hypotf-midpoints.c crmvec-avx2.o $(LIBC) crmvec-fpenv.c $(HDR) $(CR) libcrf16.a
	$(CC) $(CFLAGS) $(FP) -mavx2 -mfma -fopenmp -o $@ hypotf-midpoints.c crmvec-avx2.o $(PORTOBJ) $(LIBC) $(CR) $(CRWRAP) libcrf16.a -lm

# the float functions of crmvec-lanes.h with vector code, on every input
lcheck: lcheck.c
	$(CC) $(CFLAGS) -fopenmp -o $@ lcheck.c -ldl

# the AVX, AVX2 and AVX-512 entry points (cecheck c and d natively, and
# under emu-check.sh's qemu -cpu SandyBridge; cecheck e natively or under
# Intel SDE); baseline x86-64 like bcheck
cecheck: cecheck.c crtest-ftz.h
	$(CC) $(CFLAGS) -fopenmp -o $@ cecheck.c -ldl -lm

# the SSE2 entry points' speed (crtest times the AVX2 ones); baseline x86-64
# like the programs that call them. bvdecide.py turns its output into crmvec-bvec.h.
# crmvec-roundeven.c's roundeven and roundevenf (linked into the library,
# which then runs on glibc before 2.25) against the C library's
roundeven-check: roundeven-check.c crmvec-roundeven.c
	$(CC) $(CFLAGS) $(FP) -o $@ roundeven-check.c -lm

bbench: bbench.c
	$(CC) $(CFLAGS) -o $@ bbench.c -ldl -lm

# the AVX-512 entry points' speed (bbench's twin; VOID on a CPU without AVX-512F)
ebench: ebench.c
	$(CC) $(CFLAGS) -o $@ ebench.c -ldl -lm

# crmvec-lanes.h's functions through both x86 entry points against MPFR
# (libmpfr-dev 4.2), and the exhaustive search that proves pownf's double
# path and checks its exception table (crmvec-pownf-tab.h); both run by hand (`mpfrcheck`, `mpfrcheck controls`, `pownf-search`)
# its controls call CORE-MATH's pow directly, which the library does not export
# they load libmvec.so.1 from their own directory through an RPATH (not a
# RUNPATH, which LD_LIBRARY_PATH would override), and check that they did
mpfrcheck: mpfrcheck.c crtest-own.h crmvec-pownf-tab.h libmvec.so.1 pow/pow.c powf.c
	$(if $(MPFR42),,$(error mpfrcheck needs MPFR 4.2 or later))
	$(CC) $(CFLAGS) $(FP) -mavx2 -mfma -fopenmp -o $@ mpfrcheck.c pow/pow.c powf.c libmvec.so.1 -Wl,--disable-new-dtags,-rpath,'$$ORIGIN' -lmpfr -lm -ldl

# crmvec-f16.c's functions against MPFR on every input, all four modes
# Without MPFR 4.2 (RHEL 8 and 9, Ubuntu 22.04, openSUSE Leap 15) f16check is built without MPFR, and make check
# compares its output hash with f16check-hash.txt, recorded from a build whose outputs matched MPFR (2026-10-07)
f16check: f16check.c crmvec-f16-list.h crtest-own.h libmvec.so.1
	$(CC) $(CFLAGS) $(FP) -fopenmp $(if $(MPFR42),,-DNO_MPFR) -o $@ f16check.c libmvec.so.1 -Wl,--disable-new-dtags,-rpath,'$$ORIGIN' $(if $(MPFR42),-lmpfr) -lm -ldl

PWS     := crmvec-scalar.c $(CR)
pownf-search: pownf-search.c crmvec-pownf-tab.h $(PWS)
	$(if $(MPFR42),,$(error pownf-search needs MPFR 4.2 or later))
	$(CC) $(CFLAGS) $(FP) -fopenmp -o $@ pownf-search.c $(PWS) -lmpfr -lm

# cr_tan renamed to a counter inside crmvec.c only, to see which lanes go to it
tan-poles: tan-poles.c tan-poles.h $(LIB) crmvec-fpenv.c $(HDR) $(CR) libcrf16.a $(PORTOBJ)
	$(CC) $(CFLAGS) $(FPV) $(UNDEF) -DCRMVEC_PORT=$(X86PORT) -DCRMVEC_E512=$(E512) -mavx2 -mfma -Dcr_tan=cnt_tan -c -o tan-poles-crmvec.o crmvec.c
	$(if $(filter crmvec-port.o,$(PORTOBJ)),$(PORTCC) -O3 -ffp-contract=off -fno-math-errno -mavx2 -mfma -Dcr_tan=cnt_tan -c -o tan-poles-port.o port/crmvec-port.c)
	$(CC) $(CFLAGS) $(FP) -mavx2 -mfma -o $@ tan-poles.c tan-poles-crmvec.o $(if $(filter crmvec-port.o,$(PORTOBJ)),tan-poles-port.o) $(EOBJ) crmvec-scalar.c crmvec-f16.c $(CR) $(CRWRAP) libcrf16.a -lm
	rm -f tan-poles-crmvec.o tan-poles-port.o
# aarch64 (cross-built; checked under qemu-user): the same vector code, with
# SIMDe standing in for the x86 intrinsics (crmvec-simde.h; libsimde-dev):
# AdvSIMD entry points from crmvec-aarch64.c, SVE from crmvec-sve.c, under
# glibc's aarch64 names and every name SLEEF's GNU-ABI library exported
# (sleef-gnuabi-aarch64.txt). One set of objects, linked twice: as
# libmvec.so.1 and as libsleefgnuabi.so.3 (the SONAME programs built against
# SLEEF 3.x ask for). Needs gcc-aarch64-linux-gnu and libsimde-dev.
ifeq ($(HOSTARCH),aarch64)
A64CC   := $(CC)
else
A64CC   ?= aarch64-linux-gnu-gcc
endif
A64SRC  := $(LIB) crmvec-aarch64.c crmvec-sve.c $(HDR) $(CR) $(if $(filter 1,$(A64PORT)),port/crmvec-port-a64.c port/portable.h port/port-log.h port/port-exp.h port/port-expf.h port/port-sincos.h port/port-sinf.h port/port-hypf.h port/port-erff.h port/port-logf.h port/port-powf.h port/port-log1pf.h port/port-atanf.h port/port-tanf.h port/port-dfast.h port/port-erf.h port/port-tanh.h port/port-pow.h port/port-expm1.h port/port-sinhcosh.h port/port-asinh.h port/port-atanh.h port/port-atan.h port/port-asin.h port/port-atan2.h port/port-cbrt.h)
A64OBJ  := $(A64)/crmvec.o $(A64)/scalar.o $(A64)/f16.o $(A64)/advsimd.o $(A64)/sve.o $(if $(filter 1,$(A64PORT)),$(A64)/port.o)
aarch64: $(A64)/libmvec.so.1 $(A64)/libsleefgnuabi.so.3 $(A64)/aarch64-check $(A64)/aarch64-check-advsimd

# one recipe builds all of A64OBJ: a grouped target (&:), which GNU make
# before 4.3 reads as a target named & instead
ifeq ($(filter grouped-target,$(.FEATURES)),)
ifneq ($(filter aarch64,$(HOSTARCH))$(filter aarch64 sleef-exports $(A64)/%,$(MAKECMDGOALS)),)
$(error the aarch64 build needs GNU make 4.3 or later (grouped targets); this is $(MAKE_VERSION))
endif
endif
$(A64OBJ) &: $(A64SRC) $(F16SRC) crmvec-gcc13-bf16.h
	mkdir -p $(A64)
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) -fPIC -fvisibility=hidden -c -o $(A64)/crmvec.o crmvec.c
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) -fPIC -fvisibility=hidden -c -o $(A64)/scalar.o crmvec-scalar.c
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) -fPIC -fvisibility=hidden -c -o $(A64)/f16.o crmvec-f16.c
	rm -rf $(A64)/f16src && mkdir -p $(A64)/f16src && for f in $(F16SRC); do $(BF16FIX); $(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) $(F16FLAGS) $$x -fPIC -fvisibility=hidden -c -o $(A64)/f16src/$$(echo $$f | tr / -).o $$f || exit 1; done
	rm -f $(A64)/libcrf16.a && ar rcs $(A64)/libcrf16.a $(A64)/f16src/*.o
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) -DCRMVEC_PORT=$(A64PORT) -fPIC -fvisibility=hidden -c -o $(A64)/advsimd.o crmvec-aarch64.c
	$(if $(filter 1,$(A64PORT)),$(A64CC) $(CPPFLAGS) -O3 -ffp-contract=off -fno-math-errno -fPIC -fvisibility=hidden $(PORTDEFS) -c -o $(A64)/port.o port/crmvec-port-a64.c)
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) -fPIC -fvisibility=hidden -march=armv8-a+sve -c -o $(A64)/sve.o crmvec-sve.c

$(A64)/libmvec.so.1 $(A64)/libsleefgnuabi.so.3: $(A64OBJ) $(CR) crmvec-fpenv.c
	$(A64CC) $(CFLAGS) $(CPPFLAGS) $(FP) $(LDFLAGS) -fPIC -fvisibility=hidden -shared -Wl,-z,defs -Wl,-Bsymbolic-functions -Wl,--gc-sections -Wl,-soname,$(notdir $@) -o $@ $(A64OBJ) $(CR) $(CRWRAP) $(A64)/libcrf16.a -lm

# static when cross-built, so qemu-aarch64 runs it without a sysroot (a
# native build links dynamically: Fedora and Nix ship no static glibc by
# default). aarch64-check-advsimd is the same check built without SVE, for
# CPUs that lack it (added 2026-09-29).
A64STATIC := $(if $(filter aarch64,$(HOSTARCH)),,-static)
$(A64)/aarch64-check: port/aarch64-check.c port/pow-parity.h $(A64OBJ) $(CR) crmvec-fpenv.c
	$(A64CC) $(CFLAGS) $(FP) -march=armv8-a+sve -fopenmp $(A64STATIC) -I. -o $@ port/aarch64-check.c \
	  $(A64OBJ) $(CR) $(CRWRAP) $(A64)/libcrf16.a -lm
$(A64)/aarch64-check-advsimd: port/aarch64-check.c port/pow-parity.h $(A64OBJ) $(CR) crmvec-fpenv.c
	$(A64CC) $(CFLAGS) $(FP) -march=armv8-a -fopenmp $(A64STATIC) -I. -o $@ port/aarch64-check.c \
	  $(A64OBJ) $(CR) $(CRWRAP) $(A64)/libcrf16.a -lm

# every name in sleef-gnuabi-aarch64.txt is exported by the SLEEF-named
# library, and every AdvSIMD and SVE export carries the variant-PCS flag
# (without it a lazily bound call may clobber the vector registers the
# caller relies on)
sleef-exports: $(A64)/libsleefgnuabi.so.3
	python3 gen-sleef-aliases.py --check
	@aarch64-linux-gnu-nm -D --defined-only $< | awk '{print $$3}' | sort > $(A64)/exports.txt
	@grep -v '^#' sleef-gnuabi-aarch64.txt | sort | comm -23 - $(A64)/exports.txt > $(A64)/missing.txt; \
	 aarch64-linux-gnu-readelf -W --dyn-syms $< | awk '$$8 ~ /^_ZGV[ns]/ && $$7 != "UND" && !/VARIANT_PCS/ {print $$8}' > $(A64)/no-vpcs.txt; \
	 echo "SLEEF names: $$(grep -vc '^#' sleef-gnuabi-aarch64.txt); missing from $(notdir $<): $$(wc -l < $(A64)/missing.txt); exports without VARIANT_PCS: $$(wc -l < $(A64)/no-vpcs.txt) of $$(grep -c '^_ZGV[ns]' $(A64)/exports.txt)"; \
	 test ! -s $(A64)/missing.txt && test ! -s $(A64)/no-vpcs.txt

# riscv64 (added 2026-09-28): the only vector math names a compiler calls on
# riscv64 are SLEEF's RVV ones (clang 20's -fveclib=SLEEF; glibc has no
# riscv64 libmvec, gcc 13 makes no vector clones there), so crmvec there is
# a libsleef.so.3 answering them from the portable core, VLEN-agnostic
# (port/crmvec-port-rv64.c), and the 34 other names in LLVM's table lane by
# lane from CORE-MATH or libm, plus 8 of SLEEF's own spellings where they
# differ from LLVM's (94 names; sincos and modf in SLEEF's packed-pair
# convention). The port file needs clang: gcc 13 lowers its
# generic vectors to scalar code. Needs gcc-riscv64-linux-gnu and clang-20;
# checked under qemu-riscv64 at several VLENs:
#   make riscv64
#   qemu-riscv64 -cpu rv64,v=true,vlen=256 build-riscv64/rv64-check
#   LD_LIBRARY_PATH=build-riscv64 qemu-riscv64 -L /usr/riscv64-linux-gnu \
#       -cpu rv64,v=true,vlen=256 build-riscv64/rv64-dropin
RV64    := build-riscv64
RVCC    ?= riscv64-linux-gnu-gcc
RVCLANG ?= clang-20
RVFLAGS := --target=riscv64-linux-gnu -march=rv64gcv -mabi=lp64d
PORTHDR := port/portable.h $(wildcard port/port-*.h)
riscv64: $(RV64)/libsleef.so.3 $(RV64)/rv64-check $(RV64)/rv64-dropin $(RV64)/rv64-bench

$(RV64)/port.o: port/crmvec-port-rv64.c $(PORTHDR) $(HDR)
	mkdir -p $(RV64)
	$(RVCLANG) $(RVFLAGS) -O3 -ffp-contract=off -fno-math-errno -fPIC -fvisibility=hidden -DCRMVEC_RV_DISPATCH -c -o $@ $<

# the same 52 functions for VLEN 256 exactly, in 64-byte blocks (1.5-3.4x
# faster on a SpacemiT X60); port.o calls them only when vlenb is 32, since
# code built with -mrvv-vector-bits crashes at any other VLEN
RVFLAGS256 := --target=riscv64-linux-gnu -march=rv64gcv_zvl256b -mrvv-vector-bits=zvl -mabi=lp64d
$(RV64)/port-z256.o: port/crmvec-port-rv64.c $(PORTHDR) $(HDR)
	mkdir -p $(RV64)
	$(RVCLANG) $(RVFLAGS256) -O3 -ffp-contract=off -fno-math-errno -fPIC -fvisibility=hidden -DVB=64 -DCRMVEC_RV_Z256 -c -o $@ $<

$(RV64)/libcr.a: $(CR)
	mkdir -p $(RV64)/cr
	for f in $(CR); do $(RVCC) $(CFLAGS) $(FP) -fPIC -fvisibility=hidden -c -o $(RV64)/cr/$$(echo $$f | tr / -).o $$f || exit 1; done
	rm -f $@ && ar rcs $@ $(RV64)/cr/*.o

$(RV64)/libsleef.so.3: $(RV64)/port.o $(RV64)/port-z256.o $(RV64)/libcr.a
	$(RVCC) -shared -fPIC -Wl,-z,defs -Wl,-Bsymbolic-functions -Wl,-soname,libsleef.so.3 -o $@ $(RV64)/port.o $(RV64)/port-z256.o $(RV64)/libcr.a -lm
	@n=$$(riscv64-linux-gnu-readelf -W --dyn-syms $@ | awk '$$8 ~ /^Sleef_/ && $$7 != "UND" && !/VARIANT_CC/' | tee /dev/stderr | wc -l); \
	 [ "$$n" = 0 ] || { echo "$@: $$n Sleef_ exports without VARIANT_CC (above)"; rm -f $@; exit 1; }

# every Sleef_*rvvm2 entry point (the 52, then the other 42 names)
# against scalar CORE-MATH or libm (static, so qemu-riscv64 runs it without
# a sysroot)
$(RV64)/rv64-check: port/rv64-check.c port/pow-parity.h $(RV64)/port.o $(RV64)/port-z256.o $(RV64)/libcr.a
	$(RVCLANG) $(RVFLAGS) -O2 -ffp-contract=off -c -o $(RV64)/rv64-check.o port/rv64-check.c
	$(RVCC) -static -o $@ $(RV64)/rv64-check.o $(RV64)/port.o $(RV64)/port-z256.o $(RV64)/libcr.a -lm

# loops clang 20 vectorizes with -fveclib=SLEEF (the 52, then the 16 other
# names it calls), against whichever libsleef.so.3 the dynamic linker
# finds, and against CORE-MATH or libm
$(RV64)/rv64-dropin: port/rv64-dropin-loop.c port/rv64-dropin-extra.c port/rv64-dropin-main.c port/rv64-extra-functions.h crmvec-functions.h $(RV64)/libsleef.so.3 $(RV64)/libcr.a
	$(RVCLANG) $(RVFLAGS) -O3 -ffp-contract=off -fno-math-errno -fveclib=SLEEF -c -o $(RV64)/dropin-loop.o port/rv64-dropin-loop.c
	$(RVCLANG) $(RVFLAGS) -O3 -ffp-contract=off -fno-math-errno -fveclib=SLEEF -c -o $(RV64)/dropin-extra.o port/rv64-dropin-extra.c
	$(RVCC) $(CFLAGS) $(FPV) -c -o $(RV64)/dropin-main.o port/rv64-dropin-main.c
	$(RVCC) -o $@ $(RV64)/dropin-main.o $(RV64)/dropin-loop.o $(RV64)/dropin-extra.o $(RV64)/libcr.a $(RV64)/libsleef.so.3 -lm

# the 52 entry points' speed, one column per library (nbench's twin; needs
# real hardware, qemu times nothing):
#   rv64-bench build-riscv64/libsleef.so.3 /usr/lib/riscv64-linux-gnu/libsleef.so.3
$(RV64)/rv64-bench: port/rv64-bench.c $(RV64)/libcr.a
	$(RVCLANG) $(RVFLAGS) -O2 -c -o $(RV64)/rv64-bench.o port/rv64-bench.c
	$(RVCC) -o $@ $(RV64)/rv64-bench.o $(RV64)/libcr.a -ldl -lm

clean:
	rm -f check.log libmvec.so.1 crmvec.o crmvec-avx2.o crmvec-port.o crmvec-port-e.o crtest libcrref.so bcheck hypot-midpoints hypotf-midpoints tan-poles bbench ebench mpfrcheck pownf-search libcrf16.a f16check cecheck lcheck roundeven-check rsqrt-vcheck craccuracy craccuracy-zsign cpupath
	rm -rf rsqrt-plant fast/obj fast/drv
	rm -f fast/crmvec.o fast/libmvec.so.1 fast/fastcheck fast/fastbench fast/fastbits
	rm -rf $(A64) $(RV64) build-sleef build-f16
	-$(MAKE) -C crpreload clean

# a few minutes of the checks, for users and packagers (the full list is the
# docs/checking.md); each line must print a passing verdict, and the
# controls must fail as they should. The AVX, AVX2 and AVX-512 entry points
# are checked only on CPUs that have them (elsewhere the checker itself would
# fault). Needs libmpfr-dev, as `make` does.
VERDICTS := 'IDENTICAL|CORRECTLY ROUNDED|all four differ|ALL EXPORTED|, 0 differ from cr_hypot|^TOTAL 0 differ|EVERY CALL WRAPPED|ONLY EXACT LIBM'

# every CORE-MATH function in the library is called through a crmvec-fpenv.c
# wrapper, so runs with flush-to-zero off: a function CORE-MATH gains would
# otherwise run in the caller's (added 2026-09-29; the half and bfloat16
# ones, cr_*f16 and cr_*_bf16, are covered per call in crmvec-f16.c)
wrapcheck: lib
	@lib=$(firstword $(LIBS_BUILT)); d=$$(mktemp -d); \
	 nm "$$lib" | awk '$$2 ~ /^[tT]$$/ && $$3 ~ /^cr_[a-z0-9]+$$/ && $$3 !~ /f16$$/ {print $$3}' | sort -u > $$d/defined; \
	 nm "$$lib" | awk '$$3 ~ /^__wrap_cr_/ {sub(/^__wrap_/, "", $$3); print $$3}' | sort -u > $$d/wrapped; \
	 m=$$(comm -23 $$d/defined $$d/wrapped | tr '\n' ' '); n=$$(wc -l < $$d/defined); rm -rf $$d; \
	 if [ -n "$$m" ] || [ "$$n" -eq 0 ]; then echo "wrapcheck: not wrapped (add to crmvec-fpenv.c): $$m-- FAILED"; exit 1; fi; \
	 echo "wrapcheck: $$n CORE-MATH functions in $$lib, EVERY CALL WRAPPED"
# f16check against the aarch64 library: the f16 and bf16 functions on aarch64 had no check until 2026-10-12, when a
# gcc 13 build with fp16 gave 125,576 wrong results of 54,132,736 there (GCC PR111867, crmvec-gcc13-bf16.h)
$(A64)/f16check: f16check.c crmvec-f16-list.h crtest-own.h $(A64)/libmvec.so.1
	$(A64CC) $(CFLAGS) $(FP) -fopenmp $(if $(MPFR42),,-DNO_MPFR) -o $@ f16check.c $(A64)/libmvec.so.1 -Wl,--disable-new-dtags,-rpath,'$$ORIGIN' $(if $(MPFR42),-lmpfr) -lm -ldl
ifeq ($(HOSTARCH),aarch64)
# on aarch64: every entry point against CORE-MATH (on a CPU without SVE the
# AdvSIMD ones, through aarch64-check-advsimd; until 2026-09-29 it skipped
# them all), the simd header, and loops gcc vectorized through this library
# against CORE-MATH (the drop-in check)
check: all $(A64)/dropin $(A64)/f16check libcrref.so crpreload/crpreload-check
	@set -e; v() { echo "$$1" | tee -a check.log | tail -1; echo "$$1" | tail -1 | grep -qE $(VERDICTS) || { echo "FAILED: $$2"; exit 1; }; }; \
	: > check.log; \
	if grep -qw sve /proc/cpuinfo; then ck=$(A64)/aarch64-check; else ck=$(A64)/aarch64-check-advsimd; echo "no SVE: the AdvSIMD entry points only ($$ck)"; fi; \
	v "$$($$ck sample)" "$$ck sample"; \
	v "$$(CRTEST_FTZ=1 $$ck sample 4096)" "$$ck sample under flush-to-zero (FPCR.FZ, as -ffast-math programs run)"; \
	v "$$(LD_LIBRARY_PATH=$(A64) $(A64)/dropin 2>&1 | grep -v 'no version information')" "drop-in loops (gcc, crmvec-simd.h)"; \
	v "$$(./simdcheck.sh $(CC) $(A64)/libmvec.so.1 2>&1)" "simdcheck (crmvec-simd.h: gcc vectorizes all 52 functions without -ffast-math)"; \
	v "$$($(MAKE) -s --no-print-directory wrapcheck 2>&1 | grep -vE '^make(\[[0-9]+\])?: (Entering|Leaving) directory')" "wrapcheck (every CORE-MATH function called with flush-to-zero off)"; \
	v "$$(./importcheck.sh $(A64)/libmvec.so.1)" "importcheck (no result depends on the C library's libm)"; \
	v "$$(cd crpreload && ./crpreload-check ./libcrpreload.so ../libcrref.so 16 2>&1)" "crpreload-check (the libm preload against CORE-MATH: 2^16 inputs a function, every mode, with and without FPCR.FZ)"; \
	if [ -n "$(MPFR42)" ]; then v "$$($(A64)/f16check | tail -1)" "$(A64)/f16check"; else echo "f16check against MPFR: skipped, MPFR older than 4.2 (the hash below stands in)"; fi; \
	h=$$($(A64)/f16check hash | sed -n 's/^output hash \([0-9a-f]*\) .*/\1/p'); w=$$(awk 'NR==1{print $$1}' f16check-hash.txt); \
	v "$$(if [ -n "$$h" ] && [ "$$h" = "$$w" ]; then echo "f16check hash $$h: IDENTICAL to the MPFR-checked outputs"; else echo "f16check hash $${h:-none}: differs from the MPFR-checked $$w"; fi)" "$(A64)/f16check hash (f16check-hash.txt)"; \
	echo "make check: every verdict passed (details in check.log)"

# the AdvSIMD entry points' speed, one column per library (bbench's twin):
# nbench build-aarch64/libmvec.so.1 /usr/lib/aarch64-linux-gnu/libmvec.so.1
$(A64)/nbench: port/nbench.c
	mkdir -p $(A64)
	$(A64CC) $(CFLAGS) -o $@ port/nbench.c -ldl -lm

# loops gcc vectorizes with crmvec-simd.h (no -ffast-math), linked against
# this library, against CORE-MATH's scalar functions built in
$(A64)/dropin: port/dropin-main.c port/dropin-loop.c crmvec-simd.h $(A64)/libmvec.so.1
	$(A64CC) $(CFLAGS) -O3 -ffp-contract=off -fno-math-errno -include crmvec-simd.h -c -o $(A64)/dropin-loop.o port/dropin-loop.c
	$(A64CC) $(CFLAGS) $(FP) -I. -o $@ port/dropin-main.c $(A64)/dropin-loop.o sin.c log/log.c expf.c atan2f.c $(A64)/libmvec.so.1 -lm
else
check: all rsqrt-vcheck rsqrt-plant/libmvec.so.1 crpreload/libcrpreload.so crpreload/crpreload-check
	@set -e; v() { echo "$$1" | tee -a check.log | tail -1; echo "$$1" | tail -1 | grep -qE $(VERDICTS) || { echo "FAILED: $$2"; exit 1; }; }; \
	: > check.log; \
	v "$$(./bcheck . 18)" bcheck; \
	v "$$(CRTEST_FTZ=1 ./bcheck . 14)" "bcheck under flush-to-zero (FTZ and DAZ, as -ffast-math programs run)"; \
	if grep -q ' avx ' /proc/cpuinfo; then v "$$(./cecheck c . 14)" "cecheck c"; v "$$(./cecheck d . 14)" "cecheck d"; \
	  v "$$(CRTEST_FTZ=1 ./cecheck d . 12)" "cecheck d under flush-to-zero"; else echo "cecheck c, cecheck d: skipped, no AVX"; fi; \
	if grep -q avx512f /proc/cpuinfo; then v "$$(./cecheck e . 12)" "cecheck e"; else echo "cecheck e: skipped, no AVX-512F"; fi; \
	if grep -qw avx2 /proc/cpuinfo && grep -qw fma /proc/cpuinfo; then \
	if [ -n "$(MPFR42)" ]; then v "$$(./mpfrcheck 16 all)" "mpfrcheck, four rounding modes"; \
	v "$$(./mpfrcheck controls)" "mpfrcheck controls"; else echo "mpfrcheck: skipped, no MPFR 4.2 or later"; fi; \
	v "$$(./crtest verify expf logf sinf)" "crtest verify (every input of expf, logf, sinf)"; \
	v "$$(./hypot-midpoints)" "hypot-midpoints (double hypot on exact midpoints)"; \
	v "$$(./hypotf-midpoints)" "hypotf-midpoints (float pairs near a midpoint, found by search)"; \
	v "$$(./rsqrt-vcheck ./libmvec.so.1 ./libcrref.so 24)" "rsqrt-vcheck (the vector double rsqrt, 2^24 inputs)"; \
	v "$$(./rsqrt-vcheck ./libmvec.so.1 ./libcrref.so hard)" "rsqrt-vcheck hard (CORE-MATH's hard cases at every scale)"; \
	if ./rsqrt-vcheck ./rsqrt-plant/libmvec.so.1 ./libcrref.so hard > /dev/null; then echo "FAILED: rsqrt-vcheck's control (the rounding test off) differs nowhere"; exit 1; \
	else echo "rsqrt-vcheck control: with the rounding test off, the hard cases differ, as they must"; fi; \
	else echo "mpfrcheck, crtest, hypot-midpoints, hypotf-midpoints, rsqrt-vcheck: skipped, no AVX2 and FMA (they call the AVX2 entry points)"; fi; \
	v "$$(./lcheck .)" "lcheck (every input of sinpif, cospif, tanpif, rsqrtf)"; \
	if [ -n "$(MPFR42)" ]; then v "$$(./f16check | tail -1)" "f16check"; else echo "f16check against MPFR: skipped, MPFR older than 4.2 (the hash below stands in)"; fi; \
	h=$$(./f16check hash | sed -n 's/^output hash \([0-9a-f]*\) .*/\1/p'); w=$$(awk 'NR==1{print $$1}' f16check-hash.txt); \
	v "$$(if [ -n "$$h" ] && [ "$$h" = "$$w" ]; then echo "f16check hash $$h: IDENTICAL to the MPFR-checked outputs"; else echo "f16check hash $${h:-none}: differs from the MPFR-checked $$w"; fi)" "f16check hash (f16check-hash.txt)"; \
	v "$$(./roundeven-check)" "roundeven-check (the library's own roundeven, for glibc before 2.25)"; \
	v "$$(./simdcheck.sh $(CC) ./libmvec.so.1 2>&1)" "simdcheck (crmvec-simd.h: gcc vectorizes all 52 functions without -ffast-math)"; \
	v "$$($(MAKE) -s --no-print-directory wrapcheck 2>&1 | grep -vE '^make(\[[0-9]+\])?: (Entering|Leaving) directory')" "wrapcheck (every CORE-MATH function called with flush-to-zero off)"; \
	v "$$(./importcheck.sh ./libmvec.so.1)" "importcheck (no result depends on the C library's libm)"; \
	v "$$(cd crpreload && ./crpreload-check ./libcrpreload.so ../libcrref.so 16 2>&1)" "crpreload-check (the libm preload against CORE-MATH: 2^16 inputs a function, every mode and flush setting)"; \
	echo "make check: every verdict passed (details in check.log)"
endif

print-sources:   # for the export script: every CORE-MATH source the build uses
	@echo $(CR)

# CORE-MATH's own headers, for every target built from its sources (the
# rules above list only the .c files; until 2026-09-29 editing one rebuilt
# nothing)
CRH     := log/dint.h log10/dint.h pow/pow.h pow/dint.h pow/qint.h atan2/tint.h atan2pi/tint.h
libmvec.so.1 crtest libcrref.so hypot-midpoints hypotf-midpoints tan-poles mpfrcheck pownf-search \
  $(A64)/libmvec.so.1 $(A64)/libsleefgnuabi.so.3 $(A64)/aarch64-check $(A64)/aarch64-check-advsimd $(RV64)/libcr.a: $(CRH)

.PHONY: all lib install headercheck wrapcheck check clean cc-check print-sources aarch64 sleef-exports riscv64
