Repository: pyccel/pyccel
Pull request #522 (2020-10-30 01:34:55)

ND-Arrays C code
Implement N-dimensional arrays (Numpy-style) in C:

- Struct `t_ndarray` represents an N-dim. array: it contains all the information needed to access a flat memory storage with multiple indices and arbitrary strides, and it supports multiple datatypes (int, float, double, double complex);
- Struct `t_slice` represents a slice, i.e. a range of values (start, stop, step) for an index;
- Function `new_slice` creates a slice, given (start, stop, step);
- Function `array_create` creates an empty array (not initialised);
- Function `array_slicing` creates a sliced view of an N-dim. array, given the array and N slices;
- Function `get_index` handles indexing (computes index in multi-dimensional array);
- Add unit test for C code, which is run by Pytest using a custom conftest file.

Source and header files are located in pyccel/stdlib/ndarrays. This solves #491.

EmilyBourne: Can this PR be marked as ready for review?

Patch:
diff --git a/.travis.yml b/.travis.yml
index e702a962..390e71bc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,8 +34,9 @@ before_script:  # Coverage instructions - setup
 
 script:
   - cd tests
-  - ${PYTHON} -m pytest -rx -m "not parallel and c" --ignore=ast --ignore=printers --ignore=symbolic
-  - ${PYTHON} -m pytest -rx -m "not parallel and not c" --ignore=ast --ignore=printers --ignore=symbolic
+  - ${PYTHON} -m pytest -rx -m "not parallel and c" --ignore=ast --ignore=printers --ignore=symbolic --ignore=ndarrays
+  - ${PYTHON} -m pytest -rx -m "not parallel and not c" --ignore=ast --ignore=printers --ignore=symbolic --ignore=ndarrays
+  - ${PYTHON} -m pytest ndarrays/ -rx
   - mpiexec -n 4 ${MPI_OPTS} ${PYTHON} -m pytest epyccel/test_parallel_epyccel.py -v -m parallel -rx
 #  - mpiexec -n 4 ${MPI_OPTS} ${PYTHON} -m pytest epyccel -v -m parallel -rx
   - cd ..
