/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * fill-series.c: Fill according to a linear or exponential serie. * * Authors: * Jukka-Pekka Iivonen * Andreas J. Guelzow * * (C) Copyright 2003 by Jukka-Pekka Iivonen * (C) Copyright 2003 by Andreas J. Guelzow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include "tools.h" #include "numbers.h" #include "mathfunc.h" #include "fill-series.h" #include "dao.h" #include "datetime.h" static void do_row_filling_wday (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { int steps = (i * info->step_value) + 0.5; int days = (steps / 5) * 7 + steps % 5; GDateWeekday wd; datetime_serial_to_g (&date, start, conv); wd = g_date_get_weekday (&date); if (wd + (steps % 5) > G_DATE_FRIDAY) days += 2; if (days > 0) g_date_add_days (&date, days); else g_date_subtract_days (&date, - days); dao_set_cell_float (dao, i, 0, datetime_g_to_serial (&date, conv)); } } static void do_column_filling_wday (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { int steps = (i * info->step_value) + 0.5; int days = (steps / 5) * 7 + steps % 5; GDateWeekday wd; datetime_serial_to_g (&date, start, conv); wd = g_date_get_weekday (&date); if (wd + (steps % 5) > G_DATE_FRIDAY) days += 2; if (days > 0) g_date_add_days (&date, days); else g_date_subtract_days (&date, - days); dao_set_cell_float (dao, 0,i, datetime_g_to_serial (&date, conv)); } } static void do_row_filling_month (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { datetime_serial_to_g (&date, start, conv); if (info->step_value > 0) g_date_add_months (&date, i * info->step_value); else g_date_subtract_months (&date,- i * info->step_value); dao_set_cell_float (dao, i, 0, datetime_g_to_serial (&date, conv)); } } static void do_column_filling_month (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { datetime_serial_to_g (&date, start, conv); if (info->step_value > 0) g_date_add_months (&date, i * info->step_value); else g_date_subtract_months (&date,- i * info->step_value); dao_set_cell_float (dao, 0, i, datetime_g_to_serial (&date, conv)); } } static void do_row_filling_year (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { datetime_serial_to_g (&date, start, conv); if (info->step_value > 0) g_date_add_years (&date, i * info->step_value); else g_date_subtract_years (&date,- i * info->step_value); dao_set_cell_float (dao, i, 0, datetime_g_to_serial (&date, conv)); } } static void do_column_filling_year (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; GDate date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); for (i = 0; i < info->n; i++) { datetime_serial_to_g (&date, start, conv); if (info->step_value > 0) g_date_add_years (&date, i * info->step_value); else g_date_subtract_years (&date,- i * info->step_value); dao_set_cell_float (dao, 0, i, datetime_g_to_serial (&date, conv)); } } static void do_row_filling_linear (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; gnm_float step = info->step_value; for (i = 0; i < info->n; i++) { dao_set_cell_float (dao, i, 0, start); start += step; } } static void do_column_filling_linear (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; gnm_float step = info->step_value; for (i = 0; i < info->n; i++) { dao_set_cell_float (dao, 0, i, start); start += step; } } static void do_row_filling_growth (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; gnm_float step = info->step_value; for (i = 0; i < info->n; i++) { dao_set_cell_float (dao, i, 0, start); start *= step; } } static void do_column_filling_growth (data_analysis_output_t *dao, fill_series_t *info) { int i; gnm_float start = info->start_value; gnm_float step = info->step_value; for (i = 0; i < info->n; i++) { dao_set_cell_float (dao, 0, i, start); start *= step; } } static void fill_series_adjust_variables (data_analysis_output_t *dao, fill_series_t *info) { int length_of_series = -1; int length_of_space = info->series_in_rows ? dao->cols : dao->rows; if (info->type == FillSeriesTypeDate && info->date_unit != FillSeriesUnitDay) { if (info->is_step_set) info->step_value = floorgnum (info->step_value + 0.5); else /* FIXME */ info->step_value = 1; if (info->is_stop_set) { GDate from_date, to_date; GnmDateConventions const *conv = workbook_date_conv (dao->sheet->workbook); if (info->step_value < 0) { datetime_serial_to_g (&from_date, info->stop_value, conv); datetime_serial_to_g (&to_date, info->start_value, conv); } else { datetime_serial_to_g (&from_date, info->start_value, conv); datetime_serial_to_g (&to_date, info->stop_value, conv); } switch (info->date_unit) { case FillSeriesUnitDay: /* This should not happen*/ break; case FillSeriesUnitWeekday: { int days; days = g_date_days_between (&from_date, &to_date); length_of_series = (days / 7) * 5 + 1 + (days % 7); if (length_of_series < 1) length_of_series = 1; } break; case FillSeriesUnitMonth: { GDateYear from_year, to_year; GDateMonth from_month, to_month; gint months; from_year = g_date_get_year(&from_date); to_year = g_date_get_year(&to_date); from_month = g_date_get_month(&from_date); to_month = g_date_get_month(&to_date); g_date_set_year (&to_date, from_year); if (g_date_compare (&from_date, &to_date) > 0) months = (to_year - from_year) * 12 + (to_month - from_month); else months = (to_year - from_year) * 12 + (to_month - from_month) + 1; length_of_series = months / (int)(info->step_value + 0.5); if (length_of_series < 1) length_of_series = 1; } break; case FillSeriesUnitYear: { GDateYear from_year, to_year; gint years; from_year = g_date_get_year(&from_date); to_year = g_date_get_year(&to_date); g_date_set_year (&to_date, from_year); if (g_date_compare (&from_date, &to_date) > 0) years = to_year - from_year; else years = to_year - from_year + 1; length_of_series = years / (int)(info->step_value + 0.5); if (length_of_series < 1) length_of_series = 1; } break; } } } else { if (!info->is_step_set) { switch (info->type) { case FillSeriesTypeDate: case FillSeriesTypeLinear: info->step_value = (info->stop_value - info->start_value)/ (length_of_space - 1); break; case FillSeriesTypeGrowth: info->step_value = expgnum((loggnum(info->stop_value /info->start_value))/ (length_of_space - 1)); break; } info->is_step_set = TRUE; } else if (info->is_stop_set) { switch (info->type) { case FillSeriesTypeDate: case FillSeriesTypeLinear: length_of_series = floorgnum(GNUM_EPSILON + 1 + (info->stop_value - info->start_value)/ info->step_value); if (length_of_series < 0) length_of_series = 1; break; case FillSeriesTypeGrowth: length_of_series = floorgnum(GNUM_EPSILON + 1 + (loggnum(info->stop_value /info->start_value))/ loggnum(info->step_value)); if (length_of_series < 0) length_of_series = 1; break; } } } if (info->series_in_rows) { dao_adjust (dao, length_of_series, 1); info->n = dao->cols; } else { dao_adjust (dao, 1, length_of_series); info->n = dao->rows; } if (length_of_series > 0) info->n = length_of_series; } gboolean fill_series_engine (data_analysis_output_t *dao, gpointer specs, analysis_tool_engine_t selector, gpointer result) { fill_series_t *info = specs; switch (selector) { case TOOL_ENGINE_UPDATE_DESCRIPTOR: return (dao_command_descriptor (dao, _("Fill Series (%s)"), result) == NULL); case TOOL_ENGINE_UPDATE_DAO: fill_series_adjust_variables (dao, info); return FALSE; case TOOL_ENGINE_CLEAN_UP: return FALSE; case TOOL_ENGINE_LAST_VALIDITY_CHECK: return FALSE; case TOOL_ENGINE_PREPARE_OUTPUT_RANGE: dao_prepare_output (NULL, dao, _("Fill Series")); return FALSE; case TOOL_ENGINE_FORMAT_OUTPUT_RANGE: return dao_format_output (dao, _("Fill Series")); case TOOL_ENGINE_PERFORM_CALC: default: switch (info->type) { case FillSeriesTypeLinear: if (info->series_in_rows) do_row_filling_linear (dao, info); else do_column_filling_linear (dao, info); break; case FillSeriesTypeGrowth: if (info->series_in_rows) do_row_filling_growth (dao, info); else do_column_filling_growth (dao, info); break; case FillSeriesTypeDate: switch (info->date_unit) { case FillSeriesUnitDay: if (info->series_in_rows) do_row_filling_linear (dao, info); else do_column_filling_linear (dao, info); break; case FillSeriesUnitWeekday: if (info->series_in_rows) do_row_filling_wday (dao, info); else do_column_filling_wday (dao, info); break; case FillSeriesUnitMonth: if (info->series_in_rows) do_row_filling_month (dao, info); else do_column_filling_month (dao, info); break; case FillSeriesUnitYear: if (info->series_in_rows) do_row_filling_year (dao, info); else do_column_filling_year (dao, info); break; } dao_set_date (dao, 0, 0, dao->cols - 1, dao->rows -1); break; } return FALSE; } return TRUE; /* We shouldn't get here */ }