/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/*
 * JIGL--Java Imaging and Graphics Library
 * Copyright (C)1999 Brigham Young University
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * This library 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
 * Library General Public License for more details.
 * A copy of the GNU Library General Public Licence is contained in
 * /jigl/licence.txt
 */




package jm.jigl; // Modifizierte Paketstruktur [JM]
import java.*;



/**
 * A discrete signal is a 1-d of floats
 *
 * RealSignal implements Signal
 * @version 1.3, Modifikation der urspruenglichen jigl Paketstruktur durch Jens Martin
 * @see jigl.signal.RealSignal
 */
public class RealSignal implements Signal {


	/**
	 * One dimensional float array
	 */
	protected float[] data;


	/**
	 * Length of the Signal
	 */
	protected int			length;


	/**
	 * Creates an empty one dimensional RealSignal with a height and width of zero
	 */
	public RealSignal() {
		length = 0;
		data = null;
	}


	/**
	 * Creates an empty one dimensional of length x
	 */
	public RealSignal(int x) {
		length = x;
		data = new float[length];
	}


	/**
	 * Creates a one dimensional RealSignal (shallow copy) for a RealSignal
	 */
	public RealSignal(RealSignal s) {
		length = s.length();
		data = s.data();
	}


	/**
	 * Makes a deep copy of this signal
	 * @param none
	 * @return a deep copy of RealSignal
	 */
	public Signal copy() {
		RealSignal	s = new RealSignal(length);

		s.length = length;
		for (int x = 0; x < length; x++) {
			s.data[x] = data[x];
		}
		return s;
	}


	/**
	 * Returns the length of this signal
	 */
	public final int length() {
		return length;
	}


	/**
	 * Makes a shallow copy of a JIGL signal's sample buffer
	 * @param none
	 * @return a pointer to RealSignal
	 */
	public final float[] data() {
		return data;
	}


	/**
	 * Returns the sample value at the given x, y value
	 * @param x  the X coordinant
	 */
	public final float get(int x) {
		return (float) data[x];
	}


	/**
	 * Sets the sample value at x, y to a given value
	 * @param x the X coordinant
	 * @param value the value to set the sample to
	 */
	public final void set(int x, float value) {
		data[x] = (float) value;
	}


	/**
	 * Clears the signal to zero
	 * @param none
	 */
	public final RealSignal clear() {
		clear(0);
		return this;
	}


	/**
	 * Clears to constant value
	 * @param val the value to "clear" the signal to
	 */
	public final RealSignal clear(float val) {
		for (int x = 0; x < length; x++) {
			data[x] = (float) val;
		}
		return this;
	}


	/**
	 * Adds a value to a single sample
	 * @param x X-coordinant
	 * @param value the value to add to the sample
	 */
	public final void add(int x, float value) {
		data[x] += (float) value;
	}


	/**
	 * Subtracts a value from a single sample
	 * @param x X-coordinant
	 * @param value the value to subtract from the sample
	 */
	public final void subtract(int x, float value) {
		data[x] -= (float) value;
	}


	/**
	 * Mutiplies a single sample by a value
	 * @param x X-coordinant
	 * @param value - the value to mutiply to the sample
	 */
	public final void multiply(int x, float value) {
		data[x] *= (float) value;
	}


	/**
	 * Divides a single sample by a value
	 * @param x X-coordinant
	 * @param value - the value to mutiply to the sample
	 */
	public final void divide(int x, float value) {
		data[x] /= (float) value;
	}


	/**
	 * Finds the minimum value of this signal
	 * @param none
	 * @return an float containing the minimum value
	 */
	public final float min() {
		float p;
		float min = Short.MAX_VALUE;

		for (int x = 0; x < length; x++) {
			p = data[x];
			if (p < min) {
				min = p;
			}
		}
		return (float) min;
	}


	/**
	 * Finds the maximum value of this signal
	 * @param none
	 * @return an float containing the maximum value
	 */
	public final float max() {
		float p;
		float max = Short.MIN_VALUE;

		for (int x = 0; x < length; x++) {
			p = data[x];
			if (p > max) {
				max = p;
			}
		}
		return (float) max;
	}


	/**
	 * Adds a value to all the samples in this signal
	 * @param v value to be added to the samples
	 * @return this
	 */
	public final RealSignal add(float v) {
		float sv = (float) v;

		for (int x = 0; x < length; x++) {
			data[x] += sv;
		}
		return this;
	}