diff --git a/pyccel/stdlib/ndarrays/ndarrays.c b/pyccel/stdlib/ndarrays/ndarrays.c
new file mode 100644
index 00000000..85711b0f
--- /dev/null
+++ b/pyccel/stdlib/ndarrays/ndarrays.c
@@ -0,0 +1,154 @@
+#include "ndarrays.h"
+
+/* 
+** allocation
+*/
+
+t_ndarray   array_create(int nd, int *shape, enum e_types type)
+{
+    t_ndarray arr;
+
+    arr.nd = nd;
+    arr.type = type;
+    switch (type)
+    {
+        case nd_int:
+            arr.type_size = sizeof(int);
+            break;
+        case nd_float:
+            arr.type_size = sizeof(float);
+            break;
+        case nd_double:
+            arr.type_size = sizeof(double);
+            break;
+        case nd_cdouble:
+            arr.type_size = sizeof(double complex);
+            break;
+    }
+    arr.is_slice = false;
+    arr.length = 1;
+    arr.shape = malloc(arr.nd * sizeof(int));
+    for (int i = 0; i < arr.nd; i++)
+    {
+        arr.length *= shape[i];
+        arr.shape[i] = shape[i];
+    }
+    arr.buffer_size = arr.length * arr.type_size;
+    arr.strides = malloc(nd * sizeof(int));
+    for (int i = 0; i < arr.nd; i++)
+    {
+        arr.strides[i] = 1;
+        for (int j = i + 1; j < arr.nd; j++)
+            arr.strides[i] *= arr.shape[j];
+    }
+    arr.raw_data = malloc(arr.buffer_size);
+    return (arr);
+}
+
+void   _array_fill_int(int c, t_ndarray arr)
+{
+    if (c == 0)
+        memset(arr.raw_data, 0, arr.buffer_size);
+    else
+        for (int i = 0; i < arr.length; i++)
+            arr.nd_int[i] = c;
+}
+
+void   _array_fill_double(double c, t_ndarray arr)
+{
+    if (c == 0)
+        memset(arr.raw_data, 0, arr.buffer_size);
+    else
+        for (int i = 0; i < arr.length; i++)
+            arr.nd_double[i] = c;
+}
+
+void   _array_fill_cdouble(double complex c, t_ndarray arr)
+{
+    if (c == 0)
+        memset(arr.raw_data, 0, arr.buffer_size);
+    else
+        for (int i = 0; i < arr.length; i++)
+            arr.nd_cdouble[i] = c;
+}
+
+/*
+** deallocation
+*/
+
+int free_array(t_ndarray dump)
+{
+    if (!dump.is_slice)
+    {
+        free(dump.raw_data);
+        dump.raw_data = NULL;
+    }
+    free(dump.shape);
+    dump.shape = NULL;
+    free(dump.strides);
+    dump.strides = NULL;
+    return (1);
+}
+
+/* 
+** slices
+*/
+
+t_slice new_slice(int start, int end, int step)
+{
+    t_slice slice_d;
+
+    slice_d.start = start;
+    slice_d.end = end;
+    slice_d.step = step;
+    return (slice_d);
+}
+
+t_ndarray array_slicing(t_ndarray p, ...)
+{
+    t_ndarray slice;
+    va_list  va;
+    t_slice slice_data;
+    int start = 0;
+
+    slice.nd = p.nd;
+    slice.type = p.type;
+    slice.type_size = p.type_size;
+    slice.shape = malloc(sizeof(int) * p.nd);
+    slice.strides = malloc(sizeof(int) * p.nd);
+    memcpy(slice.strides, p.strides, sizeof(int) * p.nd);
+    slice.is_slice = true;
+    va_start(va, p);
+    for (int i = 0; i < p.nd ; i++)
+    {
+        slice_data = va_arg(va, t_slice);
+        slice.shape[i] = (slice_data.end - slice_data.start + (slice_data.step - 1)) / slice_data.step; // we need to round up the shape
+        start += slice_data.start * p.strides[i];
+        slice.strides[i] *= slice_data.step;
+    }
+    va_end(va);
+    slice.raw_data = p.raw_data + start * p.type_size;
+    slice.length = 1;
+    for (int i = 0; i < slice.nd; i++)
+            slice.length *= slice.shape[i];
+    return (slice);
+}
+
+/*
+** indexing
+*/
+
+int get_index(t_ndarray arr, ...)
+{
+    va_list va;
+    int index;
+
+    va_start(va, arr);
+    index = 0;
+    for (int i = 0; i < arr.nd; i++)
+    {
+        index += va_arg(va, int) * arr.strides[i];
+    }
+    va_end(va);
+    return (index);
+}
diff --git a/pyccel/stdlib/ndarrays/ndarrays.h b/pyccel/stdlib/ndarrays/ndarrays.h
new file mode 100644
index 00000000..92739b9b
--- /dev/null
+++ b/pyccel/stdlib/ndarrays/ndarrays.h
@@ -0,0 +1,73 @@
+#ifndef NDARRAYS_H
+# define NDARRAYS_H
+
+# include <stdlib.h>
+# include <complex.h>
+# include <string.h>
+# include <stdio.h>
+# include <stdarg.h>
+# include <stdbool.h>
+
+# define array_fill(c, arr) _Generic((c), int : _array_fill_int,\
+                                        float : _array_fill_float,\
+                                        double : _array_fill_double,\
+                                        double complex : _array_fill_cdouble)(c, arr)
+typedef struct  s_slice
+{
+    int start;
+    int end;
+    int step;
+}               t_slice;
+
+enum e_types
+{
+        nd_int,
+        nd_float,
+        nd_double,
+        nd_cdouble
+};
+
+typedef struct  s_ndarray
+{
+    /* raw data buffer*/
+    union {
+            char            *raw_data;
+            int             *nd_int;
+            float           *nd_float;
+            double          *nd_double;
+            double complex  *nd_cdouble;
+            };
+    /* number of dimensions */
+    int             nd;
+    /* shape 'size of each dimension' */
+    int             *shape;
+    /* strides 'number of bytes to skip to get the next element' */
+    int             *strides;
+    /* type of the array elements */
+    enum e_types    type;
+    int             type_size;
+    int             length;
+    int             buffer_size;
+    bool            is_slice;
+}               t_ndarray;
+
+/* functions prototypes */
+
+/* allocations */
+t_ndarray   array_create(int nd, int *shape, enum e_types type);
+void        _array_fill_int(int c, t_ndarray arr);
+void        _array_fill_float(float c, t_ndarray arr);
+void        _array_fill_double(double c, t_ndarray arr);
+void        _array_fill_cdouble(complex double c, t_ndarray arr);
+
+/* slicing */
+t_slice     new_slice(int start, int end, int step);
+t_ndarray   array_slicing(t_ndarray p, ...);
+
+/* free */
+int         free_array(t_ndarray dump);
+
+/* indexing */
+int         get_index(t_ndarray arr, ...);
+
+#endif


