[gnumeric] Improve Fourier transform tool



commit 565c2b482eeff9477d46d251bdefb1dcb6bd3245
Author: Andreas J. Guelzow <aguelzow pyrshep ca>
Date:   Fri Jun 5 23:43:35 2009 -0600

    Improve Fourier transform tool
    
    2009-06-05  Andreas J. Guelzow <aguelzow pyrshep ca>
    
    	* analysis-tools.h (gnm_fourier_fft): moved to
    	  plugins/fn-tsa/functions.c
    	* analysis-tools.c (cb_store_data): deleted
    	(new_data_set): deleted
    	(destroy_data_set): deleted
    	(cb_get_data_set_list): deleted
    	(cb_get_data_set_list): deleted
    	(new_data_set_list): deleted
    	(destroy_data_set_list): deleted
    	(gnm_fourier_fft): moved to
    	  plugins/fn-tsa/functions.c
    	(analysis_tool_fourier_engine_run): rewritten
    	(analysis_tool_fourier_calc_length): use analysis_tool_calc_length
    	(analysis_tool_fourier_engine): adjust required size
    
    2009-06-05  Andreas J. Guelzow <aguelzow pyrshep ca>
    
    	* functions.c (gnm_fourier_fft): moved here from
    	  src/tools/analysis-tools.c
    	(help_interpolation): changed to new form of desription
    	(help_periodogram): ditto
    	(help_fourier): new
    	(gnumeric_fourier): new
    	(TimeSeriesAnalysis_functions): added fourier function
    	* plugin.xml.in: added fourier function
---
 BUGS                         |   15 +-
 NEWS                         |    3 +
 plugins/fn-tsa/ChangeLog     |   11 ++
 plugins/fn-tsa/functions.c   |  239 ++++++++++++++++++++++--------
 plugins/fn-tsa/plugin.xml.in |    1 +
 src/tools/ChangeLog          |   19 +++-
 src/tools/analysis-tools.c   |  332 +++++++-----------------------------------
 src/tools/analysis-tools.h   |    3 -
 8 files changed, 271 insertions(+), 352 deletions(-)

diff --git a/BUGS b/BUGS
index 428caf8..e27f47c 100644
--- a/BUGS
+++ b/BUGS
@@ -65,9 +65,6 @@ Misc stuff that should be fixed
       correspond to hiding.
     : support delete for discontinuous ranges
 
-- Remove duplication of code between analysis-tools.c and collect.c
-    (Many analysis tools may in fact want to follow the consolidate style)
-
 Worries
 -------
     : range_translate in stf.c looks suspecious.
@@ -131,8 +128,8 @@ Architecture Changes
     1.1) plugin-manager dialog
     1.2) doc meta data
 	1.2.1) Use GsfDocMetaData	{Jody}
-	    1.2.1.1) sax-in
-	    1.2.1.2) sax-out					(DONE)
+	    1.2.1.1) sax-in             (DONE)         
+	    1.2.1.2) sax-out		(DONE)
 	1.2.2) doc meta data dialog	{Trelane and etrunko}
     1.3) Insert hyperlink dialog
 	1.3.1) Integration with evo address book
@@ -321,8 +318,8 @@ Architecture Changes
 	11.3.1) import
 	11.3.2) export
     11.4) ODF
-	11.4.1) import
-	11.4.2) export
+	11.4.1) import	(conditional number formats DONE)
+	11.4.2) export	(conditional number formats DONE)
     11.6) Evaluation
 	11.6.1) Custom						(DONE)
 	11.6.2) Comparisons
@@ -357,7 +354,7 @@ Architecture Changes
 	12.3.3) Add to .xsd schema				(DONE)
     12.6) ODF
 	12.6.1) import
-	12.6.2) export
+	12.6.2) export						(DONE)
 
 16) Protection limits for selection
     16.2) I/O
@@ -721,7 +718,7 @@ Architecture Changes
     36.3) Export
          36.3.1)  We don't handle whitespace correctly. See OpenDocument
                  format 5.1.1                                   (DONE)
-    36.4) See 11.4 (Conditionals)
+    36.4) See 11.4 (Conditionals)				(DONE)
     36.5) See 12.6 (Input Messages)
     36.6) See 21.46 (Autofilters)
     36.7) See 22.13 (Validation)
