/*
* Imagimob Studio 5.1.1863.65534+3456fff120ed88592409547d1c475ffb01e79493
* Copyright © 2023- Imagimob AB, All Rights Reserved.
* 
* Generated at 08/19/2024 13:59:02 UTC. Any changes will be lost.
* 
* Model ID  eb3ea4ac-1b69-4942-8cba-1d766821a2b8
* 
* Memory    Size                      Efficiency
* Buffers   256 bytes (RAM)           100 %
* Readonly  380 bytes (Flash)         100 %
* 
* Backend              tensorflow
* Keras Version        2.15.0
* Backend Model Type   Sequential
* Backend Model Name   regr_conv_ext6
* 
* Layer                          Shape           Type       Function
* Input Layer                    [8]             float      compute
*    shape = [8]
* Reshape                        [8,1]           float      compute
*    shape = [8,1]
*    trainable = True
* Convolution 1D                 [8,4]           float      compute
*    filters = 4
*    kernel_size = 5
*    dilation_rate = 1
*    strides = 1
*    padding = same
*    activation = relu
*    use_bias = True
*    trainable = True
*    weight = float[5,1,4]
*    bias = float[4]
* Batch Normalization            [8,4]           float      compute
*    epsilon = 0.001
*    trainable = True
*    scale = True
*    center = True
*    axis = 2
*    gamma = float[4]
*    beta = float[4]
*    mean = float[4]
*    variance = float[4]
* Convolution 1D                 [8,2]           float      compute
*    filters = 2
*    kernel_size = 3
*    dilation_rate = 1
*    strides = 1
*    padding = same
*    activation = relu
*    use_bias = True
*    trainable = True
*    weight = float[3,4,2]
*    bias = float[2]
* Flatten                        [16]            float      compute
* Dense                          [2]             float      compute
*    units = 2
*    use_bias = True
*    activation = relu
*    trainable = True
*    weight = float[16,2]
*    bias = float[2]
* Dense                          [1]             float      compute
*    units = 1
*    use_bias = True
*    activation = linear
*    trainable = True
*    weight = float[2,1]
*    bias = float[1]
* 
* Exported functions:
* 
* void POS_NN_compute(const float *restrict data_in, float *restrict data_out)
*    Description: Compute a forward pass
*    Parameter data_in is Input of size float[8].
*    Parameter data_out is Output of size float[1].
* 
* void POS_NN_init(void)
*    Description: Initializes buffers to initial state. This function also works as a reset function.
* 
* 
* Disclaimer:
*   The generated code relies on the optimizations done by the C compiler.
*   For example many for-loops of length 1 must be removed by the optimizer.
*   This can only be done if the functions are inlined and simplified.
*   Check disassembly if unsure.
*   tl;dr Compile using gcc with -O3 or -Ofast
*/

#include <math.h>
#include <stdint.h>

#include "position_nn.h"

// Working memory
static int8_t _buffer[256];

// Parameters
static const uint32_t _K3[] = {
    0xbda044ee, 0xbef0259e, 0x3e98c314, 0xbf546327, 0xbd001391, 0xbda2d634, 0x3f559718, 0x3f832a05, 
    0x3e213c25, 0xbe1dc021, 0x3f55c9f6, 0xbe1b69d2, 0x3f189bdc, 0x3eab7aab, 0x3ea25be7, 0xbf75b68a, 
    0xbf5ee6a2, 0x3f5ea953, 0xbeb80a77, 0x3ce0d67c
};

static const uint32_t _K4[] = {
    0xbceb9aea, 0x402da548, 0x4007175f, 0x3e3b6289
};

static const uint32_t _K7[] = {
    0x3fe52867, 0x3d8b7f18, 0x3d928e4c, 0x3e61f9a1
};

static const uint32_t _K8[] = {
    0xbf75a96b, 0xc00da0ab, 0xc004c352, 0xbfd72a90
};

