28# define getcwd _getcwd
30# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
40# define SDDSGROUPEDENVELOPE_USE_OPENMP 1
42# define SDDSGROUPEDENVELOPE_USE_OPENMP 0
64 STAT_STANDARD_DEVIATION,
67 STAT_WSTANDARD_DEVIATION,
108 short columnMajorOrder;
111 long requestsAllocated;
138 long rowDatasAllocated;
139 int64_t zeroRowPages;
145 long groupsAllocated;
155 PROCESS_NO_INPUT = 0,
160 "sddsgroupedenvelope [options]\n"
162 "Processes pages from files in -inputDir to produce -output with\n"
163 "one page for each -groupBy value containing the specified quantities\n"
164 "across pages for each row of the specified columns.\n"
166 "File selection options:\n"
167 " -pattern <pattern> Input glob pattern. May be repeated.\n"
168 " Defaults to PSD-*.xz and PSD-*.gz.\n"
169 " -inputDir <directory> Directory containing input files (default: current directory).\n"
170 " -groupBy <parameter> Group pages by this parameter (default: PSName).\n"
171 " -groupValue <value> Process only this group value. May be repeated.\n"
172 " -output <file> Output file, relative to current directory (default: PSD-envelope.sdds).\n"
173 " -noOverwrite Refuse to replace an existing output file.\n"
174 " -majorOrder={row|column} Output data order (default: column).\n"
175 " -threads <number> Number of input files to read concurrently (default: 1).\n"
176 " -nowarnings Suppress warnings about degenerate statistics.\n"
177 " -verbose Print per-directory and row-count details.\n"
178 " -help Show this message.\n"
180 "Streaming statistic options. At least one is required:\n"
182 " -maximum=<columns> -minimum=<columns> -mean=<columns>\n"
183 " -largest=<columns> -signedLargest=<columns>\n"
184 " -sum=<power>,<columns> -rms=<columns>\n"
185 " -standarddeviations=<columns> -sigma=<columns>\n"
186 " -wmean=<weightColumn>,<columns>\n"
187 " -wstandarddeviations=<weightColumn>,<columns>\n"
188 " -wrms=<weightColumn>,<columns> -wsigma=<weightColumn>,<columns>\n"
189 " -cmaximum=<indepColumn>,<columns> -cminimum=<indepColumn>,<columns>\n"
190 " -pmaximum=<indepParameter>,<columns> -pminimum=<indepParameter>,<columns>\n"
191 " -slope=<indepParameter>,<columns> -intercept=<indepParameter>,<columns>\n"
192 " -exmmMean=<columns>\n"
194 "Exact median, percentile, and decile-range statistics are intentionally not\n"
195 "implemented because they require retaining per-row page histories.\n";
197static void *xmalloc(
size_t size) {
203 fprintf(stderr,
"error: memory allocation failed\n");
209static void *xcalloc(
size_t count,
size_t size) {
211 if (count == 0 || size == 0) {
214 ptr = calloc(count, size);
216 fprintf(stderr,
"error: memory allocation failed\n");
222static void *xrealloc(
void *ptr,
size_t size) {
225 ptr = realloc(ptr, size);
227 fprintf(stderr,
"error: memory allocation failed\n");
233static char *xstrdup(
const char *s) {
239 copy = xmalloc(len + 1);
240 memcpy(copy, s, len + 1);
244static void string_list_append_owned(
STRING_LIST *list,
char *value) {
245 if (list->items >= list->allocated) {
246 list->allocated = list->allocated ? 2 * list->allocated : 8;
247 list->item = xrealloc(list->item,
sizeof(*list->item) * list->allocated);
249 list->item[list->items++] = value;
252static void string_list_append(
STRING_LIST *list,
const char *value) {
253 string_list_append_owned(list, xstrdup(value));
258 for (i = 0; i < list->items; i++)
262 list->items = list->allocated = 0;
265static int string_list_contains(
const STRING_LIST *list,
const char *value) {
267 for (i = 0; i < list->items; i++) {
268 if (strcmp(list->item[i], value) == 0)
274static char *trimmed_copy(
const char *start,
size_t length) {
275 const char *end = start + length;
278 while (start < end && isspace((
unsigned char)*start))
280 while (end > start && isspace((
unsigned char)*(end - 1)))
282 copy = xmalloc((
size_t)(end - start) + 1);
283 memcpy(copy, start, (
size_t)(end - start));
284 copy[end - start] = 0;
288static void split_comma_list(
const char *value,
STRING_LIST *list) {
289 const char *start, *p;
291 for (p = value; ; p++) {
292 if (*p ==
',' || *p == 0) {
293 char *item = trimmed_copy(start, (
size_t)(p - start));
295 fprintf(stderr,
"error: empty item in option value '%s'\n", value);
299 string_list_append_owned(list, item);
307static int is_absolute_path(
const char *path) {
308 if (!path || !path[0])
316 if (isalpha((
unsigned char)path[0]) && path[1] ==
':')
321static int is_path_separator(
char c) {
323 return c ==
'/' || c ==
'\\';
329static char *join_path(
const char *dir,
const char *name) {
330 size_t dirLen, nameLen, needSlash;
333 if (is_absolute_path(name))
334 return xstrdup(name);
336 return xstrdup(name);
338 dirLen = strlen(dir);
339 nameLen = strlen(name);
340 needSlash = dirLen && !is_path_separator(dir[dirLen - 1]);
341 path = xmalloc(dirLen + needSlash + nameLen + 1);
342 memcpy(path, dir, dirLen);
344 path[dirLen++] =
'/';
345 memcpy(path + dirLen, name, nameLen + 1);
349static char *current_directory(
void) {
350 char buffer[PATH_MAX];
351 if (!getcwd(buffer,
sizeof(buffer))) {
352 fprintf(stderr,
"error: unable to determine current directory: %s\n", strerror(errno));
355 return xstrdup(buffer);
358static int path_exists(
const char *path) {
360 return stat(path, &st) == 0;
363static int same_existing_file(
const char *path1,
const char *path2) {
365 BY_HANDLE_FILE_INFORMATION info1, info2;
369 if (!path1 || !path2)
371 if (_stricmp(path1, path2) == 0)
373 file1 = CreateFileA(path1, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
374 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
375 if (file1 == INVALID_HANDLE_VALUE)
377 file2 = CreateFileA(path2, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
378 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
379 if (file2 != INVALID_HANDLE_VALUE) {
380 if (GetFileInformationByHandle(file1, &info1) &&
381 GetFileInformationByHandle(file2, &info2)) {
382 same = info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber &&
383 info1.nFileIndexHigh == info2.nFileIndexHigh &&
384 info1.nFileIndexLow == info2.nFileIndexLow;
391 struct stat st1, st2;
392 if (!path1 || !path2)
394 if (strcmp(path1, path2) == 0)
396 if (stat(path1, &st1) != 0 || stat(path2, &st2) != 0)
398 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
402static int is_directory(
const char *path) {
404 return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
407static const char *base_name(
const char *path) {
409 slash = strrchr(path,
'/');
412 const char *backslash = strrchr(path,
'\\');
413 if (!slash || (backslash && backslash > slash))
417 return slash ? slash + 1 : path;
420static int timestamp_seconds(
const char *path) {
421 const char *tail, *p;
422 tail = base_name(path);
423 for (p = tail; *p; p++) {
425 if (!isdigit((
unsigned char)*p))
427 if (sscanf(p,
"%2d:%2d:%2d%n", &h, &m, &s, &n) == 3 && n >= 7 &&
428 h >= 0 && h < 24 && m >= 0 && m < 60 && s >= 0 && s < 60) {
429 return h * 3600 + m * 60 + s;
435static int compare_input_files(
const void *a,
const void *b) {
436 const char *pa = *(
const char *
const *)a;
437 const char *pb = *(
const char *
const *)b;
438 int ta = timestamp_seconds(pa);
439 int tb = timestamp_seconds(pb);
442 if (ta >= 0 && tb >= 0 && ta != tb)
443 return ta < tb ? -1 : 1;
444 cmp = strcmp(base_name(pa), base_name(pb));
447 return strcmp(pa, pb);
450static int option_name_matches(
const char *arg,
const char *name,
const char **value) {
452 const char *body, *eq;
454 if (!arg || arg[0] !=
'-')
459 eq = strchr(body,
'=');
460 nameLen = eq ? (size_t)(eq - body) : strlen(body);
461 if (strlen(name) != nameLen || strncasecmp(body, name, nameLen) != 0)
464 *value = eq ? eq + 1 : NULL;
468static const char *option_value(
int *iArg,
int argc,
char **argv,
const char *arg,
const char *name) {
469 const char *value = NULL;
470 if (!option_name_matches(arg, name, &value))
474 if (*iArg + 1 >= argc) {
475 fprintf(stderr,
"error: missing value for -%s\n", name);
478 return argv[++(*iArg)];
481static long parse_long_option(
const char *name,
const char *value) {
485 result = strtol(value, &endptr, 10);
486 if (errno || !value[0] || *endptr) {
487 fprintf(stderr,
"error: -%s must be an integer\n", name);
493static const char *stat_suffix(STAT_CODE code) {
505 case STAT_SIGNED_LARGEST:
506 return "SignedLargest";
511 case STAT_STANDARD_DEVIATION:
517 case STAT_WSTANDARD_DEVIATION:
541static int stat_uses_weight(STAT_CODE code) {
542 return code == STAT_WMEAN || code == STAT_WSTANDARD_DEVIATION ||
543 code == STAT_WRMS || code == STAT_WSIGMA;
546static int stat_uses_function_column(STAT_CODE code) {
547 return code == STAT_CMAXIMUM || code == STAT_CMINIMUM;
550static int stat_uses_function_parameter(STAT_CODE code) {
551 return code == STAT_PMAXIMUM || code == STAT_PMINIMUM ||
552 code == STAT_SLOPE || code == STAT_INTERCEPT;
555static void init_options(
OPTIONS *opts) {
556 memset(opts, 0,
sizeof(*opts));
557 string_list_append(&opts->pattern,
"PSD-*.xz");
558 string_list_append(&opts->pattern,
"PSD-*.gz");
559 opts->groupBy = xstrdup(
"PSName");
560 opts->inputDir = current_directory();
561 opts->output = xstrdup(
"PSD-envelope.sdds");
564 opts->columnMajorOrder = 1;
568 string_list_clear(&request->column);
569 free(request->weightColumn);
570 free(request->functionOf);
574 free(stat->sourceColumn);
575 free(stat->resultColumn);
576 free(stat->weightColumn);
577 free(stat->functionOf);
580static void free_options(
OPTIONS *opts) {
582 string_list_clear(&opts->pattern);
583 string_list_clear(&opts->groupValue);
585 free(opts->inputDir);
587 for (i = 0; i < opts->requests; i++)
588 free_raw_request(opts->request + i);
590 for (i = 0; i < opts->stats; i++)
591 free_stat_definition(opts->stat + i);
597 if (opts->requests >= opts->requestsAllocated) {
598 opts->requestsAllocated = opts->requestsAllocated ? 2 * opts->requestsAllocated : 16;
599 opts->request = xrealloc(opts->request,
sizeof(*opts->request) * opts->requestsAllocated);
601 request = opts->request + opts->requests++;
602 memset(request, 0,
sizeof(*request));
603 request->code = code;
604 request->sumPower = 1;
608static void add_simple_stat_request(
OPTIONS *opts, STAT_CODE code,
const char *value) {
610 split_comma_list(value, &request->column);
613static void add_sum_request(
OPTIONS *opts,
const char *value) {
618 memset(&item, 0,
sizeof(item));
619 split_comma_list(value, &item);
620 if (item.items < 2) {
621 fprintf(stderr,
"error: -sum requires <power>,<columns>\n");
622 string_list_clear(&item);
625 request = add_raw_request(opts, STAT_SUM);
626 request->sumPower = parse_long_option(
"sum", item.item[0]);
627 if (request->sumPower < 1) {
628 fprintf(stderr,
"error: -sum power must be >= 1\n");
629 string_list_clear(&item);
632 for (i = 1; i < item.items; i++)
633 string_list_append(&request->column, item.item[i]);
634 string_list_clear(&item);
637static void add_weighted_request(
OPTIONS *opts, STAT_CODE code,
const char *value,
const char *optionName) {
642 memset(&item, 0,
sizeof(item));
643 split_comma_list(value, &item);
644 if (item.items < 2) {
645 fprintf(stderr,
"error: -%s requires <weightColumn>,<columns>\n", optionName);
646 string_list_clear(&item);
649 request = add_raw_request(opts, code);
650 request->weightColumn = xstrdup(item.item[0]);
651 for (i = 1; i < item.items; i++)
652 string_list_append(&request->column, item.item[i]);
653 string_list_clear(&item);
656static void add_function_request(
OPTIONS *opts, STAT_CODE code,
const char *value,
const char *optionName) {
661 memset(&item, 0,
sizeof(item));
662 split_comma_list(value, &item);
663 if (item.items < 2) {
664 fprintf(stderr,
"error: -%s requires <independent>,<columns>\n", optionName);
665 string_list_clear(&item);
668 request = add_raw_request(opts, code);
669 request->functionOf = xstrdup(item.item[0]);
670 for (i = 1; i < item.items; i++)
671 string_list_append(&request->column, item.item[i]);
672 string_list_clear(&item);
675static void parse_options(
OPTIONS *opts,
int argc,
char **argv) {
676 int iArg, patternSpecified = 0;
678 for (iArg = 1; iArg < argc; iArg++) {
679 const char *arg = argv[iArg];
682 if (option_name_matches(arg,
"help", NULL) || option_name_matches(arg,
"h", NULL)) {
683 fputs(USAGE, stdout);
685 }
else if ((value = option_value(&iArg, argc, argv, arg,
"pattern"))) {
686 if (!patternSpecified) {
687 string_list_clear(&opts->pattern);
688 patternSpecified = 1;
690 string_list_append(&opts->pattern, value);
691 }
else if ((value = option_value(&iArg, argc, argv, arg,
"inputDir"))) {
692 if (!strlen(value)) {
693 fprintf(stderr,
"error: -inputDir value may not be blank\n");
696 free(opts->inputDir);
697 opts->inputDir = xstrdup(value);
698 }
else if ((value = option_value(&iArg, argc, argv, arg,
"groupBy"))) {
699 if (!strlen(value)) {
700 fprintf(stderr,
"error: -groupBy value may not be blank\n");
704 opts->groupBy = xstrdup(value);
705 }
else if ((value = option_value(&iArg, argc, argv, arg,
"groupValue"))) {
706 string_list_append(&opts->groupValue, value);
707 }
else if ((value = option_value(&iArg, argc, argv, arg,
"output"))) {
709 opts->output = xstrdup(value);
710 }
else if ((value = option_value(&iArg, argc, argv, arg,
"majorOrder"))) {
711 if (strcasecmp(value,
"column") == 0)
712 opts->columnMajorOrder = 1;
713 else if (strcasecmp(value,
"row") == 0)
714 opts->columnMajorOrder = 0;
716 fprintf(stderr,
"error: -majorOrder must be row or column\n");
719 }
else if ((value = option_value(&iArg, argc, argv, arg,
"threads"))) {
720 long threads = parse_long_option(
"threads", value);
722 fprintf(stderr,
"error: -threads must be >= 1\n");
725 if (threads > INT_MAX) {
726 fprintf(stderr,
"error: -threads value is too large\n");
729 opts->threads = (int)threads;
730 }
else if ((value = option_value(&iArg, argc, argv, arg,
"copy"))) {
731 add_simple_stat_request(opts, STAT_COPY, value);
732 }
else if ((value = option_value(&iArg, argc, argv, arg,
"maximum"))) {
733 add_simple_stat_request(opts, STAT_MAXIMUM, value);
734 }
else if ((value = option_value(&iArg, argc, argv, arg,
"minimum"))) {
735 add_simple_stat_request(opts, STAT_MINIMUM, value);
736 }
else if ((value = option_value(&iArg, argc, argv, arg,
"mean"))) {
737 add_simple_stat_request(opts, STAT_MEAN, value);
738 }
else if ((value = option_value(&iArg, argc, argv, arg,
"largest"))) {
739 add_simple_stat_request(opts, STAT_LARGEST, value);
740 }
else if ((value = option_value(&iArg, argc, argv, arg,
"signedLargest"))) {
741 add_simple_stat_request(opts, STAT_SIGNED_LARGEST, value);
742 }
else if ((value = option_value(&iArg, argc, argv, arg,
"sum"))) {
743 add_sum_request(opts, value);
744 }
else if ((value = option_value(&iArg, argc, argv, arg,
"rms"))) {
745 add_simple_stat_request(opts, STAT_RMS, value);
746 }
else if ((value = option_value(&iArg, argc, argv, arg,
"standarddeviations")) ||
747 (value = option_value(&iArg, argc, argv, arg,
"standarddeviation"))) {
748 add_simple_stat_request(opts, STAT_STANDARD_DEVIATION, value);
749 }
else if ((value = option_value(&iArg, argc, argv, arg,
"sigmas")) ||
750 (value = option_value(&iArg, argc, argv, arg,
"sigma"))) {
751 add_simple_stat_request(opts, STAT_SIGMA, value);
752 }
else if ((value = option_value(&iArg, argc, argv, arg,
"wmean"))) {
753 add_weighted_request(opts, STAT_WMEAN, value,
"wmean");
754 }
else if ((value = option_value(&iArg, argc, argv, arg,
"wstandarddeviations")) ||
755 (value = option_value(&iArg, argc, argv, arg,
"wstandarddeviation"))) {
756 add_weighted_request(opts, STAT_WSTANDARD_DEVIATION, value,
"wstandarddeviations");
757 }
else if ((value = option_value(&iArg, argc, argv, arg,
"wrms"))) {
758 add_weighted_request(opts, STAT_WRMS, value,
"wrms");
759 }
else if ((value = option_value(&iArg, argc, argv, arg,
"wsigma")) ||
760 (value = option_value(&iArg, argc, argv, arg,
"wsigmas"))) {
761 add_weighted_request(opts, STAT_WSIGMA, value,
"wsigma");
762 }
else if ((value = option_value(&iArg, argc, argv, arg,
"cmaximum"))) {
763 add_function_request(opts, STAT_CMAXIMUM, value,
"cmaximum");
764 }
else if ((value = option_value(&iArg, argc, argv, arg,
"cminimum"))) {
765 add_function_request(opts, STAT_CMINIMUM, value,
"cminimum");
766 }
else if ((value = option_value(&iArg, argc, argv, arg,
"pmaximum"))) {
767 add_function_request(opts, STAT_PMAXIMUM, value,
"pmaximum");
768 }
else if ((value = option_value(&iArg, argc, argv, arg,
"pminimum"))) {
769 add_function_request(opts, STAT_PMINIMUM, value,
"pminimum");
770 }
else if ((value = option_value(&iArg, argc, argv, arg,
"slope"))) {
771 add_function_request(opts, STAT_SLOPE, value,
"slope");
772 }
else if ((value = option_value(&iArg, argc, argv, arg,
"intercept"))) {
773 add_function_request(opts, STAT_INTERCEPT, value,
"intercept");
774 }
else if ((value = option_value(&iArg, argc, argv, arg,
"exmmMean"))) {
775 add_simple_stat_request(opts, STAT_EXMM_MEAN, value);
776 }
else if (option_name_matches(arg,
"noOverwrite", NULL)) {
778 }
else if (option_name_matches(arg,
"overwrite", NULL)) {
780 }
else if (option_name_matches(arg,
"verbose", NULL)) {
782 }
else if (option_name_matches(arg,
"nowarnings", NULL)) {
783 opts->noWarnings = 1;
784 }
else if (option_name_matches(arg,
"median", NULL) ||
785 option_name_matches(arg,
"percentile", NULL) ||
786 option_name_matches(arg,
"decilerange", NULL)) {
787 fprintf(stderr,
"error: %s is not supported because it requires retaining page histories\n", arg);
790 fprintf(stderr,
"error: unknown option: %s\n", arg);
795 if (opts->requests == 0) {
796 fprintf(stderr,
"error: at least one statistic option is required\n");
799 if (!is_directory(opts->inputDir)) {
800 fprintf(stderr,
"error: -inputDir is not a directory: %s\n", opts->inputDir);
805static void collect_input_files(
const char *workDir,
const OPTIONS *opts,
const char *finalOutput,
STRING_LIST *files) {
807 for (i = 0; i < opts->pattern.items; i++) {
810 WIN32_FIND_DATAA findData;
812 const char *separator;
815 globPattern = is_absolute_path(opts->pattern.item[i]) ? xstrdup(opts->pattern.item[i]) : join_path(workDir, opts->pattern.item[i]);
816 separator = strrchr(globPattern,
'/');
818 const char *backslash = strrchr(globPattern,
'\\');
819 if (!separator || (backslash && backslash > separator))
820 separator = backslash;
822 prefixLength = separator ? (size_t)(separator - globPattern + 1) : 0;
823 findHandle = FindFirstFileA(globPattern, &findData);
824 if (findHandle != INVALID_HANDLE_VALUE) {
828 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
830 nameLength = strlen(findData.cFileName);
831 path = xmalloc(prefixLength + nameLength + 1);
832 memcpy(path, globPattern, prefixLength);
833 memcpy(path + prefixLength, findData.cFileName, nameLength + 1);
834 if ((!finalOutput || !same_existing_file(path, finalOutput)) &&
835 !string_list_contains(files, path))
836 string_list_append_owned(files, path);
839 }
while (FindNextFileA(findHandle, &findData));
840 if (GetLastError() != ERROR_NO_MORE_FILES)
841 fprintf(stderr,
"warning: file search failed for %s\n", globPattern);
842 FindClose(findHandle);
843 }
else if (GetLastError() != ERROR_FILE_NOT_FOUND &&
844 GetLastError() != ERROR_PATH_NOT_FOUND) {
845 fprintf(stderr,
"warning: file search failed for %s\n", globPattern);
853 globPattern = is_absolute_path(opts->pattern.item[i]) ? xstrdup(opts->pattern.item[i]) : join_path(workDir, opts->pattern.item[i]);
854 memset(&globResult, 0,
sizeof(globResult));
855 status = glob(globPattern, 0, NULL, &globResult);
857 for (j = 0; j < globResult.gl_pathc; j++) {
858 const char *path = globResult.gl_pathv[j];
859 if (finalOutput && same_existing_file(path, finalOutput))
861 if (!string_list_contains(files, path))
862 string_list_append(files, path);
864 }
else if (status != GLOB_NOMATCH) {
865 fprintf(stderr,
"warning: glob failed for %s\n", globPattern);
867 globfree(&globResult);
871 if (files->items > 1)
872 qsort(files->item, files->items,
sizeof(*files->item), compare_input_files);
877 const char *suffix = stat_suffix(stat->code);
878 const char *prefix = stat->sourceColumn;
882 if (stat->code == STAT_COPY)
883 return xstrdup(stat->sourceColumn);
884 if (stat->code == STAT_SUM && stat->sumPower != 1) {
885 snprintf(buffer,
sizeof(buffer),
"%ld%s", stat->sumPower, suffix);
888 if (stat->code == STAT_CMAXIMUM || stat->code == STAT_CMINIMUM ||
889 stat->code == STAT_PMAXIMUM || stat->code == STAT_PMINIMUM) {
890 length = strlen(stat->functionOf) + strlen(suffix) + strlen(stat->sourceColumn) + 1;
891 name = xmalloc(length);
892 snprintf(name, length,
"%s%s%s", stat->functionOf, suffix, stat->sourceColumn);
895 length = strlen(prefix) + strlen(suffix) + 1;
896 name = xmalloc(length);
897 snprintf(name, length,
"%s%s", prefix, suffix);
901static int result_name_exists(
const OPTIONS *opts,
const char *name) {
903 for (i = 0; i < opts->stats; i++) {
904 if (strcmp(opts->stat[i].resultColumn, name) == 0)
913 int32_t sourceIndex, sourceType;
916 fprintf(stderr,
"error: column %s not found\n", sourceColumn);
921 fprintf(stderr,
"error: column %s is not numeric\n", sourceColumn);
924 if (stat_uses_weight(request->code)) {
925 int32_t weightIndex, weightType;
927 fprintf(stderr,
"error: weight column %s not found\n", request->weightColumn);
932 fprintf(stderr,
"error: weight column %s is not numeric\n", request->weightColumn);
936 if (stat_uses_function_column(request->code)) {
937 int32_t functionIndex, functionType;
939 fprintf(stderr,
"error: independent column %s not found\n", request->functionOf);
944 fprintf(stderr,
"error: independent column %s is not numeric\n", request->functionOf);
948 if (stat_uses_function_parameter(request->code)) {
949 int32_t functionIndex, functionType;
951 fprintf(stderr,
"error: independent parameter %s not found\n", request->functionOf);
956 fprintf(stderr,
"error: independent parameter %s is not numeric\n", request->functionOf);
961 if (opts->stats >= opts->statsAllocated) {
962 opts->statsAllocated = opts->statsAllocated ? 2 * opts->statsAllocated : 32;
963 opts->stat = xrealloc(opts->stat,
sizeof(*opts->stat) * opts->statsAllocated);
965 stat = opts->stat + opts->stats;
966 memset(stat, 0,
sizeof(*stat));
967 stat->code = request->code;
968 stat->sourceColumn = xstrdup(sourceColumn);
969 stat->weightColumn = request->weightColumn ? xstrdup(request->weightColumn) : NULL;
970 stat->functionOf = request->functionOf ? xstrdup(request->functionOf) : NULL;
971 stat->sumPower = request->sumPower;
972 stat->sourceType = sourceType;
973 resultName = stat_column_name(stat);
974 if (result_name_exists(opts, resultName)) {
975 fprintf(stderr,
"error: duplicate output column name %s\n", resultName);
977 free_stat_definition(stat);
978 memset(stat, 0,
sizeof(*stat));
981 stat->resultColumn = resultName;
991 fprintf(stderr,
"error: input file does not contain grouping parameter %s\n", opts->groupBy);
995 if (opts->groupByType <= 0) {
996 fprintf(stderr,
"error: grouping parameter %s has invalid type\n", opts->groupBy);
1000 for (iReq = 0; iReq < opts->requests; iReq++) {
1002 for (iCol = 0; iCol < request->column.items; iCol++) {
1003 char *columnPattern = request->column.item[iCol];
1005 char **columnName = NULL;
1006 int32_t columnNames = 0;
1011 fprintf(stderr,
"error: no columns selected for wildcard %s\n", columnPattern);
1015 for (iName = 0; iName < columnNames; iName++) {
1016 if (!append_stat_definition(opts, input, request, columnName[iName])) {
1022 }
else if (!append_stat_definition(opts, input, request, columnPattern)) {
1027 if (opts->stats == 0) {
1028 fprintf(stderr,
"error: no statistic columns were selected\n");
1034static void free_copy_data(
void *data, int32_t type, int64_t rows) {
1039 char **stringData = (
char **)data;
1040 for (i = 0; i < rows; i++)
1041 free(stringData[i]);
1047 if (stat->code == STAT_COPY)
1048 free_copy_data(accumulator->copyData, stat->sourceType, rows);
1049 free(accumulator->value1);
1050 free(accumulator->value2);
1051 free(accumulator->value3);
1052 free(accumulator->value4);
1053 free(accumulator->sumWeight);
1056static void free_group_value(
void *value, int32_t type) {
1060 free(*(
char **)value);
1064#if SDDSGROUPEDENVELOPE_USE_OPENMP
1065static void *duplicate_group_value(
const void *value, int32_t type) {
1072 char **stringCopy = xmalloc(
sizeof(*stringCopy));
1073 char *
const *stringValue = (
char *
const *)value;
1074 *stringCopy = xstrdup(*stringValue ? *stringValue :
"");
1079 fprintf(stderr,
"error: invalid grouping parameter type %d\n", type);
1082 copy = xmalloc((
size_t)size);
1083 memcpy(copy, value, (
size_t)size);
1090 for (i = 0; i < groups->groups; i++) {
1093 free_group_value(group->value, opts->groupByType);
1094 for (j = 0; j < group->rowDatas; j++) {
1096 for (iStat = 0; iStat < opts->stats; iStat++)
1097 free_stat_accumulator(rowData->stat + iStat, opts->stat + iStat, rowData->rows);
1098 free(rowData->stat);
1100 free(group->rowData);
1102 free(groups->group);
1103 groups->group = NULL;
1104 groups->groups = groups->groupsAllocated = 0;
1107#if SDDSGROUPEDENVELOPE_USE_OPENMP
1108static void copy_double_array(
double *target,
const double *source, int64_t rows) {
1109 memcpy(target, source,
sizeof(*target) * (
size_t)rows);
1115 for (i = 0; i < groups->groups; i++) {
1116 if (strcmp(groups->group[i].name, groupName) == 0)
1117 return groups->group + i;
1124 if (groups->groups >= groups->groupsAllocated) {
1125 groups->groupsAllocated = groups->groupsAllocated ? 2 * groups->groupsAllocated : 64;
1126 groups->group = xrealloc(groups->group,
sizeof(*groups->group) * groups->groupsAllocated);
1128 group = groups->group + groups->groups++;
1129 memset(group, 0,
sizeof(*group));
1130 group->name = xstrdup(groupName);
1131 group->value = groupValue;
1136 switch (stat->code) {
1139 case STAT_STANDARD_DEVIATION:
1145 case STAT_WSTANDARD_DEVIATION:
1147 accumulator->value1 = xcalloc(rows,
sizeof(*accumulator->value1));
1148 accumulator->value2 = xcalloc(rows,
sizeof(*accumulator->value2));
1149 if (stat->code == STAT_WSTANDARD_DEVIATION || stat->code == STAT_WSIGMA)
1150 accumulator->sumWeight = xcalloc(rows,
sizeof(*accumulator->sumWeight));
1153 case STAT_INTERCEPT:
1154 case STAT_EXMM_MEAN:
1155 accumulator->value1 = xcalloc(rows,
sizeof(*accumulator->value1));
1156 accumulator->value2 = xcalloc(rows,
sizeof(*accumulator->value2));
1157 accumulator->value3 = xcalloc(rows,
sizeof(*accumulator->value3));
1158 accumulator->value4 = xcalloc(rows,
sizeof(*accumulator->value4));
1159 if (stat->code == STAT_EXMM_MEAN)
1160 accumulator->sumWeight = xcalloc(rows,
sizeof(*accumulator->sumWeight));
1164 accumulator->value1 = xcalloc(rows,
sizeof(*accumulator->value1));
1165 accumulator->sumWeight = xcalloc(rows,
sizeof(*accumulator->sumWeight));
1168 accumulator->value1 = xcalloc(rows,
sizeof(*accumulator->value1));
1175 for (i = 0; i < group->rowDatas; i++) {
1176 if (group->rowData[i].rows == rows)
1177 return group->rowData + i;
1186 if (group->rowDatas >= group->rowDatasAllocated) {
1187 group->rowDatasAllocated = group->rowDatasAllocated ? 2 * group->rowDatasAllocated : 2;
1188 group->rowData = xrealloc(group->rowData,
sizeof(*group->rowData) * group->rowDatasAllocated);
1190 rowData = group->rowData + group->rowDatas++;
1191 memset(rowData, 0,
sizeof(*rowData));
1192 rowData->rows = rows;
1193 rowData->stat = xcalloc(opts->stats,
sizeof(*rowData->stat));
1194 for (iStat = 0; iStat < opts->stats; iStat++)
1195 allocate_stat_accumulator(rowData->stat + iStat, opts->stat + iStat, rows);
1203 return add_row_accumulator(group, opts, rows);
1209 for (i = 0; i < group->rowDatas; i++) {
1210 if (!best || group->rowData[i].pages > best->pages)
1211 best = group->rowData + i;
1216static int read_parameter_double(
SDDS_DATASET *input,
const char *name,
double *value) {
1218 fprintf(stderr,
"error: unable to read parameter %s as double\n", name);
1225static double integer_power(
double value,
long power) {
1228 for (i = 0; i < power; i++)
1233static void accumulate_exmm(
STAT_ACCUMULATOR *accumulator,
double *data, int64_t rows) {
1235 if (!accumulator->initialized) {
1236 for (i = 0; i < rows; i++) {
1237 accumulator->value1[i] = data[i];
1238 accumulator->value2[i] = data[i];
1239 accumulator->value3[i] = data[i];
1240 accumulator->value4[i] = 1;
1241 accumulator->sumWeight[i] = 1;
1243 accumulator->initialized = 1;
1246 for (i = 0; i < rows; i++) {
1247 accumulator->value1[i] += data[i];
1248 if (data[i] < accumulator->value2[i]) {
1249 accumulator->value2[i] = data[i];
1250 accumulator->value4[i] = 1;
1251 }
else if (data[i] == accumulator->value2[i]) {
1252 accumulator->value4[i] += 1;
1254 if (data[i] > accumulator->value3[i]) {
1255 accumulator->value3[i] = data[i];
1256 accumulator->sumWeight[i] = 1;
1257 }
else if (data[i] == accumulator->value3[i]) {
1258 accumulator->sumWeight[i] += 1;
1266 double *data = NULL, *weight = NULL, *otherData = NULL;
1267 double parameterValue = 0;
1268 int64_t i, rows = rowData->rows;
1270 if (stat->code == STAT_COPY) {
1271 if (!accumulator->initialized) {
1272 if (!(accumulator->copyData =
SDDS_GetColumn(input, stat->sourceColumn))) {
1273 fprintf(stderr,
"error: unable to read copy column %s\n", stat->sourceColumn);
1277 accumulator->initialized = 1;
1283 fprintf(stderr,
"error: unable to read column %s\n", stat->sourceColumn);
1287 if (stat_uses_weight(stat->code)) {
1289 fprintf(stderr,
"error: unable to read weight column %s\n", stat->weightColumn);
1295 if (stat_uses_function_column(stat->code)) {
1297 fprintf(stderr,
"error: unable to read independent column %s\n", stat->functionOf);
1304 if (stat_uses_function_parameter(stat->code) && !read_parameter_double(input, stat->functionOf, ¶meterValue)) {
1311 switch (stat->code) {
1313 if (!accumulator->initialized) {
1314 for (i = 0; i < rows; i++)
1315 accumulator->value1[i] = data[i];
1316 accumulator->initialized = 1;
1318 for (i = 0; i < rows; i++)
1319 if (accumulator->value1[i] < data[i])
1320 accumulator->value1[i] = data[i];
1324 if (!accumulator->initialized) {
1325 for (i = 0; i < rows; i++)
1326 accumulator->value1[i] = data[i];
1327 accumulator->initialized = 1;
1329 for (i = 0; i < rows; i++)
1330 if (accumulator->value1[i] > data[i])
1331 accumulator->value1[i] = data[i];
1336 for (i = 0; i < rows; i++)
1337 accumulator->value1[i] += stat->code == STAT_SUM ? integer_power(data[i], stat->sumPower) : data[i];
1338 accumulator->initialized = 1;
1341 if (!accumulator->initialized) {
1342 for (i = 0; i < rows; i++)
1343 accumulator->value1[i] = fabs(data[i]);
1344 accumulator->initialized = 1;
1346 for (i = 0; i < rows; i++)
1347 if (accumulator->value1[i] < fabs(data[i]))
1348 accumulator->value1[i] = fabs(data[i]);
1351 case STAT_SIGNED_LARGEST:
1352 if (!accumulator->initialized) {
1353 for (i = 0; i < rows; i++)
1354 accumulator->value1[i] = data[i];
1355 accumulator->initialized = 1;
1357 for (i = 0; i < rows; i++)
1358 if (fabs(accumulator->value1[i]) < fabs(data[i]))
1359 accumulator->value1[i] = data[i];
1363 for (i = 0; i < rows; i++)
1364 accumulator->value1[i] += data[i] * data[i];
1365 accumulator->initialized = 1;
1367 case STAT_STANDARD_DEVIATION:
1369 for (i = 0; i < rows; i++) {
1370 accumulator->value1[i] += data[i];
1371 accumulator->value2[i] += data[i] * data[i];
1373 accumulator->initialized = 1;
1376 for (i = 0; i < rows; i++) {
1377 accumulator->sumWeight[i] += weight[i];
1378 accumulator->value1[i] += data[i] * weight[i];
1380 accumulator->initialized = 1;
1382 case STAT_WSTANDARD_DEVIATION:
1384 for (i = 0; i < rows; i++) {
1385 accumulator->sumWeight[i] += weight[i];
1386 accumulator->value1[i] += data[i] * weight[i];
1387 accumulator->value2[i] += data[i] * data[i] * weight[i];
1389 accumulator->initialized = 1;
1392 for (i = 0; i < rows; i++) {
1393 accumulator->sumWeight[i] += weight[i];
1394 accumulator->value1[i] += data[i] * data[i] * weight[i];
1396 accumulator->initialized = 1;
1400 if (!accumulator->initialized) {
1401 for (i = 0; i < rows; i++) {
1402 accumulator->value1[i] = otherData[i];
1403 accumulator->value2[i] = data[i];
1405 accumulator->initialized = 1;
1407 for (i = 0; i < rows; i++) {
1408 if ((stat->code == STAT_CMAXIMUM && accumulator->value2[i] < data[i]) ||
1409 (stat->code == STAT_CMINIMUM && accumulator->value2[i] > data[i])) {
1410 accumulator->value1[i] = otherData[i];
1411 accumulator->value2[i] = data[i];
1418 if (!accumulator->initialized) {
1419 for (i = 0; i < rows; i++) {
1420 accumulator->value1[i] = parameterValue;
1421 accumulator->value2[i] = data[i];
1423 accumulator->initialized = 1;
1425 for (i = 0; i < rows; i++) {
1426 if ((stat->code == STAT_PMAXIMUM && accumulator->value2[i] < data[i]) ||
1427 (stat->code == STAT_PMINIMUM && accumulator->value2[i] > data[i])) {
1428 accumulator->value1[i] = parameterValue;
1429 accumulator->value2[i] = data[i];
1435 case STAT_INTERCEPT:
1436 for (i = 0; i < rows; i++) {
1437 accumulator->value1[i] += parameterValue;
1438 accumulator->value2[i] += parameterValue * parameterValue;
1439 accumulator->value3[i] += data[i];
1440 accumulator->value4[i] += parameterValue * data[i];
1442 accumulator->initialized = 1;
1444 case STAT_EXMM_MEAN:
1445 accumulate_exmm(accumulator, data, rows);
1457#if SDDSGROUPEDENVELOPE_USE_OPENMP
1462 if (!source->initialized)
1465 if (stat->code == STAT_COPY) {
1466 if (!target->initialized) {
1467 target->copyData = source->copyData;
1468 source->copyData = NULL;
1469 target->initialized = 1;
1474 if (!target->initialized) {
1476 copy_double_array(target->value1, source->value1, rows);
1478 copy_double_array(target->value2, source->value2, rows);
1480 copy_double_array(target->value3, source->value3, rows);
1482 copy_double_array(target->value4, source->value4, rows);
1483 if (source->sumWeight)
1484 copy_double_array(target->sumWeight, source->sumWeight, rows);
1485 target->initialized = 1;
1489 switch (stat->code) {
1492 for (i = 0; i < rows; i++)
1493 if (target->value1[i] < source->value1[i])
1494 target->value1[i] = source->value1[i];
1497 for (i = 0; i < rows; i++)
1498 if (target->value1[i] > source->value1[i])
1499 target->value1[i] = source->value1[i];
1501 case STAT_SIGNED_LARGEST:
1502 for (i = 0; i < rows; i++)
1503 if (fabs(target->value1[i]) < fabs(source->value1[i]))
1504 target->value1[i] = source->value1[i];
1508 for (i = 0; i < rows; i++) {
1509 if (target->value2[i] < source->value2[i]) {
1510 target->value1[i] = source->value1[i];
1511 target->value2[i] = source->value2[i];
1517 for (i = 0; i < rows; i++) {
1518 if (target->value2[i] > source->value2[i]) {
1519 target->value1[i] = source->value1[i];
1520 target->value2[i] = source->value2[i];
1527 for (i = 0; i < rows; i++)
1528 target->value1[i] += source->value1[i];
1530 case STAT_STANDARD_DEVIATION:
1532 for (i = 0; i < rows; i++) {
1533 target->value1[i] += source->value1[i];
1534 target->value2[i] += source->value2[i];
1539 for (i = 0; i < rows; i++) {
1540 target->value1[i] += source->value1[i];
1541 target->sumWeight[i] += source->sumWeight[i];
1544 case STAT_WSTANDARD_DEVIATION:
1546 for (i = 0; i < rows; i++) {
1547 target->value1[i] += source->value1[i];
1548 target->value2[i] += source->value2[i];
1549 target->sumWeight[i] += source->sumWeight[i];
1553 case STAT_INTERCEPT:
1554 for (i = 0; i < rows; i++) {
1555 target->value1[i] += source->value1[i];
1556 target->value2[i] += source->value2[i];
1557 target->value3[i] += source->value3[i];
1558 target->value4[i] += source->value4[i];
1561 case STAT_EXMM_MEAN:
1562 for (i = 0; i < rows; i++) {
1563 target->value1[i] += source->value1[i];
1564 if (source->value2[i] < target->value2[i]) {
1565 target->value2[i] = source->value2[i];
1566 target->value4[i] = source->value4[i];
1567 }
else if (source->value2[i] == target->value2[i]) {
1568 target->value4[i] += source->value4[i];
1570 if (source->value3[i] > target->value3[i]) {
1571 target->value3[i] = source->value3[i];
1572 target->sumWeight[i] = source->sumWeight[i];
1573 }
else if (source->value3[i] == target->value3[i]) {
1574 target->sumWeight[i] += source->sumWeight[i];
1586 for (iStat = 0; iStat < opts->stats; iStat++)
1587 merge_stat_accumulator(target->stat + iStat, source->stat + iStat, opts->stat + iStat, source->rows);
1588 target->pages += source->pages;
1592 long iGroup, iRowData;
1594 for (iGroup = 0; iGroup < source->groups; iGroup++) {
1596 ENVELOPE_GROUP *targetGroup = find_group(target, sourceGroup->name);
1598 targetGroup = add_group(target, sourceGroup->name, duplicate_group_value(sourceGroup->value, opts->groupByType));
1600 targetGroup->zeroRowPages += sourceGroup->zeroRowPages;
1601 for (iRowData = 0; iRowData < sourceGroup->rowDatas; iRowData++) {
1603 ROW_ACCUMULATOR *targetRowData = get_row_accumulator(targetGroup, opts, sourceRowData->rows);
1604 merge_row_accumulator(targetRowData, sourceRowData, opts);
1611 char *symbol = NULL;
1614 const char *definitionColumn = stat->sourceColumn;
1616 if (stat->code == STAT_CMAXIMUM || stat->code == STAT_CMINIMUM)
1617 definitionColumn = stat->functionOf;
1619 if (stat->code == STAT_COPY) {
1627 fprintf(stderr,
"error: unable to clear description for output column %s\n", stat->resultColumn);
1631 fprintf(stderr,
"error: unable to set output column %s type to double\n", stat->resultColumn);
1635 fprintf(stderr,
"error: unable to read symbol for output column %s\n", stat->resultColumn);
1638 if (!symbol || !strlen(symbol)) {
1640 symbol = xstrdup(stat->sourceColumn);
1643 needed = strlen(stat_suffix(stat->code)) + strlen(symbol) + 3;
1644 newSymbol = xmalloc(needed);
1645 snprintf(newSymbol, needed,
"%s[%s]", stat_suffix(stat->code), symbol);
1649 fprintf(stderr,
"error: unable to set symbol for output column %s\n", stat->resultColumn);
1660 if (!
SDDS_InitializeOutput(output, SDDS_BINARY, 0, NULL,
"sddsgroupedenvelope output", (
char *)outputFile)) {
1661 fprintf(stderr,
"error: unable to initialize output file %s\n", outputFile);
1664 output->layout.data_mode.column_major = opts->columnMajorOrder;
1667 fprintf(stderr,
"error: unable to define grouping parameter %s in output\n", opts->groupBy);
1670 for (iStat = 0; iStat < opts->stats; iStat++) {
1671 if (!define_stat_column(output, input, opts->stat + iStat)) {
1672 fprintf(stderr,
"error: unable to define output column %s\n", opts->stat[iStat].resultColumn);
1677 fprintf(stderr,
"error: unable to write output layout for %s\n", outputFile);
1683static int read_input_file(
const char *filename,
const OPTIONS *opts,
GROUP_LIST *groups) {
1688 memset(&seen, 0,
sizeof(seen));
1690 fprintf(stderr,
"Reading %s\n", filename);
1693 string_list_clear(&seen);
1698 char *groupName = NULL;
1706 if (!groupName || !strlen(groupName)) {
1707 fprintf(stderr,
"error: blank or missing %s in %s page %d\n", opts->groupBy, filename, pageCode);
1710 string_list_clear(&seen);
1713 if (string_list_contains(&seen, groupName)) {
1714 fprintf(stderr,
"error: duplicate %s %s in %s\n", opts->groupBy, groupName, filename);
1717 string_list_clear(&seen);
1720 string_list_append(&seen, groupName);
1722 wantPage = opts->groupValue.items == 0 || string_list_contains(&opts->groupValue, groupName);
1728 if (!(group = find_group(groups, groupName))) {
1731 fprintf(stderr,
"error: unable to read grouping parameter %s\n", opts->groupBy);
1735 string_list_clear(&seen);
1738 group = add_group(groups, groupName, groupValue);
1742 group->zeroRowPages++;
1747 rowData = get_row_accumulator(group, opts, rows);
1748 for (iStat = 0; iStat < opts->stats; iStat++) {
1749 if (!accumulate_stat(&input, rowData, opts, iStat)) {
1752 string_list_clear(&seen);
1760 if (pageCode == 0) {
1761 fprintf(stderr,
"error: failed while reading pages from %s\n", filename);
1764 string_list_clear(&seen);
1768 fprintf(stderr,
"error: failed to terminate input file %s\n", filename);
1770 string_list_clear(&seen);
1773 string_list_clear(&seen);
1780 for (iFile = 0; iFile < files->items; iFile++) {
1781 if (!read_input_file(files->item[iFile], opts, groups))
1788#if SDDSGROUPEDENVELOPE_USE_OPENMP
1790 long batchStart, iBatch, batchSize;
1792 int threads = opts->threads;
1794 if (threads > files->items)
1795 threads = (int)files->items;
1797 return read_input_files_serial(files, opts, groups);
1799 result = xcalloc((
size_t)threads,
sizeof(*result));
1800 omp_set_num_threads(threads);
1802 for (batchStart = 0; ok && batchStart < files->items; batchStart += threads) {
1803 batchSize = files->items - batchStart;
1804 if (batchSize > threads)
1805 batchSize = threads;
1807#pragma omp parallel for schedule(dynamic)
1808 for (iBatch = 0; iBatch < batchSize; iBatch++)
1809 result[iBatch].ok = read_input_file(files->item[batchStart + iBatch], opts, &result[iBatch].groups);
1811 for (iBatch = 0; iBatch < batchSize; iBatch++) {
1812 if (!result[iBatch].ok)
1816 for (iBatch = 0; iBatch < batchSize; iBatch++)
1817 merge_group_list(groups, &result[iBatch].groups, opts);
1819 for (iBatch = 0; iBatch < batchSize; iBatch++) {
1820 free_group_list(&result[iBatch].groups, opts);
1821 memset(&result[iBatch], 0,
sizeof(*result));
1827 return read_input_files_serial(files, opts, groups);
1832 if (opts->threads <= 1)
1833 return read_input_files_serial(files, opts, groups);
1834 return read_input_files_threaded(files, opts, groups);
1838 if (!opts->noWarnings)
1839 fprintf(stderr,
"warning: the total weight for row %" PRId64
" of %s is zero\n", row + 1, stat->sourceColumn);
1845 int64_t i, pages = rowData->pages;
1847 switch (stat->code) {
1852 case STAT_SIGNED_LARGEST:
1860 for (i = 0; i < rowData->rows; i++)
1861 accumulator->value1[i] /= pages;
1864 for (i = 0; i < rowData->rows; i++)
1865 accumulator->value1[i] = sqrt(accumulator->value1[i] / pages);
1867 case STAT_STANDARD_DEVIATION:
1869 for (i = 0; i < rowData->rows; i++)
1870 accumulator->value1[i] = DBL_MAX;
1872 for (i = 0; i < rowData->rows; i++) {
1873 double variance = accumulator->value2[i] / pages - sqr(accumulator->value1[i] / pages);
1874 accumulator->value1[i] = variance <= 0 ? 0 : sqrt(variance * pages / (pages - 1.0));
1880 for (i = 0; i < rowData->rows; i++)
1881 accumulator->value1[i] = DBL_MAX;
1883 for (i = 0; i < rowData->rows; i++) {
1884 double variance = accumulator->value2[i] / pages - sqr(accumulator->value1[i] / pages);
1885 accumulator->value1[i] = variance <= 0 ? 0 : sqrt(variance / (pages - 1.0));
1890 for (i = 0; i < rowData->rows; i++) {
1891 if (accumulator->sumWeight[i])
1892 accumulator->value1[i] /= accumulator->sumWeight[i];
1894 warn_zero_weight(opts, stat, i);
1895 accumulator->value1[i] = DBL_MAX;
1899 case STAT_WSTANDARD_DEVIATION:
1901 for (i = 0; i < rowData->rows; i++)
1902 accumulator->value1[i] = DBL_MAX;
1904 for (i = 0; i < rowData->rows; i++) {
1905 if (accumulator->sumWeight[i]) {
1906 double mean = accumulator->value1[i] / accumulator->sumWeight[i];
1907 double variance = accumulator->value2[i] / accumulator->sumWeight[i] - mean * mean;
1908 accumulator->value1[i] = variance <= 0 ? 0 : sqrt(variance * pages / (pages - 1.0));
1910 warn_zero_weight(opts, stat, i);
1911 accumulator->value1[i] = DBL_MAX;
1917 for (i = 0; i < rowData->rows; i++) {
1918 if (accumulator->sumWeight[i])
1919 accumulator->value1[i] = sqrt(accumulator->value1[i] / accumulator->sumWeight[i]);
1921 warn_zero_weight(opts, stat, i);
1922 accumulator->value1[i] = DBL_MAX;
1928 for (i = 0; i < rowData->rows; i++)
1929 accumulator->value1[i] = DBL_MAX;
1931 for (i = 0; i < rowData->rows; i++) {
1932 if (accumulator->sumWeight[i]) {
1933 double mean = accumulator->value1[i] / accumulator->sumWeight[i];
1934 double variance = accumulator->value2[i] / accumulator->sumWeight[i] - mean * mean;
1935 accumulator->value1[i] = variance <= 0 ? 0 : sqrt(variance / (pages - 1.0));
1937 warn_zero_weight(opts, stat, i);
1938 accumulator->value1[i] = DBL_MAX;
1944 for (i = 0; i < rowData->rows; i++) {
1945 double D = pages * accumulator->value2[i] - accumulator->value1[i] * accumulator->value1[i];
1946 accumulator->value1[i] = D ? (pages * accumulator->value4[i] - accumulator->value1[i] * accumulator->value3[i]) / D : DBL_MAX;
1949 case STAT_INTERCEPT:
1950 for (i = 0; i < rowData->rows; i++) {
1951 double D = pages * accumulator->value2[i] - accumulator->value1[i] * accumulator->value1[i];
1952 accumulator->value1[i] = D ? (accumulator->value2[i] * accumulator->value3[i] - accumulator->value1[i] * accumulator->value4[i]) / D : DBL_MAX;
1955 case STAT_EXMM_MEAN:
1956 for (i = 0; i < rowData->rows; i++) {
1957 double min = accumulator->value2[i];
1958 double max = accumulator->value3[i];
1959 double excluded = min == max ? (double)pages : accumulator->value4[i] + accumulator->sumWeight[i];
1960 double kept = (double)pages - excluded;
1962 accumulator->value1[i] = min;
1964 accumulator->value1[i] = (accumulator->value1[i] - min * accumulator->value4[i] - max * accumulator->sumWeight[i]) / kept;
1973 int pagesWritten = 0;
1975 if (!setup_output_file(&output, outputFile, templateInput, opts))
1978 for (iGroup = 0; iGroup < groups->groups; iGroup++) {
1981 int64_t ignoredRows = 0;
1982 long iRowData, iStat;
1985 fprintf(stderr,
"error: all pages have zero rows for %s\n", group->name);
1986 goto error_with_output;
1988 for (iRowData = 0; iRowData < group->rowDatas; iRowData++) {
1989 if (group->rowData + iRowData != rowData)
1990 ignoredRows += group->rowData[iRowData].pages;
1992 if (opts->verbose && (group->zeroRowPages || ignoredRows)) {
1993 fprintf(stderr,
"Row-count selection for %s: keeping %" PRId64
" pages with %" PRId64
1994 " rows; ignored %" PRId64
" zero-row pages and %" PRId64
" nonmatching pages\n",
1995 group->name, rowData->pages, rowData->rows, group->zeroRowPages, ignoredRows);
1999 !
SDDS_SetParameters(&output, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE, opts->groupBy, group->value, NULL)) {
2001 goto error_with_output;
2004 for (iStat = 0; iStat < opts->stats; iStat++) {
2007 finalize_stat_values(rowData, opts, iStat);
2008 if (stat->code == STAT_COPY) {
2009 if (!
SDDS_SetColumn(&output, SDDS_SET_BY_NAME, accumulator->copyData, rowData->rows, stat->resultColumn)) {
2011 goto error_with_output;
2013 }
else if (!
SDDS_SetColumnFromDoubles(&output, SDDS_SET_BY_NAME, accumulator->value1, rowData->rows, stat->resultColumn)) {
2015 goto error_with_output;
2021 goto error_with_output;
2031 if (pagesWritten == 0) {
2032 fprintf(stderr,
"error: no envelope pages were created\n");
2036 fprintf(stderr,
"Wrote %s with %d pages\n", outputFile, pagesWritten);
2045static void warn_for_missing_requested_group_values(
const OPTIONS *opts,
const GROUP_LIST *groups) {
2047 for (i = 0; i < opts->groupValue.items; i++) {
2050 for (j = 0; j < groups->groups; j++) {
2051 if (strcmp(opts->groupValue.item[i], groups->group[j].name) == 0) {
2057 fprintf(stderr,
"warning: requested %s value not found: %s\n", opts->groupBy, opts->groupValue.item[i]);
2061static int process_directory(
const char *workDir,
OPTIONS *opts,
int requireInputs) {
2066 int templateOpen = 0;
2067 int status = PROCESS_ERROR;
2069 memset(&files, 0,
sizeof(files));
2070 memset(&groups, 0,
sizeof(groups));
2071 finalOutput = xstrdup(opts->output);
2073 collect_input_files(workDir, opts, finalOutput, &files);
2074 if (files.items == 0) {
2076 fprintf(stderr,
"error: no input files matched patterns in %s\n", workDir);
2077 status = requireInputs ? PROCESS_ERROR : PROCESS_NO_INPUT;
2081 if (path_exists(finalOutput)) {
2082 if (!opts->overwrite) {
2083 fprintf(stderr,
"error: output file already exists: %s\n", finalOutput);
2086 if (remove(finalOutput) != 0) {
2087 fprintf(stderr,
"error: unable to remove existing output file %s: %s\n", finalOutput, strerror(errno));
2092 if (opts->verbose) {
2094 fprintf(stderr,
"Using %ld input files in %s\n", files.items, workDir);
2095 for (i = 0; i < files.items; i++)
2096 fprintf(stderr,
" %s\n", base_name(files.item[i]));
2104 if (!compile_stat_definitions(opts, &templateInput))
2107 if (!read_input_files(&files, opts, &groups))
2109 warn_for_missing_requested_group_values(opts, &groups);
2110 if (groups.groups == 0) {
2111 fprintf(stderr,
"error: no %s values selected in %s\n", opts->groupBy, workDir);
2114 if (!write_output_pages(finalOutput, &templateInput, opts, &groups))
2116 status = PROCESS_DONE;
2119 free_group_list(&groups, opts);
2122 string_list_clear(&files);
2127int main(
int argc,
char **argv) {
2133 fputs(USAGE, stderr);
2134 return EXIT_FAILURE;
2137 init_options(&opts);
2138 parse_options(&opts, argc, argv);
2140 ok = process_directory(opts.inputDir, &opts, 1) == PROCESS_DONE;
2142 free_options(&opts);
2143 return ok ? EXIT_SUCCESS : EXIT_FAILURE;
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetParameters(SDDS_DATASET *SDDS_dataset, int32_t mode,...)
int32_t SDDS_SetColumnFromDoubles(SDDS_DATASET *SDDS_dataset, int32_t mode, double *data, int64_t rows,...)
Sets the values for a single data column using double-precision floating-point numbers.
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int32_t SDDS_ChangeColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Modifies a specific field in a column definition within the SDDS dataset.
int32_t SDDS_GetColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Retrieves information about a specified column in the SDDS dataset.
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_TransferColumnDefinition(SDDS_DATASET *target, SDDS_DATASET *source, char *name, char *newName)
Transfers a column definition from a source dataset to a target dataset.
int32_t SDDS_TransferParameterDefinition(SDDS_DATASET *target, SDDS_DATASET *source, char *name, char *newName)
Transfers a parameter definition from a source dataset to a target dataset.
int32_t SDDS_GetParameterType(SDDS_DATASET *SDDS_dataset, int32_t index)
Retrieves the data type of a parameter in the SDDS dataset by its index.
int32_t SDDS_GetParameterIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named parameter in the SDDS dataset.
int32_t SDDS_GetColumnIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named column in the SDDS dataset.
char ** SDDS_GetColumnNames(SDDS_DATASET *SDDS_dataset, int32_t *number)
Retrieves the names of all columns in the SDDS dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
int32_t SDDS_GetTypeSize(int32_t type)
Retrieves the size in bytes of a specified SDDS data type.
int32_t SDDS_GetColumnType(SDDS_DATASET *SDDS_dataset, int32_t index)
Retrieves the data type of a column in the SDDS dataset by its index.
#define SDDS_STRING
Identifier for the string data type.
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
#define SDDS_NUMERIC_TYPE(type)
Checks if the given type identifier corresponds to any numeric type.
int has_wildcards(char *template)
Check if a template string contains any wildcard characters.