Test patch:
diff --git a/tests/ndarrays/conftest.py b/tests/ndarrays/conftest.py
new file mode 100644
index 00000000..fae36b2b
--- /dev/null
+++ b/tests/ndarrays/conftest.py
@@ -0,0 +1,116 @@
+# pylint: disable=missing-function-docstring, missing-module-docstring/
+# pylint: disable=arguments-differ, inconsistent-return-statements, protected-access, abstract-method/
+import subprocess
+import os
+import sys
+import shutil
+import pytest
+
+def pytest_collect_file(parent, path):
+    """
+    A hook to collect test_*.c test files.
+
+    """
+    if path.ext == ".c" and path.basename.startswith("test_"):
+        return CTestFile.from_parent(parent, path)
+
+def pytest_collection_modifyitems(items):
+    """
+    a hook to modify the items before the tests for C unit test files.
+
+    """
+    for item in items:
+        if item.fspath.ext == ".c":
+            item._nodeid = item.nodeid + " < " + item.test_result["DSCR"] + " >"
+
+class CTestFile(pytest.File):
+    """
+    A custom file handler class for C unit test files.
+
+    """
+    @classmethod
+    def from_parent(cls, parent, fspath):
+        return super().from_parent(parent=parent, fspath=fspath)
+
+    def collect(self):
+        """
+        Overridden collect method to collect the results from each
+        C unit test executable.
+
+        """
+        # Run the exe that corresponds to the .c file and capture the output.
+        test_exe = os.path.splitext(str(self.fspath))[0]
+        rootdir = str(self.config.rootdir)
+        test_exe = os.path.relpath(test_exe)
+        ndarray_path =  os.path.join(rootdir , "pyccel", "stdlib", "ndarrays")
+        comp_cmd = [shutil.which("gcc"), test_exe + ".c",
+                    os.path.join(ndarray_path,"ndarrays.c"), "-I", ndarray_path, "-o", test_exe]
+        subprocess.run(comp_cmd, check= 'TRUE')
+        if sys.platform.startswith("win"):
+            test_exe += ".exe"
+        test_output = subprocess.check_output("./" + test_exe)
+
+        # Clean up the unit test output and remove non test data lines.
+        lines = test_output.decode().split("\n")
+        lines = [line.strip() for line in lines]
+        lines = [line for line in lines if line.startswith("[")]
+
+        # Extract the test metadata from the unit test output.
+        test_results = []
+        for line in lines:
+            token, data = line.split(" ", 1)
+            token = token[1:-1]
+            if token in ("PASS", "FAIL"):
+                file_name, line_number, function_name = data.split(":")
+                test_results.append({"condition": token,
+                                     "file_name": file_name,
+                                     "function_name": function_name,
+                                     "line_number": int(line_number),
+                                     "INFO" : "no data found",
+                                     "DSCR" : ""
+                                     })
+            elif token in ("INFO", "DSCR"):
+                test_results[-1][token] = data
+        for test_result in test_results:
+            yield CTestItem.from_parent(test_result["function_name"], self, test_result)
+
+
+class CTestItem(pytest.Item):
+    """
+    Pytest.Item subclass to handle each test result item. There may be
+    more than one test result from a test function.
+
+    """
+
+    def __init__(self, name, parent, test_result):
+        """Overridden constructor to pass test results dict."""
+        super().__init__(name, parent)
+        self.test_result = test_result
+
+    @classmethod
+    def from_parent(cls, name, parent, test_result):
+        return super().from_parent(name=name, parent=parent, test_result=test_result)
+
+    def runtest(self):
+        """The test has already been run. We just evaluate the result."""
+        if self.test_result["condition"] == "FAIL":
+            raise CTestException(self, self.name)
+
+    def reportinfo(self):
+        """"Called to display header information about the test case."""
+        return self.fspath, self.test_result["line_number"], self.name
+
+    def repr_failure(self, exception):
+        """
+        Called when runtest() raises an exception. The method is used
+        to format the output of the failed test result.
+
+        """
+        if isinstance(exception.value, CTestException):
+            return ("Test failed : {file_name}:{line_number} {function_name} < {DSCR} >\n"
+                        "INFO : {INFO}".format(**self.test_result))
+
+
+
+class CTestException(Exception):
+    """Custom exception to distinguish C unit test failures from others."""
diff --git a/tests/ndarrays/test_ndarrays.c b/tests/ndarrays/test_ndarrays.c
new file mode 100644
index 00000000..f1aba096
--- /dev/null
+++ b/tests/ndarrays/test_ndarrays.c
@@ -0,0 +1,459 @@
+#include "ndarrays.h"
+#include <unistd.h>
+
+
+#define getname(X) #X
+#define my_assert(X , Y, dscr) _Generic((X), double: assert_double,\
+                            float: assert_float,\
+                            int: assert_int,\
+                            double complex : assert_cdouble,\
+                            default: assert_ns)(X , Y, getname(X), getname(Y), dscr, __func__, __FILE__, __LINE__)
+
+void assert_double(double v1 , double v2,
+        const char *v1_name, const char *v2_name,const char *dscr,
+        const char * func, const char *file, int line)
+{
+    if (v1 != v2)
+    {
+        printf("[FAIL] %s:%d:%s\n", file, line, func);
+        printf("[INFO] %s:%f != %s:%f\n", v1_name, v1, v2_name,v2);
+        printf("[DSCR] %s\n", dscr);
+        return ;
+    }
+    printf("[PASS] %s:%d:%s\n", file, line, func);
+    printf("[DSCR] %s\n", dscr);
+}
+
+void assert_float(float v1 , float v2,
+        const char *v1_name, const char *v2_name,const char *dscr,
+        const char * func, const char *file, int line)
+{
+    if (v1 != v2)
+    {
+        printf("[FAIL] %s:%d:%s\n", file, line, func);
+        printf("[INFO] %s:%f != %s:%f\n", v1_name, v1, v2_name,v2);
+        printf("[DSCR] %s\n", dscr);
+        return ;
+    }
+    printf("[PASS] %s:%d:%s\n", file, line, func);
+    printf("[DSCR] %s\n", dscr);
+}
+
+void assert_int(int v1 , int v2,
+        const char *v1_name, const char *v2_name,const char *dscr,
+        const char * func, const char *file, int line)
+{
+    if (v1 != v2)
+    {
+        printf("[FAIL] %s:%d:%s\n", file, line, func);
+        printf("[INFO] %s:%d != %s:%d\n", v1_name, v1, v2_name,v2);
+        printf("[DSCR] %s\n", dscr);
+        return ;
+    }
+    printf("[PASS] %s:%d:%s\n", file, line, func);
+    printf("[DSCR] %s\n", dscr);
+}
+
+void assert_cdouble(double complex v1 , double complex v2,
+        const char *v1_name, const char *v2_name,const char *dscr,
+        const char * func, const char *file, int line)
+{
+    if (v1 != v2)
+    {
+        printf("[FAIL] %s:%d:%s\n", file, line, func);
+        printf("[INFO] %s:%f+%f*I != %s:%f+%f*I\n", v1_name, creal(v1), cimag(v1), v2_name,creal(v2), cimag(v2));
+        printf("[DSCR] %s\n", dscr);
+        return ;
+    }
+    printf("[PASS] %s:%d:%s\n", file, line, func);
+    printf("[DSCR] %s\n", dscr);
+}
+
+void assert_ns(float v1 , float v2,
+        const char *v1_name, const char *v2_name, const char *dscr,
+        const char * func, const char *file, int line)
+{
+    printf("[FAIL] %s:%d:%s\n", file, line, func);
+    printf("[INFO] not supported type\n");
+    printf("[DSCR] %s\n", dscr);
+}
+
+int test_indexing_int(void)
+{
+    int m_1[] = {2, 3, 5, 5, 6, 7, 10, 11,
+                12, 260, 6, 8, 8, 0, 45, 0,
+                1, 0, 0, 1, 200, 33, 5, 57,
+                62, 70, 103, 141, 122, 26, 36, 82,
+                8, 10, 4115, 22, 1, 11, 1, 19};
+    int m_1_shape[] = {5, 8};
+    t_ndarray x;
+    int index;
+    int c_index;
+    int value;
+    int c_value;
+
+    x = array_create(2, m_1_shape, nd_int);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    // testing the index [3, 2]
+    index = 3 * x.strides[0] + 2 * x.strides[1];
+    c_index = 26;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 2) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 2]
+    value = x.nd_int[index];
+    c_value = 103;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_indexing_double(void)
+{
+    double m_1[] = {2, 3, 5, 5, 6, 7, 10, 11,
+                    12, 260, 6.34, 8, 8.002, 0.056, 45, 0.1,
+                    1.02, 0.25, 0.00005, 1, 200, 33, 5, 57,
+                    62, 70, 103.009, 141, 122, 26.50, 36.334, 82,
+                    8.44002, 10.056, 4115, 22.1, 1.1102, 011.25, 1.01110005, 19};
+    int m_1_shape[] = {5, 8};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double value;
+    double c_value;
+
+    x = array_create(2, m_1_shape, nd_double);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    // testing the index [3, 2]
+    index = 3 * x.strides[0] + 2 * x.strides[1];
+    c_index = 26;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 2) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 2]
+    value = x.nd_double[index];
+    c_value = 103.009;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_indexing_cdouble(void)
+{
+    double complex m_1[] = {0.37 + 0.588*I,  0.92689451+0.57106791*I,
+                            0.93598206+0.30289964*I,  0.54404246+0.09516331*I,
+                            0.02827254+0.00432899*I,  0.06873651+0.24810741*I,
+                            0.94040543+0.43508215*I,  0.58532094+0.67890618*I,
+                            0.68742283+0.64951155*I,  0.15372315+0.89699101*I};
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double complex value;
+    double complex c_value;
+
+    x = array_create(2, m_1_shape, nd_cdouble);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_cdouble[index];
+    c_value = 0.58532094+0.67890618*I;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+
+/* 
+**  slicing tests
+*/
+
+int test_slicing_int(void)
+{
+    int m_1[] = {2, 3, 5, 5, 6,
+                7, 10, 11, 12, 260,
+                6, 8, 8, 0, 45,
+                0, 1, 0, 0, 1,
+                200, 33, 5, 57, 62,
+                70, 103, 141, 122, 26,
+                36, 82, 8, 10, 4115,
+                22, 1, 11, 1, 19};
+    int m_1_shape[] = {8, 5};
+    t_ndarray x;
+    t_ndarray xview;
+    int c_index;
+    int value;
+    int c_value;
+
+    x = array_create(2, m_1_shape, nd_int);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    c_index = 5;
+    for (int i = 0; i < xview.shape[0]; i++)
+    {
+        for (int j = 0; j < xview.shape[1]; j++)
+        {
+            value = xview.nd_int[get_index(xview, i, j)];
+            c_value = m_1[c_index];
+            c_index+=2;
+            if (value != c_value)
+                my_assert(value , c_value, "testing xview values");
+        }
+    }
+    c_value = 1337;
+    xview.nd_int[get_index(xview, 0, 1)] = c_value;
+    value = x.nd_int[get_index(x, 1, 2)];
+    my_assert(value , c_value, "testing xview assignment");
+    free_array(x);
+    free_array(xview);
+    return (0);
+}
+
+int test_slicing_double(void)
+{
+    double m_1[] = {2, 3, 5, 5, 6,
+                    7, 10, 11, 12, 260,
+                    6.34, 8, 8.002, 0.056, 45,
+                    0.1, 1.02, 0.25, 0.00005, 1,
+                    200, 33, 5, 57, 62,
+                    103.009, 141, 122, 26.50, 36.334,
+                    82, 8.44002, 10.056, 4115, 22.1,
+                    1.1102, 011.25, 1.01110005, 19, 70};
+    int m_1_shape[] = {8, 5};
+    t_ndarray x;
+    t_ndarray xview;
+    int c_index;
+    double value;
+    double c_value;
+
+    x = array_create(2, m_1_shape, nd_double);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    c_index = 5;
+    for (int i = 0; i < xview.shape[0]; i++)
+    {
+        for (int j = 0; j < xview.shape[1]; j++)
+        {
+            value = xview.nd_double[get_index(xview, i, j)];
+            c_value = m_1[c_index];
+            c_index+=2;
+            if (value != c_value) // to not spam the test because of the loop
+                my_assert(value , c_value, "testing xview values");
+        }
+    }
+    c_value = 0.1337;
+    xview.nd_double[get_index(xview, 0, 1)] = c_value;
+    value = x.nd_double[get_index(x, 1, 2)];
+    my_assert(value, c_value, "testing xview assignment");
+    free_array(x);
+    free_array(xview);
+    return (0);
+}
+
+int test_slicing_cdouble(void)
+{
+    double complex m_1[] = {
+                    0.37 + 0.588*I,  0.92+0.57*I, 0.93+0.30*I,  0.54+0.09*I, 0.02+0.01*I,
+                    0.03+0.24*I, 0.94+0.43*I,  0.58+0.67*I, 0.68+0.64*I,  0.15+0.89*I
+                    };
+    int m_1_shape[] = {2, 5};
+    t_ndarray x;
+    t_ndarray xview;
+    int c_index;
+    double complex value;
+    double complex c_value;
+
+    x = array_create(2, m_1_shape, nd_cdouble);
+    memcpy(x.raw_data, m_1, x.buffer_size);
+    xview = array_slicing(x, new_slice(1, 2, 1), new_slice(0, 5, 2));
+    c_index = 5;
+    for (int i = 0; i < xview.shape[0]; i++)
+    {
+        for (int j = 0; j < xview.shape[1]; j++)
+        {
+            value = xview.nd_cdouble[get_index(xview, i, j)];
+            c_value = m_1[c_index];
+            c_index+=2;
+            if (value != c_value) // to not spam the test because of the loop
+                my_assert(value , c_value, "testing xview values");
+        }
+    }
+    c_value = 0.13 + 0.37*I;
+    xview.nd_cdouble[get_index(xview, 0, 1)] = c_value;
+    value = x.nd_cdouble[get_index(x, 1, 2)];
+    my_assert(value, c_value, "testing xview assignment");
+    free_array(x);
+    free_array(xview);
+    return (0);
+}
+
+/* array_fill tests */
+
+int test_array_fill_int(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    int value;
+    int c_value;
+
+    x = array_create(2, m_1_shape, nd_int);
+    array_fill(32, x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_int[index];
+    c_value = 32;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_array_fill_double(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double value;
+    double c_value;
+
+    x = array_create(2, m_1_shape, nd_double);
+    array_fill(2., x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_double[index];
+    c_value = 2.;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_array_fill_cdouble(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double complex value;
+    double complex c_value;
+
+    x = array_create(2, m_1_shape, nd_cdouble);
+    array_fill(0.3+0.54*I, x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_cdouble[index];
+    c_value = 0.3+0.54*I;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+/* array_zeros tests */
+
+int test_array_zeros_double(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double value;
+    double c_value;
+
+    x = array_create(2, m_1_shape, nd_double);
+    array_fill(0, x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_double[index];
+    c_value = 0.;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_array_zeros_int(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    int value;
+    int c_value;
+
+    x = array_create(2, m_1_shape, nd_int);
+    array_fill(0, x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_int[index];
+    c_value = 0;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int test_array_zeros_cdouble(void)
+{
+    int m_1_shape[] = {5, 2};
+    t_ndarray x;
+    int index;
+    int c_index;
+    double complex value;
+    double complex c_value;
+
+    x = array_create(2, m_1_shape, nd_cdouble);
+    array_fill(0, x);
+    // testing the index [3, 1]
+    index = 3 * x.strides[0] + 1 * x.strides[1];
+    c_index = 7;
+    my_assert(index , c_index, "testing the strides");
+    my_assert(get_index(x, 3, 1) , c_index, "testing the indexing function");
+    // testing the value with the index [3, 1]
+    value = x.nd_cdouble[index];
+    c_value = 0+0*I;
+    my_assert(value , c_value, "testing the value");
+    free_array(x);
+    return (0);
+}
+
+int main(void)
+{
+    /* indexing tests */
+    test_indexing_double();
+    test_indexing_int();
+    test_indexing_cdouble();
+    /* slicing tests */
+    test_slicing_double();
+    test_slicing_int();
+    test_slicing_cdouble();
+    /* array_fill tests */
+    test_array_fill_int();
+    test_array_fill_double();
+    test_array_fill_cdouble();
+    /* array_zeros tests */
+    test_array_zeros_int();
+    test_array_zeros_double();
+    test_array_zeros_cdouble();
+    return (0);
+}