From 82938e88d621908a59121ecae93c4db5a039e322 Mon Sep 17 00:00:00 2001
From: "raphael.bach" <raphael.bach@etu.hesge.ch>
Date: Mon, 20 Dec 2021 16:24:02 +0100
Subject: [PATCH] Well no atomic commits for this project I guess

---
 Doxyfile                                 |   4 +-
 Makefile                                 |  10 +-
 external/cpl/cpl_type.h                  |  11 ++
 external/cpl/cpl_util.h                  |   3 +
 futharkmpi_fut.fut                       |   7 -
 include/fmpi.h                           |  38 +++++
 include/fmpi_ctx.h                       |  50 ++++++
 include/fmpi_futhark.h                   |  71 ++++++++
 include/{futharkmpi.h => fmpi_mpi.h}     |  42 ++---
 include/fmpi_reduce.h                    |  59 +++++++
 include/internal/fmpi_futhark_internal.h |  59 +++++++
 include/internal/fmpi_generic.h          | 141 ++++++++++++++++
 include/internal/fmpi_reduce_internal.h  |  51 ++++++
 makefiles/tools.mk                       |  11 +-
 notes                                    |  49 ++++++
 src/fmpi_ctx.c                           | 135 +++++++++++++++
 src/fmpi_futhark.c                       | 145 ++++++++++++++++
 src/fmpi_mpi.c                           | 194 ++++++++++++++++++++++
 src/fmpi_reduce.c                        |  83 +++++++++
 src/futhark/fmpi_c_types.fut             |  49 ++++++
 src/futhark/fmpi_fut.fut                 |  17 ++
 src/futhark/fmpi_module_reduce.fut       |  33 ++++
 src/futhark/fmpi_reduce.fut              |  10 ++
 src/futharkmpi.c                         | 203 -----------------------
 src/main.c                               |  48 +++---
 tool/gen_futhark.py                      |  51 +++++-
 types                                    |  13 --
 27 files changed, 1288 insertions(+), 299 deletions(-)
 delete mode 100644 futharkmpi_fut.fut
 create mode 100644 include/fmpi.h
 create mode 100644 include/fmpi_ctx.h
 create mode 100644 include/fmpi_futhark.h
 rename include/{futharkmpi.h => fmpi_mpi.h} (62%)
 create mode 100644 include/fmpi_reduce.h
 create mode 100644 include/internal/fmpi_futhark_internal.h
 create mode 100644 include/internal/fmpi_generic.h
 create mode 100644 include/internal/fmpi_reduce_internal.h
 create mode 100644 notes
 create mode 100644 src/fmpi_ctx.c
 create mode 100644 src/fmpi_futhark.c
 create mode 100644 src/fmpi_mpi.c
 create mode 100644 src/fmpi_reduce.c
 create mode 100644 src/futhark/fmpi_c_types.fut
 create mode 100644 src/futhark/fmpi_fut.fut
 create mode 100644 src/futhark/fmpi_module_reduce.fut
 create mode 100644 src/futhark/fmpi_reduce.fut
 delete mode 100644 src/futharkmpi.c
 delete mode 100644 types

diff --git a/Doxyfile b/Doxyfile
index b9a1128..5879167 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -32,7 +32,7 @@ DOXYFILE_ENCODING      = UTF-8
 # title of most generated pages and in a few other places.
 # The default value is: My Project.
 
-PROJECT_NAME           = "Fuhtarkmpi"
+PROJECT_NAME           = "Futharkmpi"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
 # could be handy for archiving the generated documentation or if some version
@@ -969,7 +969,7 @@ RECURSIVE              = YES
 # Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE  = dep
+EXCLUDE  = build
 EXCLUDE += include/generated
 EXCLUDE += include/impl
 EXCLUDE += test
diff --git a/Makefile b/Makefile
index aed4cca..021ab97 100644
--- a/Makefile
+++ b/Makefile
@@ -23,12 +23,12 @@ CC_ERROR := $(filter-out -pedantic-errors,$(CC_ERROR))
 include $(MAKEFILE_PATH)/targets.mk
 
 .PHONY: futharkc
-futharkc: src/futharkmpi_fut.c
+futharkc: src/fmpi_fut.c
 
-src/futharkmpi_fut.c: futharkmpi_fut.fut
-	futhark c futharkmpi_fut.fut --library
-	mv futharkmpi_fut.h $(INC_PATH)
-	mv futharkmpi_fut.c $(SRC_PATH)
+src/fmpi_fut.c: src/futhark/fmpi_fut.fut
+	futhark c src/futhark/fmpi_fut.fut --library
+	mv src/futhark/fmpi_fut.h $(INC_PATH)/internal
+	mv src/futhark/fmpi_fut.c $(SRC_PATH)
 
 # Call the program with `mpirun`
 RUN_CMD := $(RUN_CMD:%=mpirun %)
diff --git a/external/cpl/cpl_type.h b/external/cpl/cpl_type.h
index 56b22e2..6eea237 100644
--- a/external/cpl/cpl_type.h
+++ b/external/cpl/cpl_type.h
@@ -728,6 +728,17 @@
 #define CPL_TYPE_SCALAR_ABBR                                                   \
     CPL_TYPE_ARITHMETIC_ABBR,                                                  \
     CPL_TYPE_ARITHMETIC_PTR_ABBR
+
+#define CPL_TYPE_PFMT_int8_t   PRIi8
+#define CPL_TYPE_PFMT_int16_t  PRIi16
+#define CPL_TYPE_PFMT_int32_t  PRIi32
+#define CPL_TYPE_PFMT_int64_t  PRIi64
+#define CPL_TYPE_PFMT_uint8_t  PRIu8
+#define CPL_TYPE_PFMT_uint16_t PRIu16
+#define CPL_TYPE_PFMT_uint32_t PRIu32
+#define CPL_TYPE_PFMT_uint64_t PRIu64
+#define CPL_TYPE_PFMT_float    "f"
+#define CPL_TYPE_PFMT_double   "f"
 /*==============================================================================
     GUARD
 ==============================================================================*/
diff --git a/external/cpl/cpl_util.h b/external/cpl/cpl_util.h
index 5e51022..b81e514 100644
--- a/external/cpl/cpl_util.h
+++ b/external/cpl/cpl_util.h
@@ -45,6 +45,9 @@
  * }
  */
 #define CPL_DEFER(M, ...) M(__VA_ARGS__)
+
+#define CPL_STRINGIFY(...) CPL_STRINGIFY_RAW(__VA_ARGS__)
+#define CPL_STRINGIFY_RAW(...) #__VA_ARGS__
 /*==============================================================================
     GUARD
 ==============================================================================*/