	/**
	 * Makes a copy of this image with a buffer so the resulting image has a width x and height y
	 * @param none
	 * @return a deep copy of GrayImage
	 */
	public RealSignal addbuffer(int w, int color) {
		int					Y = length;
		RealSignal	g = new RealSignal(w);

		for (int y = 0; y < Y; y++) {
			if ((y < Y)) {
				g.data[y] = data[y];
			} else {
				g.data[y] = (short) color;
			}
		}
		return g;
	}


	/**
	 * Subtracts a value from all the samples in this signal
	 * @param v value to be added to the samples
	 * @return this
	 */
	public final RealSignal subtract(float v) {
		float sv = (float) v;

		for (int x = 0; x < length; x++) {
			data[x] -= sv;
		}
		return this;
	}


	/**
	 * Multiplies all the samples in this signal by a value
	 * @param v value to be added to the samples
	 * @return this
	 */
	public final RealSignal multiply(float v) {
		float sv = (float) v;

		for (int x = 0; x < length; x++) {
			data[x] *= sv;
		}
		return this;
	}


	/**
	 * Divides all the samples in this signal by a value
	 * @param v value to be added to the samples
	 * @return this
	 */
	public final RealSignal divide(float v) {
		float sv = (float) v;

		for (int x = 0; x < length; x++) {
			data[x] /= sv;
		}
		return this;
	}


	/**
	 * Adds another RealSignal to this signal
	 * @param im the RealSignal to add
	 * @return this
	 */
	public final RealSignal add(RealSignal s) {
		for (int x = 0; x < length; x++) {
			data[x] += s.get(x);
		}
		return this;
	}


	/**
	 * Subtracts a RealSignal from this signal
	 * @param im the RealSignal to subtract
	 * @return this
	 */
	public final RealSignal subtract(RealSignal s) {
		for (int x = 0; x < length; x++) {
			data[x] -= s.get(x);
		}
		return this;
	}


	/**
	 * Subtracts the second signal from the first and returns the absolute value
	 */
	public final RealSignal diff(RealSignal s) {
		for (int x = 0; x < length; x++) {
			data[x] -= s.get(x);
			if (data[x] < 0) {
				data[x] = (short) -data[x];
			}
		}
		return this;
	}


	/**
	 * Multiplies a RealSignal by this signal
	 * @param im the RealSignal to multiply
	 * @return this
	 */
	public final RealSignal multiply(RealSignal s) {
		for (int x = 0; x < length; x++) {
			data[x] *= s.get(x);
		}
		return this;
	}


	/**
	 * Divides this signal by a RealSignal
	 * @param im the RealSignal to divide
	 * @return this
	 */
	public final RealSignal divide(RealSignal s) {
		for (int x = 0; x < length; x++) {
			data[x] /= s.get(x);
		}
		return this;
	}


	/**
	 * Prints the string in float format.
	 * <DT><DL><DL>-Example of output on an signal with width 100 and height 120:</DT>
	 * <DL>       <DT>100 : 120</DT>
	 * <DT>10 20 32 12 32 56 40 59 42 39 43 ...</DT></DL></DL></DL>
	 */
	public String toString() {
		String	str = length + "\n";

		for (int x = 0; x < length; x++) {
			str += data[x] + " ";
		}
		str += "\n";
		return str;
	}


	/**
	 * Scales the range of this signal to byte (0..255)
	 * @param none
	 */
	public void byteSize() {

		// get range of this signal
		double	min = min();
		double	max = max();

		// keep byte signals in original range

		double	range = max - min;

		// convert to byte depth
		int			value = 0;

		for (int x = 0; x < length; x++) {
			value = (int) ((255.0 / range) * ((double) data[x] - min));
			value = 0x00FF & value;
			data[x] = (float) value;
		}

	}


	/**
	 * Clips the range of this signal to an arbitrary min/max
	 * @param min minimum value
	 * @param max maximum value
	 */
	public final void clip(int min, int max) {

		// clip
		float value = 0;

		for (int x = 0; x < length; x++) {
			value = data[x];
			value = (value > max) ? max : value;
			value = (value < min) ? min : value;
			data[x] = (float) value;
		}
	}


