/**
 * @file   decmp.c
 * @author Dominik Loidolt (dominik.loidolt@univie.ac.at),
 * @date   2020
 *
 * @copyright GPLv2
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * @brief software decompression library
 * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001
 *
 * To decompress a compression entity (consisting of a compression entity header
 * and the compressed data) use the decompress_cmp_entiy() function.
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#include "byteorder.h"
#include "cmp_debug.h"
#include "cmp_support.h"
#include "cmp_data_types.h"
#include "cmp_entity.h"


#define MAX_CW_LEN 32 /* maximum Golomb code word bit length */


/* maximum used bits registry */
extern struct cmp_max_used_bits max_used_bits;

/* function pointer to a code word decoder function */
typedef int (*decoder_ptr)(unsigned int, unsigned int, unsigned int, unsigned int *);

/* structure to hold a setup to encode a value */
struct decoder_setup {
	decoder_ptr decode_cw_f; /* pointer to the code word decoder (Golomb/Rice)*/
	int (*decode_method_f)(uint32_t *decoded_value, int stream_pos,
			       const struct decoder_setup *setup); /* pointer to the decoding function */
	uint32_t *bitstream_adr; /* start address of the compressed data bitstream */
	uint32_t max_stream_len; /* maximum length of the bitstream/icu_output_buf in bits */
	uint32_t encoder_par1; /* encoding parameter 1 */
	uint32_t encoder_par2; /* encoding parameter 2 */
	uint32_t outlier_par; /* outlier parameter */
	uint32_t lossy_par; /* lossy compression parameter */
	uint32_t model_value; /* model value parameter */
	uint32_t max_data_bits; /* how many bits are needed to represent the highest possible value */
};


/**
 * @brief count leading 1-bits
 *
 * @param value	input vale to count
 *
 * @returns the number of leading 1-bits in value, starting at the most
 *	significant bit position
 */

static unsigned int count_leading_ones(uint32_t value)
{
	if (value == 0xFFFFFFFF)
		return 32;

	return __builtin_clz(~value);
}


/**
 * @brief decode a Rice code word
 *
 * @param code_word	Rice code word bitstream starting at the MSB
 * @param m		Golomb parameter (not used)
 * @param log2_m	Rice parameter, must be the same used for encoding
 * @param decoded_cw	pointer where decoded value is written
 *
 * @returns the length of the decoded code word in bits (NOT the decoded value);
 *	0 on failure
 */

static int rice_decoder(uint32_t code_word, unsigned int m, unsigned int log2_m,
			unsigned int *decoded_cw)
{
	unsigned int q; /* quotient code */
	unsigned int ql; /* length of the quotient code */
	unsigned int r; /* remainder code */
	unsigned int rl = log2_m; /* length of the remainder code */
	unsigned int cw_len; /* length of the decoded code word in bits */

	(void)m; /* we don't need the Golomb parameter */

	if (log2_m > 32) /* because m has 32 bits log2_m can not be bigger than 32 */
		return 0;

	q = count_leading_ones(code_word); /* decode unary coding */
	ql = q + 1; /* Number of 1's + following 0 */

	cw_len = rl + ql;

	if (cw_len > 32) /* can only decode code words with maximum 32 bits */
		return 0;

	code_word = code_word << ql;  /* shift quotient code out */

	/* Right shifting an integer by a number of bits equal or greater than
	 * its size is undefined behavior */
	if (rl == 0)
		r = 0;
	else
		r = code_word >> (32 - rl);

	*decoded_cw = (q << rl) + r;

	return cw_len;
}


/**
 * @brief decode a Golomb code word
 *
 * @param code_word	Golomb code word bitstream starting at the MSB
 * @param m		Golomb parameter (have to be bigger than 0)
 * @param log2_m	is log_2(m) calculate outside function for better
 *			performance
 * @param decoded_cw	pointer where decoded value is written
 *
 * @returns the length of the decoded code word in bits (NOT the decoded value);
 *	0 on failure
 */

static int golomb_decoder(unsigned int code_word, unsigned int m,
			  unsigned int log2_m, unsigned int *decoded_cw)
{
	unsigned int q; /* quotient code */
	unsigned int r1; /* remainder code group 1 */
	unsigned int r2; /* remainder code group 2 */
	unsigned int r; /* remainder code */
	unsigned int rl; /* length of the remainder code */
	unsigned int cutoff; /* cutoff between group 1 and 2 */
	unsigned int cw_len; /* length of the decoded code word in bits */

	q = count_leading_ones(code_word); /* decode unary coding */

	rl = log2_m + 1;
	code_word <<= (q+1);  /* shift quotient code out */

	r2 = code_word >> (32 - rl);
	r1 = r2 >> 1;

	cutoff = (1UL << rl) - m;

	if (r1 < cutoff) { /* group 1 */
		cw_len = q + rl;
		r = r1;
	} else { /* group 2 */
		cw_len = q + rl + 1;
		r = r2 - cutoff;
	}

	if (cw_len > 32)
		return 0;

	*decoded_cw = q*m + r;
	return cw_len;
}


/**
 * @brief select the decoder based on the used Golomb parameter
 * @note if the Golomb parameter is a power of 2 we can use the faster Rice
 *	decoder
 *
 * @param golomb_par	Golomb parameter (have to be bigger than 0)
 *
 * @returns function pointer to the select decoder function; NULL on failure
 */

static decoder_ptr select_decoder(unsigned int golomb_par)
{
	if (!golomb_par)
		return NULL;

	if (is_a_pow_of_2(golomb_par))
		return &rice_decoder;
	else
		return &golomb_decoder;
}