static const uint32_t _K11[] = {
    0xbfabf2d8, 0xbf9306aa, 0x3ec52252, 0xbf8b043b, 0xbffe3258, 0xbf732abf, 0x3ca31d20, 0xbfd0be0a, 
    0x3f1900c9, 0xbfeb1403, 0xbde91476, 0xbecb0a6e, 0x3e3d92c7, 0x3f5a468e, 0x40182b14, 0x3e18fc4c, 
    0x3f513707, 0xbd4336b4, 0x3d3aa271, 0xbcf22d87, 0xbf00aab5, 0xbe8cdbb2, 0xbfba0df3, 0xbfc88368
};

static const uint32_t _K12[] = {
    0x3fa3c96f, 0x3e994f2e
};

static const uint32_t _K14[] = {
    0x3ff8633c, 0xc009bab0, 0x3fd7368e, 0xbeb7fcdc, 0x3f2c11be, 0x3dd906a2, 0x3f5040af, 0xbed1e316, 
    0x3e61cfc5, 0x3f96698a, 0x3e9fa9e7, 0x3fd363e0, 0x3e8650f9, 0xbf4d1ca2, 0xbff0e06e, 0xbdeb17e5, 
    0xbeef3135, 0xbe5aad0f, 0x3e9a7cbd, 0x3ef05dda, 0xbc9f5641, 0xbe893990, 0xbf146bcf, 0x3d23f268, 
    0xbef20404, 0xbd53cb80, 0xbe992fa5, 0xbc6f2fc4, 0xbefd1d7e, 0xbf38f3fb, 0x3d11a16b, 0x3d2669eb
};

static const uint32_t _K16[] = {
    0x3fbd4830, 0xbdededc5
};

static const uint32_t _K19[] = {
    0x3fa716c9, 0xbf818757
};

static const uint32_t _K21[] = {
    0x3ed5608a
};

// Memory mapped buffers
#define _K11             ((float *)_K11)                     // f32[2,3,4] (96 bytes) 
#define _K12             ((float *)_K12)                     // f32[2] (8 bytes) 
#define _K14             ((float *)_K14)                     // f32[2,16] (128 bytes) 
#define _K16             ((float *)_K16)                     // f32[2] (8 bytes) 
#define _K19             ((float *)_K19)                     // f32[1,2] (8 bytes) 
#define _K21             ((float *)_K21)                     // f32[1] (4 bytes) 
#define _K3              ((float *)_K3)                      // f32[4,5,1] (80 bytes) 
#define _K4              ((float *)_K4)                      // f32[4] (16 bytes) 
#define _K7              ((float *)_K7)                      // f32[4] (16 bytes) 
#define _K8              ((float *)_K8)                      // f32[4] (16 bytes) 
#define _K10             ((float *)(_buffer + 0x00000080))   // f32[8,2] (64 bytes) 
#define _K13             ((float *)(_buffer + 0x00000000))   // f32[8,2] (64 bytes) 
#define _K15             ((float *)(_buffer + 0x00000000))   // f32[2] (8 bytes) 
#define _K17             ((float *)(_buffer + 0x00000008))   // f32[2] (8 bytes) 
#define _K18             ((float *)(_buffer + 0x00000000))   // f32[2] (8 bytes) 
#define _K2              ((float *)(_buffer + 0x00000000))   // f32[8,4] (128 bytes) 
#define _K20             ((float *)(_buffer + 0x00000008))   // f32[1] (4 bytes) 
#define _K5              ((float *)(_buffer + 0x00000080))   // f32[8,4] (128 bytes) 
#define _K6              ((float *)(_buffer + 0x00000000))   // f32[8,4] (128 bytes) 
#define _K9              ((float *)(_buffer + 0x00000080))   // f32[8,4] (128 bytes) 

static inline float mac_f32(const float* restrict a, const float* restrict b, int count)
{
	float sum = 0;
	for (int i = 0; i < count; i++) {
		sum += *a++ * *b++;
	}
	return sum;
}