	/**
	 * Performs convolution in place with a kernel signal on this signal.
	 * @param kernel kernel to perform the convolution with
	 */
	public void convolve(RealSignal kernel) {

		int			Num = kernel.length();
		int			mid = Num / 2;
		double	sum = 0;
		float[] value = new float[length];
		float[] kern = ConvertSignal.toRealDiscrete(kernel).data;

		// find the sum of all values in the kernel
		for (int x = 0; x < Num; x++) {
			sum += kern[x];
		}

		// if the sum is not zero then normalize by the sum
		if (sum != 0) {
			for (int x = 0; x < Num; x++) {
				kern[x] /= sum;
			}
		}

		// for every sample in the original signal
		for (int x = 0; x < length; x++) {

			// Convolve with the kernel
			sum = 0;
			for (int i = -mid; i <= mid; i++) {
				try {
					sum += data[x + i] * kern[mid + i];
				} catch (Exception e) {

					// ignore out of bounds samples
				}
			}
			value[x] = (float) sum;
		}

		this.data = value;
	}


	/**
	 * Method declaration
	 *
	 *
	 * @param vals
	 * @param size
	 *
	 * @return
	 *
	 * @see
	 */
	private double[] sort(double vals[], int size) {
		int			i, j;
		double	temp;

		for (i = 0; i < size; i++) {
			for (j = 0; j < size - 1; j++) {
				try {
					if (vals[j] > vals[j + 1]) {
						temp = vals[j];
						vals[j] = vals[j + 1];
						vals[j + 1] = temp;
					}
				} catch (Exception e) {}
			}
		}
		return vals;
	}


	/**
	 * Performs median filter on this signal
	 * @param size the size of the median filter
	 */
	public void median(int size) {

		int			Num = (size > length) ? length : size;
		int			mid = Num / 2;
		double	value[] = new double[Num * Num];
		int			count;

		// for every sample in the original signal
		for (int x = 0; x < length; x++) {

			// find median value
			count = 0;
			for (int i = -mid; i <= mid; i++) {
				try {
					value[count++] = data[x + i];
				} catch (Exception e) {

					// ignore out of bounds samples
				}
			}
			value = sort(value, count);
			data[x] = (float) value[count / 2];
		}

	}


	/**
	 * *********************************************************************************
	 * ************************    ROI Stuff   *****************************************
	 * *******************************************************************************
	 */


	/**
	 * Makes a deep copy in a Region of Interest
	 * @param r Region of Interest
	 * @return a deep copy of a Region of Interest
	 */
	public Signal copy(ROI r) {
		RealSignal	s = new RealSignal((r.lx() - r.ux()));

		s.length = length;
		for (int x = r.ux(); x < r.lx(); x++) {
			s.data[x] = data[x];
		}
		return s;
	}


	/**
	 * Returns the sample value at the given x, y value
	 * @param x  the X coordinant
	 */

	public final float get(int x, ROI r) {
		return (float) data[(x + r.ux())];
	}


	/**
	 * Sets the sample value at x, y to a given value
	 * @param x the X coordinant
	 * @param value the value to set the sample to
	 */
	public final void set(int x, float value, ROI r) {
		data[(x + r.ux())] = (float) value;
	}


	/**
	 * Clears the signal to zero
	 * @param r Region of Interest
	 */
	public final RealSignal clear(ROI r) {
		clear(0);
		return this;
	}


	/**
	 * Clears to constant value
	 * @param val the value to "clear" the signal to
	 * @param r Region of Interest
	 */

	public final RealSignal clear(float val, ROI r) {
		for (int x = r.ux(); x < r.lx(); x++) {
			data[x] = (float) val;
		}
		return this;
	}


	/**
	 * Adds a value to a single sample
	 * @param x X-coordinant
	 * @param value the value to add to the sample
	 * @param r Region of Interest
	 */
	public final void add(int x, float value, ROI r) {
		data[(x + r.ux())] += (float) value;
	}


	/**
	 * Subtracts a value from a single sample
	 * @param x X-coordinant
	 *
	 * @param value the value to subtract from the sample
	 * @param r Region of Interest
	 */

	public final void subtract(int x, float value, ROI r) {
		data[(x + r.ux())] -= (float) value;
	}


	/**
	 * Mutiplies a single sample by a value
	 * @param x X-coordinant
	 *
	 * @param value - the value to mutiply to the sample
	 * @param r Region of Interest
	 */
	public final void multiply(int x, float value, ROI r) {
		data[(x + r.ux())] *= (float) value;
	}