/**
 * @brief read a value of up to 32 bits from a bitstream
 *
 * @param p_value		pointer to the read value, the
 *				read value will be converted to the system
 *				endianness
 * @param n_bits		number of bits to read from the bitstream
 * @param bit_offset		bit index where the bits will be read, seen from
 *				the very beginning of the bitstream
 * @param bitstream_adr		this is the pointer to the beginning of the
 *				bitstream
 * @param max_stream_len	maximum length of the bitstream in bits *
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF
 *	if the bitstream buffer is too small to read the value from the
 *	bitstream
 */

static int get_n_bits32(uint32_t *p_value, unsigned int n_bits, int bit_offset,
			uint32_t *bitstream_adr, unsigned int max_stream_len)
{
	const unsigned int *local_adr;
	unsigned int bitsLeft, bitsRight, localEndPos;
	unsigned int mask;
	int stream_len = (int)(n_bits + (unsigned int)bit_offset); /* overflow results in a negative return value */

	/*leave in case of erroneous input */
	if (bit_offset < 0)
		return -1;
	if (n_bits == 0)
		return -1;
	if (n_bits > 32)
		return -1;
	if (!bitstream_adr)
		return -1;
	if (!p_value)
		return -1;

	/* Check if bitstream buffer is large enough */
	if ((unsigned int)stream_len > max_stream_len) {
		debug_print("Error: Buffer overflow detected.\n");
		return CMP_ERROR_SMALL_BUF;

	}

	/* separate the bit_offset into word offset (set local_adr pointer) and
	 * local bit offset (bitsLeft)
	 */
	local_adr = bitstream_adr + (bit_offset >> 5);
	bitsLeft = bit_offset & 0x1f;

	localEndPos = bitsLeft + n_bits;

	if (localEndPos <= 32) {
		unsigned int shiftRight = 32 - n_bits;

		bitsRight = shiftRight - bitsLeft;

		*(p_value) = cpu_to_be32(*(local_adr)) >> bitsRight;

		mask = (0xffffffff >> shiftRight);

		*(p_value) &= mask;
	} else {
		unsigned int n1 = 32 - bitsLeft;
		unsigned int n2 = n_bits - n1;
		/* part 1 ; */
		mask = 0xffffffff >> bitsLeft;
		*(p_value) = cpu_to_be32(*(local_adr)) & mask;
		*(p_value) <<= n2;
		/*part 2: */
		/* adjust address*/
		local_adr += 1;

		bitsRight = 32 - n2;
		*(p_value) |= cpu_to_be32(*(local_adr)) >> bitsRight;
	}

	return stream_len;
}


/**
 * @brief decode a Golomb/Rice encoded code word from the bitstream
 *
 * @param decoded_value	pointer to the decoded value
 * @param stream_pos	start bit position code word to be decoded in the bitstream
 * @param setup		pointer to the decoder setup
 *
 * @returns bit index of the next code word in the bitstream on success; returns
 *	negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF if the
 *	bitstream buffer is too small to read the value from the bitstream
 */

static int decode_normal(uint32_t *decoded_value, int stream_pos,
			 const struct decoder_setup *setup)
{
	uint32_t read_val;
	unsigned int n_read_bits;
	int stream_pos_read, cw_len;

	/* check if we can read max_cw_len or less; we do not know how long the
	 * code word actually is so we try to read the maximum cw length */
	if ((unsigned int)stream_pos + 32 > setup->max_stream_len)
		n_read_bits = setup->max_stream_len - (unsigned int)stream_pos;
	else
		n_read_bits = MAX_CW_LEN;

	stream_pos_read = get_n_bits32(&read_val, n_read_bits, stream_pos,
				       setup->bitstream_adr, setup->max_stream_len);
	if (stream_pos_read < 0)
		return stream_pos_read;

	/* if we read less than 32, we shift the bitstream so that it starts at the MSB */
	read_val = read_val << (32 - n_read_bits);

	cw_len = setup->decode_cw_f(read_val, setup->encoder_par1,
				    setup->encoder_par2, decoded_value);
	if (cw_len <= 0)
		return -1;
	/* consistency check: code word length can not be bigger than the read bits */
	if (cw_len > (int)n_read_bits)
		return -1;

	return stream_pos + cw_len;
}


/**
 * @brief decode a Golomb/Rice encoded code word with zero escape system
 *	mechanism from the bitstream
 *
 * @param decoded_value	pointer to the decoded value
 * @param stream_pos	start bit position code word to be decoded in the bitstream
 * @param setup		pointer to the decoder setup
 *
 * @returns bit index of the next code word in the bitstream on success; returns
 *	negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF if the
 *	bitstream buffer is too small to read the value from the bitstream
 */

static int decode_zero(uint32_t *decoded_value, int stream_pos,
		       const struct decoder_setup *setup)
{
	stream_pos = decode_normal(decoded_value, stream_pos, setup);
	if (stream_pos < 0)
		return stream_pos;

	/* consistency check: value lager than the outlier parameter should not
	 * be Golomb/Rice encoded */
	if (*decoded_value > setup->outlier_par)
		return -1;

	if (*decoded_value == 0) {
		/* escape symbol mechanism was used; read unencoded value */
		uint32_t unencoded_val;

		stream_pos = get_n_bits32(&unencoded_val, setup->max_data_bits, stream_pos,
					  setup->bitstream_adr, setup->max_stream_len);
		if (stream_pos < 0)
			return stream_pos;
		/* consistency check: outliers must be bigger than the outlier_par */
		if (unencoded_val < setup->outlier_par && unencoded_val != 0)
			return -1;

		*decoded_value = unencoded_val;
	}

	(*decoded_value)--;
	if (*decoded_value == 0xFFFFFFFF) /* catch underflow */
		(*decoded_value) >>=  (32 - setup->max_data_bits);

	return stream_pos;
}


