diff --git a/external/Makefile b/external/Makefile index e199108bf..a289032a5 100644 --- a/external/Makefile +++ b/external/Makefile @@ -53,6 +53,10 @@ else LDLIBS += -lsodium endif +ifeq ($(HAVE_ZLIB),1) +LDLIBS += -lz +endif + EXTERNAL_LDLIBS := -L${TARGET_DIR} $(patsubst lib%.a,-l%,$(notdir $(EXTERNAL_LIBS))) submodcheck: $(FORCE) diff --git a/plugins/askrene/test/Makefile b/plugins/askrene/test/Makefile index 45aa8feab..88ee0b88a 100644 --- a/plugins/askrene/test/Makefile +++ b/plugins/askrene/test/Makefile @@ -10,7 +10,7 @@ $(PLUGIN_RENEPAY_TEST_OBJS): $(PLUGIN_ASKRENE_SRC) PLUGIN_ASKRENE_TEST_COMMON_OBJS := -plugins/askrene/test/run-bfs plugins/askrene/test/run-dijkstra plugins/askrene/test/run-flow plugins/askrene/test/run-mcf: \ +plugins/askrene/test/run-bfs plugins/askrene/test/run-dijkstra plugins/askrene/test/run-flow plugins/askrene/test/run-mcf plugins/askrene/test/run-mcf-large: \ plugins/askrene/priorityqueue.o \ plugins/askrene/graph.o diff --git a/plugins/askrene/test/data/linear_mcf.gz b/plugins/askrene/test/data/linear_mcf.gz new file mode 100644 index 000000000..b101fd557 Binary files /dev/null and b/plugins/askrene/test/data/linear_mcf.gz differ diff --git a/plugins/askrene/test/run-mcf-large.c b/plugins/askrene/test/run-mcf-large.c new file mode 100644 index 000000000..40acc149d --- /dev/null +++ b/plugins/askrene/test/run-mcf-large.c @@ -0,0 +1,136 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "../algorithm.c" + +#ifdef HAVE_ZLIB +#include + +gzFile infile; +#define BUFFER_SIZE 10000 +char buffer[BUFFER_SIZE]; + +static int myscanf(const char *fmt, ...) +{ + gzgets(infile, buffer, BUFFER_SIZE); + va_list args; + va_start(args, fmt); + int ret = vsscanf(buffer, fmt, args); + va_end(args); + return ret; +} + +#endif + +#define CHECK(arg) if(!(arg)){fprintf(stderr, "failed CHECK at line %d: %s\n", __LINE__, #arg); abort();} + +/* Read multiple testcases from a file. + * Each test case consist of: + * - one line with N and M, the number of nodes and arcs + * - for each arc a line with numbers head, tail, cap and cost, defining the + * arc's endpoints and values, + * - one line with numbers: amount and best_cost, indicating that we wish to + * send amount from node 0 to node 1 with minimum cost, the correct answer + * should contain a flow cost equal to best_cost. + * + * A feasible solution is guaranteed. + * The last test case has 0 nodes and should be ignored. */ + +static int next_bit(s64 x) +{ + int b; + for (b = 0; (1LL << b) <= x; b++) + ; + return b; +} + +static bool solve_case(const tal_t *ctx) +{ + static int c = 0; + c++; + tal_t *this_ctx = tal(ctx, tal_t); + + int N_nodes, N_arcs; + scanf("%d %d\n", &N_nodes, &N_arcs); + printf("Testcase %d\n", c); + printf("nodes %d arcs %d\n", N_nodes, N_arcs); + if (N_nodes == 0 && N_arcs == 0) + goto fail; + + const int MAX_NODES = N_nodes; + const int DUAL_BIT = next_bit(N_arcs-1); + const int MAX_ARCS = 1LL << (DUAL_BIT+1); + printf("max nodes %d max arcs %d bit %d\n", MAX_NODES, MAX_ARCS, DUAL_BIT); + + struct graph *graph = graph_new(ctx, MAX_NODES, MAX_ARCS, DUAL_BIT); + assert(graph); + + s64 *capacity = tal_arrz(ctx, s64, MAX_ARCS); + s64 *cost = tal_arrz(ctx, s64, MAX_ARCS); + + for (u32 i = 0; i < N_arcs; i++) { + u32 from, to; + scanf("%" PRIu32 " %" PRIu32 " %" PRIi64 " %" PRIi64, &from, + &to, &capacity[i], &cost[i]); + + struct arc arc = {.idx = i}; + graph_add_arc(graph, arc, node_obj(from), node_obj(to)); + + struct arc dual = arc_dual(graph, arc); + cost[dual.idx] = -cost[i]; + } + printf("Reading arcs finished\n"); + struct node src = {.idx = 0}; + struct node dst = {.idx = 1}; + + s64 amount, best_cost; + scanf("%" PRIi64 " %" PRIi64, &amount, &best_cost); + + bool result = simple_mcf(ctx, graph, src, dst, capacity, amount, cost); + assert(result); + + assert(node_balance(graph, src, capacity) == -amount); + assert(node_balance(graph, dst, capacity) == amount); + + for (u32 i = 2; i < N_nodes; i++) + assert(node_balance(graph, node_obj(i), capacity) == 0); + + const s64 total_cost = flow_cost(graph, capacity, cost); + assert(total_cost == best_cost); + + tal_free(this_ctx); + return true; + +fail: + tal_free(this_ctx); + return false; +} + +int main(int argc, char *argv[]) +{ +#ifdef HAVE_ZLIB + common_setup(argv[0]); + infile = gzopen("plugins/askrene/test/data/linear_mcf.gz", "r"); + CHECK(infile); + const tal_t *ctx = tal(NULL, tal_t); + CHECK(ctx); + + /* One test case after another. The last test case has N number of nodes + * and arcs equal to 0 and must be ignored. */ + while (solve_case(ctx)) + ; + + ctx = tal_free(ctx); + gzclose(infile); + common_shutdown(); + return 0; +#else + return 0; +#endif +} +