diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2e5b590f5289fc0743a37b71c1ac7b334750e835
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.24)
+project(
+ PPANN
+ VERSION 0.1
+ DESCRIPTION "Test"
+ LANGUAGES C
+)
+
+if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
+ set(CMAKE_C_STANDARD 17)
+ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+ include(CTest)
+endif()
+
+
+find_library(RELIC_LIB relic)
+
+add_subdirectory(src)
+add_subdirectory(apps)
+add_subdirectory(tests)
\ No newline at end of file
diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2333fb5388648f99b93ef99e1caa91bb279dfe2c
--- /dev/null
+++ b/apps/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(app app.c)
+target_link_libraries(app PRIVATE ppann_lib)
\ No newline at end of file
diff --git a/apps/app.c b/apps/app.c
new file mode 100644
index 0000000000000000000000000000000000000000..9508f4eb8b502d3904c0e085cfa8fc87a047922f
--- /dev/null
+++ b/apps/app.c
@@ -0,0 +1,63 @@
+#include "field.h"
+#include "group.h"
+#include <stdio.h>
+#include "relic/relic.h"
+
+
+int main() {
+ core_init();
+ pc_param_set_any();
+
+//
+// g a, b;
+// gt x, y;
+//
+// // Get generator and map to target group, x =
+// generator(a);
+// map(a, a, x);
+//
+//
+// g1_print(a);
+// printf("\n");
+// gt_print(x);
+// printf("\n");
+//
+// // Multiply and Exp.
+// zp m, n;
+// zp_from_int(m, 5);
+// zp_from_int(n, 5);
+//
+// // b = a^5, y = gt^10
+// multiply(b, a, m);
+//
+// g1_print(b);
+// printf("\n");
+//
+// exponentiation(y, x, n);
+//
+// gt_free(x);
+// map(b, b, x);
+//
+// printf("Compare: %i", gt_cmp(y, x));
+
+ g1_t a, b;
+ gt_t x, y, z;
+
+ g1_get_gen(a);
+
+ g1_print(a);
+
+ g1_mul_dig(b, a, 5);
+
+ g1_print(b);
+
+ pc_map(x, a, a);
+
+ pc_map(y, b, b);
+
+ gt_exp_dig(z, x, 25);
+
+ printf("Compare: %i", gt_cmp(y, z) == RLC_EQ);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/include/field.h b/include/field.h
new file mode 100644
index 0000000000000000000000000000000000000000..6c09690403863ca250fb39d1e0230a42404d1e2b
--- /dev/null
+++ b/include/field.h
@@ -0,0 +1,22 @@
+#ifndef PPANN_FIELD_H
+#define PPANN_FIELD_H
+
+#include "relic/relic.h"
+
+typedef fp_t zp;
+
+void rand_zp(zp x);
+
+void zp_zero(zp x);
+
+void zp_copy(zp x_copy, zp x);
+
+void zp_from_int(zp x, int x_int);
+
+void zp_add(zp r, zp x, zp y);
+
+void zp_multiply(zp p, zp x, zp y);
+
+void zp_inverse(zp xi, zp x);
+
+#endif //PPANN_FIELD_H
\ No newline at end of file
diff --git a/include/group.h b/include/group.h
new file mode 100644
index 0000000000000000000000000000000000000000..85c895483607fb54722042bca073d936236b4660
--- /dev/null
+++ b/include/group.h
@@ -0,0 +1,19 @@
+#ifndef PPANN_GROUP_H
+#define PPANN_GROUP_H
+
+#include "field.h"
+
+typedef g1_t g;
+typedef gt_t gt;
+
+void generator(g x);
+
+void negate(g nx, g x);
+
+void multiply(g r, g x, zp y);
+
+void exponentiation(gt r, gt x, zp y);
+
+void map(g a, g b, gt r);
+
+#endif //PPANN_GROUP_H
diff --git a/include/matrix.h b/include/matrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..e13a4d29749cc3d9fc55465ef67575c0747c4a63
--- /dev/null
+++ b/include/matrix.h
@@ -0,0 +1,25 @@
+#ifndef PPANN_MATRIX_H
+#define PPANN_MATRIX_H
+
+#include "field.h"
+#include "group.h"
+
+typedef zp* zp_mat;
+
+void matrix_zp_from_int(zp_mat x, int *int_mat, int row, int col);
+
+void matrix_zp_rand(zp_mat x, int row, int col);
+
+void matrix_identity(zp_mat x, int size);
+
+int matrix_is_identity(zp_mat x, int size);
+
+void matrix_transpose(zp_mat xt, zp_mat x, int row, int col);
+
+void matrix_merge(zp_mat xy, zp_mat x, zp_mat y, int row, int col_x, int col_y);
+
+void matrix_multiply(zp_mat xy, zp_mat x, zp_mat y, int row_x, int row_y, int col_y);
+
+void matrix_inverse(zp_mat xi, zp_mat x, int size);
+
+#endif //PPANN_MATRIX_H
diff --git a/include/vector.h b/include/vector.h
new file mode 100644
index 0000000000000000000000000000000000000000..27c1c5ca7a49090e3bd69c678432fbae8ab2f7bf
--- /dev/null
+++ b/include/vector.h
@@ -0,0 +1,22 @@
+#ifndef PPANN_VECTOR_H
+#define PPANN_VECTOR_H
+
+#include "field.h"
+#include "group.h"
+
+typedef zp *zp_vec;
+typedef g *g_vec;
+
+void vector_zp_from_int(zp_vec x, int *int_vec, int length);
+
+void vector_zp_rand(zp_vec x, int length);
+
+void vector_merge(zp_vec r, zp_vec a, zp_vec b, int size_a, int size_b);
+
+void vector_add(zp_vec r, zp_vec a, zp_vec b, int size);
+
+void vector_raise(g base, zp_vec x, g_vec gx, int size);
+
+void inner_product(gt r, g_vec a, g_vec b, int size);
+
+#endif //PPANN_VECTOR_H
\ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1b1c1ad773ee20d8c27472c34136de81f4a85e99
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(HEADER_LIST "${CMAKE_SOURCE_DIR}/include/")
+
+add_library(ppann_lib field.c group.c vector.c matrix.c ${HEADER_LIST})
+
+# We need this directory, and users of our library will need it too
+target_include_directories(ppann_lib PUBLIC ../include)
+
+# This depends on (header only) boost
+target_link_libraries(ppann_lib PRIVATE ${RELIC_LIB})
+
+# IDEs should put the headers in a nice place
+source_group(
+ TREE "${PROJECT_SOURCE_DIR}/include"
+ PREFIX "Header Files"
+ FILES ${HEADER_LIST}
+)
diff --git a/src/field.c b/src/field.c
new file mode 100644
index 0000000000000000000000000000000000000000..82b6d370b121be34e7fff521e4cc7db25d457683
--- /dev/null
+++ b/src/field.c
@@ -0,0 +1,29 @@
+#include "field.h"
+
+void rand_zp(dig_t *x) {
+ fp_rand(x);
+}
+
+void zp_zero(dig_t *x) {
+ fp_zero(x);
+}
+
+void zp_copy(dig_t *x_copy, dig_t *x) {
+ fp_copy(x_copy, x);
+}
+
+void zp_from_int(dig_t *x, int y) {
+ fp_set_dig(x, y);
+}
+
+void zp_add(dig_t *r, dig_t *x, dig_t *y) {
+ fp_add(r, x, y);
+}
+
+void zp_multiply(dig_t *p, dig_t *x, dig_t *y) {
+ fp_mul(p, x, y);
+}
+
+void zp_inverse(dig_t *xi, dig_t *x) {
+ fp_inv(xi, x);
+}
\ No newline at end of file
diff --git a/src/group.c b/src/group.c
new file mode 100644
index 0000000000000000000000000000000000000000..1868a3bba4e7795d662dba0de51a011a942dfd74
--- /dev/null
+++ b/src/group.c
@@ -0,0 +1,25 @@
+#include "group.h"
+
+void generator(ep_st *x) {
+ g1_get_gen(x);
+}
+
+void negate(ep_st *nx, ep_st *x) {
+ g1_neg(nx, x);
+}
+
+void multiply(ep_st *r, ep_st *x, dig_t *y) {
+ bn_t new_y;
+ fp_prime_back(new_y, y);
+ g1_mul(r, x, new_y);
+}
+
+void exponentiation(fp_t *r, fp_t *x, dig_t *y) {
+ bn_t new_y;
+ fp_prime_back(new_y, y);
+ gt_exp(r, x, new_y);
+}
+
+void map(ep_st *a, ep_st *b, fp_t *r) {
+ pc_map(r, a, b);
+}
diff --git a/src/matrix.c b/src/matrix.c
new file mode 100644
index 0000000000000000000000000000000000000000..ecc3e54ce0515323b67170e7989dc87351d1071c
--- /dev/null
+++ b/src/matrix.c
@@ -0,0 +1,122 @@
+#include "matrix.h"
+
+void matrix_zp_from_int(zp_mat x, int *int_mat, int row, int col) {
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ zp_from_int(x[i * col + j], int_mat[i * col + j]);
+ }
+ }
+}
+
+void matrix_zp_rand(zp_mat x, int row, int col) {
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ rand_zp(x[i * col + j]);
+ }
+ }
+}
+
+void matrix_identity(zp_mat x, int size) {
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (i == j) zp_from_int(x[i * size + j], 1);
+ else zp_zero(x[i * size + j]);
+ }
+ }
+}
+
+int matrix_is_identity(zp_mat x, int size) {
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (i == j && fp_cmp_dig(x[i * size + j], 1) != RLC_EQ) return 0;
+ if (i != j && fp_cmp_dig(x[i * size + j], 0) != RLC_EQ) return 0;
+ }
+ }
+ return 1;
+}
+
+void matrix_transpose(zp_mat xt, zp_mat x, int row, int col) {
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ zp_copy(xt[j * row + i], x[i * col + j]);
+ }
+ }
+}
+
+void matrix_merge(zp_mat xy, zp_mat x, zp_mat y, int row, int col_x, int col_y) {
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col_x; j++) {
+ zp_copy(xy[i * (col_x + col_y) + j], x[i * col_x + j]);
+ }
+ for (int j = 0; j < col_y; j++) {
+ zp_copy(xy[i * (col_x + col_y) + col_x + j], y[i * col_y + j]);
+ }
+ }
+}
+
+void matrix_multiply(zp_mat xy, zp_mat x, zp_mat y, int row_x, int row_y, int col_y) {
+ zp temp;
+ for (int i = 0; i < row_x; i++) {
+ for (int j = 0; j < col_y; j++) {
+ zp_from_int(xy[i * row_y + j], 0);
+ for (int k = 0; k < row_y; k++) {
+ zp_multiply(temp, x[i * row_y + k], y[k * col_y + j]);
+ zp_add(xy[i * col_y + j], xy[i * col_y + j], temp);
+ }
+ }
+ }
+}
+
+void matrix_inverse(zp_mat xi, zp_mat x, int size) {
+ // Declare the row echelon matrix and generate it.
+ zp *identity, *row_echelon;
+ identity = (zp *) malloc(size * size * sizeof(zp));
+ row_echelon = (zp *) malloc(2 * size * size * sizeof(zp));
+ matrix_identity(identity, size);
+ matrix_merge(row_echelon, x, identity, size, size, size);
+
+ // Declare temp value.
+ zp temp_multiplier, temp_neg;
+
+ // Bottom left half to all zeros.
+ for (int i = 0; i < size; i++) {
+ for (int j = i; j < size; j++) {
+ if (i == j && fp_cmp_dig(row_echelon[i * 2 * size + j], 1) != RLC_EQ) {
+ zp_inverse(temp_multiplier, row_echelon[i * 2 * size + i]);
+ for (int k = i; k < size * 2; k++) {
+ zp_multiply(row_echelon[j * 2 * size + k], row_echelon[j * 2 * size + k], temp_multiplier);
+ }
+ }
+
+ if (i == j && fp_cmp_dig(row_echelon[i * 2 * size + j], 0) == RLC_EQ) break;
+
+ if (i != j) {
+ zp_copy(temp_multiplier, row_echelon[j * 2 * size + i]);
+ for (int k = i; k < size * 2; k++) {
+ zp_multiply(temp_neg, temp_multiplier, row_echelon[i * 2 * size + k]);
+ fp_neg(temp_neg, temp_neg);
+ zp_add(row_echelon[j * 2 * size + k], row_echelon[j * 2 * size + k], temp_neg);
+ }
+ }
+ }
+ }
+
+ // Top right half to all zeros.
+ for (int i = size - 1; i > 0; i--) {
+ for (int j = i - 1; j >= 0; j--) {
+ zp_copy(temp_multiplier, row_echelon[j * 2 * size + i]);
+ for (int k = i; k < size * 2; k++) {
+ zp_multiply(temp_neg, temp_multiplier, row_echelon[i * 2 * size + k]);
+ fp_neg(temp_neg, temp_neg);
+ zp_add(row_echelon[j * 2 * size + k], row_echelon[j * 2 * size + k], temp_neg);
+ }
+ }
+ }
+
+ // Copy over the output.
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ zp_copy(xi[i * size + j], row_echelon[i * 2 * size + size + j]);
+ }
+ }
+}
diff --git a/src/vector.c b/src/vector.c
new file mode 100644
index 0000000000000000000000000000000000000000..43ac6d0315e50fe132be65870cea97e65edc1196
--- /dev/null
+++ b/src/vector.c
@@ -0,0 +1,32 @@
+#include "vector.h"
+
+void vector_zp_from_int(zp_vec x, int *int_vec, int length) {
+ for (int i = 0; i < length; i++) zp_from_int(x[i], int_vec[i]);
+}
+
+void vector_zp_rand(zp_vec x, int length) {
+ for (int i = 0; i < length; i++) rand_zp(x[i]);
+}
+
+void vector_merge(zp_vec r, zp_vec a, zp_vec b, int size_a, int size_b) {
+ for (int i = 0; i < size_a; i++) zp_copy(r[i], a[i]);
+ for (int i = 0; i < size_b; i++) zp_copy(r[i + size_a], b[i]);
+}
+
+void vector_add(zp_vec r, zp_vec a, zp_vec b, int size) {
+ for (int i = 0; i < size; i++) zp_add(r[i], a[i], b[i]);
+}
+
+void vector_raise(ep_st base[1], zp_vec x, g_vec gx, int size) {
+ for (int i = 0; i < size; i++) multiply(gx[i], base, x[i]);
+}
+
+void inner_product(gt r, g_vec a, g_vec b, int size) {
+ pc_map_sim(r, a, b, size);
+}
+
+
+
+
+
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ef1621db4e243c9ee0ecf000bd59ce3a57e0e82d
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Add tests as separate executables.
+add_executable(test_field test_field.c)
+add_executable(test_group test_group.c)
+add_executable(test_vector test_vector.c)
+add_executable(test_matrix test_matrix.c)
+
+# Link tests to the main library.
+target_link_libraries(test_field PRIVATE ppann_lib)
+target_link_libraries(test_group PRIVATE ppann_lib)
+target_link_libraries(test_vector PRIVATE ppann_lib)
+target_link_libraries(test_matrix PRIVATE ppann_lib)
+
+# Register the previous tests.
+add_test(NAME test_field COMMAND test_field)
+add_test(NAME test_group COMMAND test_group)
+add_test(NAME test_vector COMMAND test_vector)
+add_test(NAME test_matrix COMMAND test_matrix)
\ No newline at end of file
diff --git a/tests/test_field.c b/tests/test_field.c
new file mode 100644
index 0000000000000000000000000000000000000000..d51f818903662e5e5df494182e56d5bc61373979
--- /dev/null
+++ b/tests/test_field.c
@@ -0,0 +1,62 @@
+#include "field.h"
+
+int test_zp_zero() {
+ zp x, y;
+ zp_zero(x);
+ fp_zero(y);
+ return fp_cmp(x, y);
+}
+
+int test_zp_copy() {
+ zp x, y;
+ zp_from_int(x, 3);
+ zp_copy(y, x);
+ return fp_cmp_dig(y, 3);
+}
+
+int test_zp_from_int() {
+ zp x;
+ zp_from_int(x, 3);
+ return fp_cmp_dig(x, 3);
+}
+
+int test_zp_add() {
+ zp x, y, r;
+ zp_from_int(x, 10);
+ zp_from_int(y, 20);
+ zp_add(r, x, y);
+ return fp_cmp_dig(r, 30);
+}
+
+int test_zp_multiply() {
+ zp x, y, r;
+ zp_from_int(x, 10);
+ zp_from_int(y, 20);
+ zp_multiply(r, x, y);
+ return fp_cmp_dig(r, 200);
+}
+
+int test_zp_inverse() {
+ zp x, xi, r;
+ rand_zp(x);
+ zp_inverse(xi, x);
+ zp_multiply(r, x, xi);
+ return fp_cmp_dig(r, 1);
+}
+
+
+int main() {
+ // Init core and setup.
+ core_init();
+ pc_param_set_any();
+
+ // Perform tests.
+ if (test_zp_zero() != RLC_EQ) return 1;
+ if (test_zp_copy() != RLC_EQ) return 1;
+ if (test_zp_from_int() != RLC_EQ) return 1;
+ if (test_zp_add() != RLC_EQ) return 1;
+ if (test_zp_multiply() != RLC_EQ) return 1;
+ if (test_zp_inverse() != RLC_EQ) return 1;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tests/test_group.c b/tests/test_group.c
new file mode 100644
index 0000000000000000000000000000000000000000..0e6b9d1b040dea4aef906ed8cb4dfeb9a8b312bb
--- /dev/null
+++ b/tests/test_group.c
@@ -0,0 +1,45 @@
+#include "group.h"
+
+int test_generator() {
+ g x;
+ generator(x);
+ return g1_is_valid(x);
+}
+
+int test_all() {
+ // Set integers.
+ zp m, n;
+ zp_from_int(m, 5);
+ zp_from_int(n, 25);
+
+ // Declare variables.
+ g a, b;
+ gt x, y, z;
+
+ // Get generator g and find g^5.
+ generator(a);
+ multiply(b, a, m);
+
+ // Get e(g, g) and e(g^5, g^5).
+ map(a, a, x);
+ map(b, b, y);
+
+ // Get e(g, g)^25.
+ exponentiation(z, x, n);
+
+ // Compare e(g^5, g^5) with e(g, g)^25.
+ return gt_cmp(y, z);
+}
+
+int main(){
+ // Init core and setup.
+ core_init();
+ fp_prime_init();
+ pc_param_set_any();
+
+ // Perform tests.
+ if (test_generator() != 1) return 1;
+ if (test_all() != RLC_EQ) return 1;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tests/test_matrix.c b/tests/test_matrix.c
new file mode 100644
index 0000000000000000000000000000000000000000..2f170742140feccd8db41c22cc6f7ce320dbdff1
--- /dev/null
+++ b/tests/test_matrix.c
@@ -0,0 +1,82 @@
+#include "matrix.h"
+
+int test_zp_from_int() {
+ int row = 3, col = 3;
+ zp x[row * col];
+ int int_vec[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ matrix_zp_from_int(x, int_vec, 3, 3);
+ return fp_cmp_dig(x[2 * col + 2], 9);
+}
+
+int test_transpose() {
+ int row = 3, col = 3;
+ zp x[row * col], xt[col * row];
+ matrix_zp_rand(x, row, col);
+ matrix_transpose(xt, x, row, col);
+ return fp_cmp(xt[col - 1], x[2 * row]);
+}
+
+int test_identity() {
+ int size = 1000;
+ zp *x;
+ x = (zp *) malloc(size * size * sizeof(zp));
+ matrix_identity(x, size);
+ return matrix_is_identity(x, size);
+}
+
+int test_merge() {
+ int size = 10;
+ zp xy[(size + size) * size], x[size * size], y[size * size];
+ matrix_zp_rand(x, size, size);
+ matrix_identity(y, size);
+ matrix_merge(xy, x, y, size, size, size);
+ return fp_cmp(x[2 * size + 1], xy[4 * size + 1]);
+}
+
+int test_multiply_vector() {
+ int int_x[5] = {1, 2, 3, 4, 5};
+ int int_y[15] = {10, 20, 30,
+ 10, 20, 30,
+ 10, 20, 30,
+ 10, 20, 30,
+ 10, 20, 30};
+
+ zp x[5], y[15];
+ matrix_zp_from_int(x, int_x, 1, 5);
+ matrix_zp_from_int(y, int_y, 5, 3);
+
+ zp xy[3];
+ matrix_multiply(xy, x, y, 1, 5, 3);
+
+ return fp_cmp_dig(xy[1], 300);
+}
+
+int test_inverse() {
+ int size = 100;
+ // Allocate space.
+ zp *x, *xi, *r;
+ x = (zp *) malloc(size * size * sizeof(zp));
+ xi = (zp *) malloc(size * size * sizeof(zp));
+ r = (zp *) malloc(size * size * sizeof(zp));
+ matrix_zp_rand(x, size, size);
+ matrix_inverse(xi, x, size);
+ matrix_multiply(r, xi, x, size, size, size);
+ return matrix_is_identity(r, size);
+}
+
+
+int main() {
+ // Init core and setup.
+ core_init();
+ pc_param_set_any();
+
+ // Perform tests.
+ if (test_zp_from_int() != RLC_EQ) return 1;
+ if (test_transpose() != RLC_EQ) return 1;
+ if (test_identity() != 1) return 1;
+ if (test_merge() != RLC_EQ) return 1;
+ if (test_multiply_vector() != RLC_EQ) return 1;
+ if (test_inverse() != 1) return 1;
+
+ return 0;
+}
\ No newline at end of file
diff --git a/tests/test_vector.c b/tests/test_vector.c
new file mode 100644
index 0000000000000000000000000000000000000000..91e27a76e20143425445b0599f982294606fe401
--- /dev/null
+++ b/tests/test_vector.c
@@ -0,0 +1,63 @@
+#include "vector.h"
+
+int test_zp_from_int() {
+ zp x[4];
+ int int_vec[4] = {1, 2, 3, 4};
+ vector_zp_from_int(x, int_vec, 4);
+ return fp_cmp_dig(x[3], 4);
+}
+
+int test_merge_vector() {
+ zp x[3], y[3], z[6];
+ int int_vec_x[3] = {1, 2, 3};
+ int int_vec_y[3] = {11, 22, 33};
+ vector_zp_from_int(x, int_vec_x, 3);
+ vector_zp_from_int(y, int_vec_y, 3);
+ vector_merge(z, x, y, 3, 3);
+ return fp_cmp_dig(z[5], 33);
+}
+
+int test_add_vector() {
+ zp x[3], y[3], z[3];
+ int int_vec_x[3] = {1, 2, 3};
+ int int_vec_y[3] = {11, 22, 33};
+ vector_zp_from_int(x, int_vec_x, 3);
+ vector_zp_from_int(y, int_vec_y, 3);
+ vector_add(z, x, y, 3);
+ return fp_cmp_dig(z[2], 36);
+}
+
+int test_inner_product() {
+ zp x[3], y[3];
+ int int_vec_x[3] = {1, 2, 3};
+ int int_vec_y[3] = {4, 5, 6};
+ vector_zp_from_int(x, int_vec_x, 3);
+ vector_zp_from_int(y, int_vec_y, 3);
+
+ g g, gx[3], gy[3];
+ generator(g);
+ vector_raise(g, x, gx, 3);
+ vector_raise(g, y, gy, 3);
+
+ gt b, r;
+ inner_product(r, gx, gy, 3);
+ map(g, g, b);
+ gt_exp_dig(b, b, 32);
+
+ return gt_cmp(b, r);
+}
+
+
+int main() {
+ // Init core and setup.
+ core_init();
+ pc_param_set_any();
+
+ // Perform tests.
+ if (test_zp_from_int() != RLC_EQ) return 1;
+ if (test_merge_vector() != RLC_EQ) return 1;
+ if (test_add_vector() != RLC_EQ) return 1;
+ if (test_inner_product() != RLC_EQ) return 1;
+
+ return 0;
+}
\ No newline at end of file