/**
 * @brief decode a Golomb/Rice encoded code word with multi escape system
 *	mechanism from the bitstream
 *
 * @param decoded_value	pointer to the decoded value
 * @param stream_pos	start bit position code word to be decoded in the bitstream
 * @param setup		pointer to the decoder setup
 *
 * @returns bit index of the next code word in the bitstream on success; returns
 *	negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF if the
 *	bitstream buffer is too small to read the value from the bitstream
 */

static int decode_multi(uint32_t *decoded_value, int stream_pos,
			const struct decoder_setup *setup)
{
	stream_pos = decode_normal(decoded_value, stream_pos, setup);
	if (stream_pos < 0)
		return stream_pos;

	if (*decoded_value >= setup->outlier_par) {
		/* escape symbol mechanism was used; read unencoded value */
		uint32_t unencoded_val;
		unsigned int unencoded_len;

		unencoded_len = (*decoded_value - setup->outlier_par + 1) * 2;

		stream_pos = get_n_bits32(&unencoded_val, unencoded_len, stream_pos,
					  setup->bitstream_adr, setup->max_stream_len);
		if (stream_pos >= 0)
			*decoded_value = unencoded_val + setup->outlier_par;
	}
	return stream_pos;
}


/**
 * @brief get the value unencoded with setup->cmp_par_1 bits without any
 *	additional changes from the bitstream
 *
 * @param decoded_value	pointer to the decoded value
 * @param stream_pos	start bit position code word to be decoded in the bitstream
 * @param setup		pointer to the decoder setup
 *
 * @returns bit index of the next code word in the bitstream on success; returns
 *	negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF if the
 *	bitstream buffer is too small to read the value from the bitstream
 *
 */

static int decode_none(uint32_t *decoded_value, int stream_pos,
		       const struct decoder_setup *setup)
{
	stream_pos = get_n_bits32(decoded_value, setup->encoder_par1, stream_pos,
				  setup->bitstream_adr, setup->max_stream_len);

	return stream_pos;
}


/**
 * @brief remap an unsigned value back to a signed value
 * @note this is the reverse function of map_to_pos()
 *
 * @param value_to_unmap	unsigned value to remap
 *
 * @returns the signed remapped value
 */

static uint32_t re_map_to_pos(uint32_t value_to_unmap)
{
	if (value_to_unmap & 0x1) { /* if uneven */
		if (value_to_unmap == 0xFFFFFFFF) /* catch overflow */
			return 0x80000000;
		return -((value_to_unmap + 1) / 2);
	} else {
		return value_to_unmap / 2;
	}
}


/**
 * @brief decompress the next code word in the bitstream and decorate it with
 *	the model
 *
 * @param decoded_value	pointer to the decoded value
 * @param model		model of the decoded_value (0 if not used)
 * @param stream_pos	start bit position code word to be decoded in the bitstream
 * @param setup		pointer to the decoder setup
 *
 * @returns bit index of the next code word in the bitstream on success; returns
 *	negative in case of erroneous input; returns CMP_ERROR_SMALL_BUF if the
 *	bitstream buffer is too small to read the value from the bitstream
 */

static int decode_value(uint32_t *decoded_value, uint32_t model,
			int stream_pos, const struct decoder_setup *setup)
{
	uint32_t mask = (~0U >> (32 - setup->max_data_bits)); /* mask the used bits */

	/* decode the next value from the bitstream */
	stream_pos = setup->decode_method_f(decoded_value, stream_pos, setup);
	if (stream_pos <= 0)
		return stream_pos;

	if (setup->decode_method_f == decode_none)
		/* we are done here in stuff mode */
		return stream_pos;

	/* map the unsigned decode value back to a signed value */
	*decoded_value = re_map_to_pos(*decoded_value);

	/* decorate data the data with the model */
	*decoded_value += round_fwd(model, setup->lossy_par);

	/* we mask only the used bits in case there is an overflow when adding the model */
	*decoded_value &= mask;

	/* inverse step of the lossy compression */
	*decoded_value = round_inv(*decoded_value, setup->lossy_par);

	return stream_pos;
}


/**
 * @brief configure a decoder setup structure to have a setup to decode a vale
 *
 * @param setup		pointer to the decoder setup
 * @param cmp_par	compression parameter
 * @param spillover	spillover_par parameter
 * @param lossy_par	lossy compression parameter
 * @param max_data_bits	how many bits are needed to represent the highest possible value
 * @param cfg		pointer to the compression configuration structure
 *
 * @returns 0 on success; otherwise error
 */

static int configure_decoder_setup(struct decoder_setup *setup,
				   uint32_t cmp_par, uint32_t spillover,
				   uint32_t lossy_par, uint32_t max_data_bits,
				   const struct cmp_cfg *cfg)
{
	if (multi_escape_mech_is_used(cfg->cmp_mode))
		setup->decode_method_f = &decode_multi;
	else if (zero_escape_mech_is_used(cfg->cmp_mode))
		setup->decode_method_f = &decode_zero;
	else if (cfg->cmp_mode == CMP_MODE_STUFF)
		setup->decode_method_f = &decode_none;
	else {
		setup->decode_method_f = NULL;
		debug_print("Error: Compression mode not supported.\n");
		return -1;
	}

	setup->bitstream_adr = cfg->icu_output_buf; /* start address of the compressed data bitstream */
	if (cfg->buffer_length & 0x3) {
		debug_print("Error: The length of the compressed data is not a multiple of 4 bytes.");
		return -1;
	}
	setup->max_stream_len = (cfg->buffer_length) * CHAR_BIT;  /* maximum length of the bitstream/icu_output_buf in bits */
	setup->encoder_par1 = cmp_par; /* encoding parameter 1 */
	if (ilog_2(cmp_par) < 0)
		return -1;
	setup->encoder_par2 = ilog_2(cmp_par); /* encoding parameter 2 */
	setup->outlier_par = spillover; /* outlier parameter */
	setup->lossy_par = lossy_par; /* lossy compression parameter */
	setup->model_value = cfg->model_value; /* model value parameter */
	setup->max_data_bits = max_data_bits; /* how many bits are needed to represent the highest possible value */
	setup->decode_cw_f = select_decoder(cmp_par);

	return 0;
}


