/* PSPP - computes sample statistics.
- Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
Written by Ben Pfaff <blp@gnu.org>.
This program is free software; you can redistribute it and/or
#include "algorithm.h"
#include "alloc.h"
#include "case.h"
+#include "cat.h"
+#include "cat-routines.h"
#include "error.h"
#include "hash.h"
#include "misc.h"
#include "value-labels.h"
#include "var.h"
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
/* A dictionary. */
struct dictionary
{
d->split_cnt = s->split_cnt;
if (d->split_cnt > 0)
{
- d->split = xmalloc (d->split_cnt * sizeof *d->split);
+ d->split = xnmalloc (d->split_cnt, sizeof *d->split);
for (i = 0; i < d->split_cnt; i++)
d->split[i] = dict_lookup_var_assert (d, s->split[i]->name);
}
dict_set_documents (d, dict_get_documents (s));
d->vector_cnt = s->vector_cnt;
- d->vector = xmalloc (d->vector_cnt * sizeof *d->vector);
+ d->vector = xnmalloc (d->vector_cnt, sizeof *d->vector);
for (i = 0; i < s->vector_cnt; i++)
{
struct vector *sv = s->vector[i];
dv->idx = i;
strcpy (dv->name, sv->name);
dv->cnt = sv->cnt;
- dv->var = xmalloc (dv->cnt * sizeof *dv->var);
+ dv->var = xnmalloc (dv->cnt, sizeof *dv->var);
for (j = 0; j < dv->cnt; j++)
dv->var[j] = d->var[sv->var[j]->index];
}
if (!(exclude_classes & (1u << dict_class_from_id (d->var[i]->name))))
count++;
- *vars = xmalloc (count * sizeof **vars);
+ *vars = xnmalloc (count, sizeof **vars);
*cnt = 0;
for (i = 0; i < d->var_cnt; i++)
if (!(exclude_classes & (1u << dict_class_from_id (d->var[i]->name))))
assert (d != NULL);
assert (name != NULL);
- assert (strlen (name) >= 1);
- assert (strlen (name) <= LONG_NAME_LEN);
-
assert (width >= 0 && width < 256);
+
+ assert (var_is_valid_name(name,0));
/* Make sure there's not already a variable by that name. */
if (dict_lookup_var (d, name) != NULL)
/* Allocate and initialize variable. */
v = xmalloc (sizeof *v);
- st_trim_copy (v->name, name, sizeof v->name);
+ str_copy_trunc (v->name, sizeof v->name, name);
v->type = width == 0 ? NUMERIC : ALPHA;
v->width = width;
v->fv = d->next_value_idx;
v->init = 1;
v->reinit = dict_class_from_id (v->name) != DC_SCRATCH;
v->index = d->var_cnt;
- v->miss_type = MISSING_NONE;
+ mv_init (&v->miss, width);
if (v->type == NUMERIC)
{
- v->print.type = FMT_F;
- v->print.w = 8;
- v->print.d = 2;
-
+ v->print = f8_2;
v->alignment = ALIGN_RIGHT;
v->display_width = 8;
v->measure = MEASURE_SCALE;
}
else
{
- v->print.type = FMT_A;
- v->print.w = v->width;
- v->print.d = 0;
-
+ v->print = make_output_format (FMT_A, v->width, 0);
v->alignment = ALIGN_LEFT;
v->display_width = 8;
v->measure = MEASURE_NOMINAL;
var_clear_short_name (v);
v->aux = NULL;
v->aux_dtor = NULL;
+ v->obs_vals = NULL;
/* Update dictionary. */
if (d->var_cnt >= d->var_cap)
{
d->var_cap = 8 + 2 * d->var_cap;
- d->var = xrealloc (d->var, d->var_cap * sizeof *d->var);
+ d->var = xnrealloc (d->var, d->var_cap, sizeof *d->var);
}
d->var[v->index] = v;
d->var_cnt++;
the same short name. */
nv->init = 1;
nv->reinit = ov->reinit;
- nv->miss_type = ov->miss_type;
- memcpy (nv->missing, ov->missing, sizeof nv->missing);
+ mv_copy (&nv->miss, &ov->miss);
nv->print = ov->print;
nv->write = ov->write;
val_labs_destroy (nv->val_labs);
assert (d != NULL);
assert (name != NULL);
- st_trim_copy (v.name, name, sizeof v.name);
+ str_copy_trunc (v.name, sizeof v.name, name);
return hsh_find (d->name_tab, &v);
}
return v;
}
-/* Returns nonzero if variable V is in dictionary D. */
-int
+/* Returns true if variable V is in dictionary D,
+ false otherwise. */
+bool
dict_contains_var (const struct dictionary *d, const struct variable *v)
{
assert (d != NULL);
/* Free memory. */
val_labs_destroy (v->val_labs);
+ cat_stored_values_destroy (v);
free (v->label);
free (v);
}
dict_delete_var (d, *vars++);
}
+/* Deletes scratch variables from dictionary D. */
+void
+dict_delete_scratch_vars (struct dictionary *d)
+{
+ int i;
+
+ /* FIXME: this can be done in O(count) time, but this algorithm
+ is O(count**2). */
+ assert (d != NULL);
+
+ for (i = 0; i < d->var_cnt; )
+ if (dict_class_from_id (d->var[i]->name) == DC_SCRATCH)
+ dict_delete_var (d, d->var[i]);
+ else
+ i++;
+}
+
/* Moves V to 0-based position IDX in D. Other variables in D,
if any, retain their relative positions. Runs in time linear
in the distance moved. */
assert (count == 0 || order != NULL);
assert (count <= d->var_cnt);
- new_var = xmalloc (d->var_cnt * sizeof *new_var);
+ new_var = xnmalloc (d->var_cnt, sizeof *new_var);
memcpy (new_var, order, count * sizeof *new_var);
for (i = 0; i < count; i++)
{
|| dict_lookup_var (d, new_name) == NULL);
hsh_force_delete (d->name_tab, v);
- st_trim_copy (v->name, new_name, sizeof v->name);
+ str_copy_trunc (v->name, sizeof v->name, new_name);
hsh_force_insert (d->name_tab, v);
if (get_algorithm () == ENHANCED)
/* Renames COUNT variables specified in VARS to the names given
in NEW_NAMES within dictionary D. If the renaming would
- result in a duplicate variable name, returns zero and stores a
+ result in a duplicate variable name, returns false and stores a
name that would be duplicated into *ERR_NAME (if ERR_NAME is
- non-null). Otherwise, the renaming is successful, and nonzero
+ non-null). Otherwise, the renaming is successful, and true
is returned. */
-int
+bool
dict_rename_vars (struct dictionary *d,
struct variable **vars, char **new_names,
size_t count, char **err_name)
{
char **old_names;
size_t i;
- int success = 1;
+ bool success = true;
assert (d != NULL);
assert (count == 0 || vars != NULL);
/* Remove the variables to be renamed from the name hash,
save their names, and rename them. */
- old_names = xmalloc (count * sizeof *old_names);
+ old_names = xnmalloc (count, sizeof *old_names);
for (i = 0; i < count; i++)
{
assert (d->var[vars[i]->index] == vars[i]);
hsh_force_insert (d->name_tab, vars[i]);
}
- success = 0;
+ success = false;
goto done;
}
}
else
{
double w = case_num (c, d->weight->fv);
- if ( w < 0.0 || w == SYSMIS || is_num_user_missing(w, d->weight) )
+ if (w < 0.0 || mv_is_num_missing (&d->weight->miss, w))
w = 0.0;
if ( w == 0.0 && *warn_on_invalid ) {
*warn_on_invalid = 0;
}
}
-/* Copies values from SRC, which represents a case arranged
- according to dictionary D, to DST, which represents a case
- arranged according to the dictionary that will be produced by
- dict_compact_values(D). */
-void
-dict_compact_case (const struct dictionary *d,
- struct ccase *dst, const struct ccase *src)
-{
- size_t i;
- size_t value_idx;
-
- value_idx = 0;
- for (i = 0; i < d->var_cnt; i++)
- {
- struct variable *v = d->var[i];
-
- if (dict_class_from_id (v->name) != DC_SCRATCH)
- {
- case_copy (dst, value_idx, src, v->fv, v->nv);
- value_idx += v->nv;
- }
- }
-}
-
/* Returns the number of values that would be used by a case if
dict_compact_values() were called. */
size_t
size_t next_value_idx;
int *idx_to_fv;
- idx_to_fv = xmalloc (d->var_cnt * sizeof *idx_to_fv);
+ idx_to_fv = xnmalloc (d->var_cnt, sizeof *idx_to_fv);
next_value_idx = 0;
for (i = 0; i < d->var_cnt; i++)
{
return idx_to_fv;
}
+/* Returns true if a case for dictionary D would be smaller after
+ compaction, false otherwise. Compacting a case eliminates
+ "holes" between values and after the last value. Holes are
+ created by deleting variables (or by scratch variables).
+
+ The return value may differ from whether compacting a case
+ from dictionary D would *change* the case: compaction could
+ rearrange values even if it didn't reduce space
+ requirements. */
+bool
+dict_needs_compaction (const struct dictionary *d)
+{
+ return dict_get_compacted_value_cnt (d) < dict_get_next_value_idx (d);
+}
+\f
+/* How to copy a contiguous range of values between cases. */
+struct copy_map
+ {
+ size_t src_idx; /* Starting value index in source case. */
+ size_t dst_idx; /* Starting value index in target case. */
+ size_t cnt; /* Number of values. */
+ };
+
+/* How to compact a case. */
+struct dict_compactor
+ {
+ struct copy_map *maps; /* Array of mappings. */
+ size_t map_cnt; /* Number of mappings. */
+ };
+
+/* Creates and returns a dict_compactor that can be used to
+ compact cases for dictionary D.
+
+ Compacting a case eliminates "holes" between values and after
+ the last value. Holes are created by deleting variables (or
+ by scratch variables). */
+struct dict_compactor *
+dict_make_compactor (const struct dictionary *d)
+{
+ struct dict_compactor *compactor;
+ struct copy_map *map;
+ size_t map_allocated;
+ size_t value_idx;
+ size_t i;
+
+ compactor = xmalloc (sizeof *compactor);
+ compactor->maps = NULL;
+ compactor->map_cnt = 0;
+ map_allocated = 0;
+
+ value_idx = 0;
+ map = NULL;
+ for (i = 0; i < d->var_cnt; i++)
+ {
+ struct variable *v = d->var[i];
+
+ if (dict_class_from_id (v->name) == DC_SCRATCH)
+ continue;
+ if (map != NULL && map->src_idx + map->cnt == v->fv)
+ map->cnt += v->nv;
+ else
+ {
+ if (compactor->map_cnt == map_allocated)
+ compactor->maps = x2nrealloc (compactor->maps, &map_allocated,
+ sizeof *compactor->maps);
+ map = &compactor->maps[compactor->map_cnt++];
+ map->src_idx = v->fv;
+ map->dst_idx = value_idx;
+ map->cnt = v->nv;
+ }
+ value_idx += v->nv;
+ }
+
+ return compactor;
+}
+
+/* Compacts SRC by copying it to DST according to the scheme in
+ COMPACTOR.
+
+ Compacting a case eliminates "holes" between values and after
+ the last value. Holes are created by deleting variables (or
+ by scratch variables). */
+void
+dict_compactor_compact (const struct dict_compactor *compactor,
+ struct ccase *dst, const struct ccase *src)
+{
+ size_t i;
+
+ for (i = 0; i < compactor->map_cnt; i++)
+ {
+ const struct copy_map *map = &compactor->maps[i];
+ case_copy (dst, map->dst_idx, src, map->src_idx, map->cnt);
+ }
+}
+
+/* Destroys COMPACTOR. */
+void
+dict_compactor_destroy (struct dict_compactor *compactor)
+{
+ if (compactor != NULL)
+ {
+ free (compactor->maps);
+ free (compactor);
+ }
+}
+
/* Returns the SPLIT FILE vars (see cmd_split_file()). Call
dict_get_split_cnt() to determine how many SPLIT FILE vars
there are. Returns a null pointer if and only if there are no
assert (cnt == 0 || split != NULL);
d->split_cnt = cnt;
- d->split = xrealloc (d->split, cnt * sizeof *d->split);
+ d->split = xnrealloc (d->split, cnt, sizeof *d->split);
memcpy (d->split, split, cnt * sizeof *d->split);
}
}
/* Creates in D a vector named NAME that contains CNT variables
- VAR (see cmd_vector()). Returns nonzero if successful, or
- zero if a vector named NAME already exists in D. */
-int
+ VAR (see cmd_vector()). Returns true if successful, or
+ false if a vector named NAME already exists in D. */
+bool
dict_create_vector (struct dictionary *d,
const char *name,
struct variable **var, size_t cnt)
assert (cnt > 0);
if (dict_lookup_vector (d, name) != NULL)
- return 0;
+ return false;
- d->vector = xrealloc (d->vector, (d->vector_cnt + 1) * sizeof *d->vector);
+ d->vector = xnrealloc (d->vector, d->vector_cnt + 1, sizeof *d->vector);
vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector);
vector->idx = d->vector_cnt++;
- st_trim_copy (vector->name, name, sizeof vector->name);
- vector->var = xmalloc (cnt * sizeof *var);
+ str_copy_trunc (vector->name, sizeof vector->name, name);
+ vector->var = xnmalloc (cnt, sizeof *var);
for (i = 0; i < cnt; i++)
{
assert (dict_contains_var (d, var[i]));
}
vector->cnt = cnt;
- return 1;
+ return true;
}
/* Returns the vector in D with index IDX, which must be less