1.12.42
[gnumeric.git] / src / tools / fill-series.c
blobd22023819319c5928698d07e1e84cf7880dc2752
1 /*
2 * fill-series.c: Fill according to a linear or exponential serie.
4 * Authors:
5 * Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
6 * Andreas J. Guelzow <aguelzow@taliesin.ca>
8 * (C) Copyright 2003 by Jukka-Pekka Iivonen <jiivonen@hutcs.cs.hut.fi>
9 * (C) Copyright 2003 by Andreas J. Guelzow <aguelzow@taliesin.ca>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses/>.
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
27 #include <gnumeric.h>
29 #include <sheet.h>
30 #include <sheet-filter.h>
31 #include <cell.h>
32 #include <ranges.h>
33 #include <value.h>
34 #include <gnm-format.h>
35 #include <workbook.h>
36 #include <tools/tools.h>
37 #include <numbers.h>
38 #include <gnm-datetime.h>
40 #include <mathfunc.h>
41 #include <tools/fill-series.h>
42 #include <goffice/goffice.h>
44 static void
45 do_row_filling_wday (data_analysis_output_t *dao, fill_series_t *info)
47 int i;
48 gnm_float start = info->start_value;
49 GDate date;
50 GODateConventions const *conv =
51 sheet_date_conv (dao->sheet);
54 for (i = 0; i < info->n; i++) {
55 int steps = (i * info->step_value) + 0.5;
56 int days = (steps / 5) * 7 + steps % 5;
57 GDateWeekday wd;
59 go_date_serial_to_g (&date, start, conv);
60 wd = g_date_get_weekday (&date);
61 if (wd + (steps % 5) > G_DATE_FRIDAY)
62 days += 2;
63 gnm_date_add_days (&date, days);
65 dao_set_cell_float (dao, i, 0,
66 go_date_g_to_serial (&date, conv));
71 static void
72 do_column_filling_wday (data_analysis_output_t *dao, fill_series_t *info)
74 int i;
75 gnm_float start = info->start_value;
76 GDate date;
77 GODateConventions const *conv =
78 sheet_date_conv (dao->sheet);
81 for (i = 0; i < info->n; i++) {
82 int steps = (i * info->step_value) + 0.5;
83 int days = (steps / 5) * 7 + steps % 5;
84 GDateWeekday wd;
86 go_date_serial_to_g (&date, start, conv);
87 wd = g_date_get_weekday (&date);
88 if (wd + (steps % 5) > G_DATE_FRIDAY)
89 days += 2;
90 gnm_date_add_days (&date, days);
92 dao_set_cell_float (dao, 0,i,
93 go_date_g_to_serial (&date, conv));
99 static void
100 do_row_filling_month (data_analysis_output_t *dao, fill_series_t *info)
102 int i;
103 gnm_float start = info->start_value;
104 GDate date;
105 GODateConventions const *conv =
106 sheet_date_conv (dao->sheet);
109 for (i = 0; i < info->n; i++) {
110 go_date_serial_to_g (&date, start, conv);
111 gnm_date_add_months (&date, i * info->step_value);
113 dao_set_cell_float (dao, i, 0,
114 go_date_g_to_serial (&date, conv));
118 static void
119 do_column_filling_month (data_analysis_output_t *dao, fill_series_t *info)
121 int i;
122 gnm_float start = info->start_value;
123 GDate date;
124 GODateConventions const *conv =
125 sheet_date_conv (dao->sheet);
128 for (i = 0; i < info->n; i++) {
129 go_date_serial_to_g (&date, start, conv);
130 gnm_date_add_months (&date, i * info->step_value);
132 dao_set_cell_float (dao, 0, i,
133 go_date_g_to_serial (&date, conv));
137 static void
138 do_row_filling_year (data_analysis_output_t *dao, fill_series_t *info)
140 int i;
141 gnm_float start = info->start_value;
142 GDate date;
143 GODateConventions const *conv =
144 sheet_date_conv (dao->sheet);
147 for (i = 0; i < info->n; i++) {
148 go_date_serial_to_g (&date, start, conv);
149 gnm_date_add_years (&date, i * info->step_value);
151 dao_set_cell_float (dao, i, 0,
152 go_date_g_to_serial (&date, conv));
156 static void
157 do_column_filling_year (data_analysis_output_t *dao, fill_series_t *info)
159 int i;
160 gnm_float start = info->start_value;
161 GDate date;
162 GODateConventions const *conv =
163 sheet_date_conv (dao->sheet);
166 for (i = 0; i < info->n; i++) {
167 go_date_serial_to_g (&date, start, conv);
168 gnm_date_add_years (&date, i * info->step_value);
170 dao_set_cell_float (dao, 0, i,
171 go_date_g_to_serial (&date, conv));
175 static void
176 do_row_filling_linear (data_analysis_output_t *dao, fill_series_t *info)
178 int i;
179 gnm_float start = info->start_value;
180 gnm_float step = info->step_value;
182 for (i = 0; i < info->n; i++) {
183 dao_set_cell_float (dao, i, 0, start);
184 start += step;
188 static void
189 do_column_filling_linear (data_analysis_output_t *dao, fill_series_t *info)
191 int i;
192 gnm_float start = info->start_value;
193 gnm_float step = info->step_value;
195 for (i = 0; i < info->n; i++) {
196 dao_set_cell_float (dao, 0, i, start);
197 start += step;
201 static void
202 do_row_filling_growth (data_analysis_output_t *dao, fill_series_t *info)
204 int i;
205 gnm_float start = info->start_value;
206 gnm_float step = info->step_value;
208 for (i = 0; i < info->n; i++) {
209 dao_set_cell_float (dao, i, 0, start);
210 start *= step;
214 static void
215 do_column_filling_growth (data_analysis_output_t *dao, fill_series_t *info)
217 int i;
218 gnm_float start = info->start_value;
219 gnm_float step = info->step_value;
221 for (i = 0; i < info->n; i++) {
222 dao_set_cell_float (dao, 0, i, start);
223 start *= step;
227 static void
228 fill_series_adjust_variables (data_analysis_output_t *dao, fill_series_t *info)
230 int length_of_series = -1;
231 int length_of_space = info->series_in_rows
232 ? dao->cols : dao->rows;
234 if (info->type == FillSeriesTypeDate &&
235 info->date_unit != FillSeriesUnitDay) {
236 if (info->is_step_set)
237 info->step_value = gnm_floor (info->step_value + 0.5);
238 else /* FIXME */
239 info->step_value = 1;
240 if (info->is_stop_set) {
241 GDate from_date, to_date;
242 GODateConventions const *conv =
243 sheet_date_conv (dao->sheet);
245 if (info->step_value < 0) {
246 go_date_serial_to_g (&from_date,
247 info->stop_value, conv);
248 go_date_serial_to_g (&to_date,
249 info->start_value, conv);
250 } else {
251 go_date_serial_to_g (&from_date,
252 info->start_value, conv);
253 go_date_serial_to_g (&to_date,
254 info->stop_value, conv);
256 switch (info->date_unit) {
257 case FillSeriesUnitDay:
258 /* This should not happen*/
259 break;
260 case FillSeriesUnitWeekday:
262 int days;
263 days = g_date_days_between
264 (&from_date, &to_date);
265 length_of_series = (days / 7) * 5 + 1
266 + (days % 7);
267 if (length_of_series < 1)
268 length_of_series = 1;
270 break;
271 case FillSeriesUnitMonth:
273 GDateYear from_year, to_year;
274 GDateMonth from_month, to_month;
275 gint months;
277 from_year = g_date_get_year(&from_date);
278 to_year = g_date_get_year(&to_date);
279 from_month = g_date_get_month(&from_date);
280 to_month = g_date_get_month(&to_date);
281 g_date_set_year (&to_date, from_year);
283 if (g_date_compare (&from_date, &to_date) > 0)
284 months = (to_year - from_year) * 12 +
285 (to_month - from_month);
286 else
287 months = (to_year - from_year) * 12 +
288 (to_month - from_month) + 1;
289 length_of_series = months
290 / (int)(info->step_value + 0.5);
291 if (length_of_series < 1)
292 length_of_series = 1;
294 break;
295 case FillSeriesUnitYear:
297 GDateYear from_year, to_year;
298 gint years;
300 from_year = g_date_get_year(&from_date);
301 to_year = g_date_get_year(&to_date);
302 g_date_set_year (&to_date, from_year);
303 if (g_date_compare (&from_date, &to_date) > 0)
304 years = to_year - from_year;
305 else
306 years = to_year - from_year + 1;
307 length_of_series = years
308 / (int)(info->step_value + 0.5);
309 if (length_of_series < 1)
310 length_of_series = 1;
312 break;
316 } else {
317 if (!info->is_step_set) {
318 switch (info->type) {
319 case FillSeriesTypeDate:
320 case FillSeriesTypeLinear:
321 info->step_value =
322 (info->stop_value - info->start_value)/
323 (length_of_space - 1);
324 break;
325 case FillSeriesTypeGrowth:
326 info->step_value =
327 gnm_exp((gnm_log(info->stop_value
328 /info->start_value))/
329 (length_of_space - 1));
330 break;
332 info->is_step_set = TRUE;
333 } else if (info->is_stop_set) {
334 switch (info->type) {
335 case FillSeriesTypeDate:
336 case FillSeriesTypeLinear:
337 length_of_series
338 = gnm_floor(GNM_EPSILON + 1 +
339 (info->stop_value
340 - info->start_value)/
341 info->step_value);
342 if (length_of_series < 1)
343 length_of_series = 1;
344 break;
345 case FillSeriesTypeGrowth:
346 length_of_series
347 = gnm_floor(GNM_EPSILON + 1 +
348 (gnm_log(info->stop_value
349 /info->start_value))/
350 gnm_log(info->step_value));
351 if (length_of_series < 1)
352 length_of_series = 1;
353 break;
357 if (info->series_in_rows) {
358 dao_adjust (dao, length_of_series, 1);
359 info->n = dao->cols;
360 } else {
361 dao_adjust (dao, 1, length_of_series);
362 info->n = dao->rows;
364 if (length_of_series > 0)
365 info->n = length_of_series;
368 gboolean fill_series_engine (G_GNUC_UNUSED GOCmdContext *gcc, data_analysis_output_t *dao, gpointer specs,
369 analysis_tool_engine_t selector, gpointer result)
371 fill_series_t *info = specs;
373 switch (selector) {
374 case TOOL_ENGINE_UPDATE_DESCRIPTOR:
375 return (dao_command_descriptor (dao, _("Fill Series (%s)"),
376 result) == NULL);
377 case TOOL_ENGINE_UPDATE_DAO:
378 fill_series_adjust_variables (dao, info);
379 return FALSE;
380 case TOOL_ENGINE_CLEAN_UP:
381 return FALSE;
382 case TOOL_ENGINE_LAST_VALIDITY_CHECK:
383 return FALSE;
384 case TOOL_ENGINE_PREPARE_OUTPUT_RANGE:
385 dao_prepare_output (NULL, dao, _("Fill Series"));
386 return FALSE;
387 case TOOL_ENGINE_FORMAT_OUTPUT_RANGE:
388 return dao_format_output (dao, _("Fill Series"));
389 case TOOL_ENGINE_PERFORM_CALC:
390 default:
391 switch (info->type) {
392 case FillSeriesTypeLinear:
393 if (info->series_in_rows)
394 do_row_filling_linear (dao, info);
395 else
396 do_column_filling_linear (dao, info);
397 break;
398 case FillSeriesTypeGrowth:
399 if (info->series_in_rows)
400 do_row_filling_growth (dao, info);
401 else
402 do_column_filling_growth (dao, info);
403 break;
404 case FillSeriesTypeDate:
405 switch (info->date_unit) {
406 case FillSeriesUnitDay:
407 if (info->series_in_rows)
408 do_row_filling_linear (dao, info);
409 else
410 do_column_filling_linear (dao, info);
411 break;
412 case FillSeriesUnitWeekday:
413 if (info->series_in_rows)
414 do_row_filling_wday (dao, info);
415 else
416 do_column_filling_wday (dao, info);
417 break;
418 case FillSeriesUnitMonth:
419 if (info->series_in_rows)
420 do_row_filling_month (dao, info);
421 else
422 do_column_filling_month (dao, info);
423 break;
424 case FillSeriesUnitYear:
425 if (info->series_in_rows)
426 do_row_filling_year (dao, info);
427 else
428 do_column_filling_year (dao, info);
429 break;
431 dao_set_date (dao, 0, 0,
432 dao->cols - 1, dao->rows -1);
433 break;
435 return FALSE;
437 return TRUE; /* We shouldn't get here */