/**
 * @brief decompress imagette data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_imagette(struct cmp_cfg *cfg)
{
	int err;
	size_t i;
	int stream_pos = 0;
	struct decoder_setup setup;
	uint16_t *decompressed_data = cfg->input_buf;
	uint16_t *model_buf = cfg->model_buf;
	uint16_t *up_model_buf = cfg->icu_new_model_buf;
	uint32_t decoded_value = 0;
	uint16_t model;

	err = configure_decoder_setup(&setup, cfg->golomb_par, cfg->spill,
				      cfg->round, max_used_bits.nc_imagette, cfg);
	if (err)
		return -1;


	for (i = 0; i < cfg->samples; i++) {
		if (model_mode_is_used(cfg->cmp_mode))
			model = model_buf[i];
		else
			model = decoded_value;

		stream_pos = decode_value(&decoded_value, model, stream_pos, &setup);
		if (stream_pos <= 0)
			return stream_pos;
		decompressed_data[i] = decoded_value;

		if (up_model_buf) {
			up_model_buf[i] = cmp_up_model(decoded_value, model,
						       cfg->model_value, setup.lossy_par);
		}
	}

	return stream_pos;
}


/**
 * @brief decompress the multi-entry packet header structure and sets the data,
 *	model and up_model pointers to the data after the header
 *
 * @param data		pointer to a pointer pointing to the data to be compressed
 * @param model		pointer to a pointer pointing to the model of the data
 * @param up_model	pointer to a pointer pointing to the updated model buffer
 * @param cfg		pointer to the compression configuration structure
 *
 * @returns the bit length of the bitstream on success
 *
 * @note the (void **) cast relies on all pointer types having the same internal
 *	representation which is common, but not universal; http://www.c-faq.com/ptrs/genericpp.html
 */

static int decompress_multi_entry_hdr(void **data, void **model, void **up_model,
				      const struct cmp_cfg *cfg)
{
	if (cfg->buffer_length < MULTI_ENTRY_HDR_SIZE)
		return -1;

	if (*data) {
		if (cfg->icu_output_buf)
			memcpy(*data, cfg->icu_output_buf, MULTI_ENTRY_HDR_SIZE);
		*data = (uint8_t *)*data + MULTI_ENTRY_HDR_SIZE;
	}

	if (*model) {
		if (cfg->icu_output_buf)
			memcpy(*model, cfg->icu_output_buf, MULTI_ENTRY_HDR_SIZE);
		*model = (uint8_t *)*model + MULTI_ENTRY_HDR_SIZE;
	}

	if (*up_model) {
		if (cfg->icu_output_buf)
			memcpy(*up_model, cfg->icu_output_buf, MULTI_ENTRY_HDR_SIZE);
		*up_model = (uint8_t *)*up_model + MULTI_ENTRY_HDR_SIZE;
	}

	return MULTI_ENTRY_HDR_SIZE * CHAR_BIT;
}