diff --git a/NEWS b/NEWS
index 340dc0a..aa0e50a 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,9 @@ Andreas:
 	* Add superscipt and subscript buttons. [#583327]
 	* Improve ODF import.
 	* Improve 2 factor ANOVA tool
+	* Improve Rank & Percentiles tool
+	* Improve Fourier transform tool
+	* New FOURIER function
 
 Morten:
 	* Add search-for-number.
diff --git a/plugins/fn-tsa/ChangeLog b/plugins/fn-tsa/ChangeLog
index 8b983f8..cd10283 100644
--- a/plugins/fn-tsa/ChangeLog
+++ b/plugins/fn-tsa/ChangeLog
@@ -1,3 +1,14 @@
+2009-06-05  Andreas J. Guelzow <aguelzow pyrshep ca>
+
+	* functions.c (gnm_fourier_fft): moved here from 
+	  src/tools/analysis-tools.c
+	(help_interpolation): changed to new form of desription
+	(help_periodogram): ditto
+	(help_fourier): new
+	(gnumeric_fourier): new
+	(TimeSeriesAnalysis_functions): added fourier function
+	* plugin.xml.in: added fourier function
+	
 2009-05-23  Morten Welinder <terra gnome org>
 
 	* Release 1.9.8
diff --git a/plugins/fn-tsa/functions.c b/plugins/fn-tsa/functions.c
index 583714d..b10c11b 100644
--- a/plugins/fn-tsa/functions.c
+++ b/plugins/fn-tsa/functions.c
@@ -5,6 +5,7 @@
  *
  * Copyright (C) 2006 Laurency Franck
  * Copyright (C) 2007 Jean Bréfort <jean brefort normalesup org>
+ * Copyright (C) 2009 Andreas Guelzow <aguelzow pyrshep ca>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -48,6 +49,7 @@
 #include <goffice/math/go-cspline.h>
 #include <gnm-plugin.h>
 #include <tools/analysis-tools.h>
+#include <complex.h>
 
 #include <math.h>
 #include <stdlib.h>
@@ -89,12 +91,57 @@ enum {
 #	define gnm_cspline_get_integrals go_cspline_get_integrals
 #endif
 
+#define INTERPOLATIONMETHODS { GNM_FUNC_HELP_DESCRIPTION, F_("Possible interpolation methods are:\n"\
+	   "0: linear;\n"\
+	   "1: linear with averaging;\n"\
+	   "2: staircase;\n"\
+	   "3: staircase with averaging;\n"\
+	   "4: natural cubic spline;\n"\
+	   "5: natural cubic spline with averaging.") }
+
 /**************************************************************************/
 /**************************************************************************/
 /*           CALCULATION FUNCTIONS FOR TOOLS                                */
 /**************************************************************************/
 /**************************************************************************/
 
+static void
+gnm_fourier_fft (complex_t const *in, int n, int skip, complex_t **fourier, gboolean inverse)
+{
+	complex_t  *fourier_1, *fourier_2;
+	int        i;
+	int        nhalf = n / 2;
+	gnm_float argstep;
+
+	*fourier = g_new (complex_t, n);
+
+	if (n == 1) {
+		(*fourier)[0] = in[0];
+		return;
+	}
+
+	gnm_fourier_fft (in, nhalf, skip * 2, &fourier_1, inverse);
+	gnm_fourier_fft (in + skip, nhalf, skip * 2, &fourier_2, inverse);
+
+	argstep = (inverse ? M_PIgnum : -M_PIgnum) / nhalf;
+	for (i = 0; i < nhalf; i++) {
+		complex_t dir, tmp;
+
+		complex_from_polar (&dir, 1, argstep * i);
+		complex_mul (&tmp, &fourier_2[i], &dir);
+
+		complex_add (&((*fourier)[i]), &fourier_1[i], &tmp);
+		complex_scale_real (&((*fourier)[i]), 0.5);
+
+		complex_sub (&((*fourier)[i + nhalf]), &fourier_1[i], &tmp);
+		complex_scale_real (&((*fourier)[i + nhalf]), 0.5);
+	}
+
+	g_free (fourier_1);
+	g_free (fourier_2);
+}
+
+
 /*******LINEAR INTERPOLATION*******/
 
 static gnm_float*