static inline void conv1d_flat_f32(
	const float* restrict input,
	const float* restrict weight,
	float* restrict output,
	int top,
	int bottom,
	int n_output_rows,
	int filters,
	int strides,
	int kernel_size)
{
	for (int i = 0; i < n_output_rows; i++) {
		const float* wp = weight;		// Weight matrix
		const float* bp = input;		// Input matrix
		const int step = i * strides;	// Row size
		int len = kernel_size;			// Normally do one kernel

		int skip = top - step;			// Pad top?
		if (skip > 0) {
			len -= skip;				// Trim kernel length
			wp += skip;					// Advance kernel
		}
		else {						// No top padding,
			bp -= skip;					// Rollback input
		}

		skip = step + len - bottom;		// Pad bottom?
		if (skip > 0)
			len -= skip;				// Just cut the kernel at end

		float* op = output + i * filters;
		for (int j = 0; j < filters; j++) {
			*op++ = mac_f32(wp + j * kernel_size, bp, len);
		}
	}
}

static inline void add_f32(
	const float* restrict a,
	const float* restrict b,
	int l, int g1, int m, int g2, int r,
	float* restrict output)
{
	int index = 0;
	for (int x = 0; x < l; x++) {
		for (int i = 0; i < g1; i++) {
			for (int y = 0; y < m; y++) {
				for (int j = 0; j < g2; j++) {
					for (int z = 0; z < r; z++) {
						output[index] = a[index] + b[x * m * r + y * r + z];
						index++;
					}
				}
			}
		}
	}
}

static inline void relu_f32(const float* restrict x, int count, float* restrict result)
{
	for (int i = 0; i < count; i++) {
		const float value = *x++;
		*result++ = value > 0 ? value : 0;
	}
}

static inline void mul_f32(
	const float* restrict a,
	const float* restrict b,
	int l, int g1, int m, int g2, int r,
	float* restrict output)
{
	int index = 0;
	for (int x = 0; x < l; x++) {
		for (int i = 0; i < g1; i++) {
			for (int y = 0; y < m; y++) {
				for (int j = 0; j < g2; j++) {
					for (int z = 0; z < r; z++) {
						output[index] = a[index] * b[x * m * r + y * r + z];
						index++;
					}
				}
			}
		}
	}
}

static inline void dott_f32(const float *restrict a, const float *restrict b, float *restrict out, int d0, int d1, int d2)
{
	for (int i = 0; i < d2; i++) {
		float* op = out;
		for (int j = 0; j < d1; j++) {
			*op++ = mac_f32(a + j * d0, b, d0);
		}
		out += d1;
		b += d0;
	}
}

#define __RETURN_ERROR(_exp) do { int __ret = (_exp); if(__ret < 0) return __ret; } while(0)
#define __RETURN_ERROR_BREAK_EMPTY(_exp) {  int __ret = (_exp); if(__ret == -1) break; if(__ret < 0) return __ret;  } 

void POS_NN_compute(const float *restrict data_in, float *restrict data_out) {
    conv1d_flat_f32(data_in, _K3, _K2, 2, 10, 8, 4, 1, 5);
    add_f32(_K2, _K4, 1, 1, 1, 8, 4, _K5);
    relu_f32(_K5, 32, _K2);
    mul_f32(_K2, _K7, 1, 1, 1, 8, 4, _K9);
    add_f32(_K9, _K8, 1, 1, 1, 8, 4, _K6);
    conv1d_flat_f32(_K6, _K11, _K10, 4, 36, 8, 2, 4, 12);
    add_f32(_K10, _K12, 1, 1, 1, 8, 2, _K13);
    relu_f32(_K13, 16, _K10);
    dott_f32(_K14, _K10, _K15, 16, 2, 1);
    add_f32(_K15, _K16, 1, 1, 1, 1, 2, _K17);
    relu_f32(_K17, 2, _K18);
    dott_f32(_K19, _K18, _K20, 2, 1, 1);
    add_f32(_K20, _K21, 1, 1, 1, 1, 1, data_out);
}

void POS_NN_init(void) {
}