/**
 * @brief decompress short normal light flux (S_FX) data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_s_fx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx;
	struct s_fx *data_buf = cfg->input_buf;
	struct s_fx *model_buf = cfg->model_buf;
	struct s_fx *up_model_buf = NULL;
	struct s_fx *next_model_p;
	struct s_fx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.s_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.s_fx, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress S_FX_EFX data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_s_fx_efx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_efx;
	struct s_fx_efx *data_buf = cfg->input_buf;
	struct s_fx_efx *model_buf = cfg->model_buf;
	struct s_fx_efx *up_model_buf = NULL;
	struct s_fx_efx *next_model_p;
	struct s_fx_efx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.s_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.s_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.s_efx, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
							   cfg->model_value, setup_efx.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress short S_FX_NCOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_s_fx_ncob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_ncob;
	struct s_fx_ncob *data_buf = cfg->input_buf;
	struct s_fx_ncob *model_buf = cfg->model_buf;
	struct s_fx_ncob *up_model_buf = NULL;
	struct s_fx_ncob *next_model_p;
	struct s_fx_ncob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.s_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.s_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.s_ncob, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
							      cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
							      cfg->model_value, setup_ncob.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress short S_FX_NCOB_ECOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_s_fx_efx_ncob_ecob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_ncob, setup_efx, setup_ecob;
	struct s_fx_efx_ncob_ecob *data_buf = cfg->input_buf;
	struct s_fx_efx_ncob_ecob *model_buf = cfg->model_buf;
	struct s_fx_efx_ncob_ecob *up_model_buf = NULL;
	struct s_fx_efx_ncob_ecob *next_model_p;
	struct s_fx_efx_ncob_ecob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.s_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.s_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.s_ncob, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.s_efx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ecob, cfg->cmp_par_ecob, cfg->spill_ecob,
				    cfg->round, max_used_bits.s_ecob, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_x, stream_pos,
					  &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_y, stream_pos,
					  &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_y = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
							      cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
							      cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
							   cfg->model_value, setup_efx.lossy_par);
			up_model_buf[i].ecob_x = cmp_up_model(data_buf[i].ecob_x, model.ecob_x,
							      cfg->model_value, setup_ecob.lossy_par);
			up_model_buf[i].ecob_y = cmp_up_model(data_buf[i].ecob_y, model.ecob_y,
							      cfg->model_value, setup_ecob.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress fast normal light flux (F_FX) data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_f_fx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_fx;
	struct f_fx *data_buf = cfg->input_buf;
	struct f_fx *model_buf = cfg->model_buf;
	struct f_fx *up_model_buf = NULL;
	struct f_fx *next_model_p;
	struct f_fx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.f_fx, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		if (up_model_buf)
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress F_FX_EFX data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_f_fx_efx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_fx, setup_efx;
	struct f_fx_efx *data_buf = cfg->input_buf;
	struct f_fx_efx *model_buf = cfg->model_buf;
	struct f_fx_efx *up_model_buf = NULL;
	struct f_fx_efx *next_model_p;
	struct f_fx_efx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.f_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.f_efx, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
							   cfg->model_value, setup_efx.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress short F_FX_NCOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_f_fx_ncob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_fx, setup_ncob;
	struct f_fx_ncob *data_buf = cfg->input_buf;
	struct f_fx_ncob *model_buf = cfg->model_buf;
	struct f_fx_ncob *up_model_buf = NULL;
	struct f_fx_ncob *next_model_p;
	struct f_fx_ncob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.f_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.f_ncob, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
							      cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
							      cfg->model_value, setup_ncob.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress short F_FX_NCOB_ECOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_f_fx_efx_ncob_ecob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_fx, setup_ncob, setup_efx, setup_ecob;
	struct f_fx_efx_ncob_ecob *data_buf = cfg->input_buf;
	struct f_fx_efx_ncob_ecob *model_buf = cfg->model_buf;
	struct f_fx_efx_ncob_ecob *up_model_buf = NULL;
	struct f_fx_efx_ncob_ecob *next_model_p;
	struct f_fx_efx_ncob_ecob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.f_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.f_ncob, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.f_efx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ecob, cfg->cmp_par_ecob, cfg->spill_ecob,
				    cfg->round, max_used_bits.f_ecob, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y, stream_pos,
					  &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_x, stream_pos,
					  &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_y, stream_pos,
					  &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_y = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
				cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
				cfg->model_value, setup_efx.lossy_par);
			up_model_buf[i].ecob_x = cmp_up_model(data_buf[i].ecob_x, model.ecob_x,
				cfg->model_value, setup_ecob.lossy_par);
			up_model_buf[i].ecob_y = cmp_up_model(data_buf[i].ecob_y, model.ecob_y,
				cfg->model_value, setup_ecob.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress long normal light flux (L_FX) data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_l_fx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_fx_var;
	struct l_fx *data_buf = cfg->input_buf;
	struct l_fx *model_buf = cfg->model_buf;
	struct l_fx *up_model_buf = NULL;
	struct l_fx *next_model_p;
	struct l_fx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.l_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.l_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_fx_variance, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx_variance, stream_pos,
					  &setup_fx_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx_variance = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].fx_variance = cmp_up_model(data_buf[i].fx_variance, model.fx_variance,
								   cfg->model_value, setup_fx_var.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress L_FX_EFX data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_l_fx_efx(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_efx, setup_fx_var;
	struct l_fx_efx *data_buf = cfg->input_buf;
	struct l_fx_efx *model_buf = cfg->model_buf;
	struct l_fx_efx *up_model_buf = NULL;
	struct l_fx_efx *next_model_p;
	struct l_fx_efx model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.l_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.l_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.l_efx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_fx_variance, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx_variance, stream_pos,
					  &setup_fx_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx_variance = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
								 cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
							  cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
							   cfg->model_value, setup_efx.lossy_par);
			up_model_buf[i].fx_variance = cmp_up_model(data_buf[i].fx_variance, model.fx_variance,
								   cfg->model_value, setup_fx_var.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress L_FX_NCOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_l_fx_ncob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_ncob,
			     setup_fx_var, setup_cob_var;
	struct l_fx_ncob *data_buf = cfg->input_buf;
	struct l_fx_ncob *model_buf = cfg->model_buf;
	struct l_fx_ncob *up_model_buf = NULL;
	struct l_fx_ncob *next_model_p;
	struct l_fx_ncob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.l_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.l_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.l_ncob, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_fx_variance, cfg))
		return -1;
	if (configure_decoder_setup(&setup_cob_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_cob_variance, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x,
					  stream_pos, &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y,
					  stream_pos, &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx_variance,
					  stream_pos, &setup_fx_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx_variance = decoded_value;

		stream_pos = decode_value(&decoded_value, model.cob_x_variance,
					  stream_pos, &setup_cob_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].cob_x_variance = decoded_value;

		stream_pos = decode_value(&decoded_value, model.cob_y_variance,
					  stream_pos, &setup_cob_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].cob_y_variance = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
				cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
				cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].fx_variance = cmp_up_model(data_buf[i].fx_variance, model.fx_variance,
				cfg->model_value, setup_fx_var.lossy_par);
			up_model_buf[i].cob_x_variance = cmp_up_model(data_buf[i].cob_x_variance, model.cob_x_variance,
				cfg->model_value, setup_cob_var.lossy_par);
			up_model_buf[i].cob_y_variance = cmp_up_model(data_buf[i].cob_y_variance, model.cob_y_variance,
				cfg->model_value, setup_cob_var.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress L_FX_EFX_NCOB_ECOB data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_l_fx_efx_ncob_ecob(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_exp_flags, setup_fx, setup_ncob, setup_efx,
			     setup_ecob, setup_fx_var, setup_cob_var;
	struct l_fx_efx_ncob_ecob *data_buf = cfg->input_buf;
	struct l_fx_efx_ncob_ecob *model_buf = cfg->model_buf;
	struct l_fx_efx_ncob_ecob *up_model_buf = NULL;
	struct l_fx_efx_ncob_ecob *next_model_p;
	struct l_fx_efx_ncob_ecob model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_exp_flags, cfg->cmp_par_exp_flags, cfg->spill_exp_flags,
				    cfg->round, max_used_bits.l_exp_flags, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx, cfg->cmp_par_fx, cfg->spill_fx,
				    cfg->round, max_used_bits.l_fx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ncob, cfg->cmp_par_ncob, cfg->spill_ncob,
				    cfg->round, max_used_bits.l_ncob, cfg))
		return -1;
	if (configure_decoder_setup(&setup_efx, cfg->cmp_par_efx, cfg->spill_efx,
				    cfg->round, max_used_bits.l_efx, cfg))
		return -1;
	if (configure_decoder_setup(&setup_ecob, cfg->cmp_par_ecob, cfg->spill_ecob,
				    cfg->round, max_used_bits.l_ecob, cfg))
		return -1;
	if (configure_decoder_setup(&setup_fx_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_fx_variance, cfg))
		return -1;
	if (configure_decoder_setup(&setup_cob_var, cfg->cmp_par_fx_cob_variance, cfg->spill_fx_cob_variance,
				    cfg->round, max_used_bits.l_cob_variance, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.exp_flags,
					  stream_pos, &setup_exp_flags);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].exp_flags = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx, stream_pos,
					  &setup_fx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_x,
					  stream_pos, &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ncob_y,
					  stream_pos, &setup_ncob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ncob_y = decoded_value;

		stream_pos = decode_value(&decoded_value, model.efx, stream_pos,
					  &setup_efx);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].efx = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_x,
					  stream_pos, &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_x = decoded_value;

		stream_pos = decode_value(&decoded_value, model.ecob_y,
					  stream_pos, &setup_ecob);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].ecob_y = decoded_value;

		stream_pos = decode_value(&decoded_value, model.fx_variance,
					  stream_pos, &setup_fx_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].fx_variance = decoded_value;

		stream_pos = decode_value(&decoded_value, model.cob_x_variance,
					  stream_pos, &setup_cob_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].cob_x_variance = decoded_value;

		stream_pos = decode_value(&decoded_value, model.cob_y_variance,
					  stream_pos, &setup_cob_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].cob_y_variance = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].exp_flags = cmp_up_model(data_buf[i].exp_flags, model.exp_flags,
				cfg->model_value, setup_exp_flags.lossy_par);
			up_model_buf[i].fx = cmp_up_model(data_buf[i].fx, model.fx,
				cfg->model_value, setup_fx.lossy_par);
			up_model_buf[i].ncob_x = cmp_up_model(data_buf[i].ncob_x, model.ncob_x,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].ncob_y = cmp_up_model(data_buf[i].ncob_y, model.ncob_y,
				cfg->model_value, setup_ncob.lossy_par);
			up_model_buf[i].efx = cmp_up_model(data_buf[i].efx, model.efx,
				cfg->model_value, setup_efx.lossy_par);
			up_model_buf[i].ecob_x = cmp_up_model(data_buf[i].ecob_x, model.ecob_x,
				cfg->model_value, setup_ecob.lossy_par);
			up_model_buf[i].ecob_y = cmp_up_model(data_buf[i].ecob_y, model.ecob_y,
				cfg->model_value, setup_ecob.lossy_par);
			up_model_buf[i].fx_variance = cmp_up_model(data_buf[i].fx_variance, model.fx_variance,
				cfg->model_value, setup_fx_var.lossy_par);
			up_model_buf[i].cob_x_variance = cmp_up_model(data_buf[i].cob_x_variance, model.cob_x_variance,
				cfg->model_value, setup_cob_var.lossy_par);
			up_model_buf[i].cob_y_variance = cmp_up_model(data_buf[i].cob_y_variance, model.cob_y_variance,
				cfg->model_value, setup_cob_var.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress N-CAM offset data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_nc_offset(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_mean, setup_var;
	struct nc_offset *data_buf = cfg->input_buf;
	struct nc_offset *model_buf = cfg->model_buf;
	struct nc_offset *up_model_buf = NULL;
	struct nc_offset *next_model_p;
	struct nc_offset model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_mean, cfg->cmp_par_mean, cfg->spill_mean,
				    cfg->round, max_used_bits.nc_offset_mean, cfg))
		return -1;
	if (configure_decoder_setup(&setup_var, cfg->cmp_par_variance, cfg->spill_variance,
				    cfg->round, max_used_bits.nc_offset_variance, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.mean, stream_pos,
					  &setup_mean);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].mean = decoded_value;

		stream_pos = decode_value(&decoded_value, model.variance, stream_pos,
					  &setup_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].variance = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].mean = cmp_up_model(data_buf[i].mean,
				model.mean, cfg->model_value, setup_mean.lossy_par);
			up_model_buf[i].variance = cmp_up_model(data_buf[i].variance,
				model.variance, cfg->model_value, setup_var.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress N-CAM background data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_nc_background(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_mean, setup_var, setup_pix;
	struct nc_background *data_buf = cfg->input_buf;
	struct nc_background *model_buf = cfg->model_buf;
	struct nc_background *up_model_buf = NULL;
	struct nc_background *next_model_p;
	struct nc_background model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_mean, cfg->cmp_par_mean, cfg->spill_mean,
				    cfg->round, max_used_bits.nc_background_mean, cfg))
		return -1;
	if (configure_decoder_setup(&setup_var, cfg->cmp_par_variance, cfg->spill_variance,
				    cfg->round, max_used_bits.nc_background_variance, cfg))
		return -1;
	if (configure_decoder_setup(&setup_pix, cfg->cmp_par_pixels_error, cfg->spill_pixels_error,
				    cfg->round, max_used_bits.nc_background_outlier_pixels, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.mean, stream_pos,
					  &setup_mean);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].mean = decoded_value;

		stream_pos = decode_value(&decoded_value, model.variance, stream_pos,
					  &setup_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].variance = decoded_value;

		stream_pos = decode_value(&decoded_value, model.outlier_pixels, stream_pos,
					  &setup_pix);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].outlier_pixels = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].mean = cmp_up_model(data_buf[i].mean,
				model.mean, cfg->model_value, setup_mean.lossy_par);
			up_model_buf[i].variance = cmp_up_model(data_buf[i].variance,
				model.variance, cfg->model_value, setup_var.lossy_par);
			up_model_buf[i].outlier_pixels = cmp_up_model(data_buf[i].outlier_pixels,
				model.outlier_pixels, cfg->model_value, setup_pix.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress N-CAM smearing data
 *
 * @param cfg	pointer to the compression configuration structure
 *
 * @returns bit position of the last read bit in the bitstream on success;
 *	returns negative on error, returns CMP_ERROR_SMALL_BUF if the bitstream
 *	buffer is too small to read the value from the bitstream
 */