	/**
	 * Divides a single sample by a value
	 * @param x X-coordinant
	 *
	 * @param value - the value to mutiply to the sample
	 * @param r Region of Interest
	 */
	public final void divide(int x, float value, ROI r) {
		data[(x + r.ux())] /= (float) value;
	}


	/**
	 * Finds the minimum value of in a Region of Interest
	 * @param r Region of Interest
	 * @return an float containing the minimum value
	 */
	public final float min(ROI r) {
		float p;
		float min = Short.MAX_VALUE;

		for (int x = r.ux(); x < r.lx(); x++) {
			p = data[x];
			if (p < min) {
				min = p;
			}
		}
		return (float) min;
	}


	/**
	 * Finds the maximum value of in a Region of Interest
	 * @param r Region of Interest
	 * @return an float containing the maximum value
	 */
	public final float max(ROI r) {
		float p;
		float max = Short.MIN_VALUE;

		for (int x = r.ux(); x < r.lx(); x++) {
			p = data[x];
			if (p > max) {
				max = p;
			}
		}
		return (float) max;
	}


	/**
	 * Adds a value to all the samples in in a Region of Interest
	 * @param v value to be added to the samples
	 * @param r Region of Interest
	 * @return this
	 */
	public final RealSignal add(float v, ROI r) {
		float sv = (float) v;

		for (int x = r.ux(); x < r.lx(); x++) {
			data[x] += sv;
		}
		return this;
	}


	/**
	 * Subtracts a value from all the samples in in a Region of Interest
	 * @param v value to be added to the samples
	 * @param r Region of Interest
	 * @return this
	 */
	public final RealSignal subtract(float v, ROI r) {
		float sv = (float) v;

		for (int x = r.ux(); x < r.lx(); x++) {
			data[x] -= sv;
		}
		return this;
	}


	/**
	 * Multiplies all the samples in in a Region of Interest by a value
	 * @param v value to be added to the samples
	 * @param r Region of Interest
	 * @return this
	 */
	public final RealSignal multiply(float v, ROI r) {
		float sv = (float) v;

		for (int x = r.ux(); x < r.lx(); x++) {
			data[x] *= sv;
		}
		return this;
	}


	/**
	 * Divides all the samples in in a Region of Interest by a value
	 * @param v value to be added to the samples
	 * @param r Region of Interest
	 * @return this
	 */
	public final RealSignal divide(float v, ROI r) {
		float sv = (float) v;

		for (int x = r.ux(); x < r.lx(); x++) {
			data[x] /= sv;
		}
		return this;
	}


	/**
	 * Adds a Region of Interest of another RealSignal to a Region of Interest of this signal
	 * @param s the RealSignal to add
	 * @param sourceSignal Region of Interest for Source Signal
	 * @param destSignal Region of Interest for Destination Signal
	 * @return this
	 */
	public final RealSignal subtract(RealSignal s, ROI sourceSignal, ROI destSignal) {
		for (int x = sourceSignal.ux(); x < sourceSignal.lx(); x++) {
			data[x] += s.get((x - sourceSignal.ux() + destSignal.ux()));
		}
		return this;
	}


	/**
	 * Subtracts a Region of Interest from another RealSignal from a Region of Interest of this signal
	 * @param s the RealSignal to subtract
	 * @param sourceSignal Region of Interest for Source Signal
	 * @param destSignal Region of Interest for Destination Signal
	 * @return this
	 */
	public final RealSignal multiply(RealSignal s, ROI sourceSignal, ROI destSignal) {
		for (int x = sourceSignal.ux(); x < sourceSignal.lx(); x++) {
			data[x] += s.get((x - sourceSignal.ux() + destSignal.ux()));
		}
		return this;
	}


	/**
	 * Multiplies a Region of Interest of another RealSignal to a Region of Interest of this signal
	 * @param s the RealSignal to multiply
	 * @param sourceSignal Region of Interest for Source Signal
	 * @param destSignal Region of Interest for Destination Signal
	 * @return this
	 */
	public final RealSignal divide(RealSignal s, ROI sourceSignal, ROI destSignal) {
		for (int x = sourceSignal.ux(); x < sourceSignal.lx(); x++) {
			data[x] += s.get((x - sourceSignal.ux() + destSignal.ux()));
		}
		return this;
	}


