+
+ struct casereader *input = casereader_create_filter_weight (proc_open (ds),
+ dataset_dict (ds),
+ NULL, NULL);
+ bool warn_on_invalid = true;
+ for (struct ccase *c = casereader_read (input); c;
+ case_unref (c), c = casereader_read (input))
+ {
+ double weight = dict_get_case_weight (dataset_dict (ds), c,
+ &warn_on_invalid);
+ for (size_t i = 0; i < n_fts; i++)
+ {
+ struct ctables_freqtab *ft = fts[i];
+
+ size_t hash = 0;
+
+ for (size_t j = 0; j < ft->vars.n; j++)
+ {
+ const struct variable *var = ft->vars.vars[j];
+ hash = value_hash (case_data (c, var), var_get_width (var), hash);
+ }
+
+ struct freq *f;
+ HMAP_FOR_EACH_WITH_HASH (f, struct freq, node, hash, &ft->data)
+ {
+ for (size_t j = 0; j < ft->vars.n; j++)
+ {
+ const struct variable *var = ft->vars.vars[j];
+ if (!value_equal (case_data (c, var), &f->values[j],
+ var_get_width (var)))
+ goto next_hash_node;
+ }
+
+ f->count += weight;
+ goto next_ft;
+
+ next_hash_node: ;
+ }
+
+ f = xmalloc (table_entry_size (ft->vars.n));
+ f->count = weight;
+ for (size_t j = 0; j < ft->vars.n; j++)
+ {
+ const struct variable *var = ft->vars.vars[j];
+ value_clone (&f->values[j], case_data (c, var),
+ var_get_width (var));
+ }
+ hmap_insert (&ft->data, &f->node, hash);
+
+ next_ft: ;
+ }
+ }
+ casereader_destroy (input);
+
+ for (size_t i = 0; i < n_fts; i++)
+ {
+ struct ctables_freqtab *ft = fts[i];
+ struct freq *f, *next;
+ HMAP_FOR_EACH_SAFE (f, next, struct freq, node, &ft->data)
+ {
+ hmap_delete (&ft->data, &f->node);
+ for (size_t j = 0; j < ft->vars.n; j++)
+ {
+ const struct variable *var = ft->vars.vars[j];
+ value_destroy (&f->values[j], var_get_width (var));
+ }
+ free (f);
+ }
+ hmap_destroy (&ft->data);
+ var_array_uninit (&ft->vars);
+ free (ft);
+ }
+ free (fts);
+
+ return proc_commit (ds);