diff --git a/futharkmpi_fut.fut b/futharkmpi_fut.fut
deleted file mode 100644
index 97bbe35..0000000
--- a/futharkmpi_fut.fut
+++ /dev/null
@@ -1,7 +0,0 @@
-import "./futharkmpi_reduce"
--- Reduce Range
-entry reduce_range_add (a: f64) (b: f64): f64 = f64.i64 (reduce (+) 0 (i64.f64 a ... i64.f64 b))
-entry reduce_range_mul (a: f64) (b: f64): f64 = f64.i64 (reduce (*) 1 (i64.f64 a ... i64.f64 b))
--- Reduce Array
-entry reduce_array_add (xs: []f64): f64 = reduce (+) 0 xs
-entry reduce_array_mul (xs: []f64): f64 = reduce (*) 1 xs
diff --git a/include/fmpi.h b/include/fmpi.h
new file mode 100644
index 0000000..682ff2d
--- /dev/null
+++ b/include/fmpi.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_H_20211124145123
+#define FMPI_H_20211124145123
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Internal
+#include "fmpi_ctx.h"
+#include "fmpi_futhark.h"
+#include "fmpi_mpi.h"
+#include "fmpi_reduce.h"
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_H_20211124145123
diff --git a/include/fmpi_ctx.h b/include/fmpi_ctx.h
new file mode 100644
index 0000000..9a0c582
--- /dev/null
+++ b/include/fmpi_ctx.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_INIT_H_20211218002623
+#define FMPI_INIT_H_20211218002623
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// C Standard Library
+// External
+// Internal
+/*==============================================================================
+    STRUCT
+==============================================================================*/
+struct fmpi_ctx {
+    struct fmpi_mpi_ctx * mpi;
+    struct futhark_context_config * futhark_cfg;
+    struct futhark_context * futhark;
+};
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+struct fmpi_ctx * fmpi_init(int * argc, char ** argv[]);
+void fmpi_exit(struct fmpi_ctx * ctx);
+_Noreturn void fmpi_abort(struct fmpi_ctx * ctx);
+struct fmpi_ctx * fmpi_ctx_create(void);
+void fmpi_ctx_destroy(struct fmpi_ctx ** const ctx);
+void fmpi_ctx_print(const struct fmpi_ctx * const ctx);
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_INIT_H_20211218002623
diff --git a/include/fmpi_futhark.h b/include/fmpi_futhark.h
new file mode 100644
index 0000000..c7bf37d
--- /dev/null
+++ b/include/fmpi_futhark.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_FUTHARK_H_20211219234207
+#define FMPI_FUTHARK_H_20211219234207
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// C Standard Library
+// External
+// Internal
+#include "internal/fmpi_futhark_internal.h"
+/*==============================================================================
+    STRUCT
+==============================================================================*/
+struct fmpi_ctx;
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+int fmpi_futhark_init(struct fmpi_ctx * const ctx);
+void fmpi_futhark_exit(struct fmpi_ctx * const ctx);
+void fmpi_futhark_sync(struct fmpi_ctx * const ctx);
+/*------------------------------------------------------------------------------
+    fmpi_futhark_has_error()
+------------------------------------------------------------------------------*/
+/**
+ * Check if the last function called before calling
+ * fmpi_has_futhark_error() generated an error.
+ *
+ * @param[in,out] ctx       : Pointer to a fmpi_ctx structure.
+ * @param[in]     err_id    : Error id returned by a futhark function.
+ * @param[in]     func_name : Function name.
+ *
+ * @return
+ * - `true` if an error is detected.
+ * - `false` if no error was detected.
+ *
+ * @warning
+ * - \p{ctx} must be a valid pointer allocated with fmpi_init().
+ * - \p{func_name} must not be `NULL`.
+ */
+_Bool fmpi_futhark_has_error(struct fmpi_ctx * const ctx, const char * const func_name);
+void fmpi_futhark_abort_on_error(struct fmpi_ctx * ctx, const char * const func_name);
+
+#define fmpi_new_1d(ctx, array, length)                       \
+    FMPI_GENERIC_FUNC_FUTHARK(array, new, 1d, FMPI_FUTHARK_TYPES)(ctx, array, length)
+
+#define fmpi_free_1d(ctx, array, length)                       \
+    FMPI_GENERIC_FUNC_FUTHARK(array, free, 1d, FMPI_FUTHARK_TYPES)(ctx, array)
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_FUTHARK_H_20211219234207
diff --git a/include/futharkmpi.h b/include/fmpi_mpi.h
similarity index 62%
rename from include/futharkmpi.h
rename to include/fmpi_mpi.h
index d30a8c5..90ede0e 100644
--- a/include/futharkmpi.h
+++ b/include/fmpi_mpi.h
@@ -19,48 +19,42 @@
 /*==============================================================================
     GUARD
 ==============================================================================*/
-#ifndef FUTHARKMPI_H_20211124145123
-#define FUTHARKMPI_H_20211124145123
+#ifndef FMPI_MPI_H_20211219234201
+#define FMPI_MPI_H_20211219234201
 /*==============================================================================
     INCLUDE
 ==============================================================================*/
 // C Standard Library
 // External
-#include <mpi.h>
+#include <mpi.h> // MPI_MAX_PROCESSOR_NAME
 // Internal
-#include "futharkmpi_fut.h"
-/*==============================================================================
-    DEFINE
-==============================================================================*/
-#define FUTHARKMPI_ADD 0
-#define FUTHARKMPI_MUL 1
 /*==============================================================================
     STRUCT
 ==============================================================================*/