@@ -321,36 +368,17 @@ spline_averaging (const gnm_float *absc, const gnm_float *ord, int nb_knots,
 /******************************************************************************/
 
 static GnmFuncHelp const help_interpolation[] = {
-	{ GNM_FUNC_HELP_OLD,
-	F_("@FUNCTION=INTERPOLATION\n"
-	   "@SYNTAX=INTERPOLATION(abscissas,ordinates,targets[,interpolation])\n"
-
-	   "@DESCRIPTION= \n"
-	   "INTERPOLATION returns interpolated values corresponding\n"
-	   "to the given abscissa targets as a one column matrix.\n"
-	   "\n"
-	   "@abscissas are the absicssas of the data to interpolate.\n"
-	   "@ordinates are the ordinates of the data to interpolate.\n"
-	   "* Strings and empty cells in @abscissas and @ordinates are simply ignored.\n"
-	   "@targets are the abscissas of the interpolated data. If several data\n"
-	   "are provided, they must be in the same column, in consecutive cells\n"
-	   "@interpolation is the method to be used for the interpolation;\n"
-	   "possible values are:\n"
-	   "- 0: linear;\n"
-	   "- 1: linear with averaging;\n"
-	   "- 2: staircase;\n"
-	   "- 3: staircase with averaging;\n"
-	   "- 4: natural cubic spline;\n"
-	   "- 5: natural cubic spline with averaging.\n"
-	   "\n"
-	   "If an averaging method is used, the number of returned values\n"
-	   "is one less than the number of targets since the evaluation is made by\n"
-	   "averaging the interpolation over the interval between two consecutive data;\n"
-	   "in that case, the targets values must be given in increasing order.\n"
-	   "@EXAMPLES=\n"
-	   "\n"
-	   "@SEEALSO=PERIODOGRAM")
-	},
+	{ GNM_FUNC_HELP_NAME, F_("Interpolation:interpolated values corresponding to the given abscissa targets.") },
+	{ GNM_FUNC_HELP_ARG, F_("abscissas:The absicssas of the data to interpolate.") },
+	{ GNM_FUNC_HELP_ARG, F_("ordinates:The ordinates of the data to interpolate.") },
+	{ GNM_FUNC_HELP_ARG, F_("targets:The abscissas of the interpolated data.") },
+	{ GNM_FUNC_HELP_ARG, F_("interpolation:The method of interpolation to be used, defaults to no interpolation") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("If an interpolation method is used, the number of returned values is one less than the number of targets and the targets values must be given in increasing order.") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("The output consists always of one column of numbers.") },
+	INTERPOLATIONMETHODS,
+	{ GNM_FUNC_HELP_NOTE, F_("Strings and empty cells in @{abscissas} and @{ordinates} are ignored.") },
+	{ GNM_FUNC_HELP_NOTE, F_("If several target data are provided they must be in the same column in consecutive cells.") },
+	{ GNM_FUNC_HELP_SEEALSO, "PERIODOGRAM" },
 	{ GNM_FUNC_HELP_END }
 };
 
@@ -614,39 +642,23 @@ gnumeric_interpolation (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
 /******************************************************************************/
 
 static GnmFuncHelp const help_periodogram[] = {
-	{ GNM_FUNC_HELP_OLD,
-	F_("@FUNCTION=PERIODOGRAM\n"
-	   "@SYNTAX=PERIODOGRAM(ordinates,[filter,[abscissas,[interpolation,[number]]]])\n"
-
-	   "@DESCRIPTION= \n"
-	   "periodogram returns the periodogram of the data\n"
-	   "as a one column matrix.\n"
-	   "\n"
-	   "@ordinates are the ordinates of the data to interpolate.\n"
-	   "@filter gives the window function to  be used. Possible values are:\n"
-	   "- 0: no filter (rectangular window);\n"
-	   "- 1: Bartlett (triangular window);\n"
-	   "- 2: Hahn (cosine window);\n"
-	   "- 3: Welch (parabolic window);\n"
-	   "@abscissas are the absicssas of the data to interpolate. If no\n"
-	   "abscissa is given, it is supposed that the data absicssas are regularly\n"
-	   "spaced. Otherwise, an interpolation method will be used to evaluate\n"
-	   "regularly spaced data.\n"
-	   "* Strings and empty cells in @abscissas and @ordinates are simply ignored.\n"
-	   "@interpolation is the method to be used for the interpolation;\n"
-	   "possible values are:\n"
-	   "- 0: linear;\n"
-	   "- 1: linear with averaging;\n"
-	   "- 2: staircase;\n"
-	   "- 3: staircase with averaging;\n"
-	   "- 4: natural cubic spline;\n"
-	   "- 5: natural cubic spline with averaging.\n"
-	   "@number is the number of interpolated data to be used. If not given,\n"
-	   "a default number is automatically evaluated.\n"
-	   "@EXAMPLES=\n"
-	   "\n"
-	   "@SEEALSO=INTERPOLATION")
-	},
+	{ GNM_FUNC_HELP_NAME, F_("PERIODOGRAM: Periodogram of the given data.") },
+	{ GNM_FUNC_HELP_ARG, F_("ordinates:The ordinates of the data to interpolate.") },
+	{ GNM_FUNC_HELP_ARG, F_("filter:Window function to  be used, defaults to no window function.") },
+	{ GNM_FUNC_HELP_ARG, F_("abscissas:The abscissas of the data to interpolate, defaults to regularly spaced abscissa.") },
+	{ GNM_FUNC_HELP_ARG, F_("interpolation:The method of interpolation to be used, defaults to no interpolation") },
+	{ GNM_FUNC_HELP_ARG, F_("number: Number of interpolated data points to be used.") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("If an interpolation method is used, the number of returned values is one less than the number of targets and the targets values must be given in increasing order.") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("The output consists always of one column of numbers.") },
+	INTERPOLATIONMETHODS,
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("Possible window functions are:\n"
+	"0: no filter (rectangular window)\n"
+	"1: Bartlett (triangular window)\n"
+	"2: Hahn (cosine window)\n"
+	"3: Welch (parabolic window)") },
+	{ GNM_FUNC_HELP_NOTE, F_("Strings and empty cells in @{abscissas} and @{ordinates} are ignored.") },
+	{ GNM_FUNC_HELP_NOTE, F_("If several target data are provided they must be in the same column in consecutive cells.") },
+	{ GNM_FUNC_HELP_SEEALSO, "INTERPOLATION" },
 	{ GNM_FUNC_HELP_END }
 };
 
@@ -892,6 +904,103 @@ gnumeric_periodogram (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
 	return res;
 }
 
+/******************************************************************************/
+/*                    Fourier Transform                                       */
+/******************************************************************************/
+
+static GnmFuncHelp const help_fourier[] = {
+	{ GNM_FUNC_HELP_NAME, F_("FOURIER:Fourier or inverse Fourier transform.") },
+	{ GNM_FUNC_HELP_ARG, F_("Sequence: the data sequence to be transformed") },
+	{ GNM_FUNC_HELP_ARG, F_("Inverse:if false, the inverse Fourier transform is calculated. Defaults to false") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("This array function returns the Fourier or inverse Fourier transform of the given data sequence.") },
+	{ GNM_FUNC_HELP_DESCRIPTION, F_("The output consists always of one column of complex numbers.") },
+	{ GNM_FUNC_HELP_NOTE, F_("If @{Sequence} is neither an n by 1 nor 1 by n array, this function returns #NUM!") },
+	{ GNM_FUNC_HELP_END }
+};
+
+static GnmValue *
+gnumeric_fourier (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+{
+	gnm_float *ord;
+	gboolean inverse = FALSE;
+	int n0, nb;
+	GnmValue *error = NULL;
+	GnmValue *res;
+	CollectFlags flags;
+	GnmEvalPos const * const ep = ei->pos;
+	GnmValue const * const Pt = argv[0];
+	int i;
+	GSList *missing0 = NULL;
+	complex_t *in, *out = NULL;
+
+	int const cols = value_area_get_width (Pt, ep);
+	int const rows = value_area_get_height (Pt, ep);
+
+	char f[5 + 4 * sizeof (int) + sizeof (GNM_FORMAT_g)];
+
+	if (cols != 1 && rows != 1) {
+		res = value_new_error_std (ei->pos, GNM_ERROR_VALUE);
+		return res;
+	}
+
+	flags=COLLECT_IGNORE_BLANKS | COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS;
+
+	ord = collect_floats_value_with_info (argv[0], ei->pos, flags,
+				      &n0, &missing0, &error);
+	if (error) {
+		g_slist_free (missing0);
+		return error;
+	}
+
+	if (n0 == 0) {
+		res = value_new_error_std (ei->pos, GNM_ERROR_VALUE);
+		return res;
+	}
+
+	if (argv[1]) {
+		inverse = 0 != (int) gnm_floor (value_get_as_float (argv[1]));
+	}
+	
+	if (missing0) {
+		GArray *gval;
+		
+		gval = g_array_new (FALSE, FALSE, sizeof (gnm_float));
+		gval = g_array_append_vals (gval, ord, n0);
+		g_free (ord);
+		gnm_strip_missing (gval, missing0);
+		ord = (gnm_float *) gval->data;
+		n0 = gval->len;
+		g_array_free (gval, FALSE);
+		
+		g_slist_free (missing0);
+	}
+
+	nb = 1;
+	while (nb < n0)
+		nb *= 2;
+
+	/* Transform and return the result */
+	in = g_new0 (complex_t, nb);
+	for (i = 0; i < n0; i++)
+	     in[i].re = ord[i];
+	g_free (ord);
+	gnm_fourier_fft (in, nb, 1, &out, inverse);
+	g_free (in);
+
+	if (out) {
+		res = value_new_array_empty (1 , nb);
+		sprintf (f, "%%.%d" GNM_FORMAT_g, GNM_DIG);
+		for (i = 0; i < nb; i++)
+			res->v_array.vals[0][i] = value_new_string_nocopy 
+				(complex_to_string (&(out[i]), f, f, 'i'));
+		g_free (out);
+	} else
+		res = value_new_error_std (ei->pos, GNM_ERROR_VALUE);
+
+	return res;
+}
+
+
 const GnmFuncDescriptor TimeSeriesAnalysis_functions[] = {
 
         { "interpolation",       "AAA|f",   N_("Abscissas,Ordinates,Targets,Interpolation"),
@@ -902,5 +1011,9 @@ const GnmFuncDescriptor TimeSeriesAnalysis_functions[] = {
 	  help_periodogram, gnumeric_periodogram, NULL, NULL, NULL, NULL,
 	  GNM_FUNC_RETURNS_NON_SCALAR, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
 
+	{ "fourier",       "A|b",   N_("Sequence,Inverse"),
+	  help_fourier, gnumeric_fourier, NULL, NULL, NULL, NULL,
+	  GNM_FUNC_RETURNS_NON_SCALAR, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
+
         {NULL}
 };
diff --git a/plugins/fn-tsa/plugin.xml.in b/plugins/fn-tsa/plugin.xml.in
index a944758..6da0bbb 100644
--- a/plugins/fn-tsa/plugin.xml.in
+++ b/plugins/fn-tsa/plugin.xml.in
@@ -14,6 +14,7 @@
 			<functions>
             	<function name="interpolation"/>
             	<function name="periodogram"/>
+            	<function name="fourier"/>
 			</functions>
 		</service>
 	</services> 
diff --git a/src/tools/ChangeLog b/src/tools/ChangeLog
index 2af4cc2..fcdc9c8 100644
--- a/src/tools/ChangeLog
+++ b/src/tools/ChangeLog
@@ -1,4 +1,21 @@
-2009-06-06  Andreas J. Guelzow <aguelzow pyrshep ca>
+2009-06-05  Andreas J. Guelzow <aguelzow pyrshep ca>
+
+	* analysis-tools.h (gnm_fourier_fft): moved to
+	  plugins/fn-tsa/functions.c
+	* analysis-tools.c (cb_store_data): deleted
+	(new_data_set): deleted
+	(destroy_data_set): deleted
+	(cb_get_data_set_list): deleted
+	(cb_get_data_set_list): deleted
+	(new_data_set_list): deleted
+	(destroy_data_set_list): deleted
+	(gnm_fourier_fft): moved to
+	  plugins/fn-tsa/functions.c
+	(analysis_tool_fourier_engine_run): rewritten
+	(analysis_tool_fourier_calc_length): use analysis_tool_calc_length
+	(analysis_tool_fourier_engine): adjust required size
+	
+2009-06-05  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* Makefile.am: added  analysis-anova.[ch]
 	* analysis-tools.h (analysis_tool_anova_two_factor_engine):
diff --git a/src/tools/analysis-tools.c b/src/tools/analysis-tools.c
index bfd863e..27e6c5d 100644
--- a/src/tools/analysis-tools.c
+++ b/src/tools/analysis-tools.c
@@ -99,21 +99,6 @@ make_rangeref (int dx0, int dy0, int dx1, int dy1)
 }
 
 
-/*************************************************************************/
-/*
- *  data_set_t: a data set format (optionally) keeping track of missing
- *  observations.
- *
- */
-typedef struct {
-        GArray  *data;
-	char *label;
-	GSList *missing;
-	gboolean complete;
-	gboolean read_label;
-} data_set_t;
-
-
 static gboolean 
 gnm_check_input_range_list_homogeneity (GSList *input_range);
 
@@ -147,166 +132,6 @@ cb_adjust_areas (gpointer data, G_GNUC_UNUSED gpointer user_data)
 	range->v_range.cell.b.row_relative = 0;
 }
 
-static GnmValue *
-cb_store_data (GnmCellIter const *iter, gpointer user)
-{
-	data_set_t *data_set = (data_set_t *)user;
-	GnmCell *cell = iter->cell;
-	gnm_float the_data;
-
-	if (data_set->read_label) {
-		if (cell != NULL) {
-			data_set->label = cell->value
-				? value_get_as_string (cell->value)
-				: NULL;
-			if (data_set->label == NULL || strlen (data_set->label) == 0) {
-				g_free (data_set->label);
-				data_set->label = NULL;
-			}
-		}
-		data_set->read_label = FALSE;
-		return NULL;
-	}
-	if (cell == NULL || !VALUE_IS_NUMBER (cell->value)) {
-		if (data_set->complete) {
-			data_set->missing = g_slist_prepend (data_set->missing,
-						 GUINT_TO_POINTER (data_set->data->len));
-			the_data = 0;
-			g_array_append_val (data_set->data, the_data);
-		}
-	} else {
-		the_data =  value_get_as_float (cell->value);
-		g_array_append_val (data_set->data, the_data);
-	}
-	return NULL;
-}
-
-/*
- *  new_data_set:
- *  @range: GnmValue *       the data location, usually a single column or row
- *  @ignore_non_num: gboolean   whether simply to ignore non-numerical values
- *  @read_label: gboolean       whether the first entry contains a label
- *  @format: char*              format string for default label
- *  @i: guint                    index for default label
- */
-static data_set_t *
-new_data_set (GnmValue *range, gboolean ignore_non_num, gboolean read_label,
-	      char *format, gint i, Sheet* sheet)
-{
-	GnmValue *result;
-	GnmEvalPos  *pos = g_new (GnmEvalPos, 1);
-	data_set_t * the_set = g_new (data_set_t, 1);
-
-	pos = eval_pos_init_sheet (pos, sheet);
-	the_set->data = g_array_new (FALSE, FALSE, sizeof (gnm_float)),
-	the_set->missing = NULL;
-        the_set->label = NULL;
-	the_set->complete = !ignore_non_num;
-	the_set->read_label = read_label;
-
-	result = workbook_foreach_cell_in_range (pos, range, CELL_ITER_ALL,
-						 cb_store_data, the_set);
-	g_free (pos);
-
-	if (result != NULL) value_release (result);
-	the_set->missing = g_slist_reverse (the_set->missing);
-	if (the_set->label == NULL)
-		the_set->label = g_strdup_printf (format, i);
-	return the_set;
-}
-
-/*
- *  destroy_data_set:
- *  @data_set:
- */
-static void
-destroy_data_set (data_set_t *data_set)
-{
-
-	if (data_set->data != NULL)
-		g_array_free (data_set->data, TRUE);
-	g_slist_free (data_set->missing);
-	g_free (data_set->label);
-	g_free (data_set);
-}
-
-/*
- *  cb_get_data_set_list:
- *  @data:
- *  @user_data:
- */
-static void
-cb_get_data_set_list (gpointer data, gpointer user_data)
-{
-	GnmValue * the_range = (GnmValue *) data;
-	data_list_specs_t *specs = (data_list_specs_t *)user_data;
-
-	specs->length++;
-	g_ptr_array_add (specs->data_lists,
-			 new_data_set (the_range, specs->ignore_non_num,
-				       specs->read_label, specs->format,
-				       specs->length, specs->sheet));
-}
-
-
-/*
- *  new_data_set_list:
- *  @ranges: GSList *           the data location
- *  @group_by: group_by_t       how to group the data
- *  @ignore_non_num: gboolean   whether simply to ignore non-numerical values
- *  @read_label: gboolean       whether the first entry contains a label
- */
-static GPtrArray *
-new_data_set_list (GSList *ranges, group_by_t group_by,
-		   gboolean ignore_non_num, gboolean read_labels, Sheet *sheet)
-{
-	data_list_specs_t specs = {NULL, NULL, FALSE, FALSE, 0};
-
-	if (ranges == NULL)
-		return NULL;
-
-	specs.read_label = read_labels;
-	specs.data_lists = g_ptr_array_new ();
-	specs.ignore_non_num = ignore_non_num;
-	specs.sheet = sheet;
-
-	switch (group_by) {
-	case GROUPED_BY_ROW:
-		specs.format = _("Row %i");
-		break;
-	case GROUPED_BY_COL:
-		specs.format = _("Column %i");
-		break;
-	case GROUPED_BY_BIN:
-		specs.format = _("Bin %i");
-		break;
-	case GROUPED_BY_AREA:
-	default:
-		specs.format = _("Area %i");
-		break;
-	}
-
-	g_slist_foreach (ranges, cb_get_data_set_list, &specs);
-
-	return specs.data_lists;
-}
-
-/*
- *  destroy_data_set_list:
- *  @the_list:
- */
-static void
-destroy_data_set_list (GPtrArray * the_list)
-{
-	guint i;
-
-	for (i = 0; i < the_list->len; i++) {
-		data_set_t *data = g_ptr_array_index (the_list, i);
-		destroy_data_set (data);
-	}
-	g_ptr_array_free (the_list, TRUE);
-}
-
 /*
  *  analysis_tools_remove_label:
  *  @val: range to extract label from
@@ -4339,127 +4164,82 @@ analysis_tool_anova_single_engine (data_analysis_output_t *dao, gpointer specs,
  * This tool performes a fast fourier transform calculating the fourier
  * transform as defined in Weaver: Theory of dis and cont Fouriere Analysis
  *
- * If the length of the given sequence is not a power of 2, an appropriate
- * number of 0s are added.
- *
- *
  *
  **/
 
-void
-gnm_fourier_fft (complex_t const *in, int n, int skip, complex_t **fourier, gboolean inverse)
+
+static gboolean
+analysis_tool_fourier_engine_run (data_analysis_output_t *dao,
+				  analysis_tools_data_fourier_t *info)
 {
-	complex_t  *fourier_1, *fourier_2;
-	int        i;
-	int        nhalf = n / 2;
-	gnm_float argstep;
+	GSList *data = info->base.input;
+	int col = 0;
 
-	*fourier = g_new (complex_t, n);
+	GnmFunc *fd_fourier;
+	GnmFunc *fd_imaginary;
+	GnmFunc *fd_imreal;
 
-	if (n == 1) {
-		(*fourier)[0] = in[0];
-		return;
-	}
+	fd_fourier = gnm_func_lookup ("FOURIER", NULL);
+	gnm_func_ref (fd_fourier);
+	fd_imaginary = gnm_func_lookup ("IMAGINARY", NULL);
+	gnm_func_ref (fd_imaginary);
+	fd_imreal = gnm_func_lookup ("IMREAL", NULL);
+	gnm_func_ref (fd_imreal);
 
-	gnm_fourier_fft (in, nhalf, skip * 2, &fourier_1, inverse);
-	gnm_fourier_fft (in + skip, nhalf, skip * 2, &fourier_2, inverse);
 
-	argstep = (inverse ? M_PIgnum : -M_PIgnum) / nhalf;
-	for (i = 0; i < nhalf; i++) {
-		complex_t dir, tmp;
+	dao_set_merge (dao, 0, 0, 1, 0);
+	dao_set_italic (dao, 0, 0, 0, 0);
+	dao_set_cell (dao, 0, 0, info->inverse ? _("Inverse Fourier Transform") 
+		      : _("Fourier Transform"));
 
-		complex_from_polar (&dir, 1, argstep * i);
-		complex_mul (&tmp, &fourier_2[i], &dir);
+	for (; data; data = data->next, col++) {
+		GnmValue *val_org = value_dup (data->data);
+		GnmExpr const *expr_fourier;
+		int rows, n;
 
-		complex_add (&((*fourier)[i]), &fourier_1[i], &tmp);
-		complex_scale_real (&((*fourier)[i]), 0.5);
+		dao_set_italic (dao, 0, 1, 1, 2);
+		dao_set_cell (dao, 0, 2, _("Real"));
+		dao_set_cell (dao, 1, 2, _("Imaginary"));
+		dao_set_merge (dao, 0, 1, 1, 1);
+		analysis_tools_write_label (val_org, dao, &info->base, 0, 1, col + 1);
 
-		complex_sub (&((*fourier)[i + nhalf]), &fourier_1[i], &tmp);
-		complex_scale_real (&((*fourier)[i + nhalf]), 0.5);
+		n = (val_org->v_range.cell.b.row - val_org->v_range.cell.a.row + 1) *
+			(val_org->v_range.cell.b.col - val_org->v_range.cell.a.col + 1);
+		rows = 1;
+		while (rows < n)
+			rows *= 2;
+
+		expr_fourier = gnm_expr_new_funcall2
+			(fd_fourier,
+			 gnm_expr_new_constant (val_org),
+			 gnm_expr_new_constant (value_new_bool (info->inverse)));
+
+		dao_set_array_expr (dao, 0, 3, 1, rows, 
+				    gnm_expr_new_funcall1 (fd_imreal, 
+							   gnm_expr_copy (expr_fourier)));
+		dao_set_array_expr (dao, 1, 3, 1, rows, 
+				    gnm_expr_new_funcall1 (fd_imaginary, 
+							   expr_fourier));
+		dao->offset_col += 2;
 	}
 
-	g_free (fourier_1);
-	g_free (fourier_2);
-}
+	gnm_func_unref (fd_fourier);
+	gnm_func_unref (fd_imaginary);
+	gnm_func_unref (fd_imreal);
 
-static gboolean
-analysis_tool_fourier_engine_run (data_analysis_output_t *dao,
-				  analysis_tools_data_fourier_t *info)
-{
-	GPtrArray     *data;
-	guint         dataset;
-	gint          col = 0;
-
-	data = new_data_set_list (info->base.input, info->base.group_by,
-				  TRUE, info->base.labels, dao->sheet);
-
-	for (dataset = 0; dataset < data->len; dataset++) {
-		data_set_t    *current;
-		complex_t     *in, *fourier;
-		int           row;
-		int           given_length;
-		int           desired_length = 1;
-		int           i;
-		gnm_float    zero_val = 0.0;
-
-		current = g_ptr_array_index (data, dataset);
-		given_length = current->data->len;
-		while (given_length > desired_length)
-			desired_length *= 2;
-		for (i = given_length; i < desired_length; i++)
-			current->data = g_array_append_val (current->data, zero_val);
-
-		dao_set_cell_printf (dao, col, 0, "%s", current->label);
-		dao_set_cell_printf (dao, col, 1, _("Real"));
-		dao_set_cell_printf (dao, col + 1, 1, _("Imaginary"));
-
-		in = g_new (complex_t, desired_length);
-		for (i = 0; i < desired_length; i++)
-			complex_real (&in[i],
-				      ((gnm_float const *)current->data->data)[i]);
-
-		gnm_fourier_fft (in, desired_length, 1, &fourier, info->inverse);
-		g_free (in);
-
-		if (fourier) {
-			for (row = 0; row < given_length; row++) {
-				dao_set_cell_float (dao, col, row + 2,
-						fourier[row].re);
-				dao_set_cell_float (dao, col + 1, row + 2,
-						fourier[row].im);
-			}
-			g_free (fourier);
-		}
-
-		col += 2;
-	}
-	dao_set_italic (dao, 0, 0, col - 1, 1);
-
-	destroy_data_set_list (data);
+	dao_redraw_respan (dao);
 
-	return 0;
+	return FALSE;
 }
 
 static int
 analysis_tool_fourier_calc_length (analysis_tools_data_fourier_t *info)
 {
-	Sheet         *sheet = wb_control_cur_sheet(info->base.wbc);
-	GPtrArray     *data = new_data_set_list (info->base.input, info->base.group_by,
-						 TRUE, info->base.labels, sheet);
-	int           result = 1;
-	guint         dataset;
-
-	for (dataset = 0; dataset < data->len; dataset++) {
-		data_set_t    *current;
-		int           given_length;
+	int m = 1, n = analysis_tool_calc_length (&info->base);
 
-		current = g_ptr_array_index (data, dataset);
-		given_length = current->data->len;
-		if (given_length > result)
-			result = given_length;
-	}
-	destroy_data_set_list (data);
-	return result;
+	while (m < n)
+		m *= 2;
+	return m;
 }
 
 
@@ -4476,7 +4256,7 @@ analysis_tool_fourier_engine (data_analysis_output_t *dao, gpointer specs,
 	case TOOL_ENGINE_UPDATE_DAO:
 		prepare_input_range (&info->base.input, info->base.group_by);
 		dao_adjust (dao, 2 * g_slist_length (info->base.input),
-			    2 + analysis_tool_fourier_calc_length (specs));
+			    3 + analysis_tool_fourier_calc_length (specs));
 		return FALSE;
 	case TOOL_ENGINE_CLEAN_UP:
 		return analysis_tool_generic_clean (specs);
diff --git a/src/tools/analysis-tools.h b/src/tools/analysis-tools.h
index f100e3c..29f7bb5 100644
--- a/src/tools/analysis-tools.h
+++ b/src/tools/analysis-tools.h
@@ -217,9 +217,6 @@ const GnmExpr *make_rangeref (int dx0, int dy0, int dx1, int dy1);
 
 gnm_float *range_sort (gnm_float const *xs, int n);
 
-void gnm_fourier_fft (complex_t const *in, int n, int skip,
-					  complex_t **fourier, gboolean inverse);
-
 void set_cell_text_row (data_analysis_output_t *dao, 
 			int col, int row, const char *text);
 void set_cell_text_col (data_analysis_output_t *dao, 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]