Libpolycomp  1.0
A compression/decompression library that implements the polynomial compression and other simple compression schemes
poly.c
1 /* poly.c - Polynomial (de)compression routines
2  *
3  * Copyright (c) 2015 Maurizio Tomasi
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "libpolycomp.h"
27 #include <assert.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <gsl/gsl_block_double.h>
33 #include <gsl/gsl_vector_double.h>
34 #include <gsl/gsl_matrix_double.h>
35 #include <gsl/gsl_multifit.h>
36 
37 #include <fftw3.h>
38 
39 /**********************************************************************/
40 
113 /**********************************************************************/
114 
115 static double integer_power(int x, int y)
116 {
117  double dbl_x = (double)x;
118 
119  if (y < 0)
120  abort();
121 
122  if (y == 0)
123  return 1.0;
124  else if (y == 1)
125  return dbl_x;
126  else {
127  double result = dbl_x * dbl_x;
128  int cur_power = 2;
129  while (2 * cur_power < y) {
130  result *= result;
131  cur_power *= 2;
132  }
133 
134  if (y > cur_power)
135  result *= integer_power(x, y - cur_power);
136 
137  return result;
138  }
139 }
140 
141 /***********************************************************************
142  * Types and functions used for polynomial least-square fitting
143  */
144 
146  size_t num_of_samples;
147  size_t num_of_coeffs;
148  gsl_multifit_linear_workspace* workspace;
149  gsl_matrix* matrix;
150  gsl_vector* y;
151  gsl_vector* c;
152  gsl_matrix* cov_matrix;
153 };
154 
174  size_t num_of_coeffs)
175 {
176  size_t i, j;
177 
178  pcomp_poly_fit_data_t* poly_fit
179  = malloc(sizeof(pcomp_poly_fit_data_t));
180  if (poly_fit == NULL)
181  abort();
182 
183  poly_fit->num_of_samples = num_of_samples;
184  poly_fit->num_of_coeffs = num_of_coeffs;
185  poly_fit->workspace
186  = gsl_multifit_linear_alloc(num_of_samples, num_of_coeffs);
187  poly_fit->matrix = gsl_matrix_alloc(num_of_samples, num_of_coeffs);
188  poly_fit->y = gsl_vector_alloc(num_of_samples);
189  poly_fit->c = gsl_vector_alloc(num_of_coeffs);
190  poly_fit->cov_matrix
191  = gsl_matrix_alloc(num_of_coeffs, num_of_coeffs);
192 
193  for (i = 0; i < num_of_samples; ++i) {
194  for (j = 0; j < num_of_coeffs; ++j) {
195  gsl_matrix_set(poly_fit->matrix, i, j,
196  integer_power(i + 1, j));
197  }
198  }
199 
200  return poly_fit;
201 }
202 
211 {
212  if (poly_fit == NULL)
213  return;
214 
215  gsl_matrix_free(poly_fit->matrix);
216  gsl_vector_free(poly_fit->y);
217  gsl_vector_free(poly_fit->c);
218  gsl_matrix_free(poly_fit->cov_matrix);
219  gsl_multifit_linear_free(poly_fit->workspace);
220 
221  free(poly_fit);
222 }
223 
233 size_t
235 {
236  if (poly_fit == NULL)
237  abort();
238 
239  return poly_fit->num_of_samples;
240 }
241 
252 size_t
254 {
255  if (poly_fit == NULL)
256  abort();
257 
258  return poly_fit->num_of_coeffs;
259 }
260 
301 int pcomp_run_poly_fit(pcomp_poly_fit_data_t* poly_fit, double* coeffs,
302  const double* points)
303 {
304  size_t idx;
305  double chisq;
306 
307  if (poly_fit == NULL || coeffs == NULL || points == NULL)
308  abort();
309 
310  for (idx = 0; idx < poly_fit->num_of_samples; ++idx) {
311  gsl_vector_set(poly_fit->y, idx, points[idx]);
312  }
313 
314  if (gsl_multifit_linear(poly_fit->matrix, poly_fit->y, poly_fit->c,
315  poly_fit->cov_matrix, &chisq,
316  poly_fit->workspace) != 0) {
317  return PCOMP_STAT_INVALID_FIT;
318  }
319 
320  for (idx = 0; idx < poly_fit->num_of_coeffs; ++idx) {
321  coeffs[idx] = gsl_vector_get(poly_fit->c, idx);
322  }
323 
324  return PCOMP_STAT_SUCCESS;
325 }
326 
327 /***********************************************************************
328  * Types and functions used for computing Chebyshev transforms
329  */
330 
332  double* input;
333  double* output;
334  size_t num_of_samples;
335  fftw_plan fftw_plan_ptr;
337 };
338 
367 {
368  pcomp_chebyshev_t* chebyshev = malloc(sizeof(pcomp_chebyshev_t));
369  if (chebyshev == NULL)
370  abort();
371 
372  chebyshev->input = fftw_alloc_real(num_of_samples);
373  chebyshev->output = fftw_alloc_real(num_of_samples);
374  chebyshev->num_of_samples = num_of_samples;
375  chebyshev->fftw_plan_ptr = fftw_plan_r2r_1d(
376  num_of_samples, chebyshev->input, chebyshev->output,
377  FFTW_REDFT00, FFTW_ESTIMATE);
378  chebyshev->dir = dir;
379 
380  return chebyshev;
381 }
382 
391 {
392  if (plan == NULL)
393  return;
394 
395  if (plan->input != NULL)
396  fftw_free(plan->input);
397 
398  if (plan->output != NULL)
399  fftw_free(plan->output);
400 
401  if (plan->fftw_plan_ptr != NULL)
402  fftw_destroy_plan(plan->fftw_plan_ptr);
403 
404  free(plan);
405 }
406 
417 {
418  if (plan == NULL)
419  abort();
420 
421  return plan->num_of_samples;
422 }
423 
436 {
437  if (plan == NULL)
438  abort();
439 
440  return plan->dir;
441 }
442 
443 static double chebyshev_normalization(pcomp_transform_direction_t dir,
444  size_t num_of_samples)
445 {
446  if (dir == PCOMP_TD_DIRECT)
447  return 1.0 / (((double)num_of_samples) - 1.0);
448  else
449  return 0.5;
450 }
451 
492  pcomp_transform_direction_t dir, double* output,
493  const double* input)
494 {
495  double norm;
496  size_t idx;
497 
498  if (plan == NULL)
499  abort();
500 
501  if (input != NULL) {
502  for (idx = 0; idx < plan->num_of_samples; ++idx) {
503  plan->input[idx] = input[idx];
504  }
505  }
506 
507  fftw_execute(plan->fftw_plan_ptr);
508  norm = chebyshev_normalization(dir, plan->num_of_samples);
509 
510  for (idx = 0; idx < plan->num_of_samples; ++idx) {
511  plan->output[idx] *= norm;
512  }
513 
514  if (output != NULL && output != plan->output) {
515  for (idx = 0; idx < plan->num_of_samples; ++idx) {
516  output[idx] = plan->output[idx];
517  }
518  }
519 
520  return PCOMP_STAT_SUCCESS;
521 }
522 
537 const double* pcomp_chebyshev_input(const pcomp_chebyshev_t* plan)
538 {
539  if (plan == NULL)
540  abort();
541  return plan->input;
542 }
543 
558 const double* pcomp_chebyshev_output(const pcomp_chebyshev_t* plan)
559 {
560  if (plan == NULL)
561  abort();
562  return plan->output;
563 }
564 
565 /***********************************************************************
566  * Types and functions used for applying the combined
567  * fitting/Chebyshev transforms
568  */
569 
571  size_t samples_per_chunk;
572  pcomp_poly_fit_data_t* poly_fit;
573  pcomp_chebyshev_t* chebyshev;
574  pcomp_chebyshev_t* inv_chebyshev;
575  double max_allowable_error;
576  pcomp_polycomp_algorithm_t algorithm;
577  double period;
578 };
579 
598 pcomp_init_polycomp(pcomp_chunk_size_t samples_per_chunk,
599  pcomp_poly_size_t num_of_coeffs,
600  double max_allowable_error,
601  pcomp_polycomp_algorithm_t algorithm)
602 {
603  pcomp_polycomp_t* params = malloc(sizeof(pcomp_polycomp_t));
604  if (params == NULL)
605  abort();
606 
607  params->samples_per_chunk = samples_per_chunk;
608  params->poly_fit
609  = pcomp_init_poly_fit(samples_per_chunk, num_of_coeffs);
610  params->chebyshev
611  = pcomp_init_chebyshev(samples_per_chunk, PCOMP_TD_DIRECT);
612  params->inv_chebyshev
613  = pcomp_init_chebyshev(samples_per_chunk, PCOMP_TD_INVERSE);
614  params->max_allowable_error = max_allowable_error;
615  params->algorithm = algorithm;
616  params->period = 0.0;
617 
618  return params;
619 }
620 
629 {
630  if (params == NULL)
631  return;
632 
633  pcomp_free_poly_fit(params->poly_fit);
634  pcomp_free_chebyshev(params->chebyshev);
635  pcomp_free_chebyshev(params->inv_chebyshev);
636 
637  free(params);
638 }
639 
654 pcomp_chunk_size_t
656 {
657  if (params == NULL)
658  abort();
659 
660  return params->samples_per_chunk;
661 }
662 
676 pcomp_poly_size_t
678 {
679  if (params == NULL || params->poly_fit == NULL)
680  abort();
681 
682  return params->poly_fit->num_of_coeffs;
683 }
684 
696 {
697  if (params == NULL)
698  abort();
699 
700  return params->max_allowable_error;
701 }
702 
715 {
716  if (params == NULL)
717  abort();
718 
719  return params->algorithm;
720 }
721 
734 {
735  if (params == NULL)
736  abort();
737 
738  return params->chebyshev;
739 }
740 
753 {
754  if (params == NULL)
755  abort();
756 
757  return params->inv_chebyshev;
758 }
759 
774 {
775  if (params == NULL)
776  abort();
777 
778  return params->period;
779 }
780 
799 void pcomp_polycomp_set_period(pcomp_polycomp_t* params, double period)
800 {
801  if (params == NULL)
802  abort();
803 
804  params->period = period;
805 }
806 
807 /***********************************************************************
808  * Evaluate the value of a polynomial at a point using Horner's formula
809  */
810 
811 static double eval_poly(double* coeffs, size_t num_of_coeffs, double x)
812 {
813  if (coeffs == NULL)
814  abort();
815 
816  if (num_of_coeffs >= 1) {
817  size_t idx = num_of_coeffs - 1;
818  double result = coeffs[idx];
819 
820  if (num_of_coeffs == 1)
821  return result;
822 
823  --idx;
824  while (1) {
825  result = result * x + coeffs[idx];
826 
827  if (idx > 0)
828  --idx;
829  else
830  break;
831  }
832 
833  return result;
834  }
835  else
836  return 0.0;
837 }
838 
839 /***********************************************************************/
840 
864 void pcomp_straighten(double* output, const double* input,
865  size_t num_of_samples, double period)
866 {
867  size_t idx;
868 
869  if (input == NULL || output == NULL)
870  abort();
871 
872  if (period > 0) {
873  double half_period = period * 0.5;
874  double offset = 0.0;
875 
876  output[0] = input[0];
877 
878  for (idx = 1; idx < num_of_samples; ++idx) {
879  double diff_with_previous = input[idx] - input[idx - 1];
880  if (diff_with_previous > half_period)
881  offset -= period;
882  else if (diff_with_previous < -half_period)
883  offset += period;
884 
885  output[idx] = input[idx] + offset;
886  }
887  }
888  else {
889  for (idx = 0; idx < num_of_samples; ++idx)
890  output[idx] = input[idx];
891  }
892 }
893 
894 /***********************************************************************
895  * Chunk initialization/destruction
896  */
897 
898 /* Information about a chunk of data compressed using the polynomial
899  * compression */
901  /* Number of samples in this chunk */
902  size_t num_of_samples;
903 
904  /* Is this chunk compressed using polynomial/Chebyshev
905  * coefficients? */
906  int is_compressed;
907  /* If the chunk is not compressed (is_compressed == 0), this
908  * points to a buffer which holds "num_of_samples" uncompressed
909  * samples */
910  double* uncompressed;
911 
912  /* Polynomial coefficients, from the lowest-order to the
913  * highest-order */
914  size_t num_of_poly_coeffs;
915  double* poly_coeffs;
916 
917  /* Chebyshev coefficients */
918  uint8_t* cheby_mask;
919  size_t num_of_cheby_coeffs; /* This is always less than
920  * num_of_samples, as the Chebyshev
921  * series is chopped. */
922  double* cheby_coeffs;
923 };
924 
936 pcomp_init_chunk(pcomp_chunk_size_t num_of_samples)
937 {
939  = malloc(sizeof(pcomp_polycomp_chunk_t));
940  if (chunk == NULL)
941  abort();
942 
943  chunk->num_of_samples = num_of_samples;
944 
945  chunk->is_compressed = 0;
946  chunk->uncompressed
947  = malloc(sizeof(double) * sizeof(chunk->num_of_samples));
948 
949  chunk->num_of_poly_coeffs = 0;
950  chunk->poly_coeffs = NULL;
951 
952  chunk->num_of_cheby_coeffs = 0;
953  chunk->cheby_coeffs = NULL;
954  chunk->cheby_mask = NULL;
955 
956  return chunk;
957 }
958 
975 pcomp_init_uncompressed_chunk(pcomp_chunk_size_t num_of_samples,
976  const double* samples)
977 {
979  = malloc(sizeof(pcomp_polycomp_chunk_t));
980  const size_t num_of_bytes = sizeof(double) * num_of_samples;
981  if (chunk == NULL)
982  abort();
983 
984  chunk->num_of_samples = num_of_samples;
985 
986  chunk->is_compressed = 0;
987  chunk->uncompressed = malloc(num_of_bytes);
988  if (chunk->uncompressed == NULL)
989  abort();
990  memcpy(chunk->uncompressed, samples, num_of_bytes);
991 
992  chunk->num_of_poly_coeffs = 0;
993  chunk->poly_coeffs = NULL;
994 
995  chunk->num_of_cheby_coeffs = 0;
996  chunk->cheby_coeffs = NULL;
997  chunk->cheby_mask = NULL;
998 
999  return chunk;
1000 }
1001 
1034  pcomp_chunk_size_t num_of_samples,
1035  pcomp_poly_size_t num_of_poly_coeffs, const double* poly_coeffs,
1036  pcomp_chunk_size_t num_of_cheby_coeffs, const uint8_t* cheby_mask,
1037  const double* cheby_coeffs)
1038 {
1039  size_t size;
1040  pcomp_polycomp_chunk_t* chunk;
1041 
1042  if (num_of_samples == 0 || poly_coeffs == NULL)
1043  abort();
1044 
1045  chunk = malloc(sizeof(pcomp_polycomp_chunk_t));
1046  if (chunk == NULL)
1047  abort();
1048 
1049  chunk->num_of_samples = num_of_samples;
1050  chunk->is_compressed = 1;
1051  chunk->uncompressed = NULL;
1052 
1053  chunk->num_of_poly_coeffs = num_of_poly_coeffs;
1054  size = num_of_poly_coeffs * sizeof(double);
1055  chunk->poly_coeffs = malloc(size);
1056  if (chunk->poly_coeffs == NULL)
1057  abort();
1058  memcpy(chunk->poly_coeffs, poly_coeffs, size);
1059 
1060  chunk->num_of_cheby_coeffs = num_of_cheby_coeffs;
1061  if (num_of_cheby_coeffs > 0) {
1062  size = num_of_cheby_coeffs * sizeof(double);
1063  chunk->cheby_coeffs = malloc(size);
1064  if (chunk->cheby_coeffs == NULL)
1065  abort();
1066  memcpy(chunk->cheby_coeffs, cheby_coeffs, size);
1067 
1068  size = pcomp_chunk_cheby_mask_size(num_of_samples)
1069  * sizeof(uint8_t);
1070  chunk->cheby_mask = malloc(size);
1071  if (chunk->cheby_mask == NULL)
1072  abort();
1073  memcpy(chunk->cheby_mask, cheby_mask, size);
1074  }
1075  else {
1076  chunk->cheby_mask = NULL;
1077  chunk->cheby_coeffs = NULL;
1078  }
1079 
1080  return chunk;
1081 }
1082 
1096 {
1097  if (chunk == NULL)
1098  return;
1099 
1100  if (chunk->uncompressed != NULL)
1101  free(chunk->uncompressed);
1102 
1103  if (chunk->poly_coeffs != NULL)
1104  free(chunk->poly_coeffs);
1105 
1106  if (chunk->cheby_coeffs != NULL)
1107  free(chunk->cheby_coeffs);
1108 
1109  if (chunk->cheby_mask != NULL)
1110  free(chunk->cheby_mask);
1111 
1112  free(chunk);
1113 }
1114 
1123 pcomp_chunk_size_t
1125 {
1126  if (chunk == NULL)
1127  abort();
1128 
1129  return chunk->num_of_samples;
1130 }
1131 
1144 {
1145  if (chunk == NULL)
1146  abort();
1147 
1148  if (chunk->is_compressed) {
1149  /* The size is calculated as follows:
1150  * - the "compressed" flag (int8_t)
1151  * - the number of samples (pcomp_chunk_size_t)
1152  * - the number N of polynomial coefficients (pcomp_poly_size_t)
1153  * - the size of the Chebyshev mask
1154  * - the number M of Chebyshev coefficients (pcomp_chunk_size_t)
1155  * - Nx8 bytes for the polynomial
1156  * - Mx8 bytes for the Chebyshev coefficients
1157  */
1158  return sizeof(int8_t) + sizeof(pcomp_chunk_size_t)
1159  + sizeof(pcomp_poly_size_t)
1160  + pcomp_chunk_cheby_mask_size(chunk->num_of_samples)
1161  + sizeof(pcomp_chunk_size_t)
1162  + (chunk->num_of_poly_coeffs
1163  + chunk->num_of_cheby_coeffs) * sizeof(double);
1164  }
1165  else {
1166  /* The size is calculated as follows:
1167  * - 1 byte for the "uncompressed" flag
1168  * - 4 bytes for the number of samples
1169  * - Nx8 bytes for the samples
1170  */
1171  return sizeof(int8_t) + sizeof(pcomp_chunk_size_t)
1172  + chunk->num_of_samples * sizeof(double);
1173  }
1174 }
1175 
1181 {
1182  if (chunk == NULL)
1183  abort();
1184 
1185  return chunk->is_compressed;
1186 }
1187 
1193 const double*
1195 {
1196  if (chunk == NULL)
1197  abort();
1198 
1199  if (chunk->is_compressed)
1200  return NULL;
1201 
1202  return chunk->uncompressed;
1203 }
1204 
1211 pcomp_poly_size_t
1213 {
1214  if (chunk == NULL)
1215  abort();
1216 
1217  if (!chunk->is_compressed)
1218  return 0;
1219 
1220  return chunk->num_of_poly_coeffs;
1221 }
1222 
1229 const double*
1231 {
1232  if (chunk == NULL)
1233  abort();
1234 
1235  if (!chunk->is_compressed || chunk->num_of_poly_coeffs == 0)
1236  return NULL;
1237 
1238  return chunk->poly_coeffs;
1239 }
1240 
1247 pcomp_chunk_size_t
1249 {
1250  if (chunk == NULL)
1251  abort();
1252 
1253  if (!chunk->is_compressed)
1254  return 0;
1255 
1256  return chunk->num_of_cheby_coeffs;
1257 }
1258 
1265 const double*
1267 {
1268  if (chunk == NULL)
1269  abort();
1270 
1271  if (!chunk->is_compressed || chunk->num_of_cheby_coeffs == 0)
1272  return NULL;
1273 
1274  return chunk->cheby_coeffs;
1275 }
1276 
1295 size_t pcomp_chunk_cheby_mask_size(pcomp_chunk_size_t chunk_size)
1296 {
1297  return chunk_size / CHAR_BIT
1298  + ((chunk_size % CHAR_BIT) > 0 ? 1 : 0);
1299 }
1300 
1311 const uint8_t*
1313 {
1314  if (chunk == NULL)
1315  abort();
1316 
1317  return chunk->cheby_mask;
1318 }
1319 
1320 /**********************************************************************/
1321 
1358  double* coeffs, double* cheby_residuals,
1359  const double* input,
1360  double* max_residual)
1361 {
1362  size_t idx;
1363  int status;
1364  double running_max = -1.0; /* Negative stands for "uninitialized" */
1365 
1366  status = pcomp_run_poly_fit(params->poly_fit, coeffs, input);
1367  if (status != PCOMP_STAT_SUCCESS)
1368  return status;
1369 
1370  for (idx = 0; idx < params->samples_per_chunk; ++idx) {
1371  double abs_residual;
1372 
1373  params->chebyshev->input[idx]
1374  = input[idx]
1375  - eval_poly(coeffs, params->poly_fit->num_of_coeffs,
1376  idx + 1.0);
1377 
1378  abs_residual = fabs(params->chebyshev->input[idx]);
1379  if (abs_residual > running_max || running_max < 0.0)
1380  running_max = abs_residual;
1381  }
1382 
1383  if (max_residual != NULL)
1384  *max_residual = running_max;
1385 
1386  if (params->algorithm != PCOMP_ALG_NO_CHEBYSHEV) {
1387  status = pcomp_run_chebyshev(
1388  params->chebyshev, params->chebyshev->dir, NULL, NULL);
1389 
1390  if (cheby_residuals != NULL
1391  && cheby_residuals != params->chebyshev->output) {
1392  memcpy(cheby_residuals, params->chebyshev->output,
1393  sizeof(params->chebyshev->output[0])
1394  * params->chebyshev->num_of_samples);
1395  }
1396  }
1397 
1398  return status;
1399 }
1400 
1401 /***********************************************************************
1402  * Sort the array "positions" according to the absolute values of
1403  * "coeffs", in *descending* order. The function uses the merge sort
1404  * algorithm. */
1405 
1406 static void sort_positions(pcomp_chunk_size_t positions[],
1407  const double coeffs[], size_t num)
1408 {
1409  size_t front, back;
1410  double pivot;
1411  pcomp_chunk_size_t temp;
1412 
1413  if (num < 2)
1414  return;
1415 
1416  pivot = fabs(coeffs[positions[num / 2]]);
1417  for (front = 0, back = num - 1;; front++, back--) {
1418  while (fabs(coeffs[positions[front]]) > pivot) {
1419  front++;
1420  }
1421 
1422  while (pivot > fabs(coeffs[positions[back]])) {
1423  if (back == 0)
1424  break;
1425  back--;
1426  }
1427 
1428  if (front >= back)
1429  break;
1430 
1431  temp = positions[front];
1432  positions[front] = positions[back];
1433  positions[back] = temp;
1434  }
1435  sort_positions(positions, coeffs, front);
1436  sort_positions(positions + front, coeffs, num - front);
1437 }
1438 
1450 int pcomp_mask_get_bit(const uint8_t* mask, size_t pos)
1451 {
1452  return (mask[pos / CHAR_BIT] & (1 << (pos % CHAR_BIT))) != 0;
1453 }
1454 
1466 void pcomp_mask_set_bit(uint8_t* mask, size_t pos, int value)
1467 {
1468  if (value != 0) {
1469  mask[pos / CHAR_BIT] |= (1 << (pos % CHAR_BIT));
1470  }
1471  else {
1472  mask[pos / CHAR_BIT] &= ~(((uint8_t)1) << (pos % CHAR_BIT));
1473  }
1474 }
1475 
1476 static double compute_discrepancy(double a[], double b[], size_t num)
1477 {
1478  size_t idx;
1479  double err = 0.0;
1480  for (idx = 0; idx < num; ++idx) {
1481  double cur_err = fabs(a[idx] - b[idx]);
1482  if (cur_err > err)
1483  err = cur_err;
1484  }
1485 
1486  return err;
1487 }
1488 
1523  pcomp_chebyshev_t* inv_chebyshev,
1524  double max_allowable_error,
1525  uint8_t* mask, double* max_error)
1526 {
1527  size_t idx;
1528  size_t cur_coeff = 0;
1529  pcomp_chunk_size_t* positions;
1530  double err;
1531 
1532  if (chebyshev == NULL || inv_chebyshev == NULL || mask == NULL
1533  || chebyshev->num_of_samples != inv_chebyshev->num_of_samples)
1534  abort();
1535 
1536  if (max_allowable_error <= 0.0)
1537  return 0;
1538 
1539  /* The "positions" array contains the indexes to the
1540  * chebyshev->output array, i.e., the list of Chebyshev
1541  * coefficients to sort in decreasing order. At the beginning each
1542  * entry is set to its own index:
1543  *
1544  * +---+---+---+-----+
1545  * positions: | 0 | 1 | 2 | ... |
1546  * +---+---+---+-----+
1547  *
1548  * After the call to "sort_positions", the "positions" array
1549  * contains the indexes to "chebyshev->output" ordered according
1550  * to the decreasing absolute value of the latters, e.g.:
1551  *
1552  * +---+---+---+-----+
1553  * positions: | 7 | 2 | 5 | ... |
1554  * +---+---+---+-----+
1555  */
1556  positions = malloc(chebyshev->num_of_samples
1557  * sizeof(pcomp_chunk_size_t));
1558  if (positions == NULL)
1559  abort();
1560  for (idx = 0; idx < chebyshev->num_of_samples; ++idx) {
1561  positions[idx] = idx;
1562  }
1563  sort_positions(positions, chebyshev->output,
1564  chebyshev->num_of_samples);
1565 
1566  /* Start by setting all the coefficients to zero */
1567  memset(&inv_chebyshev->input[0], 0,
1568  chebyshev->num_of_samples * sizeof(inv_chebyshev->input[0]));
1569  memset(&mask[0], 0,
1570  pcomp_chunk_cheby_mask_size(chebyshev->num_of_samples));
1571 
1572  /* Add the coefficients one by one until the error is below
1573  * "max_allowable_error" */
1574  cur_coeff = 0;
1575  while (cur_coeff < chebyshev->num_of_samples) {
1576 
1577  inv_chebyshev->input[positions[cur_coeff]]
1578  = chebyshev->output[positions[cur_coeff]];
1579  pcomp_mask_set_bit(mask, positions[cur_coeff], 1);
1580  ++cur_coeff;
1581 
1582  pcomp_run_chebyshev(inv_chebyshev, inv_chebyshev->dir, NULL,
1583  NULL);
1584  err = compute_discrepancy(chebyshev->input,
1585  inv_chebyshev->output,
1586  chebyshev->num_of_samples);
1587 
1588  if (err < max_allowable_error)
1589  break;
1590  }
1591 
1592  if (max_error != NULL)
1593  *max_error = err;
1594 
1595  return cur_coeff;
1596 }
1597 
1598 /* This function is used internally by "pcomp_run_polycomp_on_chunk"
1599  * and "pcomp_decompress_poly_chunk" to make sure there is no memory
1600  * leak on the chunk passed as argument. */
1601 static void clear_chunk(pcomp_polycomp_chunk_t* chunk)
1602 {
1603  /* Leave chunk->uncompressed as it is, as it never changes
1604  */
1605 
1606  chunk->num_of_poly_coeffs = 0;
1607  if (chunk->poly_coeffs != NULL)
1608  free(chunk->poly_coeffs);
1609 
1610  chunk->num_of_cheby_coeffs = 0;
1611  if (chunk->cheby_coeffs != NULL)
1612  free(chunk->cheby_coeffs);
1613 }
1614 
1667  const double* input,
1668  pcomp_chunk_size_t num_of_samples,
1669  pcomp_polycomp_chunk_t* chunk,
1670  double* max_error)
1671 {
1672  uint8_t* mask = NULL;
1673  double* coeffs = NULL;
1674  size_t cheby_coeffs_to_retain = 0;
1675  int apply_chebyshev = 1;
1676  double* buf = NULL;
1677  const double* straightened_input;
1678 
1679  if (chunk == NULL || input == NULL || params == NULL
1680  || params->poly_fit == NULL || params->chebyshev == NULL
1681  || params->inv_chebyshev == NULL)
1682  abort();
1683 
1684  clear_chunk(chunk);
1685 
1686  if (num_of_samples != params->samples_per_chunk)
1687  return PCOMP_STAT_INVALID_BUFFER;
1688 
1689  if (params->period > 0.0) {
1690  buf = malloc(num_of_samples * sizeof(input[0]));
1691  if (buf == NULL)
1692  abort();
1693  pcomp_straighten(buf, input, num_of_samples, params->period);
1694  straightened_input = buf; /* This preserve const-correctness */
1695  }
1696  else {
1697  straightened_input = input;
1698  }
1699 
1700  if (num_of_samples <= params->poly_fit->num_of_coeffs) {
1701  /* The number of element is so small that is better to store
1702  * them uncompressed */
1703  chunk->is_compressed = 0;
1704  }
1705  else {
1706  double max_residual;
1707 
1708  /* Compute the polynomial fit and the full Chebyshev
1709  * transform */
1710  coeffs
1711  = malloc(sizeof(double) * params->poly_fit->num_of_coeffs);
1712  if (coeffs == NULL)
1713  abort();
1714  pcomp_polyfit_and_chebyshev(params, coeffs, NULL,
1715  straightened_input, &max_residual);
1716  apply_chebyshev
1717  = (max_residual >= params->max_allowable_error)
1718  && (params->algorithm != PCOMP_ALG_NO_CHEBYSHEV);
1719 
1720  /* If the Chebyshev transform is needed, chop it as much as
1721  * possible */
1722  if (apply_chebyshev) {
1723  mask = malloc(pcomp_chunk_cheby_mask_size(num_of_samples));
1724  if (mask == NULL)
1725  abort();
1726  cheby_coeffs_to_retain = pcomp_find_chebyshev_mask(
1727  params->chebyshev, params->inv_chebyshev,
1728  params->max_allowable_error, mask, max_error);
1729 
1730  chunk->is_compressed = (cheby_coeffs_to_retain
1731  + params->poly_fit->num_of_coeffs)
1732  < num_of_samples;
1733  if (!chunk->is_compressed) {
1734  free(mask);
1735  mask = NULL;
1736  }
1737  }
1738  else {
1739  /* Assume that num_of_samples > deg(p) + 1 */
1740  chunk->is_compressed
1741  = (max_residual <= params->max_allowable_error);
1742  }
1743  }
1744 
1745  chunk->num_of_samples = num_of_samples;
1746  if (chunk->is_compressed) {
1747  size_t idx;
1748 
1749  chunk->num_of_poly_coeffs = params->poly_fit->num_of_coeffs;
1750  chunk->poly_coeffs = coeffs;
1751  if (apply_chebyshev) {
1752  size_t cheby_idx;
1753 
1754  chunk->num_of_cheby_coeffs = cheby_coeffs_to_retain;
1755  chunk->cheby_mask = mask;
1756  chunk->cheby_coeffs
1757  = malloc(sizeof(double) * cheby_coeffs_to_retain);
1758  if (chunk->cheby_coeffs == NULL)
1759  abort();
1760  cheby_idx = 0;
1761  for (idx = 0; idx < params->chebyshev->num_of_samples;
1762  ++idx) {
1763  if (pcomp_mask_get_bit(mask, idx)) {
1764  chunk->cheby_coeffs[cheby_idx++]
1765  = params->chebyshev->output[idx];
1766  }
1767  }
1768  }
1769  else {
1770  chunk->num_of_cheby_coeffs = 0;
1771  chunk->cheby_mask = NULL;
1772  chunk->cheby_coeffs = NULL;
1773  }
1774  }
1775  else {
1776  size_t idx;
1777 
1778  if (coeffs != NULL)
1779  free(coeffs);
1780 
1781  chunk->uncompressed = malloc(sizeof(double) * num_of_samples);
1782  if (chunk->uncompressed == NULL)
1783  abort();
1784  for (idx = 0; idx < num_of_samples; ++idx)
1785  chunk->uncompressed[idx] = input[idx];
1786 
1787  if (max_error != NULL)
1788  *max_error = 0.0;
1789  }
1790 
1791  if (buf != NULL) {
1792  free(buf);
1793  }
1794 
1795  return PCOMP_STAT_SUCCESS;
1796 }
1797 
1834  const pcomp_polycomp_chunk_t* chunk,
1835  pcomp_chebyshev_t* inv_chebyshev)
1836 {
1837  if (output == NULL || chunk == NULL || inv_chebyshev == NULL)
1838  abort();
1839 
1840  if (chunk->is_compressed) {
1841  size_t idx;
1842 
1843  /* Compute the values of the polynomial at the points 1,
1844  * 2, ...
1845  */
1846  for (idx = 0; idx < chunk->num_of_samples; ++idx) {
1847  output[idx] = eval_poly(chunk->poly_coeffs,
1848  chunk->num_of_poly_coeffs, idx + 1);
1849  }
1850 
1851  /* If present, add the contribution of the Chebyshev
1852  * transform
1853  */
1854  if (chunk->num_of_cheby_coeffs > 0) {
1855  size_t cur_cheby_idx = 0;
1856  if (chunk->cheby_coeffs == NULL)
1857  abort();
1858 
1859  for (idx = 0; idx < chunk->num_of_samples; ++idx) {
1860  if (pcomp_mask_get_bit(chunk->cheby_mask, idx)) {
1861  if (cur_cheby_idx >= chunk->num_of_cheby_coeffs) {
1862  abort();
1863  }
1864 
1865  inv_chebyshev->input[idx]
1866  = chunk->cheby_coeffs[cur_cheby_idx++];
1867  }
1868  else {
1869  inv_chebyshev->input[idx] = 0.0;
1870  }
1871  }
1872  pcomp_run_chebyshev(inv_chebyshev, inv_chebyshev->dir, NULL,
1873  NULL);
1874 
1875  for (idx = 0; idx < chunk->num_of_samples; ++idx) {
1876  output[idx] += inv_chebyshev->output[idx];
1877  }
1878  }
1879  }
1880  else {
1881  memcpy(output, chunk->uncompressed,
1882  sizeof(chunk->uncompressed[0]) * chunk->num_of_samples);
1883  }
1884 
1885  return PCOMP_STAT_SUCCESS;
1886 }
1887 
1952  size_t* num_of_chunks,
1953  const double* input_buf, size_t input_size,
1954  const pcomp_polycomp_t* params)
1955 {
1956  size_t idx;
1957  const double* cur_input = input_buf;
1958  pcomp_polycomp_t* chunk_params;
1959 
1960  if (output_buf == NULL || num_of_chunks == NULL || input_buf == NULL
1961  || params == NULL || params->poly_fit == NULL)
1962  abort();
1963 
1964  /* Calculate how many chunks we'll create */
1965  *num_of_chunks = input_size / params->samples_per_chunk;
1966  if (input_size % params->samples_per_chunk != 0)
1967  ++(*num_of_chunks);
1968 
1969  *output_buf
1970  = malloc(sizeof(pcomp_polycomp_chunk_t*) * (*num_of_chunks));
1971  if (*output_buf == NULL)
1972  abort();
1973 
1974  chunk_params = pcomp_init_polycomp(
1975  params->samples_per_chunk, params->poly_fit->num_of_coeffs,
1976  params->max_allowable_error, params->algorithm);
1977 
1978  /* Loop over the chunks and call
1979  * "pcomp_run_polycomp_on_chunk" for
1980  * each of them */
1981  for (idx = 0; idx < *num_of_chunks; ++idx) {
1982  size_t cur_chunk_size = params->samples_per_chunk;
1983 
1984  if ((cur_input - input_buf) + cur_chunk_size > input_size)
1985  cur_chunk_size
1986  = (size_t)(input_size - (cur_input - input_buf));
1987 
1988  if (cur_chunk_size != chunk_params->samples_per_chunk) {
1989  pcomp_free_polycomp(chunk_params);
1990 
1991  chunk_params = pcomp_init_polycomp(
1992  cur_chunk_size, params->poly_fit->num_of_coeffs,
1993  params->max_allowable_error, params->algorithm);
1994  }
1995 
1996  (*output_buf)[idx] = pcomp_init_chunk(cur_chunk_size);
1997  pcomp_run_polycomp_on_chunk(chunk_params, cur_input,
1998  cur_chunk_size, (*output_buf)[idx],
1999  NULL);
2000 
2001  cur_input += cur_chunk_size;
2002  }
2003 
2004  return PCOMP_STAT_SUCCESS;
2005 }
2006 
2021 size_t
2023  size_t num_of_chunks)
2024 {
2025  size_t total = 0;
2026  size_t idx;
2027  if (chunk_array == NULL)
2028  abort();
2029 
2030  for (idx = 0; idx < num_of_chunks; ++idx) {
2031  if (chunk_array[idx] == NULL)
2032  abort();
2033 
2034  total += chunk_array[idx]->num_of_samples;
2035  }
2036 
2037  return total;
2038 }
2039 
2060  double* output_buf, pcomp_polycomp_chunk_t* const chunk_array[],
2061  size_t num_of_chunks)
2062 {
2063  size_t idx;
2064  double* cur_output_pos = output_buf;
2065  pcomp_chebyshev_t* inv_chebyshev = NULL;
2066 
2067  if (output_buf == NULL || chunk_array == NULL)
2068  abort();
2069 
2070  for (idx = 0; idx < num_of_chunks; ++idx) {
2071  if (chunk_array[idx] == NULL)
2072  abort();
2073 
2074  if (inv_chebyshev == NULL
2075  || inv_chebyshev->num_of_samples
2076  != chunk_array[idx]->num_of_samples) {
2077 
2078  /* This does no harm if inv_chebyshev==NULL */
2079  pcomp_free_chebyshev(inv_chebyshev);
2080 
2081  inv_chebyshev = pcomp_init_chebyshev(
2082  chunk_array[idx]->num_of_samples, PCOMP_TD_INVERSE);
2083  }
2084 
2086  cur_output_pos, chunk_array[idx], inv_chebyshev);
2087 
2088  cur_output_pos += chunk_array[idx]->num_of_samples;
2089  }
2090 
2091  pcomp_free_chebyshev(inv_chebyshev);
2092 
2093  return PCOMP_STAT_SUCCESS;
2094 }
2095 
2107  size_t num_of_chunks)
2108 {
2109  size_t idx;
2110 
2111  if (chunk_array == NULL)
2112  return;
2113 
2114  for (idx = 0; idx < num_of_chunks; ++idx) {
2115  pcomp_free_chunk(chunk_array[idx]);
2116  }
2117 
2118  free(chunk_array);
2119 }
2120 
2139  size_t num_of_chunks)
2140 {
2141  size_t result = sizeof(size_t); /* Room for the number of chunks */
2142  size_t idx;
2143 
2144  for (idx = 0; idx < num_of_chunks; ++idx) {
2145  result += pcomp_chunk_num_of_bytes(chunks[idx]);
2146  }
2147 
2148  return result;
2149 }
2150 
2151 /***********************************************************************
2152  * Encode/decode a list of chunks into a raw stream of bytes (suitable
2153  * for I/O).
2154  */
2155 
2156 #define SAVE_TO_PTR_AND_INCREMENT(buf, value, type) \
2157  { \
2158  *((type*)buf) = value; \
2159  buf = ((type*)buf) + 1; \
2160  }
2161 
2193 int pcomp_encode_chunks(void* buf, size_t* buf_size,
2194  pcomp_polycomp_chunk_t* const chunk_array[],
2195  size_t num_of_chunks)
2196 {
2197  void* buf_ptr = buf;
2198  size_t chunk_idx;
2199 
2200  if (chunk_array == NULL || num_of_chunks == 0)
2201  abort();
2202 
2203  SAVE_TO_PTR_AND_INCREMENT(buf_ptr, num_of_chunks, size_t);
2204 
2205  for (chunk_idx = 0; chunk_idx < num_of_chunks; ++chunk_idx) {
2206  const pcomp_polycomp_chunk_t* cur_chunk
2207  = chunk_array[chunk_idx];
2208  size_t idx; /* Used for inner loops */
2209 
2210  SAVE_TO_PTR_AND_INCREMENT(buf_ptr, cur_chunk->is_compressed,
2211  uint8_t);
2212  SAVE_TO_PTR_AND_INCREMENT(buf_ptr, cur_chunk->num_of_samples,
2213  pcomp_chunk_size_t);
2214 
2215  if (cur_chunk->is_compressed) {
2216  size_t cheby_mask_size = pcomp_chunk_cheby_mask_size(
2217  cur_chunk->num_of_samples);
2218 
2219  SAVE_TO_PTR_AND_INCREMENT(buf_ptr,
2220  cur_chunk->num_of_poly_coeffs,
2221  pcomp_poly_size_t);
2222  for (idx = 0; idx < cur_chunk->num_of_poly_coeffs; idx++) {
2223  SAVE_TO_PTR_AND_INCREMENT(
2224  buf_ptr, cur_chunk->poly_coeffs[idx], double);
2225  }
2226 
2227  SAVE_TO_PTR_AND_INCREMENT(buf_ptr,
2228  cur_chunk->num_of_cheby_coeffs,
2229  pcomp_chunk_size_t);
2230  if (cur_chunk->num_of_cheby_coeffs > 0) {
2231  /* Mask */
2232  for (idx = 0; idx < cheby_mask_size; ++idx) {
2233  SAVE_TO_PTR_AND_INCREMENT(
2234  buf_ptr, cur_chunk->cheby_mask[idx], uint8_t);
2235  }
2236  /* Chebyshev coefficients */
2237  for (idx = 0; idx < cur_chunk->num_of_cheby_coeffs;
2238  idx++) {
2239  SAVE_TO_PTR_AND_INCREMENT(
2240  buf_ptr, cur_chunk->cheby_coeffs[idx], double);
2241  }
2242  }
2243  }
2244  else {
2245  for (idx = 0; idx < cur_chunk->num_of_samples; idx++) {
2246  SAVE_TO_PTR_AND_INCREMENT(
2247  buf_ptr, cur_chunk->uncompressed[idx], double);
2248  }
2249  }
2250  }
2251 
2252  *buf_size = ((uint8_t*)buf_ptr) - ((uint8_t*)buf);
2253  return PCOMP_STAT_SUCCESS;
2254 }
2255 
2256 #define READ_FROM_PTR_AND_INCREMENT(var, pointer, type) \
2257  { \
2258  var = *((type*)(pointer)); \
2259  pointer = ((type*)(pointer)) + 1; \
2260  }
2261 
2288  size_t* num_of_chunks, const void* buf)
2289 {
2290  const void* cur_ptr = buf;
2291  size_t chunk_idx;
2292  double* poly_buf = NULL;
2293  size_t poly_buf_size = 0;
2294  double* cheby_buf = NULL;
2295  size_t cheby_buf_size = 0;
2296  uint8_t* cheby_mask_buf = NULL;
2297  size_t cheby_mask_buf_size = 0;
2298  double* uncompr_buf = NULL;
2299  size_t uncompr_buf_size = 0;
2300 
2301  if (buf == NULL || chunk_array == NULL || num_of_chunks == NULL)
2302  abort();
2303 
2304  READ_FROM_PTR_AND_INCREMENT(*num_of_chunks, cur_ptr, size_t);
2305  *chunk_array
2306  = malloc(sizeof(pcomp_polycomp_chunk_t*) * (*num_of_chunks));
2307  if (*chunk_array == NULL)
2308  abort();
2309 
2310  for (chunk_idx = 0; chunk_idx < *num_of_chunks; ++chunk_idx) {
2311  uint8_t is_compressed;
2312  size_t num_of_samples;
2313  size_t idx; /* Used for inner loops */
2314 
2315  READ_FROM_PTR_AND_INCREMENT(is_compressed, cur_ptr, uint8_t);
2316  READ_FROM_PTR_AND_INCREMENT(num_of_samples, cur_ptr,
2317  pcomp_chunk_size_t);
2318 
2319  if (is_compressed) {
2320  size_t num_of_poly_coeffs;
2321  size_t num_of_cheby_coeffs;
2322  size_t cheby_mask_size
2323  = pcomp_chunk_cheby_mask_size(num_of_samples);
2324 
2325  READ_FROM_PTR_AND_INCREMENT(num_of_poly_coeffs, cur_ptr,
2326  pcomp_poly_size_t);
2327  if (num_of_poly_coeffs > poly_buf_size) {
2328  poly_buf_size = num_of_poly_coeffs;
2329  poly_buf
2330  = realloc(poly_buf, poly_buf_size * sizeof(double));
2331  }
2332  for (idx = 0; idx < num_of_poly_coeffs; ++idx) {
2333  READ_FROM_PTR_AND_INCREMENT(poly_buf[idx], cur_ptr,
2334  double);
2335  }
2336 
2337  READ_FROM_PTR_AND_INCREMENT(num_of_cheby_coeffs, cur_ptr,
2338  pcomp_chunk_size_t);
2339  if (num_of_cheby_coeffs > 0) {
2340  if (cheby_mask_size > cheby_mask_buf_size) {
2341  cheby_mask_buf_size = cheby_mask_size;
2342  cheby_mask_buf = realloc(cheby_mask_buf,
2343  cheby_mask_buf_size
2344  * sizeof(uint8_t));
2345  }
2346  for (idx = 0; idx < cheby_mask_size; ++idx) {
2347  READ_FROM_PTR_AND_INCREMENT(cheby_mask_buf[idx],
2348  cur_ptr, uint8_t);
2349  }
2350 
2351  if (num_of_cheby_coeffs > cheby_buf_size) {
2352  cheby_buf_size = num_of_cheby_coeffs;
2353  cheby_buf = realloc(
2354  cheby_buf, cheby_buf_size * sizeof(double));
2355  }
2356  for (idx = 0; idx < num_of_cheby_coeffs; ++idx) {
2357  READ_FROM_PTR_AND_INCREMENT(cheby_buf[idx], cur_ptr,
2358  double);
2359  }
2360  }
2361 
2362  (*chunk_array)[chunk_idx] = pcomp_init_compressed_chunk(
2363  num_of_samples, num_of_poly_coeffs, poly_buf,
2364  num_of_cheby_coeffs, cheby_mask_buf, cheby_buf);
2365  }
2366  else {
2367  if (num_of_samples > uncompr_buf_size) {
2368  uncompr_buf_size = num_of_samples;
2369  uncompr_buf = realloc(
2370  uncompr_buf, uncompr_buf_size * sizeof(double));
2371  }
2372  for (idx = 0; idx < num_of_samples; ++idx) {
2373  READ_FROM_PTR_AND_INCREMENT(uncompr_buf[idx], cur_ptr,
2374  double);
2375  }
2376 
2377  (*chunk_array)[chunk_idx] = pcomp_init_uncompressed_chunk(
2378  num_of_samples, uncompr_buf);
2379  }
2380  }
2381 
2382  if (poly_buf != NULL)
2383  free(poly_buf);
2384 
2385  if (cheby_buf != NULL)
2386  free(cheby_buf);
2387 
2388  if (uncompr_buf != NULL)
2389  free(uncompr_buf);
2390 
2391  return PCOMP_STAT_SUCCESS;
2392 }
const double * pcomp_chunk_cheby_coeffs(const pcomp_polycomp_chunk_t *chunk)
If the chunks contain compressed data, returns a pointer to the first element of the Chebyshev transf...
Definition: poly.c:1266
pcomp_chebyshev_t * pcomp_init_chebyshev(size_t num_of_samples, pcomp_transform_direction_t dir)
Allocate a new instance of the pcomp_chebyshev_t structure on the heap.
Definition: poly.c:365
int pcomp_run_poly_fit(pcomp_poly_fit_data_t *poly_fit, double *coeffs, const double *points)
Calculates a polynomial least-squares fit.
Definition: poly.c:301
double pcomp_polycomp_period(const pcomp_polycomp_t *params)
Return the period of the input data, or a number less than or equal to 0 if the data have no periodic...
Definition: poly.c:773
pcomp_transform_direction_t pcomp_chebyshev_direction(const pcomp_chebyshev_t *plan)
Return the direction of a Chebyshev transform.
Definition: poly.c:435
int pcomp_decompress_polycomp_chunk(double *output, const pcomp_polycomp_chunk_t *chunk, pcomp_chebyshev_t *inv_chebyshev)
Decompress the data in a chunk.
Definition: poly.c:1833
pcomp_poly_size_t pcomp_polycomp_num_of_poly_coeffs(const pcomp_polycomp_t *params)
Return the number of coefficients for the fitting polynomial used in the polynomial compression...
Definition: poly.c:677
int pcomp_encode_chunks(void *buf, size_t *buf_size, pcomp_polycomp_chunk_t *const chunk_array[], size_t num_of_chunks)
Encode a list of chunks into a sequence of raw bytes.
Definition: poly.c:2193
Compute a backward Chebyshev transform, with a normalization factor equal to one. ...
Definition: libpolycomp.h:326
pcomp_polycomp_algorithm_t
Kind of algorithm used for the polynomial compression.
Definition: libpolycomp.h:355
int pcomp_decode_chunks(pcomp_polycomp_chunk_t **chunk_array[], size_t *num_of_chunks, const void *buf)
Decode a byte sequence created by pcomp_encode_chunks into an array of chunks.
Definition: poly.c:2287
Compute a forward Chebyshev transform, with a normalization factor 1/(N + 1)
Definition: libpolycomp.h:323
const double * pcomp_chunk_poly_coeffs(const pcomp_polycomp_chunk_t *chunk)
If the chunks contain compressed data, returns a pointer to the first element of the array of coeffic...
Definition: poly.c:1230
pcomp_polycomp_chunk_t * pcomp_init_compressed_chunk(pcomp_chunk_size_t num_of_samples, pcomp_poly_size_t num_of_poly_coeffs, const double *poly_coeffs, pcomp_chunk_size_t num_of_cheby_coeffs, const uint8_t *cheby_mask, const double *cheby_coeffs)
Allocate memory for a pcomp_polycomp_chunk_t object and fill it with data compressed using the polyno...
Definition: poly.c:1033
double pcomp_polycomp_max_error(const pcomp_polycomp_t *params)
Return the upper bound on the error of the polynomial compression.
Definition: poly.c:695
void pcomp_free_polycomp(pcomp_polycomp_t *params)
Free the memory allocated by pcomp_init_polycomp for a pcomp_polycomp_t structure.
Definition: poly.c:628
pcomp_polycomp_chunk_t * pcomp_init_chunk(pcomp_chunk_size_t num_of_samples)
Allocate memory for a pcomp_polycomp_chunk_t object.
Definition: poly.c:936
const double * pcomp_chunk_uncompressed_data(const pcomp_polycomp_chunk_t *chunk)
If the chunks contain uncompressed data, returns a pointer to the first element. Otherwise, return NULL.
Definition: poly.c:1194
pcomp_chunk_size_t pcomp_chunk_num_of_samples(const pcomp_polycomp_chunk_t *chunk)
Return the number of samples in a chunk.
Definition: poly.c:1124
pcomp_chebyshev_t * pcomp_polycomp_forward_cheby(const pcomp_polycomp_t *params)
Return a pointer to a pcomp_chebyshev_t structure representing the forward Chebyshev transform...
Definition: poly.c:733
pcomp_chunk_size_t pcomp_polycomp_samples_per_chunk(const pcomp_polycomp_t *params)
Return the number of samples per chunk.
Definition: poly.c:655
pcomp_poly_size_t pcomp_chunk_num_of_poly_coeffs(const pcomp_polycomp_chunk_t *chunk)
If the chunks contain compressed data, returns the number of polynomial coefficients used in the comp...
Definition: poly.c:1212
size_t pcomp_find_chebyshev_mask(pcomp_chebyshev_t *chebyshev, pcomp_chebyshev_t *inv_chebyshev, double max_allowable_error, uint8_t *mask, double *max_error)
Find the smallest subset of Chebyshev coefficients that can approximate a Chebyshev transform with an...
Definition: poly.c:1522
int pcomp_mask_get_bit(const uint8_t *mask, size_t pos)
Return the value of the bit at the position pos in the bitmask mask.
Definition: poly.c:1450
void pcomp_free_chebyshev(pcomp_chebyshev_t *plan)
Free the memory allocated by a previous call to pcomp_init_chebyshev.
Definition: poly.c:390
size_t pcomp_chebyshev_num_of_samples(const pcomp_chebyshev_t *plan)
Return the number of samples in a Chebyshev transform.
Definition: poly.c:416
const uint8_t * pcomp_chunk_cheby_mask(const pcomp_polycomp_chunk_t *chunk)
Return a pointer to the bitmask of nonzero Chebyshev coefficients for a chunk.
Definition: poly.c:1312
int pcomp_polyfit_and_chebyshev(pcomp_polycomp_t *params, double *coeffs, double *cheby_residuals, const double *input, double *max_residual)
Compute a polynomial fit of the data in input and a Chebyshev transform of the residuals.
Definition: poly.c:1357
const double * pcomp_chebyshev_output(const pcomp_chebyshev_t *plan)
Return the output (Chebyshev transform) of the last call to pcomp_run_chebyshev.
Definition: poly.c:558
void pcomp_straighten(double *output, const double *input, size_t num_of_samples, double period)
Remove sudden jumps from input.
Definition: poly.c:864
size_t pcomp_total_num_of_samples(pcomp_polycomp_chunk_t *const chunk_array[], size_t num_of_chunks)
Compute the sum of the number of samples encoded in chunk_array.
Definition: poly.c:2022
pcomp_polycomp_chunk_t * pcomp_init_uncompressed_chunk(pcomp_chunk_size_t num_of_samples, const double *samples)
Allocate memory for a pcomp_polycomp_chunk_t object and fill it with data in uncompressed form...
Definition: poly.c:975
size_t pcomp_chunk_num_of_bytes(const pcomp_polycomp_chunk_t *chunk)
Return the number of bytes necessary to encode a chunk.
Definition: poly.c:1143
size_t pcomp_poly_fit_num_of_coeffs(const pcomp_poly_fit_data_t *poly_fit)
Return the number of coefficients of the least-squares fitting polynomial.
Definition: poly.c:253
void pcomp_mask_set_bit(uint8_t *mask, size_t pos, int value)
Set the value of the bit at position pos in the bitmask .
Definition: poly.c:1466
int pcomp_run_polycomp_on_chunk(pcomp_polycomp_t *params, const double *input, pcomp_chunk_size_t num_of_samples, pcomp_polycomp_chunk_t *chunk, double *max_error)
Compress the first num_of_samples elements in input and store them in chunk.
Definition: poly.c:1666
size_t pcomp_chunk_cheby_mask_size(pcomp_chunk_size_t chunk_size)
Return the number of bytes required for the bitmask of nonzero Chebyshev coefficients.
Definition: poly.c:1295
pcomp_poly_fit_data_t * pcomp_init_poly_fit(size_t num_of_samples, size_t num_of_coeffs)
Allocate a new instance of the pcomp_poly_fit_data_t structure on the heap.
Definition: poly.c:173
void pcomp_polycomp_set_period(pcomp_polycomp_t *params, double period)
Set the periodicity of the data to be compressed.
Definition: poly.c:799
void pcomp_free_poly_fit(pcomp_poly_fit_data_t *poly_fit)
Free an instance of the pcomp_poly_fit_data_t that has been allocated via a call to pcomp_init_poly_f...
Definition: poly.c:210
void pcomp_free_chunk(pcomp_polycomp_chunk_t *chunk)
Free memory associated with a pcomp_poly_chunk_t.
Definition: poly.c:1095
pcomp_chebyshev_t * pcomp_polycomp_backward_cheby(const pcomp_polycomp_t *params)
Return a pointer to a pcomp_chebyshev_t structure representing the forward Chebyshev transform...
Definition: poly.c:752
pcomp_polycomp_t * pcomp_init_polycomp(pcomp_chunk_size_t samples_per_chunk, pcomp_poly_size_t num_of_coeffs, double max_allowable_error, pcomp_polycomp_algorithm_t algorithm)
Allocate space for a pcomp_polycomp_t structure.
Definition: poly.c:598
int pcomp_decompress_polycomp(double *output_buf, pcomp_polycomp_chunk_t *const chunk_array[], size_t num_of_chunks)
Decompress a sequence of chunks.
Definition: poly.c:2059
pcomp_transform_direction_t
Direction of a Chebyshev transform.
Definition: libpolycomp.h:320
pcomp_polycomp_algorithm_t pcomp_polycomp_algorithm(const pcomp_polycomp_t *params)
Return the kind of algorithm used for a polynomial compression.
Definition: poly.c:714
int pcomp_compress_polycomp(pcomp_polycomp_chunk_t **output_buf[], size_t *num_of_chunks, const double *input_buf, size_t input_size, const pcomp_polycomp_t *params)
Compress the array input_buf using polynomial compression.
Definition: poly.c:1951
size_t pcomp_chunks_num_of_bytes(pcomp_polycomp_chunk_t *const chunks[], size_t num_of_chunks)
Number of bytes required by pcomp_encode_chunks.
Definition: poly.c:2138
int pcomp_chunk_is_compressed(const pcomp_polycomp_chunk_t *chunk)
Return nonzero if the chunk holds data in uncompressed form.
Definition: poly.c:1180
If the absolute value of the residuals of a polynomial fit are too large, store the data in uncompres...
Definition: libpolycomp.h:361
size_t pcomp_poly_fit_num_of_samples(const pcomp_poly_fit_data_t *poly_fit)
Return the number of samples to be used in a polynomial fit.
Definition: poly.c:234
void pcomp_free_chunks(pcomp_polycomp_chunk_t *chunk_array[], size_t num_of_chunks)
Free an array of chunks.
Definition: poly.c:2106
int pcomp_run_chebyshev(pcomp_chebyshev_t *plan, pcomp_transform_direction_t dir, double *output, const double *input)
Compute a forward/backward Chebyshev discrete transform.
Definition: poly.c:491
const double * pcomp_chebyshev_input(const pcomp_chebyshev_t *plan)
Return the input data used in the last call to pcomp_run_chebyshev.
Definition: poly.c:537
Header file for Libpolycomp.
pcomp_chunk_size_t pcomp_chunk_num_of_cheby_coeffs(const pcomp_polycomp_chunk_t *chunk)
If the chunks contain compressed data, returns the number of nonzero Chebyshev coefficients held in t...
Definition: poly.c:1248