-struct mpi_ctx {
+struct fmpi_mpi_ctx {
     int rank;
     int size;
     char cpu_name[MPI_MAX_PROCESSOR_NAME];
     int cpu_name_length;
     int root;
+    int futhark_err_class;
+    int futhark_err_code;
 };
-
-struct futharkmpi_ctx {
-    struct mpi_ctx mpi;
-    struct futhark_context_config * futhark_cfg;
-    struct futhark_context * futhark;
-};
+struct fmpi_ctx;
 /*==============================================================================
     PUBLIC FUNCTION
 ==============================================================================*/
-struct futharkmpi_ctx * futharkmpi_init(int * argc, char ** argv[]);
-void futharkmpi_exit(struct futharkmpi_ctx * ctx);
-void futharkmpi_print_ctx(struct futharkmpi_ctx * ctx);
-double futharkmpi_reduce_mul(struct futharkmpi_ctx * ctx, void * src, MPI_Datatype type);
-double futharkmpi_local_range_reduce_add(struct futharkmpi_ctx * ctx, double a, double b);
-double futharkmpi_local_range_reduce_mul(struct futharkmpi_ctx * ctx, double a, double b);
-double futharkmpi_local_array_reduce_mul(struct futharkmpi_ctx * ctx, double * array);
+struct fmpi_mpi_ctx * fmpi_mpi_init(int * argc, char ** argv[]);
+void fmpi_mpi_exit(struct fmpi_mpi_ctx * ctx);
+void fmpi_mpi_abort(struct fmpi_mpi_ctx * ctx);
+_Bool fmpi_mpi_is_root(const struct fmpi_mpi_ctx * const ctx);
+_Bool fmpi_mpi_has_error(struct fmpi_mpi_ctx * const ctx, int err_id, const char * const func_name);
+_Bool fmpi_mpi_is_initialized(struct fmpi_mpi_ctx * const ctx);
+_Bool fmpi_mpi_is_finalized(struct fmpi_mpi_ctx * const ctx);
+struct fmpi_mpi_ctx * fmpi_mpi_create(int root);
+void fmpi_mpi_destroy(struct fmpi_mpi_ctx ** const ctx);
+void fmpi_mpi_ctx_print(const struct fmpi_mpi_ctx * const ctx);
 /*==============================================================================
     GUARD
 ==============================================================================*/
-#endif // FUTHARKMPI_H_20211124145123
+#endif // FMPI_MPI_H_20211219234201
diff --git a/include/fmpi_reduce.h b/include/fmpi_reduce.h
new file mode 100644
index 0000000..353f338
--- /dev/null
+++ b/include/fmpi_reduce.h
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_REDUCE_H_20211218002000
+#define FMPI_REDUCE_H_20211218002000
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// C Standard Library
+// External
+// Internal
+#include "fmpi_ctx.h"
+#include "internal/fmpi_reduce_internal.h"
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*------------------------------------------------------------------------------
+    fmpi_local_reduce_prod()
+------------------------------------------------------------------------------*/
+/**
+ * Return the locally computed product of all elements in an \p{array}.
+ *
+ * @param[in,out] ctx    : Pointer to a fmpi_ctx structure.
+ * @param[in]     array  : Array to reduce.
+ * @param[in]     length : Length of the \p{array}.
+ *
+ * @return The product of all elements in \p{array}.
+ *
+ * @warning
+ * - \p{ctx} must be a valid pointer allocated with fmpi_init().
+ * - \p{array} must not be `NULL`.
+ */
+#define fmpi_local_reduce_prod(ctx, array, length)                       \
+    FMPI_GENERIC_FUNC(array, local_reduce_prod, FMPI_REDUCE_PROD_TYPES)(ctx, array, length)
+
+#define fmpi_reduce_prod(ctx, array)                                     \
+    FMPI_GENERIC_FUNC(array, reduce_prod, FMPI_REDUCE_PROD_TYPES)(ctx, array)
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_REDUCE_H_20211218002000
diff --git a/include/internal/fmpi_futhark_internal.h b/include/internal/fmpi_futhark_internal.h
new file mode 100644
index 0000000..824c88b
--- /dev/null
+++ b/include/internal/fmpi_futhark_internal.h
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_FUTHARK_INTERNAL_H_20211219235241
+#define FMPI_FUTHARK_INTERNAL_H_20211219235241
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Internal
+#include "fmpi_ctx.h"
+#include "fmpi_generic.h"
+/*==============================================================================
+    MACRO
+==============================================================================*/
+#define FMPI_FUTHARK_TYPES \
+    FMPI_TYPE_REAL
+
+#define FMPI_PROTO_NEW_1D(T)                                             \
+    struct futhark_##T##_1d * fmpi_new_##T##_1d(struct fmpi_ctx * ctx, const T * array, int length)
+#define FMPI_PROTO_NEW_2D(T)                                             \
+    struct futhark_##T##_2d * fmpi_new_##T##_2d(struct fmpi_ctx * ctx, const T * array, int length)
+#define FMPI_PROTO_NEW_3D(T)                                             \
+    struct futhark_##T##_3d * fmpi_new_##T##_3d(struct fmpi_ctx * ctx, const T * array, int length)
+
+#define FMPI_PROTO_FREE_1D(T)                                            \
+    void fmpi_free_##T##_1d(struct fmpi_ctx * ctx, struct futhark_##T##_1d * array)
+#define FMPI_PROTO_FREE_2D(T)                                            \
+    void fmpi_free_##T##_2d(struct fmpi_ctx * ctx, struct futhark_##T##_2d * array)
+#define FMPI_PROTO_FREE_3D(T)                                            \
+    void fmpi_free_##T##_3d(struct fmpi_ctx * ctx, struct futhark_##T##_3d * array)
+
+FMPI_DECLARE_FUNC(
+    FMPI_PROTO_NEW_1D, FMPI_FUTHARK_TYPES
+);
+FMPI_DECLARE_FUNC(
+    FMPI_PROTO_FREE_1D, FMPI_FUTHARK_TYPES
+);
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_FUTHARK_INTERNAL_H_20211219235241
diff --git a/include/internal/fmpi_generic.h b/include/internal/fmpi_generic.h
new file mode 100644
index 0000000..40d0edf
--- /dev/null
+++ b/include/internal/fmpi_generic.h
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_GENERIC_H_20211201184118
+#define FMPI_GENERIC_H_20211201184118
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// C Standard Library
+#include <inttypes.h>
+#include <stdint.h>
+// External
+#include "../external/cpl/cpl_concat.h"
+#include "../external/cpl/cpl_logic.h"
+#include "../external/cpl/cpl_map.h"
+#include "../external/cpl/cpl_token.h"
+#include "../external/cpl/cpl_type.h"
+#include "../external/cpl/cpl_util.h"
+// Internal
+/*==============================================================================
+    TYPEDEF
+==============================================================================*/
+// Match C type name with Futhark type name
+typedef int8_t   i8;
+typedef int16_t  i16;
+typedef int32_t  i32;
+typedef int64_t  i64;
+typedef uint8_t  u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef float    f32;
+typedef double   f64;
+//typedef _Bool    bool;
+/*==============================================================================
+    MACRO
+==============================================================================*/
+#define FMPI_TYPE_SIGNED                                                 \
+    i8,                                                                        \
+    i16,                                                                       \
+    i32,                                                                       \
+    i64
+
+#define FMPI_TYPE_UNSIGNED                                               \
+    u8,                                                                        \
+    u16,                                                                       \
+    u32,                                                                       \
+    u64
+
+#define FMPI_TYPE_INTEGER                                                \
+    FMPI_TYPE_SIGNED,                                                    \
+    FMPI_TYPE_UNSIGNED
+
+#define FMPI_TYPE_FLOATING                                               \
+    f32,                                                                       \
+    f64
+
+#define FMPI_TYPE_REAL                                                   \
+    FMPI_TYPE_INTEGER,                                                   \
+    FMPI_TYPE_FLOATING
+
+#define FMPI_TYPE_BOOLEAN                                                \
+    bool
+
+#define FMPI_TYPE_ALL                                                    \
+    FMPI_TYPE_REAL,                                                      \
+    FMPI_TYPE_BOOLEAN
+
+#define FMPI_DEFAULT_VALUE_i8   0
+#define FMPI_DEFAULT_VALUE_i16  0
+#define FMPI_DEFAULT_VALUE_i32  0
+#define FMPI_DEFAULT_VALUE_i64  0
+#define FMPI_DEFAULT_VALUE_u8   0
+#define FMPI_DEFAULT_VALUE_u16  0
+#define FMPI_DEFAULT_VALUE_u32  0
+#define FMPI_DEFAULT_VALUE_u64  0
+#define FMPI_DEFAULT_VALUE_f32  0.0f
+#define FMPI_DEFAULT_VALUE_f64  0.0
+//#define FMPI_DEFAULT_VALUE_bool false
+
+#define FMPI_PFMT(T) \
+    CPL_CONCAT_2(CPL_TYPE_PFMT_, T)
+
+#define FMPI_MPI_DATATYPE_i8   MPI_INT8_T
+#define FMPI_MPI_DATATYPE_i16  MPI_INT16_T
+#define FMPI_MPI_DATATYPE_i32  MPI_INT32_T
+#define FMPI_MPI_DATATYPE_i64  MPI_INT64_T
+#define FMPI_MPI_DATATYPE_u8   MPI_UINT8_T
+#define FMPI_MPI_DATATYPE_u16  MPI_UINT16_T
+#define FMPI_MPI_DATATYPE_u32  MPI_UINT32_T
+#define FMPI_MPI_DATATYPE_u64  MPI_UINT64_T
+#define FMPI_MPI_DATATYPE_f32  MPI_FLOAT
+#define FMPI_MPI_DATATYPE_f64  MPI_DOUBLE
+//#define FMPI_MPI_DATATYPE_bool MPI_C_BOOL
+
+#define FMPI_GENERIC_ASSOC(FUNC, TYPE)                                   \
+    TYPE : fmpi_##FUNC##_##TYPE
+
+#define FMPI_GENERIC_ASSOC_FUTHARK(FUNC, DIM, TYPE)                                   \
+    TYPE : fmpi_##FUNC##_##TYPE##_##DIM
+
+#define FMPI_GENERIC_FUNC(CTR_EXPR, FUNC, ...)                           \
+    _Generic((*CTR_EXPR), default: 0,                                          \
+        CPL_MAP_FIXED(FMPI_GENERIC_ASSOC, CPL_COMMA, (FUNC), __VA_ARGS__)\
+    )
+
+#define FMPI_GENERIC_FUNC_FUTHARK(CTR_EXPR, FUNC, DIM, ...)                           \
+    _Generic((*CTR_EXPR), default: 0,                                          \
+        CPL_MAP_FIXED(FMPI_GENERIC_ASSOC_FUTHARK, CPL_COMMA, (FUNC, DIM), __VA_ARGS__)\
+    )
+
+#define FMPI_DECLARE_FUNC(FUNC, ...)                                     \
+    CPL_MAP(FUNC, CPL_SEMICOLON, __VA_ARGS__)
+
+#define FMPI_DEFINE_FUNC(FUNC, ...)                                      \
+    CPL_MAP(FUNC, CPL_EMPTY, __VA_ARGS__)
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_GENERIC_H_20211201184118
diff --git a/include/internal/fmpi_reduce_internal.h b/include/internal/fmpi_reduce_internal.h
new file mode 100644
index 0000000..968c106
--- /dev/null
+++ b/include/internal/fmpi_reduce_internal.h
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#ifndef FMPI_REDUCE_IMPL_H_20211218005919
+#define FMPI_REDUCE_IMPL_H_20211218005919
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Internal
+#include "fmpi_ctx.h"
+#include "fmpi_generic.h"
+/*==============================================================================
+    MACRO
+==============================================================================*/
+#define FMPI_REDUCE_PROD_TYPES                                           \
+    FMPI_TYPE_REAL
+
+#define FMPI_PROTO_LOCAL_REDUCE_PROD(T)                                  \
+    T fmpi_local_reduce_prod_##T(struct fmpi_ctx * const ctx, T * const array, int length)
+
+#define FMPI_PROTO_REDUCE_PROD(T)                                        \
+    T fmpi_reduce_prod_##T(struct fmpi_ctx * const ctx, T * const array)
+
+FMPI_DECLARE_FUNC(
+    FMPI_PROTO_LOCAL_REDUCE_PROD, FMPI_REDUCE_PROD_TYPES
+);
+FMPI_DECLARE_FUNC(
+    FMPI_PROTO_REDUCE_PROD, FMPI_REDUCE_PROD_TYPES
+);
+/*==============================================================================
+    GUARD
+==============================================================================*/
+#endif // FMPI_REDUCE_IMPL_H_20211218005919
diff --git a/makefiles/tools.mk b/makefiles/tools.mk
index 5b08745..8c7882a 100644
--- a/makefiles/tools.mk
+++ b/makefiles/tools.mk
@@ -55,15 +55,8 @@ endif
 ################################################################################
 # Output path for data generated by doxygen                                  [R]
 DOXYGEN_PATH_OUT = $(DOC_PATH)/doxygen
-# Executable name                                                            [R]
-LD_LIBRARY_PATH += /home/user1/app/llvm-project/lib
-DOXYGEN_EXEC := LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) doxygen
-# Doxygen command ran by the `doc` target                                    [+]
-DOXYGEN_CMD = $(DOXYGEN_EXEC)
-ifneq ($(shell which $(DOXYGEN_EXEC)),)
-#DOC_CMD += $(DOXYGEN_CMD)
-#DOC_DIR_DEPS += $(DOXYGEN_PATH_OUT)
-endif
+DOC_DIR_DEPS += $(DOXYGEN_PATH_OUT)
+DOC_CMD += doxygen
 ################################################################################
 # GCOV
 ################################################################################
