void oph_aggregate_operator_add(UDF_INIT * initid, UDF_ARGS * args, char *is_null, char *error)
{
	if (*error || !args->args[2])
		return;

	//Assume that:
	//1. Type of each element is the same
	//2. Length of each array is the same
	oph_string measure;
	oph_agg_oper_data *dat = (oph_agg_oper_data *) initid->ptr;
	oph_requestPtr res = (oph_requestPtr) & (dat->result);

	/* Setting of the aggregate result */
	if (!res->measure.content) {
		//It's the first row 
		core_set_type(&(res->measure), args->args[0], &(args->lengths[0]));

		if (args->arg_count < 4) {
			core_set_oper(res, NULL, 0);
		} else {
			core_set_oper(res, args->args[3], &(args->lengths[3]));
		}

		if (!res->oper) {
			pmesg(1, __FILE__, __LINE__, "Operator not recognized\n");
			*is_null = 0;
			*error = 1;
			return;
		}
		core_set_hier(res, NULL, 0);
		res->measure.length = malloc(sizeof(unsigned long));
		if (!res->measure.length) {
			pmesg(1, __FILE__, __LINE__, "Error allocating result string\n");
			*is_null = 0;
			*error = 1;
			return;
		}
		*(res->measure.length) = args->lengths[2];

		core_set_elemsize(&(res->measure));

		if (core_set_numelem(&(res->measure))) {
			pmesg(1, __FILE__, __LINE__, "Error on counting elements\n");
			*is_null = 0;
			*error = 1;
			return;
		}

		res->measure.content = (char *) malloc(*(res->measure.length));
		if (!res->measure.content) {
			pmesg(1, __FILE__, __LINE__, "Error allocating result string\n");
			*is_null = 0;
			*error = 1;
			return;
		}

		switch (res->oper) {
			case OPH_COUNT:
				dat->core_oph_oper = core_oph_count_array;
				break;
			case OPH_MAX:
				dat->core_oph_oper = core_oph_max_array;
				break;
			case OPH_MIN:
				dat->core_oph_oper = core_oph_min_array;
				break;
			case OPH_SUM:
				dat->core_oph_oper = core_oph_sum_array;
				break;
			case OPH_AVG:
				dat->core_oph_oper = NULL;
				break;
			default:
				pmesg(1, __FILE__, __LINE__, "Unable to recognize operator\n");
				*is_null = 0;
				*error = 1;
				return;
		}

		oph_string output_array;
		core_set_type(&output_array, args->args[1], &(args->lengths[1]));
		if (!output_array.type) {
			pmesg(1, __FILE__, __LINE__, "Unable to recognize measures type\n");
			*is_null = 0;
			*error = 1;
			return;
		}
		if (core_set_elemsize(&output_array)) {
			pmesg(1, __FILE__, __LINE__, "Unable to recognize measures type\n");
			*is_null = 0;
			*error = 1;
			return;
		}
		dat->result_size = output_array.elemsize;
		dat->result_type = output_array.type;
		dat->result_length = res->measure.numelem * dat->result_size;
		dat->result_data = (char *) malloc(dat->result_length);
		if (!dat->result_data) {
			pmesg(1, __FILE__, __LINE__, "Error allocating result string\n");
			*is_null = 0;
			*error = 1;
			return;
		}
	}

	if (!dat->count && (res->oper == OPH_AVG)) {
		dat->count = (int *) malloc(res->measure.numelem * sizeof(int));
		if (!dat->count) {
			pmesg(1, __FILE__, __LINE__, "Error allocating result string\n");
			*is_null = 0;
			*error = 1;
			return;
		}
		size_t i;
		for (i = 0; i < res->measure.numelem; ++i)
			dat->count[i] = 0;
	}

	measure.type = res->measure.type;
	measure.content = args->args[2];
	measure.length = res->measure.length;
	measure.elemsize = res->measure.elemsize;
	measure.numelem = res->measure.numelem;

	double missingvalue;
	if ((args->arg_count > 4) && args->args[4]) {
		missingvalue = *((double *) (args->args[4]));
		measure.missingvalue = &missingvalue;
	} else
		measure.missingvalue = NULL;

	if (res->oper == OPH_AVG) {
		if (core_oph_sum_array2(&measure, dat->first ? NULL : &(res->measure), res->measure.content, dat->count)) {
			pmesg(1, __FILE__, __LINE__, "Unable to compute result\n");
			*is_null = 0;
			*error = 1;
			return;
		}
	} else if (dat->first && (res->oper != OPH_COUNT)) {
		//It's the first row or the first row in the group of the GRUOP BY clause
		//Only perform the copy of the tuple
		memcpy(res->measure.content, args->args[2], *(res->measure.length));
	} else if (dat->core_oph_oper(&measure, dat->first ? NULL : &(res->measure), res->measure.content)) {
		pmesg(1, __FILE__, __LINE__, "Unable to compute result\n");
		*is_null = 0;
		*error = 1;
		return;
	}

	dat->first = 0;
}
char* oph_reduce2(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error)
{
        oph_request inp_req;

        long long hierarchy_set = 0;
        long long result_length = 0;
	long long block_size = 1, numelement_to_reduce, max_size = 0;
        int i = 0, j;
	unsigned long lll, olll, k;

	int (*core_oph_oper) (oph_stringPtr byte_array, char *res);

	if (args->arg_count > 5)
	{
		block_size = *((long long*)(args->args[5]));
		if(block_size<=0)
		{
		        pmesg(1,  __FILE__, __LINE__, "Wrong arguments to oph_reduce2 function\n");
		        *length=0;
		        *is_null=0;
		        *error=1;
		        return NULL;
		}
	}

        core_set_type(&(inp_req.measure), args->args[0], &(args->lengths[0]));
	if(!inp_req.measure.type){
                pmesg(1,  __FILE__, __LINE__, "Unable to recognize measures type\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

        if(args->arg_count < 4){
                core_set_oper(&inp_req, NULL, 0);
        }
        else{
                core_set_oper(&inp_req, args->args[3], &(args->lengths[3]));
        }

	if(!inp_req.oper){
                pmesg(1,  __FILE__, __LINE__, "Operator not recognized\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

        core_set_hier(&inp_req, NULL, 0);
	if(!inp_req.hier){
                pmesg(1,  __FILE__, __LINE__, "Hierarchy not recognized\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

        inp_req.measure.content = args->args[2];
        inp_req.measure.length = &(args->lengths[2]);

	if(core_set_elemsize(&(inp_req.measure))){
                pmesg(1,  __FILE__, __LINE__, "Unable to recognize measures type\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

        if(core_set_numelem(&(inp_req.measure))){
        	pmesg(1,  __FILE__, __LINE__, "Error on counting elements\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

	if (args->arg_count < 6)
		numelement_to_reduce = (long long)inp_req.measure.numelem;
	else
	{
		if(((long long)inp_req.measure.numelem) % block_size)
		{
			pmesg(1,  __FILE__, __LINE__, "Error: 'block_size' %lld not applicable on %ld elements\n", block_size, inp_req.measure.numelem);
		        *length=0;
		        *is_null=0;
		        *error=1;
		        return NULL;
		}
		numelement_to_reduce = (long long)inp_req.measure.numelem / block_size;
	}

	if (args->arg_count > 6) max_size = *((long long*)(args->args[6]));
	if (!max_size) max_size = numelement_to_reduce;

        if((args->arg_count < 5) || !args->args[4])
                hierarchy_set = (long long) (inp_req.measure.numelem);
        else
	{
		hierarchy_set = *((long long*)(args->args[4]));
		if (!hierarchy_set || (hierarchy_set>max_size)) hierarchy_set = max_size;
		else if ((hierarchy_set<0) || (max_size%hierarchy_set)) 
		{
			pmesg(1,  __FILE__, __LINE__, "Wrong parameter value 'count' %lld\n",hierarchy_set);
		        *length=0;
		        *is_null=0;
		        *error=1;
		        return NULL;
		}
        }

        /* Check if I can apply the hierarchy */
        if(numelement_to_reduce%hierarchy_set)
	{
        	pmesg(1,  __FILE__, __LINE__, "Error: hierarchy_set %lld not applicable on %ld elements\n", hierarchy_set, inp_req.measure.numelem);
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }

        result_length = inp_req.measure.numelem/hierarchy_set;

	oph_type result_type = inp_req.measure.type;
	size_t result_size = inp_req.measure.elemsize;
        switch(inp_req.oper)
	{
                case OPH_COUNT:
			core_oph_oper = core_oph_count;
			break;
                case OPH_MAX:
			core_oph_oper = core_oph_max;
			break;
                case OPH_MIN:
			core_oph_oper = core_oph_min;
			break;
                case OPH_SUM:
			core_oph_oper = core_oph_sum;
			break;
		case OPH_AVG:
			core_oph_oper = core_oph_avg;
			break;
		case OPH_STD:
			core_oph_oper = core_oph_std;
			break;
		case OPH_VAR:
			core_oph_oper = core_oph_var;
			break;
		case OPH_CMOMENT:
			core_oph_oper = core_oph_cmoment;
			break;
		case OPH_ACMOMENT:
			core_oph_oper = core_oph_acmoment;
			break;
		case OPH_RMOMENT:
			core_oph_oper = core_oph_rmoment;
			break;
		case OPH_ARMOMENT:
			core_oph_oper = core_oph_armoment;
			break;
		case OPH_QUANTILE:
			core_oph_oper = core_oph_quantile;
			break;
		default:
                        pmesg(1,  __FILE__, __LINE__, "Unable to recognize operator\n");
                        *length=0;
                        *is_null=0;
                        *error=1;
                        return NULL;
        }
	switch(inp_req.oper)
	{
		case OPH_COUNT:
			result_type = OPH_LONG;
			result_size = core_sizeof(OPH_LONG);
			break;
		case OPH_AVG:
		case OPH_STD:
		case OPH_VAR:
		case OPH_CMOMENT:
		case OPH_ACMOMENT:
		case OPH_RMOMENT:
		case OPH_ARMOMENT:
		case OPH_QUANTILE:
			if(inp_req.measure.type == OPH_BYTE || inp_req.measure.type == OPH_SHORT || inp_req.measure.type == OPH_INT)
			{
				result_type = OPH_FLOAT;
				result_size = core_sizeof(OPH_FLOAT);
			}
			else if(inp_req.measure.type == OPH_LONG)
			{
				result_type = OPH_DOUBLE;
				result_size = core_sizeof(OPH_DOUBLE);
			}
			break;
		default:;
	}

	oph_string output_array;
        core_set_type(&output_array, args->args[1], &(args->lengths[1]));
	if(!output_array.type){
                pmesg(1,  __FILE__, __LINE__, "Unable to recognize measures type\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }
	if(core_set_elemsize(&output_array)){
                pmesg(1,  __FILE__, __LINE__, "Unable to recognize measures type\n");
                *length=0;
                *is_null=0;
                *error=1;
                return NULL;
        }
	olll = result_length*output_array.elemsize;

        /* Allocate the right space for the result set */
	oph_reduce2_param* tmp;
        if(!initid->ptr)
	{
                tmp = (oph_reduce2_param*)malloc(sizeof(oph_reduce2_param));
		initid->ptr = (char*)tmp;
		if(!initid->ptr)
		{
			pmesg(1,  __FILE__, __LINE__, "Error allocating measures string\n");
	                *length=0;
		        *is_null=0;
        		*error=1;
                	return NULL;
		}
		tmp->result = (char *)malloc(olll);
		if(!tmp->result)
		{
			pmesg(1,  __FILE__, __LINE__, "Error allocating measures string\n");
	                *length=0;
		        *is_null=0;
        		*error=1;
                	return NULL;
		}
		if (block_size>1)
		{
			tmp->temp = (char *)malloc(hierarchy_set*inp_req.measure.elemsize);
			if(!tmp->temp)
			{
				pmesg(1,  __FILE__, __LINE__, "Error allocating measures string\n");
			        *length=0;
				*is_null=0;
				*error=1;
		        	return NULL;
			}
		}
		else tmp->temp = NULL;
		if (inp_req.oper == OPH_QUANTILE)
		{
			tmp->temp2 = (char *)malloc(*inp_req.measure.length);
			if(!tmp->temp2)
			{
				pmesg(1,  __FILE__, __LINE__, "Error allocating measures string\n");
				*length=0;
				*is_null=0;
				*error=1;
				return NULL;
			}
		}
		else tmp->temp2 = NULL;
        }
	else tmp = (oph_reduce2_param*)initid->ptr;

	oph_string curr_array;
        curr_array.type = inp_req.measure.type;
        curr_array.elemsize = inp_req.measure.elemsize;
        curr_array.numelem = hierarchy_set;
        curr_array.length = &lll;
        *curr_array.length = (unsigned long)(hierarchy_set*curr_array.elemsize);
	if(args->arg_count > 7)
	{
		curr_array.param = *((double*)(args->args[7]));
		if (curr_array.param<0)
		{
			pmesg(1,  __FILE__, __LINE__, "Wrong parameter value 'order' %f\n",curr_array.param);
		        *length=0;
		        *is_null=0;
		        *error=1;
		        return NULL;
		}
	}
	else curr_array.param = 2.0;
	curr_array.extend = (void*)tmp->temp2;

	char temporary[result_size];
        for(i=0; i < result_length; i++)
	{
		if (block_size==1) curr_array.content = inp_req.measure.content+(i*(*curr_array.length));
		else
		{
			for (j=0, k=0; k<inp_req.measure.numelem; ++k) // Loop on the input array
				if (k%block_size + (k/(block_size*hierarchy_set))*block_size == i)
				{
					memcpy(tmp->temp+j*inp_req.measure.elemsize,inp_req.measure.content+k*inp_req.measure.elemsize,inp_req.measure.elemsize);
					++j;
					if (j>=hierarchy_set) break; // Found all the elements to be reduced
				}
			if (k>=inp_req.measure.numelem)
			{
				pmesg(1,  __FILE__, __LINE__, "Unable to find an element to be aggregated\n");
				*length=0;
				*is_null=0;
				*error=1;
				return NULL;
			}
			curr_array.content = tmp->temp;
		}
                if(core_oph_oper(&curr_array, temporary))
		{
                        pmesg(1,  __FILE__, __LINE__, "Unable to find result\n");
                        *length=0;
                        *is_null=0;
                        *error=1;
			return NULL;
                }
		if(core_oph_type_cast(temporary, tmp->result+i*output_array.elemsize, result_type, output_array.type))
		{
                        pmesg(1,  __FILE__, __LINE__, "Unable to find result\n");
                        *length=0;
                        *is_null=0;
                        *error=1;
			return NULL;
                }
                pmesg(3,  __FILE__, __LINE__, "RES: %f\n", *(double*)(tmp->result));
        }
        *length = (unsigned long) (olll);

	*is_null=0;
	*error=0;

        return tmp->result;
}
char *oph_operator(UDF_INIT * initid, UDF_ARGS * args, char *result, unsigned long *length, char *is_null, char *error)
{
	if (*error) {
		*length = 0;
		*is_null = 0;
		*error = 1;
		return NULL;
	}
	if (*is_null || !args->lengths[2]) {
		*length = 0;
		*is_null = 1;
		*error = 0;
		return NULL;
	}

	oph_generic_param_multi *param;
	if (!initid->ptr) {
		param = (oph_generic_param_multi *) malloc(sizeof(oph_generic_param_multi));
		if (!param) {
			pmesg(1, __FILE__, __LINE__, "Error in allocating parameters\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		param->measure = NULL;
		param->result = NULL;
		param->error = 0;
		param->extend = NULL;

		initid->ptr = (char *) param;
	} else
		param = (oph_generic_param_multi *) initid->ptr;

	if (param->error) {
		*length = 0;
		*is_null = 0;
		*error = 1;
		return NULL;
	}

	oph_multistring *measure;
	if (!param->error && !param->measure) {
		if (core_set_oph_multistring(&measure, args->args[0], &(args->lengths[0]))) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Error setting measure structure\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		if (!measure->islast) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Wrong number of input measure\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		measure->length = args->lengths[2];
		if (measure->length % measure->blocksize) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Wrong input type or data corrupted\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		measure->numelem = measure->length / measure->blocksize;

		oph_request req;

		if (args->arg_count < 4)
			core_set_oper(&req, NULL, 0);
		else
			core_set_oper(&req, args->args[3], &(args->lengths[3]));
		if (!req.oper) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Operator not recognized\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		switch (req.oper) {
			case OPH_COUNT:
				param->core_oph_oper_multi = core_oph_count_multi;
				break;
			case OPH_MAX:
				param->core_oph_oper_multi = core_oph_max_multi;
				break;
			case OPH_MIN:
				param->core_oph_oper_multi = core_oph_min_multi;
				break;
			case OPH_SUM:
				param->core_oph_oper_multi = core_oph_sum_multi;
				break;
			case OPH_AVG:
				param->core_oph_oper_multi = core_oph_avg_multi;
				break;
			case OPH_STD:
				param->core_oph_oper_multi = core_oph_std_multi;
				break;
			case OPH_CMOMENT:
				param->core_oph_oper_multi = core_oph_cmoment_multi;
				break;
			case OPH_ACMOMENT:
				param->core_oph_oper_multi = core_oph_acmoment_multi;
				break;
			case OPH_RMOMENT:
				param->core_oph_oper_multi = core_oph_rmoment_multi;
				break;
			case OPH_ARMOMENT:
				param->core_oph_oper_multi = core_oph_armoment_multi;
				break;
			default:
				param->error = 1;
				pmesg(1, __FILE__, __LINE__, "Unable to recognize operator\n");
				*length = 0;
				*is_null = 0;
				*error = 1;
				return NULL;
		}

		if (args->arg_count < 5)
			core_set_hier(&req, NULL, 0);
		else
			core_set_hier(&req, args->args[4], &(args->lengths[4]));
		if (!req.hier) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Hierarchy not recognized\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		switch (req.hier) {
			case OPH_ALL:
				param->extend = (void *) 1;
				break;
			default:
				param->error = 1;
				pmesg(1, __FILE__, __LINE__, "Unable to recognize hierarchy\n");
				*length = 0;
				*is_null = 0;
				*error = 1;
				return NULL;
		}

		if (args->arg_count > 5) {
			measure->param = *((double *) (args->args[5]));
			if (measure->param < 0) {
				param->error = 1;
				pmesg(1, __FILE__, __LINE__, "Wrong parameter value 'order' %f\n", measure->param);
				*length = 0;
				*is_null = 0;
				*error = 1;
				return NULL;
			}
		} else
			measure->param = 2.0;

		param->measure = measure;
	} else
		measure = param->measure;

	double missingvalue;
	if ((args->arg_count > 6) && args->args[6]) {
		missingvalue = *((double *) (args->args[6]));
		measure->missingvalue = &missingvalue;
	} else
		measure->missingvalue = NULL;

	measure->content = args->args[2];

	oph_multistring *output;
	if (!param->error && !param->result) {
		if (core_set_oph_multistring(&output, args->args[1], &(args->lengths[1]))) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Error setting measure structure\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		if (output->num_measure != measure->num_measure) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Wrong number of output data type\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}
		output->numelem = param->extend ? 1 : 0;
		output->length = output->numelem * output->blocksize;
		if (!output->length) {
			*length = 0;
			*is_null = 1;
			*error = 0;
			return NULL;
		}
		output->content = (char *) malloc(output->length);
		if (!output->content) {
			param->error = 1;
			pmesg(1, __FILE__, __LINE__, "Error allocating measures string\n");
			*length = 0;
			*is_null = 0;
			*error = 1;
			return NULL;
		}

		param->result = output;
	} else
		output = param->result;

	if (!param->error && param->core_oph_oper_multi(param->measure, param->result)) {
		param->error = 1;
		pmesg(1, __FILE__, __LINE__, "Unable to compute result\n");
		*length = 0;
		*is_null = 0;
		*error = 1;
		return NULL;
	}

	*length = output->length;
	*error = 0;
	*is_null = 0;

	return (result = output->content);
}