static int decompress_smearing(const struct cmp_cfg *cfg)
{
	size_t i;
	int stream_pos = 0;
	uint32_t decoded_value;
	struct decoder_setup setup_mean, setup_var, setup_pix;
	struct smearing *data_buf = cfg->input_buf;
	struct smearing *model_buf = cfg->model_buf;
	struct smearing *up_model_buf = NULL;
	struct smearing *next_model_p;
	struct smearing model;

	if (model_mode_is_used(cfg->cmp_mode))
		up_model_buf = cfg->icu_new_model_buf;

	stream_pos = decompress_multi_entry_hdr((void **)&data_buf, (void **)&model_buf,
						(void **)&up_model_buf, cfg);

	if (model_mode_is_used(cfg->cmp_mode)) {
		model = model_buf[0];
		next_model_p = &model_buf[1];
	} else {
		memset(&model, 0, sizeof(model));
		next_model_p = data_buf;
	}

	if (configure_decoder_setup(&setup_mean, cfg->cmp_par_mean, cfg->spill_mean,
				    cfg->round, max_used_bits.smearing_mean, cfg))
		return -1;
	if (configure_decoder_setup(&setup_var, cfg->cmp_par_variance, cfg->spill_variance,
				    cfg->round, max_used_bits.smearing_variance_mean, cfg))
		return -1;
	if (configure_decoder_setup(&setup_pix, cfg->cmp_par_pixels_error, cfg->spill_pixels_error,
				    cfg->round, max_used_bits.smearing_outlier_pixels, cfg))
		return -1;

	for (i = 0; ; i++) {
		stream_pos = decode_value(&decoded_value, model.mean, stream_pos,
					  &setup_mean);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].mean = decoded_value;

		stream_pos = decode_value(&decoded_value, model.variance_mean, stream_pos,
					  &setup_var);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].variance_mean = decoded_value;

		stream_pos = decode_value(&decoded_value, model.outlier_pixels, stream_pos,
					  &setup_pix);
		if (stream_pos <= 0)
			return stream_pos;
		data_buf[i].outlier_pixels = decoded_value;

		if (up_model_buf) {
			up_model_buf[i].mean = cmp_up_model(data_buf[i].mean,
				model.mean, cfg->model_value, setup_mean.lossy_par);
			up_model_buf[i].variance_mean = cmp_up_model(data_buf[i].variance_mean,
				model.variance_mean, cfg->model_value, setup_var.lossy_par);
			up_model_buf[i].outlier_pixels = cmp_up_model(data_buf[i].outlier_pixels,
				model.outlier_pixels, cfg->model_value, setup_pix.lossy_par);
		}

		if (i >= cfg->samples-1)
			break;

		model = next_model_p[i];
	}
	return stream_pos;
}