diff --git a/notes b/notes
new file mode 100644
index 0000000..f65e4bb
--- /dev/null
+++ b/notes
@@ -0,0 +1,49 @@
+========== FUTHARK =============================================================
+Return code:
+    - 0 on success
+    - non-zero or NULL on failure
+
+futhark_context_get_error():
+    - Returned string must be free() manually
+
+futhark_context_free()
+    - futhark_context_sync() must be called right before
+
+futhark_new_*():
+    - Asynchronous -> futhark_context_sync() must be called before using the
+      return value.
+
+futhark_values_*()
+    - Asynchronous -> futhark_context_sync() must be called before using the
+      return value.
+
+futhark_entry_*()
+    - Asynchronous -> futhark_context_sync() must be called before using the
+      return value.
+    - Check return code of both futhark_entry_*() AND futhark_context_sync()
+
+========== MPI =================================================================
+MPI_Error_class():
+    - Thread-safe
+    - Can be called before MPI is initialized
+    - Can be called after MPI is finalized
+
+MPI_Error_string():
+    - Thread-safe
+    - Can be called before MPI is initialized
+    - Can be called after MPI is finalized
+
+========== MISC ================================================================
+     MPI     |    C     | Futhark
+-------------+----------+---------
+MPI_C_BOOL   | _Bool    | bool
+MPI_INT8_T   | int8_t   | i8
+MPI_INT16_T  | int16_t  | i16
+MPI_INT32_T  | int32_t  | i32
+MPI_INT64_T  | int64_t  | i64
+MPI_UINT8_T  | uint8_t  | u8
+MPI_UINT16_T | uint16_t | u16
+MPI_UINT32_T | uint32_t | u32
+MPI_UINT64_T | uint64_t | u64
+MPI_FLOAT    | float    | f32
+MPI_DOUBLE   | double   | f64
diff --git a/src/fmpi_ctx.c b/src/fmpi_ctx.c
new file mode 100644
index 0000000..c55cfcf
--- /dev/null
+++ b/src/fmpi_ctx.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Own header
+#include "fmpi_ctx.h"
+// C Standard Library
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+// Internal
+#include "fmpi_futhark.h"
+#include "fmpi_mpi.h"
+#include "internal/fmpi_fut.h"
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*------------------------------------------------------------------------------
+    fmpi_init()
+------------------------------------------------------------------------------*/
+struct fmpi_ctx * fmpi_init(int * argc, char ** argv[])
+{
+    struct fmpi_ctx * ctx = fmpi_ctx_create();
+    if(ctx == NULL) {
+        fprintf(stderr, "fmpi_ctx_create(fmpi_ctx) failed!\n");
+        return NULL;
+    }
+
+    ctx->mpi = fmpi_mpi_init(argc, argv);
+    if(ctx->mpi == NULL) {
+        fprintf(stderr, "fmpi_mpi_init() failed!\n");
+        fmpi_ctx_destroy(&ctx);
+        return NULL;
+    }
+
+    const int err_id = fmpi_futhark_init(ctx);
+    if(err_id != MPI_SUCCESS) {
+        fprintf(stderr, "fmpi_futhark_init() failed!\n");
+        fmpi_ctx_destroy(&ctx);
+        return NULL;
+    }
+    return ctx;
+}
+/*------------------------------------------------------------------------------
+    fmpi_exit()
+------------------------------------------------------------------------------*/
+void fmpi_exit(struct fmpi_ctx * ctx)
+{
+    assert(ctx != NULL);
+
+    fmpi_futhark_exit(ctx);
+    fmpi_mpi_destroy(&ctx->mpi);
+    fmpi_mpi_exit(ctx->mpi);
+    fmpi_ctx_destroy(&ctx);
+}
+/*------------------------------------------------------------------------------
+    fmpi_abort()
+------------------------------------------------------------------------------*/
+_Noreturn void fmpi_abort(struct fmpi_ctx * ctx)
+{
+    if(ctx != NULL) {
+        fmpi_futhark_exit(ctx);
+        fmpi_mpi_destroy(&ctx->mpi);
+        fmpi_ctx_destroy(&ctx);
+    }
+    fmpi_mpi_abort(NULL);
+    abort();
+}
+/*------------------------------------------------------------------------------
+    fmpi_ctx_create()
+------------------------------------------------------------------------------*/
+struct fmpi_ctx * fmpi_ctx_create(void)
+{
+    struct fmpi_ctx * ctx = malloc(sizeof(*ctx));
+    if(ctx == NULL) {
+        fprintf(stderr, "malloc(fmpi_ctx) failed!\n");
+        return NULL;
+    }
+    ctx->mpi = NULL;
+    ctx->futhark_cfg = NULL;
+    ctx->futhark = NULL;
+    return ctx;
+}
+/*------------------------------------------------------------------------------
+    fmpi_ctx_create()
+------------------------------------------------------------------------------*/
+void fmpi_ctx_destroy(struct fmpi_ctx ** const ctx)
+{
+    assert(ctx != NULL);
+    assert(*ctx != NULL);
+
+    if((*ctx)->futhark_cfg != NULL && (*ctx)->futhark != NULL) {
+        fmpi_futhark_exit(*ctx);
+    }
+    /*if((*ctx)->mpi != NULL) {
+        fmpi_mpi_exit(*ctx);
+    }*/
+    free((*ctx)->mpi);
+    (*ctx)->mpi = NULL;
+    free(*ctx);
+    *ctx = NULL;
+}
+/*------------------------------------------------------------------------------
+    fmpi_ctx_print()
+------------------------------------------------------------------------------*/
+void fmpi_ctx_print(const struct fmpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+
+    if(ctx->mpi != NULL) {
+        fmpi_mpi_ctx_print(ctx->mpi);
+    }
+}
+/*==============================================================================
+    PRIVATE FUNCTION
+==============================================================================*/
diff --git a/src/fmpi_futhark.c b/src/fmpi_futhark.c
new file mode 100644
index 0000000..b3d84d4
--- /dev/null
+++ b/src/fmpi_futhark.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Own header
+#include "fmpi_futhark.h"
+// C Standard Library
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+// External
+// Internal
+#include "fmpi_ctx.h"
+#include "fmpi_mpi.h"
+#include "internal/fmpi_fut.h"
+#include "internal/fmpi_generic.h"
+/*==============================================================================
+    STRUCT
+==============================================================================*/
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*------------------------------------------------------------------------------
+    fmpi_futhark_init()
+------------------------------------------------------------------------------*/
+int fmpi_futhark_init(struct fmpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+    assert(ctx->futhark == NULL);
+    assert(ctx->futhark_cfg == NULL);
+
+    ctx->futhark_cfg = futhark_context_config_new();
+    if(ctx->futhark_cfg == NULL) {
+        fprintf(stderr, "[FUTHARK ERROR rank=%d] futhark_context_config_new() failed!\n", ctx->mpi->rank);
+        return ctx->mpi->futhark_err_class;
+    }
+    ctx->futhark = futhark_context_new(ctx->futhark_cfg);
+    char * err_ptr = futhark_context_get_error(ctx->futhark);
+    if(ctx->futhark == NULL || err_ptr != NULL) {
+        const char * err_str = err_ptr != NULL ? err_ptr : "";
+        fprintf(stderr, "[FUTHARK ERROR rank=%d] futhark_context_new() failed! %s\n", ctx->mpi->rank, err_str);
+        futhark_context_free(ctx->futhark);
+        futhark_context_config_free(ctx->futhark_cfg);
+        free(ctx->futhark);
+        return ctx->mpi->futhark_err_class;
+    }
+    return MPI_SUCCESS;
+}
+/*------------------------------------------------------------------------------
+    fmpi_futhark_exit()
+------------------------------------------------------------------------------*/
+void fmpi_futhark_exit(struct fmpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+    assert(ctx->futhark_cfg != NULL);
+    assert(ctx->futhark != NULL);
+
+    fmpi_futhark_sync(ctx);
+    futhark_context_free(ctx->futhark);
+    ctx->futhark = NULL;
+    futhark_context_config_free(ctx->futhark_cfg);
+    ctx->futhark_cfg = NULL;
+}
+/*------------------------------------------------------------------------------
+    fmpi_futhark_sync()
+------------------------------------------------------------------------------*/
+void fmpi_futhark_sync(struct fmpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+    futhark_context_sync(ctx->futhark);
+    fmpi_futhark_abort_on_error(ctx, "futhark_context_sync()");
+}
+/*------------------------------------------------------------------------------
+    fmpi_futhark_has_error()
+------------------------------------------------------------------------------*/
+_Bool fmpi_futhark_has_error(struct fmpi_ctx * const ctx, const char * const func_name)
+{
+    assert(ctx != NULL);
+
+    char * err_str = futhark_context_get_error(ctx->futhark);
+    if(err_str != NULL) {
+        const char * func_str = func_name != NULL ? func_name : "NO_NAME";
+        fprintf(stderr, "[FUTHARK ERROR rank=%d] %s failed! %s\n", ctx->mpi->rank, func_str, err_str);
+        free(err_str); err_str = NULL;
+        return 1;
+    }
+    return 0;
+}
+/*------------------------------------------------------------------------------
+    fmpi_futhark_abort_on_error()
+------------------------------------------------------------------------------*/
+void fmpi_futhark_abort_on_error(struct fmpi_ctx * ctx, const char * const func_name)
+{
+    assert(ctx != NULL);
+    if(fmpi_futhark_has_error(ctx, func_name)) {
+        fmpi_abort(ctx);
+    }
+}
+/*------------------------------------------------------------------------------
+    fmpi_new_1d()
+------------------------------------------------------------------------------*/
+#define FMPI_DEFINE_NEW_1D(T)                                 \
+FMPI_PROTO_NEW_1D(T)                                          \
+{                                                                              \
+    assert(ctx != NULL);                                                       \
+    assert(array != NULL);                                                     \
+    struct futhark_##T##_1d * xs = futhark_new_##T##_1d(ctx->futhark, array, length); \
+    fmpi_futhark_abort_on_error(ctx, CPL_STRINGIFY(futhark_new_##T##_1d())); \
+    fmpi_futhark_sync(ctx);\
+    return xs;\
+}
+/*------------------------------------------------------------------------------
+    fmpi_free_1d()
+------------------------------------------------------------------------------*/
+#define FMPI_DEFINE_FREE_1D(T)                                 \
+FMPI_PROTO_FREE_1D(T)                                          \
+{                                                                              \
+    assert(ctx != NULL);                                                       \
+    assert(array != NULL);                                                     \
+    fmpi_futhark_sync(ctx);\
+    futhark_free_##T##_1d(ctx->futhark, array);                                   \
+    fmpi_futhark_abort_on_error(ctx, CPL_STRINGIFY(futhark_free_##T##_1d())); \
+}
+
+FMPI_DEFINE_FUNC(FMPI_DEFINE_NEW_1D, FMPI_FUTHARK_TYPES)
+FMPI_DEFINE_FUNC(FMPI_DEFINE_FREE_1D, FMPI_FUTHARK_TYPES)
diff --git a/src/fmpi_mpi.c b/src/fmpi_mpi.c
new file mode 100644
index 0000000..cc78b47
--- /dev/null
+++ b/src/fmpi_mpi.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Own header
+#include "fmpi_mpi.h"
+// C Standard Library
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+// External
+#include <mpi.h>
+// Internal
+#include "fmpi_ctx.h"
+/*==============================================================================
+    DEFINE
+==============================================================================*/
+#define FMPI_MPI_RANK_ROOT_DEFAULT 0
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+/*------------------------------------------------------------------------------
+    fmpi_mpi_init()
+------------------------------------------------------------------------------*/
+struct fmpi_mpi_ctx * fmpi_mpi_init(int * argc, char ** argv[])
+{
+    struct fmpi_mpi_ctx * ctx = fmpi_mpi_create(FMPI_MPI_RANK_ROOT_DEFAULT);
+    if(ctx == NULL) {
+        fprintf(stderr, "fmpi_mpi_create() failed!\n");
+        return NULL;
+    }
+
+    int err_id = MPI_Init(argc, argv);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Init()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    err_id = MPI_Comm_rank(MPI_COMM_WORLD, &ctx->rank);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Comm_rank()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    err_id = MPI_Comm_size(MPI_COMM_WORLD, &ctx->size);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Comm_size()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    // `size` is used with `malloc()` and `malloc(0)` is implementation-defined.
+    assert(ctx->size > 0);
+    err_id = MPI_Get_processor_name(ctx->cpu_name, &ctx->cpu_name_length);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Get_processor_name()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    err_id = MPI_Add_error_class(&ctx->futhark_err_class);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Get_processor_name()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    err_id = MPI_Add_error_code(ctx->futhark_err_class, &ctx->futhark_err_code);
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Get_processor_name()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    err_id = MPI_Add_error_string(ctx->futhark_err_code, "");
+    if(fmpi_mpi_has_error(ctx, err_id, "MPI_Get_processor_name()")) {
+        fmpi_mpi_destroy(&ctx); return NULL;
+    }
+    return ctx;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_exit()
+------------------------------------------------------------------------------*/
+void fmpi_mpi_exit(struct fmpi_mpi_ctx * ctx)
+{
+    if(ctx != NULL) {
+        fmpi_mpi_destroy(&ctx);
+    }
+    if(fmpi_mpi_is_initialized(ctx) && !fmpi_mpi_is_finalized(ctx)) {
+        const int err_id = MPI_Finalize();
+        fmpi_mpi_has_error(ctx, err_id, "MPI_Finalize()");
+    }
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_abort()
+------------------------------------------------------------------------------*/
+void fmpi_mpi_abort(struct fmpi_mpi_ctx * ctx)
+{
+    if(ctx != NULL) {
+        fmpi_mpi_destroy(&ctx);
+    }
+    if(fmpi_mpi_is_initialized(ctx) && !fmpi_mpi_is_finalized(ctx)) {
+        MPI_Abort(MPI_COMM_WORLD, MPI_ERR_UNKNOWN);
+    }
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_is_root()
+------------------------------------------------------------------------------*/
+_Bool fmpi_mpi_is_root(const struct fmpi_mpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+    return ctx->rank == ctx->root;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_has_error()
+------------------------------------------------------------------------------*/
+_Bool fmpi_mpi_has_error(struct fmpi_mpi_ctx * const ctx, int err_id, const char * const func_name)
+{
+    int rank = -1;
+    if(ctx != NULL) {
+        rank = ctx->rank;
+    }
+    if(err_id != MPI_SUCCESS) {
+        char err_array[MPI_MAX_ERROR_STRING] = {0};
+        int err_str_len = 0;
+        const int err_str_id = MPI_Error_string(err_id, err_array, &err_str_len);
+        if(err_str_id != MPI_SUCCESS) {
+            fprintf(stderr, "[MPI ERROR rank=%d] MPI_Error_string() failed!\n", rank);
+        }
+        const char * err_str = err_str_id == MPI_SUCCESS ? err_array : "";
+        fprintf(stderr, "[MPI ERROR rank=%d] %s failed!: %s\n", rank, func_name, err_str);
+        return 1;
+    }
+    return 0;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_is_initialized()
+------------------------------------------------------------------------------*/
+_Bool fmpi_mpi_is_initialized(struct fmpi_mpi_ctx * const ctx)
+{
+    int is_initialized = 0;
+    const int err_id = MPI_Initialized(&is_initialized);
+    fmpi_mpi_has_error(ctx, err_id, "MPI_Initialized()");
+    return is_initialized;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_is_finalized()
+------------------------------------------------------------------------------*/
+_Bool fmpi_mpi_is_finalized(struct fmpi_mpi_ctx * const ctx)
+{
+    int is_finalized = 0;
+    const int err_id = MPI_Finalized(&is_finalized);
+    fmpi_mpi_has_error(ctx, err_id, "MPI_Finalized()");
+    return is_finalized;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_create()
+------------------------------------------------------------------------------*/
+struct fmpi_mpi_ctx * fmpi_mpi_create(int root)
+{
+    struct fmpi_mpi_ctx * ctx = malloc(sizeof(*ctx));
+    if(ctx == NULL) {
+        fprintf(stderr, "malloc(fmpi_mpi_ctx) failed!\n");
+        return NULL;
+    }
+    ctx->root = root;
+    return ctx;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_destroy()
+------------------------------------------------------------------------------*/
+void fmpi_mpi_destroy(struct fmpi_mpi_ctx ** const ctx)
+{
+    assert(ctx != NULL);
+    assert(*ctx != NULL);
+
+    free((*ctx));
+    (*ctx) = NULL;
+}
+/*------------------------------------------------------------------------------
+    fmpi_mpi_ctx_print()
+------------------------------------------------------------------------------*/
+void fmpi_mpi_ctx_print(const struct fmpi_mpi_ctx * const ctx)
+{
+    assert(ctx != NULL);
+
+    printf("MPI size: %d\n", ctx->size);
+    printf("MPI rank: %d\n", ctx->rank);
+    printf("MPI CPU name: %s\n", ctx->cpu_name);
+    printf("MPI root: %s\n", ctx->cpu_name);
+    printf("MPI futhark error class: %d\n", ctx->futhark_err_class);
+    printf("MPI futhark error code: %d\n", ctx->futhark_err_code);
+}
diff --git a/src/fmpi_reduce.c b/src/fmpi_reduce.c
new file mode 100644
index 0000000..2eca439
--- /dev/null
+++ b/src/fmpi_reduce.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: 0BSD
+/*!
+ * @file
+ * @license{
+ * BSD Zero Clause License
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * }
+ */
+/*==============================================================================
+    INCLUDE
+==============================================================================*/
+// Own header
+#include "fmpi_reduce.h"
+// C Standard Library
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+// Internal
+#include "fmpi_futhark.h"
+#include "fmpi_mpi.h"
+#include "internal/fmpi_fut.h"
+#include "internal/fmpi_generic.h"
+/*==============================================================================
+    STRUCT
+==============================================================================*/
+/*==============================================================================
+    PUBLIC FUNCTION
+==============================================================================*/
+#define FMPI_DEFINE_LOCAL_REDUCE_PROD(T)                                 \
+FMPI_PROTO_LOCAL_REDUCE_PROD(T)                                          \
+{                                                                              \
+    assert(ctx != NULL);                                                       \
+    assert(array != NULL);                                                     \
+                                                                               \
+    struct futhark_##T##_1d * xs = fmpi_new_##T##_1d(ctx, array, length);\
+    T result = FMPI_DEFAULT_VALUE_##T;                                   \
+    futhark_entry_reduce_prod_##T(ctx->futhark, &result, xs);        \
+    fmpi_futhark_abort_on_error(ctx, CPL_STRINGIFY(futhark_entry_reduce_prod_##T())); \
+    fmpi_futhark_sync(ctx);                                     \
+    fmpi_free_##T##_1d(ctx, xs);                                   \
+    return result; \
+}
+
+#define FMPI_DEFINE_REDUCE_PROD(T)                                       \
+FMPI_PROTO_REDUCE_PROD(T) \
+{ \
+    assert(ctx != NULL); \
+    assert(array != NULL); \
+ \
+    T result = FMPI_DEFAULT_VALUE_##T; \
+    /*! @cast MPI uses `int` for `size`. */ \
+    /* Casting `int` to `size_t` should be safe if the value isn't negative. */ \
+    /* This is asserted in`fmpi_init()`.*/ \
+    T * fact_global = malloc((size_t)ctx->mpi->size * sizeof(*fact_global)); \
+    if(fact_global == NULL) { \
+        fprintf(stderr, "malloc(fact_global) failed!\n"); \
+    } \
+    int err_id = MPI_Gather(array, 1, FMPI_MPI_DATATYPE_##T, fact_global, 1, FMPI_MPI_DATATYPE_##T, ctx->mpi->root, MPI_COMM_WORLD); \
+    if(fmpi_mpi_has_error(ctx->mpi, err_id, "MPI_Gather()")) { \
+        free(fact_global); \
+        fmpi_abort(ctx); \
+    } \
+    if(fmpi_mpi_is_root(ctx->mpi)) { \
+        result = fmpi_local_reduce_prod_##T(ctx, fact_global, ctx->mpi->size); \
+    } \
+    free(fact_global); \
+    return result; \
+}
+
+FMPI_DEFINE_FUNC(FMPI_DEFINE_LOCAL_REDUCE_PROD, FMPI_REDUCE_PROD_TYPES)
+FMPI_DEFINE_FUNC(FMPI_DEFINE_REDUCE_PROD, FMPI_REDUCE_PROD_TYPES)
diff --git a/src/futhark/fmpi_c_types.fut b/src/futhark/fmpi_c_types.fut
new file mode 100644
index 0000000..c8fe63e
--- /dev/null
+++ b/src/futhark/fmpi_c_types.fut
@@ -0,0 +1,49 @@
+-- SPDX-License-Identifier: 0BSD
+--
+-- @license{
+-- BSD Zero Clause License
+--
+-- Permission to use, copy, modify, and/or distribute this software for any
+-- purpose with or without fee is hereby granted.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+-- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+-- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+-- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+-- PERFORMANCE OF THIS SOFTWARE.
+-- }
+entry make_futhark_i8_1d (xs: []i8): i8 = 0
+entry make_futhark_i8_2d (xs: [][]i8): i8 = 0
+entry make_futhark_i8_3d (xs: [][][]i8): i8 = 0
+entry make_futhark_i16_1d (xs: []i16): i16 = 0
+entry make_futhark_i16_2d (xs: [][]i16): i16 = 0
+entry make_futhark_i16_3d (xs: [][][]i16): i16 = 0
+entry make_futhark_i32_1d (xs: []i32): i32 = 0
+entry make_futhark_i32_2d (xs: [][]i32): i32 = 0
+entry make_futhark_i32_3d (xs: [][][]i32): i32 = 0
+entry make_futhark_i64_1d (xs: []i64): i64 = 0
+entry make_futhark_i64_2d (xs: [][]i64): i64 = 0
+entry make_futhark_i64_3d (xs: [][][]i64): i64 = 0
+entry make_futhark_u8_1d (xs: []u8): u8 = 0
+entry make_futhark_u8_2d (xs: [][]u8): u8 = 0
+entry make_futhark_u8_3d (xs: [][][]u8): u8 = 0
+entry make_futhark_u16_1d (xs: []u16): u16 = 0
+entry make_futhark_u16_2d (xs: [][]u16): u16 = 0
+entry make_futhark_u16_3d (xs: [][][]u16): u16 = 0
+entry make_futhark_u32_1d (xs: []u32): u32 = 0
+entry make_futhark_u32_2d (xs: [][]u32): u32 = 0
+entry make_futhark_u32_3d (xs: [][][]u32): u32 = 0
+entry make_futhark_u64_1d (xs: []u64): u64 = 0
+entry make_futhark_u64_2d (xs: [][]u64): u64 = 0
+entry make_futhark_u64_3d (xs: [][][]u64): u64 = 0
+entry make_futhark_f32_1d (xs: []f32): f32 = 0
+entry make_futhark_f32_2d (xs: [][]f32): f32 = 0
+entry make_futhark_f32_3d (xs: [][][]f32): f32 = 0
+entry make_futhark_f64_1d (xs: []f64): f64 = 0
+entry make_futhark_f64_2d (xs: [][]f64): f64 = 0
+entry make_futhark_f64_3d (xs: [][][]f64): f64 = 0
+entry make_futhark_bool_1d (xs: []bool): bool = true
+entry make_futhark_bool_2d (xs: [][]bool): bool = true
+entry make_futhark_bool_3d (xs: [][][]bool): bool = true
diff --git a/src/futhark/fmpi_fut.fut b/src/futhark/fmpi_fut.fut
new file mode 100644
index 0000000..ed1e76b
--- /dev/null
+++ b/src/futhark/fmpi_fut.fut
@@ -0,0 +1,17 @@
+--import "futharkmpi_reduce"
+-- Reduce Range
+entry reduce_range_add (a: f64) (b: f64): f64 = f64.i64 (reduce (+) 0 (i64.f64 a ... i64.f64 b))
+entry reduce_range_prod (a: f64) (b: f64): f64 = f64.i64 (reduce (*) 1 (i64.f64 a ... i64.f64 b))
+-- Reduce Array
+entry reduce_array_add (xs: []f64): f64 = reduce (+) 0 xs
+
+entry reduce_prod_i8 (xs: []i8): i8 = i8.product xs
+entry reduce_prod_i16 (xs: []i16): i16 = i16.product xs
+entry reduce_prod_i32 (xs: []i32): i32 = i32.product xs
+entry reduce_prod_i64 (xs: []i64): i64 = i64.product xs
+entry reduce_prod_u8 (xs: []u8): u8 = u8.product xs
+entry reduce_prod_u16 (xs: []u16): u16 = u16.product xs
+entry reduce_prod_u32 (xs: []u32): u32 = u32.product xs
+entry reduce_prod_u64 (xs: []u64): u64 = u64.product xs
+entry reduce_prod_f32 (xs: []f32): f32 = f32.product xs
+entry reduce_prod_f64 (xs: []f64): f64 = f64.product xs
diff --git a/src/futhark/fmpi_module_reduce.fut b/src/futhark/fmpi_module_reduce.fut
new file mode 100644
index 0000000..7a0fb8d
--- /dev/null
+++ b/src/futhark/fmpi_module_reduce.fut
@@ -0,0 +1,33 @@
+-- Default operations for MPI_Reduce()
+-- See https://www.open-mpi.org/doc/current/man3/MPI_Reduce.3.php#sect10
+local module type local_reduce = {
+    type t
+    val sum  [n]: [n]t -> t
+    val prod [n]: [n]t -> t
+    val min  [n]: [n]t -> t
+    val max  [n]: [n]t -> t
+}
+
+module module_reduce (T: numeric): local_reduce with t = T.t = {
+    type t = T.t
+    let sum  [n] (xs: [n]t): t = T.sum xs
+    let prod [n] (xs: [n]t): t = T.product xs
+    let min  [n] (xs: [n]t): t = T.minimum xs
+    let max  [n] (xs: [n]t): t = T.maximum xs
+}
+
+
+-- #[generic=all]
+--entry reduce_sum (xs: []f64): f64 = f64.sum xs
+--entry reduce_prod (xs: []f64): f64 = f64.product xs
+--entry reduce_min (xs: []f64): f64 = f64.minimum xs
+--entry reduce_max (xs: []f64): f64 = f64.maximum xs
+-- #[generic=integer]
+--entry reduce_band (xs: []i64): i64 = reduce (&) 1 xs
+--entry reduce_bor (xs: []i64): i64 = reduce (|) 0 xs
+--entry reduce_bxor (xs: []i64): i64 reduce (^) xs
+--entry reduce_land (xs: []bool): bool = bool.i32 5
+--MPI_LOR             logical or
+--MPI_LXOR            logical xor
+--MPI_MAXLOC          max value and location
+--MPI_MINLOC          min value and location
diff --git a/src/futhark/fmpi_reduce.fut b/src/futhark/fmpi_reduce.fut
new file mode 100644
index 0000000..2feec6c
--- /dev/null
+++ b/src/futhark/fmpi_reduce.fut
@@ -0,0 +1,10 @@
+entry reduce_prod_i8 (xs: []i8): i8 = i8.product xs
+entry reduce_prod_i16 (xs: []i16): i16 = i16.product xs
+entry reduce_prod_i32 (xs: []i32): i32 = i32.product xs
+entry reduce_prod_i64 (xs: []i64): i64 = i64.product xs
+entry reduce_prod_u8 (xs: []u8): u8 = u8.product xs
+entry reduce_prod_u16 (xs: []u16): u16 = u16.product xs
+entry reduce_prod_u32 (xs: []u32): u32 = u32.product xs
+entry reduce_prod_u64 (xs: []u64): u64 = u64.product xs
+entry reduce_prod_f32 (xs: []f32): f32 = f32.product xs
+entry reduce_prod_f64 (xs: []f64): f64 = f64.product xs
diff --git a/src/futharkmpi.c b/src/futharkmpi.c
deleted file mode 100644
index 5592257..0000000
--- a/src/futharkmpi.c
+++ /dev/null
@@ -1,203 +0,0 @@
-// SPDX-License-Identifier: 0BSD
-/*!
- * @file
- * @license{
- * BSD Zero Clause License
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- * }
- */
-/*==============================================================================
-    INCLUDE
-==============================================================================*/
-// Own header
-#include "futharkmpi.h"
-// C Standard Library
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-// External
-#include <mpi.h>
-// Internal
-#include "futharkmpi_fut.h"
-/*==============================================================================
-    DEFINE
-==============================================================================*/
-#define FUTHARKMPI_RANK_ROOT 0
-/*==============================================================================
-    STRUCT
-==============================================================================*/
-extern struct futhark_context_config * futhark_context_config_new(void);
-extern void futhark_context_config_free(struct futhark_context_config * cfg);
-extern struct futhark_context * futhark_context_new(struct futhark_context_config * cfg);
-extern void futhark_context_free(struct futhark_context * ctx);
-extern int futhark_context_sync(struct futhark_context * ctx);
-extern char * futhark_context_get_error(struct futhark_context * ctx);
-/*==============================================================================
-    PUBLIC FUNCTION
-==============================================================================*/
-struct futharkmpi_ctx * futharkmpi_init(int * argc, char ** argv[])
-{
-    struct futharkmpi_ctx * ctx = malloc(sizeof(*ctx));
-    if(ctx == NULL) {
-        fprintf(stderr, "malloc(futharkmpi_ctx) failed!\n");
-        return NULL;
-    }
-
-    int err = MPI_Init(argc, argv);
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Init() failed!\n");
-        goto abort;
-    }
-
-    int size = 0;
-    err = MPI_Comm_size(MPI_COMM_WORLD, &size);
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Comm_size() failed!\n");
-        goto abort;
-    }
-    // `size` is used with `malloc()` and `malloc(0)` is implementation-defined.
-    assert(size > 0);
-    ctx->mpi.size = size;
-
-    int rank = 0;
-    err = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Comm_rank() failed!\n");
-        goto abort;
-    }
-    ctx->mpi.rank = rank;
-
-    int cpu_name_length = 0;
-    err = MPI_Get_processor_name(ctx->mpi.cpu_name, &cpu_name_length);
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Get_processor_name() failed!\n");
-        goto abort;
-    }
-    ctx->mpi.cpu_name_length = cpu_name_length;
-
-    ctx->mpi.root = FUTHARKMPI_RANK_ROOT;
-
-    struct futhark_context_config * futhark_cfg = futhark_context_config_new();
-    if(futhark_cfg == NULL) {
-        fprintf(stderr, "futhark_context_config_new() failed!\n");
-        goto abort;
-    }
-    ctx->futhark_cfg = futhark_cfg;
-
-    struct futhark_context * futhark_ctx = futhark_context_new(futhark_cfg);
-    char * error = futhark_context_get_error(futhark_ctx);
-    if(futhark_ctx == NULL || error != NULL) {
-        fprintf(stderr, "futhark_context_new() failed!: %s\n", error);
-        futhark_context_sync(futhark_ctx);
-        futhark_context_free(futhark_ctx);
-        futhark_context_config_free(futhark_cfg);
-        goto abort;
-    }
-    ctx->futhark = futhark_ctx;
-    return ctx;
-
-abort:
-    err = MPI_Abort(MPI_COMM_WORLD, err);
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Abort() failed!\n");
-    }
-    err = MPI_Finalize();
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Finalize() failed!\n");
-    }
-    return NULL;
-}
-void futharkmpi_exit(struct futharkmpi_ctx * ctx)
-{
-    assert(ctx != NULL);
-
-    futhark_context_sync(ctx->futhark);
-    futhark_context_free(ctx->futhark);
-    futhark_context_config_free(ctx->futhark_cfg);
-
-    int err = MPI_Finalize();
-    if(err != MPI_SUCCESS) {
-        fprintf(stderr, "MPI_Finalize() failed!\n");
-    }
-    free(ctx);
-}
-void futharkmpi_print_ctx(struct futharkmpi_ctx * ctx)
-{
-    assert(ctx != NULL);
-
-    printf("MPI size: %d\n", ctx->mpi.size);
-    printf("MPI rank: %d\n", ctx->mpi.rank);
-    printf("MPI CPU name: %s\n", ctx->mpi.cpu_name);
-}
-double futharkmpi_reduce_mul(struct futharkmpi_ctx * ctx, void * src, MPI_Datatype type)
-{
-    assert(ctx != NULL);
-    assert(src != NULL);
-
-    double result = 0.0;
-    /*! @cast MPI uses `int` for `size`. Casting `int` to `size_t` should be
-     * safe if the value isn't negative. This is asserted in`futharkmpi_init()`.
-     */
-    double * fact_global = malloc((size_t)ctx->mpi.size * sizeof(*fact_global));
-    MPI_Gather(src, 1, type, fact_global, 1, type, ctx->mpi.root, MPI_COMM_WORLD);
-    if(ctx->mpi.rank == ctx->mpi.root) {
-        result = futharkmpi_local_array_reduce_mul(ctx, fact_global);
-        //printf("rank=%d fact_global=%f\n", ctx->mpi.rank, result);
-        //memcpy(dest, &fact_global, sizeof(fact_global));
-    }
-    free(fact_global);
-    return result;
-}
-double futharkmpi_local_range_reduce_add(struct futharkmpi_ctx * ctx, double a, double b)
-{
-    assert(ctx != NULL);
-
-    double result = 0.0;
-    int err = futhark_entry_reduce_range_add(ctx->futhark, &result, a, b);
-    if(err != 0) {
-        fprintf(stderr, "futhark_entry_reduce_add() failed!: %s\n", futhark_context_get_error(ctx->futhark));
-    }
-    return result;
-}
-double futharkmpi_local_range_reduce_mul(struct futharkmpi_ctx * ctx, double a, double b)
-{
-    assert(ctx != NULL);
-
-    double result = 0.0;
-    int err = futhark_entry_reduce_range_mul(ctx->futhark, &result, a, b);
-    if(err != 0) {
-        fprintf(stderr, "futhark_entry_reduce_mul() failed!: %s\n", futhark_context_get_error(ctx->futhark));
-    }
-    return result;
-}
-double futharkmpi_local_array_reduce_mul(struct futharkmpi_ctx * ctx, double * array)
-{
-    assert(ctx != NULL);
-    assert(array != NULL);
-
-    struct futhark_f64_1d * x_arr = futhark_new_f64_1d(ctx->futhark, array, 4);
-    double result = 0.0;
-    int err = futhark_entry_reduce_array_mul(ctx->futhark, &result, x_arr);
-    futhark_context_sync(ctx->futhark);
-    if(err != 0) {
-        fprintf(stderr, "futhark_entry_reduce_mul() failed!: %s\n", futhark_context_get_error(ctx->futhark));
-    }
-    return result;
-}
-
-        /*for(int i = 0; i < 4; i++) {
-            printf("fact_global=%f src=%f\n", fact_global, fact_local[i]);
-            fact_global *= fact_local[i];
-        }*/
diff --git a/src/main.c b/src/main.c
index e61445d..37221b3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,41 +1,31 @@
-
-#include "futharkmpi.h"
+#include "fmpi.h"
 #include <assert.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <mpi.h>
-
-double fact_chunk(double x, int length);
 
+#define ARRAY_SIZE 16
+#define T double
 int main(int argc, char * argv[])
 {
-    struct futharkmpi_ctx * ctx = futharkmpi_init(&argc, &argv);
+    struct fmpi_ctx * ctx = fmpi_init(&argc, &argv);
     if(ctx == NULL) {
-        fprintf(stderr, "futharkmpi_init() failed!\n");
-        return EXIT_FAILURE;
+        fprintf(stderr, "fmpi_init() failed!\n");
+        fmpi_abort(NULL);
     }
-    const double a = (ctx->mpi.rank * ctx->mpi.size) + 1;
-    const double b = a + ctx->mpi.size - 1;
-    double fact_local = futharkmpi_local_range_reduce_mul(ctx, a, b);
-    double fact_global = futharkmpi_reduce_mul(ctx, &fact_local, MPI_DOUBLE);
-    printf("rank=%d fact_local=%f\n", ctx->mpi.rank, fact_local);
-    if(ctx->mpi.rank == ctx->mpi.root) {
-        printf("rank=%d fact_global=%f\n", ctx->mpi.rank, fact_global);
-    } else {
+    if(fmpi_mpi_is_root(ctx->mpi)) {
+        fmpi_ctx_print(ctx);
     }
-    futharkmpi_exit(ctx);
-    return EXIT_SUCCESS;
-}
-
-double fact_chunk(double x, int length)
-{
-    double result = x;
-    for(double i = 1; i < length; i++) {
-        result *= x + i;
+    const size_t start = (size_t)(ctx->mpi->rank * ctx->mpi->size);
+    T array[ARRAY_SIZE] = {
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+    };
+    T fact_local = fmpi_local_reduce_prod(ctx, (array+start), ARRAY_SIZE/ctx->mpi->size);
+    printf("rank=%d fact_local=%"FMPI_PFMT(T)"\n", ctx->mpi->rank, fact_local);
+    T fact_global = fmpi_reduce_prod(ctx, &fact_local);
+    if(fmpi_mpi_is_root(ctx->mpi)) {
+        printf("rank=%d fact_global=%"FMPI_PFMT(T)"\n", ctx->mpi->rank, fact_global);
     }
-    return result;
+    fmpi_exit(ctx);
+    return EXIT_SUCCESS;
 }
-
-    //printf("rank=%d a=%f b=%f fact_chunk=%f\n", ctx->mpi.rank, a, b, fact_local);
-    //futharkmpi_print_ctx(ctx);
diff --git a/tool/gen_futhark.py b/tool/gen_futhark.py
index 64bb5ce..e99e197 100644
--- a/tool/gen_futhark.py
+++ b/tool/gen_futhark.py
@@ -17,7 +17,7 @@
 # PERFORMANCE OF THIS SOFTWARE.
 # }
 
-license_str = """\
+LICENSE_STR = """\
 -- SPDX-License-Identifier: 0BSD
 --
 -- @license{
@@ -36,15 +36,52 @@ license_str = """\
 -- }
 """
 
+OUTPUT_PATH = './'
+INPUT_PATH = OUTPUT_PATH
 DIM_MAX = 3
+FUTHARK_TYPE_LIST = [
+    'i8', 'i16', 'i32', 'i64',
+    'u8', 'u16', 'u32', 'u64',
+    'f32', 'f64', 'bool'
+]
+C_TYPE_LIST = [
+    'int8_t', 'int16_t', 'int32_t', 'int64_t',
+    'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t',
+    'float', 'double', '_Bool'
+]
+TYPE_LIST_LENGTH = len(FUTHARK_TYPE_LIST)
 
-def main(args):
-    f = open('./futharkmpi_c_types.fut', 'w')
-    f.write(license_str)
-    type_list = ['i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool']
-    for type in type_list:
+def futharkmpi_c_types():
+    str = ''
+    for type in TYPE_LIST:
         for i in range (1, DIM_MAX+1):
-            f.write('entry make_futhark_{type}_{dim}d (xs: {array}{type}): {type} = 0\n'.format(type = type, dim = i, array = i * "[]"))
+            str += 'entry make_futhark_{type}_{dim}d (xs: {array}{type}): {type} = 0\n'.format(type = type, dim = i, array = i * "[]")
+    return str
+
+def futharkmpi_reduce():
+    str = 'import "{path}"\n\n'.format(path=INPUT_PATH + 'futharkmpi_module_reduce')
+    func_list = ['sum', 'prod', 'min', 'max']
+    for i in range(TYPE_LIST_LENGTH):
+        str += 'module reduce_{type} = module_reduce {type}\n'.format(type = FUTHARK_TYPE_LIST[i])
+        for func in func_list:
+            str += 'entry reduce_{func}_{ctype} = reduce_{ftype}.{func}\n'.format(func = func, ctype = C_TYPE_LIST[i], ftype = FUTHARK_TYPE_LIST[i])
+    return str
+
+##
+# Create a file and write generated code to it.
+#
+# @param generator : A function returning a string containing the generated
+#                    code. Its name is used as a prefix for the file name.
+def generate_file(generator):
+    file_name = generator.__name__ + '.fut'
+    with open(OUTPUT_PATH + file_name, 'w') as fd:
+        fd.write(LICENSE_STR)
+        fd.write(generator())
+
+def main(args):
+    generator_list = [futharkmpi_reduce]
+    for generator in generator_list:
+        generate_file(generator)
     return 0
 
 if __name__ == '__main__':
diff --git a/types b/types
deleted file mode 100644
index c828f7d..0000000
--- a/types
+++ /dev/null
@@ -1,13 +0,0 @@
-     MPI     |    C     | Futhark
--------------+----------+---------
-MPI_C_BOOL   | _Bool    | bool
-MPI_INT8_T   | int8_t   | i8
-MPI_INT16_T  | int16_t  | i16
-MPI_INT32_T  | int32_t  | i32
-MPI_INT64_T  | int64_t  | i64
-MPI_UINT8_T  | uint8_t  | u8
-MPI_UINT16_T | uint16_t | u16
-MPI_UINT32_T | uint32_t | u32
-MPI_UINT64_T | uint64_t | u64
-MPI_FLOAT    | float    | f32
-MPI_DOUBLE   | double   | f64
-- 
GitLab