+ free(rrd_values);
+ return 0;
+ }
+ }
+ }
+
+ /* allocate queues, one for each data source */
+ buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *) * row_length);
+ for (i = 0; i < row_length; ++i) {
+ queue_alloc(&(buffers[i]), 2 * offset + 1);
+ }
+ /* need working average initialized to 0 */
+ working_average = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
+ baseline = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t));
+
+ /* compute sums of the first 2*offset terms */
+ for (i = 0; i < 2 * offset; ++i) {
+ k = MyMod(i - offset, row_count);
+ for (j = 0; j < row_length; ++j) {
+ queue_push(buffers[j], rrd_values[k * row_length + j]);
+ working_average[j] += rrd_values[k * row_length + j];
+ }
+ }
+
+ /* compute moving averages */
+ for (i = offset; i < row_count + offset; ++i) {
+ for (j = 0; j < row_length; ++j) {
+ k = MyMod(i, row_count);
+ /* add a term to the sum */
+ working_average[j] += rrd_values[k * row_length + j];
+ queue_push(buffers[j], rrd_values[k * row_length + j]);
+
+ /* reset k to be the center of the window */
+ k = MyMod(i - offset, row_count);
+ /* overwrite rdd_values entry, the old value is already
+ * saved in buffers */
+ rrd_values[k * row_length + j] =
+ working_average[j] / (2 * offset + 1);
+ baseline[j] += rrd_values[k * row_length + j];
+
+ /* remove a term from the sum */
+ working_average[j] -= queue_pop(buffers[j]);
+ }
+ }
+
+ for (i = 0; i < row_length; ++i) {
+ queue_dealloc(buffers[i]);
+ baseline[i] /= row_count;
+ }
+ free(buffers);
+ free(working_average);
+
+ if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
+ rrd_value_t (
+ *init_seasonality) (
+ rrd_value_t seasonal_coef,
+ rrd_value_t intercept);
+
+ switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) {
+ case CF_HWPREDICT:
+ init_seasonality = hw_additive_init_seasonality;
+ break;
+ case CF_MHWPREDICT:
+ init_seasonality = hw_multiplicative_init_seasonality;
+ break;
+ default:
+ rrd_set_error("apply smoother: SEASONAL rra doesn't have "
+ "valid dependency: %s",
+ rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam);
+ return -1;
+ }
+
+ for (j = 0; j < row_length; ++j) {
+ for (i = 0; i < row_count; ++i) {
+ rrd_values[i * row_length + j] =
+ init_seasonality(rrd_values[i * row_length + j],
+ baseline[j]);
+ }
+ /* update the baseline coefficient,
+ * first, compute the cdp_index. */
+ offset = hw_dep_idx(rrd, rra_idx) * row_length + j;
+ (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val +=
+ baseline[j];
+ }
+ /* flush cdp to disk */
+ if (rrd_seek(rrd_file, sizeof(stat_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
+ rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
+ sizeof(live_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(pdp_prep_t), SEEK_SET)) {
+ rrd_set_error("apply_smoother: seek to cdp_prep failed");
+ free(rrd_values);
+ return -1;
+ }
+ if (rrd_write(rrd_file, rrd->cdp_prep,
+ sizeof(cdp_prep_t) *
+ (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt)
+ != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) *
+ (rrd->stat_head->ds_cnt))) {
+ rrd_set_error("apply_smoother: cdp_prep write failed");
+ free(rrd_values);
+ return -1;
+ }
+ }
+
+ /* endif CF_SEASONAL */
+ /* flush updated values to disk */
+ if (rrd_seek(rrd_file, rra_start, SEEK_SET)) {
+ rrd_set_error("apply_smoother: seek to pos %d failed", rra_start);
+ free(rrd_values);
+ return -1;
+ }
+ /* write as a single block */
+ if (rrd_write
+ (rrd_file, rrd_values, sizeof(rrd_value_t) * row_length * row_count)
+ != (ssize_t) (sizeof(rrd_value_t) * row_length * row_count)) {
+ rrd_set_error("apply_smoother: write failed to %lu", rra_start);
+ free(rrd_values);
+ return -1;
+ }
+
+ free(rrd_values);
+ free(baseline);
+ return 0;