/**
 * @brief decompress the data based on a compression configuration
 *
 * @param cfg	pointer to a compression configuration
 *
 * @note cfg->buffer_length is measured in bytes (instead of samples as by the
 *	compression)
 *
 * @returns the size of the decompressed data on success; returns negative on failure
 */

static int decompressed_data_internal(struct cmp_cfg *cfg)
{
	int data_size, strem_len_bit = -1;

	if (!cfg)
		return -1;

	if (!cfg->icu_output_buf)
		return -1;

	data_size = cmp_cal_size_of_data(cfg->samples, cfg->data_type);
	if (!cfg->input_buf || !data_size)
		return data_size;

	if (model_mode_is_used(cfg->cmp_mode))
		if (!cfg->model_buf)
			return -1;

	if (cfg->cmp_mode == CMP_MODE_RAW) {

		if ((unsigned int)data_size < cfg->buffer_length/CHAR_BIT)
			return -1;

		if (cfg->input_buf) {
			memcpy(cfg->input_buf, cfg->icu_output_buf, data_size);
			if (cmp_input_big_to_cpu_endianness(cfg->input_buf, data_size, cfg->data_type))
				return -1;
			strem_len_bit = data_size * CHAR_BIT;
		}

	} else {
		switch (cfg->data_type) {
		case DATA_TYPE_IMAGETTE:
		case DATA_TYPE_IMAGETTE_ADAPTIVE:
		case DATA_TYPE_SAT_IMAGETTE:
		case DATA_TYPE_SAT_IMAGETTE_ADAPTIVE:
		case DATA_TYPE_F_CAM_IMAGETTE:
		case DATA_TYPE_F_CAM_IMAGETTE_ADAPTIVE:
			strem_len_bit = decompress_imagette(cfg);
			break;
		case DATA_TYPE_S_FX:
			strem_len_bit = decompress_s_fx(cfg);
			break;
		case DATA_TYPE_S_FX_EFX:
			strem_len_bit = decompress_s_fx_efx(cfg);
			break;
		case DATA_TYPE_S_FX_NCOB:
			strem_len_bit = decompress_s_fx_ncob(cfg);
			break;
		case DATA_TYPE_S_FX_EFX_NCOB_ECOB:
			strem_len_bit = decompress_s_fx_efx_ncob_ecob(cfg);
			break;

		case DATA_TYPE_F_FX:
			strem_len_bit = decompress_f_fx(cfg);
			break;
		case DATA_TYPE_F_FX_EFX:
			strem_len_bit = decompress_f_fx_efx(cfg);
			break;
		case DATA_TYPE_F_FX_NCOB:
			strem_len_bit = decompress_f_fx_ncob(cfg);
			break;
		case DATA_TYPE_F_FX_EFX_NCOB_ECOB:
			strem_len_bit = decompress_f_fx_efx_ncob_ecob(cfg);
			break;

		case DATA_TYPE_L_FX:
			strem_len_bit = decompress_l_fx(cfg);
			break;
		case DATA_TYPE_L_FX_EFX:
			strem_len_bit = decompress_l_fx_efx(cfg);
			break;
		case DATA_TYPE_L_FX_NCOB:
			strem_len_bit = decompress_l_fx_ncob(cfg);
			break;
		case DATA_TYPE_L_FX_EFX_NCOB_ECOB:
			strem_len_bit = decompress_l_fx_efx_ncob_ecob(cfg);
			break;

		case DATA_TYPE_OFFSET:
			strem_len_bit = decompress_nc_offset(cfg);
			break;
		case DATA_TYPE_BACKGROUND:
			strem_len_bit = decompress_nc_background(cfg);
			break;
		case DATA_TYPE_SMEARING:
			strem_len_bit = decompress_smearing(cfg);
			break;

		case DATA_TYPE_F_CAM_OFFSET:
		case DATA_TYPE_F_CAM_BACKGROUND:
		case DATA_TYPE_UNKNOWN:
		default:
			strem_len_bit = -1;
			debug_print("Error: Compressed data type not supported.\n");
			break;
		}
	}
	if (strem_len_bit <= 0)
		return -1;

	return data_size;
}


