/* histogram cumulativisation
 *
 * Author: N. Dessipris
 * Written on: 02/08/1990
 * 24/5/95 JC
 *	- tidied up and ANSIfied
 * 20/7/95 JC
 *	- smartened up again
 *	- now works for hists >256 elements
 * 3/3/01 JC
 *	- broken into cum and norm ... helps im_histspec()
 *	- better behaviour for >8 bit hists
 * 31/10/05 JC
 * 	- was broken for vertical histograms, gah
 * 	- neater im_histnorm()
 * 23/7/07
 * 	- eek, off by 1 for more than 1 band hists
 * 12/5/08
 * 	- histcum works for signed hists now as well
 * 24/3/10
 * 	- gtkdoc
 * 	- small cleanups
 * 12/8/13
 * 	- redone im_histcum() as a class, vips_hist_cum()
 */

/*

	This file is part of VIPS.

	VIPS is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
	02110-1301  USA

 */

/*

	These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>

#include <stdio.h>
#include <stdint.h>

#include <vips/vips.h>

#include "phistogram.h"
#include "hist_unary.h"

typedef VipsHistUnary VipsHistCum;
typedef VipsHistUnaryClass VipsHistCumClass;

G_DEFINE_TYPE(VipsHistCum, vips_hist_cum, VIPS_TYPE_HIST_UNARY);

#define ACCUMULATE(ITYPE, OTYPE) \
	{ \
		for (b = 0; b < nb; b++) { \
			ITYPE *p = (ITYPE *) in[0]; \
			OTYPE *q = (OTYPE *) out; \
			OTYPE total; \
\
			total = 0; \
			for (x = b; x < mx; x += nb) { \
				total += p[x]; \
				q[x] = total; \
			} \
		} \
	}

/* Special case for VIPS_FORMAT_INT, to prevent UB.
 */
#define ACCUMULATE_INT64(ITYPE, OTYPE) \
	{ \
		for (b = 0; b < nb; b++) { \
			ITYPE *p = (ITYPE *) in[0]; \
			OTYPE *q = (OTYPE *) out; \
			int64_t total; \
\
			total = 0; \
			for (x = b; x < mx; x += nb) { \
				total += p[x]; \
				q[x] = total; \
			} \
		} \
	}

static void
vips_hist_cum_process(VipsHistogram *histogram,
	VipsPel *out, VipsPel **in, int width)
{
	const int bands = vips_image_get_bands(histogram->ready[0]);
	const int nb =
		vips_band_format_iscomplex(histogram->ready[0]->BandFmt)
		? bands * 2
		: bands;
	int mx = width * nb;

	int x, b;

	switch (vips_image_get_format(histogram->ready[0])) {
	case VIPS_FORMAT_CHAR:
		ACCUMULATE(signed char, signed int);
		break;
	case VIPS_FORMAT_UCHAR:
		ACCUMULATE(unsigned char, unsigned int);
		break;
	case VIPS_FORMAT_SHORT:
		ACCUMULATE(signed short, signed int);
		break;
	case VIPS_FORMAT_USHORT:
		ACCUMULATE(unsigned short, unsigned int);
		break;
	case VIPS_FORMAT_INT:
		ACCUMULATE_INT64(signed int, signed int);
		break;
	case VIPS_FORMAT_UINT:
		ACCUMULATE(unsigned int, unsigned int);
		break;

	case VIPS_FORMAT_FLOAT:
	case VIPS_FORMAT_COMPLEX:
		ACCUMULATE(float, float);
		break;
	case VIPS_FORMAT_DOUBLE:
	case VIPS_FORMAT_DPCOMPLEX:
		ACCUMULATE(double, double);
		break;

	default:
		g_assert_not_reached();
	}
}

/* Save a bit of typing.
 */
#define UC VIPS_FORMAT_UCHAR
#define C VIPS_FORMAT_CHAR
#define US VIPS_FORMAT_USHORT
#define S VIPS_FORMAT_SHORT
#define UI VIPS_FORMAT_UINT
#define I VIPS_FORMAT_INT
#define F VIPS_FORMAT_FLOAT
#define X VIPS_FORMAT_COMPLEX
#define D VIPS_FORMAT_DOUBLE
#define DX VIPS_FORMAT_DPCOMPLEX

static const VipsBandFormat vips_hist_cum_format_table[10] = {
	/* Band format:  UC  C  US  S  UI  I  F  X  D  DX */
	/* Promotion: */ UI, I, UI, I, UI, I, F, F, D, D
};

static void
vips_hist_cum_class_init(VipsHistCumClass *class)
{
	VipsObjectClass *object_class = (VipsObjectClass *) class;
	VipsHistogramClass *hclass = VIPS_HISTOGRAM_CLASS(class);

	object_class->nickname = "hist_cum";
	object_class->description = _("form cumulative histogram");

	hclass->format_table = vips_hist_cum_format_table;
	hclass->process = vips_hist_cum_process;
}

static void
vips_hist_cum_init(VipsHistCum *hist_cum)
{
}

/**
 * vips_hist_cum: (method)
 * @in: input image
 * @out: (out): output image
 * @...: `NULL`-terminated list of optional named arguments
 *
 * Form cumulative histogram.
 *
 * ::: seealso
 *     [method@Image.hist_norm].
 *
 * Returns: 0 on success, -1 on error
 */
int
vips_hist_cum(VipsImage *in, VipsImage **out, ...)
{
	va_list ap;
	int result;

	va_start(ap, out);
	result = vips_call_split("hist_cum", ap, in, out);
	va_end(ap);

	return result;
}