	/**
	 * Divides this signal's Region of Interest by a Region of Interest of another RealSignal
	 * @param s the RealSignal to divide
	 * @param sourceSignal Region of Interest for Source Signal
	 * @param destSignal Region of Interest for Destination Signal
	 * @return this
	 */
	public final RealSignal add(RealSignal s, ROI sourceSignal, ROI destSignal) {
		for (int x = sourceSignal.ux(); x < sourceSignal.lx(); x++) {
			data[x] += s.get((x - sourceSignal.ux() + destSignal.ux()));
		}
		return this;
	}


	/**
	 * Prints the string in float format.
	 * <DT><DL><DL>-Example of output on an signal with length 100:</DT>
	 * <DL>       <DT>100</DT>
	 * <DT>10 20 32 12 32 56 40 59 42 39 43 ...</DT></DL></DL></DL>
	 */
	public String toString(ROI r) {
		String	str = length + "\n";

		for (int x = r.ux(); x < r.lx(); x++) {
			str += data[x] + " ";
		}
		str += "\n";
		return str;
	}


	/**
	 * Scales the range of a Region of Interest to byte (0..255)
	 * @param r Region of Interest
	 */
	public void byteSize(ROI r) {

		// get range of this signal
		double	min = min();
		double	max = max();

		// keep byte signals in original range

		double	range = max - min;

		// convert to byte depth
		int			value = 0;

		for (int x = r.ux(); x < r.lx(); x++) {
			value = (int) ((255.0 / range) * ((double) data[x] - min));
			value = 0x00FF & value;
			data[x] = (float) value;
		}

	}


	/**
	 * Clips the range of in a Region of Interest to an arbitrary min/max
	 * @param min minimum value
	 * @param max maximum value
	 * @param r Region of Interest
	 */
	public final void clip(int min, int max, ROI r) {

		// clip
		float value = 0;

		for (int x = r.ux(); x < r.lx(); x++) {
			value = data[x];
			value = (value > max) ? max : value;
			value = (value < min) ? min : value;
			data[x] = (float) value;
		}
	}


	/**
	 * Performs convolution in place with a kernel signal on a Region of Interest.
	 * @param kernel kernel to perform the convolution with
	 * @param r Region of Interest
	 */
	public void convolve(RealSignal kernel, ROI r) {

		int			Num = kernel.length();
		int			mid = Num / 2;
		double	sum = 0;
		float[] value = new float[length];
		float[] kern = ConvertSignal.toRealDiscrete(kernel).data;

		// find the sum of all values in the kernel
		for (int x = r.ux(); x < Num; x++) {
			sum += kern[x];
		}

		// if the sum is not zero then normalize by the sum
		if (sum != 0) {
			for (int x = r.ux(); x < Num; x++) {
				kern[x] /= sum;
			}
		}

		// for every sample in the original signal
		for (int x = r.ux(); x < r.lx(); x++) {

			// Convolve with the kernel
			sum = 0;
			for (int i = -mid; i <= mid; i++) {
				try {
					sum += data[x + i] * kern[mid + i];
				} catch (Exception e) {

					// ignore out of bounds samples
				}
			}
			value[x] = (float) sum;
		}

		this.data = value;
	}


	/**
	 * Method declaration
	 *
	 *
	 * @param vals
	 * @param size
	 * @param r
	 *
	 * @return
	 *
	 * @see
	 */
	private double[] sort(double vals[], int size, ROI r) {
		int			i, j;
		double	temp;

		for (i = 0; i < size; i++) {
			for (j = 0; j < size - 1; j++) {
				try {
					if (vals[j] > vals[j + 1]) {
						temp = vals[j];
						vals[j] = vals[j + 1];
						vals[j + 1] = temp;
					}
				} catch (Exception e) {}
			}
		}
		return vals;
	}


	/**
	 * Performs median filter on a Region of Interest
	 * @param size the size of the median filter
	 * @param r Region of Interest
	 */
	public void median(int size, ROI r) {

		int			Num = (size > length) ? length : size;
		int			mid = Num / 2;
		double	value[] = new double[Num * Num];
		int			count;

		// for every sample in the original signal
		for (int x = r.ux(); x < r.lx(); x++) {

			// find median value
			count = 0;
			for (int i = -mid; i <= mid; i++) {
				try {
					value[count++] = data[x + i];
				} catch (Exception e) {

					// ignore out of bounds samples
				}
			}
			value = sort(value, count);
			data[x] = (float) value[count / 2];
		}

	}


}


/*--- formatting done in "My Own Convention" style on 08-08-2000 ---*/