/**
 * @brief decompress a compression entity
 *
 * @param ent			pointer to the compression entity to be decompressed
 * @param model_of_data		pointer to model data buffer (can be NULL if no
 *				model compression mode is used)
 * @param updated_model		pointer to store the updated model for the next model
 *				mode compression (can be the same as the model_of_data
 *				buffer for in-place update or NULL if updated model is not needed)
 * @param decompressed_data	pointer to the decompressed data buffer (can be NULL)
 *
 * @returns the size of the decompressed data on success; returns negative on failure
 */

int decompress_cmp_entiy(struct cmp_entity *ent, void *model_of_data,
			 void *up_model_buf, void *decompressed_data)
{
	int err;
	struct cmp_cfg cfg = {0};

	cfg.model_buf = model_of_data;
	cfg.icu_new_model_buf = up_model_buf;
	cfg.input_buf = decompressed_data;

	if (!ent)
		return -1;

	err = cmp_ent_read_header(ent, &cfg);
	if (err)
		return -1;

	return decompressed_data_internal(&cfg);
}


/**
 * @brief decompress RDCU compressed data without a compression entity header
 *
 * @param compressed_data	pointer to the RDCU compressed data (without a
 *				compression entity header)
 * @param model_of_data		pointer to model data buffer (can be NULL if no
 *				model compression mode is used)
 * @param updated_model		pointer to store the updated model for the next model
 *				mode compression (can be the same as the model_of_data
 *				buffer for in-place update or NULL if updated model is not needed)
 * @param decompressed_data	pointer to the decompressed data buffer (can be NULL)
 *
 * @returns the size of the decompressed data on success; returns negative on failure
 */

int decompress_rdcu_data(uint32_t *compressed_data, const struct cmp_info *info,
			 uint16_t *model_of_data, uint16_t *up_model_buf,
			 uint16_t *decompressed_data)

{
	struct cmp_cfg cfg = {0};

	if (!compressed_data)
		return -1;

	if (!info)
		return -1;

	if (info->cmp_err)
		return -1;

	cfg.data_type = DATA_TYPE_IMAGETTE;
	cfg.model_buf = model_of_data;
	cfg.icu_new_model_buf = up_model_buf;
	cfg.input_buf = decompressed_data;

	cfg.cmp_mode = info->cmp_mode_used;
	cfg.model_value = info->model_value_used;
	cfg.round = info->round_used;
	cfg.spill = info->spill_used;
	cfg.golomb_par = info->golomb_par_used;
	cfg.samples = info->samples_used;
	cfg.icu_output_buf = compressed_data;
	cfg.buffer_length = cmp_bit_to_4byte(info->cmp_size);

	return decompressed_data_internal(&cfg);
}