From 0a2cdacebb16d9a13c1b382c5ffa29e32cf858ea Mon Sep 17 00:00:00 2001 From: John Darrington Date: Fri, 22 Oct 2004 07:59:34 +0000 Subject: [PATCH] Added framework for the ONEWAY command. --- src/Makefile.am | 8 +- src/command.def | 1 + src/oneway.q | 600 ++++++++++++++++++++++++++++++++++++++++++++++++ src/q2c.c | 85 ++++++- src/subclist.c | 74 ++++++ src/subclist.h | 72 ++++++ src/tab.c | 6 + src/val.h | 2 + src/vars-atr.c | 16 ++ 9 files changed, 855 insertions(+), 9 deletions(-) create mode 100644 src/oneway.q create mode 100644 src/subclist.c create mode 100644 src/subclist.h diff --git a/src/Makefile.am b/src/Makefile.am index 1cd16e88cf..e6ce583903 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,10 +43,10 @@ $(q_sources_c): q2c$(EXEEXT) ./q2c $< $@ q_sources_c = correlations.c crosstabs.c file-handle.c \ -frequencies.c list.c means.c set.c t-test.c +frequencies.c list.c means.c oneway.c set.c t-test.c q_sources_q = correlations.q crosstabs.q file-handle.q \ -frequencies.q list.q means.q set.q t-test.q +frequencies.q list.q means.q oneway.q set.q t-test.q pspp_SOURCES = $(q_sources_c) aggregate.c algorithm.c algorithm.h \ alloc.c alloc.h apply-dict.c ascii.c autorecode.c bitvector.h \ @@ -66,8 +66,8 @@ moments.c moments.h numeric.c output.c output.h pfm-read.c pfm-write.c \ pfm.h pool.c pool.h postscript.c print.c random.c random.h recode.c \ rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h \ sfm-read.c sfm-write.c sfm.h sfmP.h som.c som.h sort.c sort.h \ -split-file.c str.c str.h sysfile-info.c tab.c tab.h temporary.c \ -stat.h \ +split-file.c str.c str.h subclist.c \ +sysfile-info.c tab.c tab.h temporary.c stat.h \ title.c t-test.h val.h val-labs.c value-labels.c value-labels.h \ var-labs.c var.h vars-atr.c vars-prs.c vector.c version.c version.h \ vfm.c vfm.h vfmP.h weight.c diff --git a/src/command.def b/src/command.def index cfb74112b0..d4adcf412b 100644 --- a/src/command.def +++ b/src/command.def @@ -93,6 +93,7 @@ DEFCMD ("N OF CASES", INIT, INPU, TRAN, TRAN, cmd_n_of_cases) UNIMPL ("NUMBERED", INIT, INPU, TRAN, PROC) DEFCMD ("NUMERIC", ERRO, INPU, TRAN, TRAN, cmd_numeric) UNIMPL ("UNNUMBERED", INIT, INPU, TRAN, PROC) +DEFCMD ("ONEWAY", ERRO, ERRO, PROC, PROC, cmd_oneway) DEFCMD ("PEARSON CORRELATIONS", ERRO, ERRO, PROC, PROC, cmd_correlations) UNIMPL ("POINT", ERRO, INPU, ERRO, ERRO) UNIMPL ("PRESERVE", INIT, INPU, TRAN, PROC) diff --git a/src/oneway.q b/src/oneway.q new file mode 100644 index 0000000000..57fefc7f22 --- /dev/null +++ b/src/oneway.q @@ -0,0 +1,600 @@ +/* PSPP - One way ANOVA. -*-c-*- + + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Author: John Darrington 2004 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include +#include "error.h" +#include +#include +#include +#include "alloc.h" +#include "str.h" +#include "case.h" +#include "command.h" +#include "lexer.h" +#include "error.h" +#include "magic.h" +#include "misc.h" +#include "tab.h" +#include "som.h" +#include "value-labels.h" +#include "var.h" +#include "vfm.h" +#include "hash.h" +#include "casefile.h" +#include "levene.h" + +/* (specification) + "ONEWAY" (oneway_): + *variables=custom; + +missing=miss:!analysis/listwise, + incl:include/!exclude; + contrast= double list; + statistics[st_]=descriptives,homogeneity. +*/ +/* (declarations) */ +/* (functions) */ + + + +static struct cmd_oneway cmd; + +/* The independent variable */ +static struct variable *indep_var; + +/* A hash of the values of the independent variable */ +struct hsh_table *ind_vals; + +/* Number of factors (groups) */ +static int n_groups; + +/* Number of dependent variables */ +static int n_vars; + +/* The dependent variables */ +static struct variable **vars; + + + + + +/* Function to use for testing for missing values */ +static is_missing_func value_is_missing; + + +static void calculate(const struct casefile *cf, void *_mode); + + +/* Routines to show the output tables */ +static void show_anova_table(void); +static void show_descriptives(void); +static void show_homogeneity(void); +static void show_contrast_coeffs(void); +static void show_contrast_tests(void); + + + +int +cmd_oneway(void) +{ + int i; + + if ( !parse_oneway(&cmd) ) + return CMD_FAILURE; + + /* If /MISSING=INCLUDE is set, then user missing values are ignored */ + if (cmd.incl == ONEWAY_INCLUDE ) + value_is_missing = is_system_missing; + else + value_is_missing = is_missing; + + multipass_procedure_with_splits (calculate, &cmd); + + /* Check the sanity of the given contrast values */ + for (i = 0 ; i < cmd.sbc_contrast ; ++i ) + { + int j; + double sum = 0; + + if ( subc_list_double_count(&cmd.dl_contrast[i]) != n_groups ) + { + msg(SE, _("Number of contrast coefficients must equal the number of groups")); + return CMD_FAILURE; + } + + for (j=0; j < n_groups ; ++j ) + sum += subc_list_double_at(&cmd.dl_contrast[i],j); + + if ( sum != 0.0 ) + msg(SW,_("Coefficients for contrast %d do not total zero"),i + 1); + } + + + /* Show the statistics tables */ + if ( cmd.sbc_statistics ) + { + for (i = 0 ; i < ONEWAY_ST_count ; ++i ) + { + if ( ! cmd.a_statistics[i] ) continue; + + switch (i) { + case ONEWAY_ST_DESCRIPTIVES: + show_descriptives(); + break; + case ONEWAY_ST_HOMOGENEITY: + show_homogeneity(); + break; + } + + } + } + + show_anova_table(); + + if (cmd.sbc_contrast) + { + show_contrast_coeffs(); + show_contrast_tests(); + } + + hsh_destroy(ind_vals); + + return CMD_SUCCESS; +} + + + + + +/* Parser for the variables sub command */ +static int +oneway_custom_variables(struct cmd_oneway *cmd UNUSED) +{ + + lex_match('='); + + if ((token != T_ID || dict_lookup_var (default_dict, tokid) == NULL) + && token != T_ALL) + return 2; + + + if (!parse_variables (default_dict, &vars, &n_vars, + PV_DUPLICATE + | PV_NUMERIC | PV_NO_SCRATCH) ) + { + free (vars); + return 0; + } + + assert(n_vars); + + if ( ! lex_match(T_BY)) + return 2; + + + indep_var = parse_variable(); + + if ( !indep_var ) + { + msg(SE,_("`%s' is not a variable name"),tokid); + return 0; + } + + + return 1; +} + + +/* Show the ANOVA table */ +static void +show_anova_table(void) +{ + int i; + int n_cols =7; + int n_rows = n_vars * 3 + 1; + + struct tab_table *t; + + + t = tab_create (n_cols,n_rows,0); + tab_headers (t, 2, 0, 1, 0); + tab_dim (t, tab_natural_dimensions); + + + tab_box (t, + TAL_2, TAL_2, + -1, TAL_1, + 0, 0, + n_cols - 1, n_rows - 1); + + tab_hline (t, TAL_2, 0, n_cols - 1, 1 ); + tab_vline (t, TAL_2, 2, 0, n_rows - 1); + tab_vline (t, TAL_0, 1, 0, 0); + + tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Sum of Squares")); + tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df")); + tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Mean Square")); + tab_text (t, 5, 0, TAB_CENTER | TAT_TITLE, _("F")); + tab_text (t, 6, 0, TAB_CENTER | TAT_TITLE, _("Significance")); + + + for ( i=0 ; i < n_vars ; ++i ) + { + char *s = (vars[i]->label) ? vars[i]->label : vars[i]->name; + + tab_text (t, 0, i * 3 + 1, TAB_LEFT | TAT_TITLE, s); + tab_text (t, 1, i * 3 + 1, TAB_LEFT | TAT_TITLE, _("Between Groups")); + tab_text (t, 1, i * 3 + 2, TAB_LEFT | TAT_TITLE, _("Within Groups")); + tab_text (t, 1, i * 3 + 3, TAB_LEFT | TAT_TITLE, _("Total")); + + if (i > 0) + tab_hline(t, TAL_1, 0, n_cols - 1 , i * 3 + 1); + } + + + tab_title (t, 0, "ANOVA"); + tab_submit (t); + + +} + + +static void +calculate(const struct casefile *cf, void *cmd_) +{ + struct casereader *r; + struct ccase c; + + struct cmd_t_test *cmd = (struct cmd_t_test *) cmd_; + + + ind_vals = hsh_create(4, (hsh_compare_func *) compare_values, + (hsh_hash_func *) hash_value, + 0, (void *) indep_var->width ); + + for(r = casefile_get_reader (cf); + casereader_read (r, &c) ; + case_destroy (&c)) + { + + const union value *val = case_data (&c, indep_var->fv); + + hsh_insert(ind_vals, (void *) val); + + /* + if (! value_is_missing(val,v) ) + { + gs->n+=weight; + gs->sum+=weight * val->f; + gs->ssq+=weight * val->f * val->f; + } + */ + + } + casereader_destroy (r); + + + n_groups = hsh_count(ind_vals); + + +} + + +/* Show the descriptives table */ +static void +show_descriptives(void) +{ + int v; + int n_cols =10; + int n_rows = n_vars * (n_groups + 1 )+ 2; + + struct tab_table *t; + + + t = tab_create (n_cols,n_rows,0); + tab_headers (t, 2, 0, 2, 0); + tab_dim (t, tab_natural_dimensions); + + + /* Put a frame around the entire box, and vertical lines inside */ + tab_box (t, + TAL_2, TAL_2, + -1, TAL_1, + 0, 0, + n_cols - 1, n_rows - 1); + + /* Underline headers */ + tab_hline (t, TAL_2, 0, n_cols - 1, 2 ); + tab_vline (t, TAL_2, 2, 0, n_rows - 1); + + tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("N")); + tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Mean")); + tab_text (t, 4, 1, TAB_CENTER | TAT_TITLE, _("Std. Deviation")); + tab_text (t, 5, 1, TAB_CENTER | TAT_TITLE, _("Std. Error")); + + + tab_vline(t, TAL_0, 7, 0, 0); + tab_hline(t, TAL_1, 6, 7, 1); + tab_joint_text (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE, _("95% Confidence Interval for Mean")); + + tab_text (t, 6, 1, TAB_CENTER | TAT_TITLE, _("Lower Bound")); + tab_text (t, 7, 1, TAB_CENTER | TAT_TITLE, _("Upper Bound")); + + tab_text (t, 8, 1, TAB_CENTER | TAT_TITLE, _("Minimum")); + tab_text (t, 9, 1, TAB_CENTER | TAT_TITLE, _("Maximum")); + + + tab_title (t, 0, "Descriptives"); + + + for ( v=0 ; v < n_vars ; ++v ) + { + struct hsh_iterator g; + union value *group_value; + int count = 0 ; + char *s = (vars[v]->label) ? vars[v]->label : vars[v]->name; + + tab_text (t, 0, v * ( n_groups + 1 ) + 2, TAB_LEFT | TAT_TITLE, s); + if ( v > 0) + tab_hline(t, TAL_1, 0, n_cols - 1 , v * (n_groups + 1) + 2); + + + for (group_value = hsh_first (ind_vals,&g); + group_value != 0; + group_value = hsh_next(ind_vals,&g)) + { + char *lab; + + lab = val_labs_find(indep_var->val_labs,*group_value); + + if ( lab ) + tab_text (t, 1, v * (n_groups + 1)+ count + 2, + TAB_LEFT | TAT_TITLE ,lab); + else + tab_text (t, 1, v * (n_groups + 1) + count + 2, + TAB_LEFT | TAT_TITLE | TAT_PRINTF, "%g", group_value->f); + + count++ ; + } + + tab_text (t, 1, v * (n_groups + 1)+ count + 2, + TAB_LEFT | TAT_TITLE ,_("Total")); + + + } + + + tab_submit (t); + + +} + + +/* Show the homogeneity table */ +static void +show_homogeneity(void) +{ + int v; + int n_cols = 5; + int n_rows = n_vars + 1; + + struct tab_table *t; + + + t = tab_create (n_cols,n_rows,0); + tab_headers (t, 1, 0, 1, 0); + tab_dim (t, tab_natural_dimensions); + + /* Put a frame around the entire box, and vertical lines inside */ + tab_box (t, + TAL_2, TAL_2, + -1, TAL_1, + 0, 0, + n_cols - 1, n_rows - 1); + + + tab_hline(t, TAL_2, 0, n_cols - 1, 1); + tab_vline(t, TAL_2, 1, 0, n_rows - 1); + + + tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic")); + tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("df1")); + tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df2")); + tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Significance")); + + + tab_title (t, 0, _("Test of Homogeneity of Variances")); + + for ( v=0 ; v < n_vars ; ++v ) + { + char *s = (vars[v]->label) ? vars[v]->label : vars[v]->name; + + tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s); + } + + tab_submit (t); + + +} + + +/* Show the contrast coefficients table */ +static void +show_contrast_coeffs(void) +{ + char *s; + int n_cols = 2 + n_groups; + int n_rows = 2 + cmd.sbc_contrast; + struct hsh_iterator g; + union value *group_value; + int count = 0 ; + + + struct tab_table *t; + + + t = tab_create (n_cols,n_rows,0); + tab_headers (t, 2, 0, 2, 0); + tab_dim (t, tab_natural_dimensions); + + /* Put a frame around the entire box, and vertical lines inside */ + tab_box (t, + TAL_2, TAL_2, + -1, TAL_1, + 0, 0, + n_cols - 1, n_rows - 1); + + + tab_box (t, + -1,-1, + TAL_0, TAL_0, + 2, 0, + n_cols - 1, 0); + + tab_box (t, + -1,-1, + TAL_0, TAL_0, + 0,0, + 1,1); + + + tab_hline(t, TAL_1, 2, n_cols - 1, 1); + + + tab_hline(t, TAL_2, 0, n_cols - 1, 2); + tab_vline(t, TAL_2, 2, 0, n_rows - 1); + + + tab_title (t, 0, _("Contrast Coefficients")); + + tab_text (t, 0, 2, TAB_LEFT | TAT_TITLE, _("Contrast")); + + s = (indep_var->label) ? indep_var->label : indep_var->name; + + tab_joint_text (t, 2, 0, n_cols - 1, 0, TAB_CENTER | TAT_TITLE, s); + + for (group_value = hsh_first (ind_vals,&g); + group_value != 0; + group_value = hsh_next(ind_vals,&g)) + { + int i; + char *lab; + + lab = val_labs_find(indep_var->val_labs,*group_value); + + if ( lab ) + tab_text (t, count + 2, 1, + TAB_CENTER | TAT_TITLE ,lab); + else + tab_text (t, count + 2, 1, + TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%g", group_value->f); + + for (i = 0 ; i < cmd.sbc_contrast ; ++i ) + { + tab_text(t, 1, i + 2, TAB_CENTER | TAT_PRINTF, "%d", i + 1); + tab_text(t, count + 2, i + 2, TAB_RIGHT | TAT_PRINTF, "%g", + subc_list_double_at(&cmd.dl_contrast[i],count) + ); + } + + count++ ; + } + + tab_submit (t); + +} + + + +/* Show the results of the contrast tests */ +static void +show_contrast_tests(void) +{ + int v; + int n_cols = 8; + int n_rows = 1 + n_vars * 2 * cmd.sbc_contrast; + + struct tab_table *t; + + t = tab_create (n_cols,n_rows,0); + tab_headers (t, 3, 0, 1, 0); + tab_dim (t, tab_natural_dimensions); + + /* Put a frame around the entire box, and vertical lines inside */ + tab_box (t, + TAL_2, TAL_2, + -1, TAL_1, + 0, 0, + n_cols - 1, n_rows - 1); + + + tab_box (t, + -1,-1, + TAL_0, TAL_0, + 0, 0, + 2, 0); + + tab_hline(t, TAL_2, 0, n_cols - 1, 1); + tab_vline(t, TAL_2, 3, 0, n_rows - 1); + + + tab_title (t, 0, _("Contrast Tests")); + + tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Contrast")); + tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast")); + tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Std. Error")); + tab_text (t, 5, 0, TAB_CENTER | TAT_TITLE, _("t")); + tab_text (t, 6, 0, TAB_CENTER | TAT_TITLE, _("df")); + tab_text (t, 7, 0, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)")); + + for ( v = 0 ; v < n_vars ; ++v ) + { + int i; + int lines_per_variable = 2 * cmd.sbc_contrast; + + tab_text (t, 0, (v * lines_per_variable) + 1, TAB_LEFT | TAT_TITLE, + vars[v]->label?vars[v]->label:vars[v]->name); + + for ( i = 0 ; i < cmd.sbc_contrast ; ++i ) + { + tab_text (t, 1, (v * lines_per_variable) + i*2 + 1, + TAB_LEFT | TAT_TITLE, + _("Assume equal variances")); + + tab_text (t, 1, (v * lines_per_variable) + i*2 + 2, + TAB_LEFT | TAT_TITLE, + _("Does not assume equal")); + + + tab_text (t, 2, (v * lines_per_variable) + i*2 + 1, + TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1); + + tab_text (t, 2, (v * lines_per_variable) + i*2 + 2, + TAB_CENTER | TAT_TITLE | TAT_PRINTF, "%d",i+1); + + } + + if ( v > 0 ) + tab_hline(t, TAL_1, 0, n_cols - 1, (v * lines_per_variable) + 1); + } + + tab_submit (t); + +} diff --git a/src/q2c.c b/src/q2c.c index 1c0ddb7966..e8f09d8d5d 100644 --- a/src/q2c.c +++ b/src/q2c.c @@ -30,6 +30,7 @@ #endif #include "str.h" + /* Brokenness. */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 @@ -837,7 +838,12 @@ parse_subcommand (subcommand *sbc) else if (match_id ("PINT")) sbc->type = SBC_PINT; else if (match_id ("DOUBLE")) - sbc->type = match_id ("LIST") ? SBC_DBL_LIST : SBC_DBL; + { + if ( match_id ("LIST") ) + sbc->type = SBC_DBL_LIST; + else + sbc->type = SBC_DBL; + } else if (match_id ("STRING")) { sbc->type = SBC_STRING; @@ -1033,6 +1039,12 @@ dump_declarations (void) } } + /* Write out some type definitions */ + { + dump (0, "#define MAXLISTS 10"); + } + + /* For every array subcommand, write out the associated enumerated values. */ { @@ -1132,6 +1144,17 @@ dump_declarations (void) dump (0, "double n_%s;", st_lower (sbc->name)); break; + case SBC_DBL_LIST: + dump (0, "subc_list_double dl_%s[MAXLISTS];", + st_lower(sbc->name)); + break; + + case SBC_INT_LIST: + dump (0, "subc_list_int il_%s[MAXLISTS];", + st_lower(sbc->name)); + break; + + default:; /* nothing */ } @@ -1227,9 +1250,20 @@ dump_vars_init (int persistent) { switch (sbc->type) { - case SBC_DBL: case SBC_INT_LIST: + break; + case SBC_DBL_LIST: + dump (0, "int i;"); + dump (0, "for (i = 0; i < MAXLISTS; ++i)"); + dump (1, "{"); + dump (0, "subc_list_double_create(&p->dl_%s[i]) ;", + st_lower (sbc->name) + ); + dump (-1, "}"); + break; + + case SBC_DBL: case SBC_CUSTOM: /* nothing */ break; @@ -1595,7 +1629,7 @@ dump_subcommand (const subcommand *sbc) { char buf[1024]; dump (1, "if (!(%s))", sbc->restriction); - dump (1, "{"); + dump (1, "{"); sprintf(buf,sbc->message,sbc->name); if ( sbc->translatable ) dump (0, "msg (SE, gettext(\"%s\"));",buf); @@ -1615,6 +1649,30 @@ dump_subcommand (const subcommand *sbc) dump (-1, "p->n_%s = lex_integer ();", st_lower (sbc->name)); dump (0, "lex_match (')');"); } + else if (sbc->type == SBC_DBL_LIST) + { + dump (0, "if ( p->sbc_%s > MAXLISTS)",st_lower(sbc->name)); + dump (1, "{"); + dump (0, "msg (SE, \"No more than %%d %s subcommands allowed\",MAXLISTS);",st_lower(sbc->name)); + dump (0, "goto lossage;"); + dump (-1,"}"); + + dump (1, "while (token != '/' && token != '.')"); + dump (1, "{"); + dump (0, "lex_match(',');"); + dump (0, "if (!lex_force_num ())"); + dump (1, "{"); + dump (0, "goto lossage;"); + dump (-1,"}"); + + dump (0, "subc_list_double_push(&p->dl_%s[p->sbc_%s-1],lex_double ());", + st_lower (sbc->name),st_lower (sbc->name) + ); + + dump (0, "lex_get();"); + dump (-1,"}"); + + } else if (sbc->type == SBC_CUSTOM) { dump (1, "switch (%scustom_%s (p))", @@ -1927,6 +1985,7 @@ dump_header (void) dump (0, nullstr); dump (0, "#include \"settings.h\""); + dump (0, "#include \"subclist.h\""); dump (0, nullstr); } @@ -1943,8 +2002,13 @@ dump_free (int persistent) if ( ! persistent ) { for (sbc = subcommands; sbc; sbc = sbc->next) + { if (sbc->type == SBC_STRING) used = 1; + if (sbc->type == SBC_DBL_LIST) + used = 1; + } + } dump (0, "static void"); @@ -1956,8 +2020,19 @@ dump_free (int persistent) { for (sbc = subcommands; sbc; sbc = sbc->next) - if (sbc->type == SBC_STRING) - dump (0, "free (p->s_%s);", st_lower (sbc->name)); + { + switch (sbc->type) + { + case SBC_STRING: + dump (0, "free (p->s_%s);", st_lower (sbc->name)); + break; + case SBC_DBL_LIST: + dump (0, "subc_list_double_destroy(p->dl_%s);", st_lower (sbc->name)); + break; + default: + break; + } + } } dump (-1, "}"); diff --git a/src/subclist.c b/src/subclist.c new file mode 100644 index 0000000000..d2add44c17 --- /dev/null +++ b/src/subclist.c @@ -0,0 +1,74 @@ +/* subclist - lists for PSPP subcommands + +Copyright (C) 2004 Free Software Foundation, Inc. + +Written by John Darrington + + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + + +#include "subclist.h" +#include + +/* I call these objects `lists' but they are in fact simple dynamic arrays */ + +#define CHUNKSIZE 16 + +/* Create a list */ +void +subc_list_double_create(subc_list_double *l) +{ + l->data = (double *) malloc(CHUNKSIZE * sizeof (double)); + l->sz = CHUNKSIZE; + l->n_data = 0; +} + +/* Push a value onto the list */ +void +subc_list_double_push(subc_list_double *l, double d) +{ + l->data[l->n_data++] = d; + + if (l->n_data >= l->sz ) + { + l->sz += CHUNKSIZE; + l->data = realloc(l->data, l->sz * sizeof(double)); + } + +} + +/* Return the number of items in the list */ +int +subc_list_double_count(subc_list_double *l) +{ + return l->n_data; +} + + +/* Index into the list (array) */ +double +subc_list_double_at(subc_list_double *l, int idx) +{ + return l->data[idx]; +} + +/* Free up the list */ +void +subc_list_double_destroy(subc_list_double *l) +{ + free(l->data); +} diff --git a/src/subclist.h b/src/subclist.h new file mode 100644 index 0000000000..b311bc67bc --- /dev/null +++ b/src/subclist.h @@ -0,0 +1,72 @@ +#ifndef SUBCLIST_H +#define SUBCLIST_H + +/* subclist - lists for PSPP subcommands + + Copyright (C) 2004 Free Software Foundation, Inc. + + Written by John Darrington + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + + + +#include + +/* This module provides a rudimentary list class + It is intended for use by the command line parser for list subcommands +*/ + + +struct subc_list_double { + double *data ; + size_t sz; + int n_data; +}; + +struct subc_list_int { + int *data ; + size_t sz; + int n_data; +}; + + +typedef struct subc_list_double subc_list_double ; +typedef struct subc_list_int subc_list_int ; + +/* Create a list */ +void subc_list_double_create(subc_list_double *l) ; +void subc_list_int_create(subc_list_int *l) ; + +/* Push a value onto the list */ +void subc_list_double_push(subc_list_double *l, double d) ; +void subc_list_int_push(subc_list_int *l, int i) ; + +/* Index into the list */ +double subc_list_double_at(subc_list_double *l, int idx); +int subc_list_int_at(subc_list_int *l, int idx); + +/* Return the number of values in the list */ +int subc_list_double_count(subc_list_double *l); +int subc_list_int_count(subc_list_int *l); + +/* Destroy the list */ +void subc_list_double_destroy(subc_list_double *l) ; +void subc_list_int_destroy(subc_list_int *l) ; + + +#endif diff --git a/src/tab.c b/src/tab.c index 8afb8f0be0..dd901493db 100644 --- a/src/tab.c +++ b/src/tab.c @@ -210,6 +210,12 @@ void tab_headers (struct tab_table *table, int l, int r, int t, int b) { assert (table != NULL); + assert (l < table->nc); + assert (r < table->nc); + assert (t < table->nr); + assert (b < table->nr); + + table->l = l; table->r = r; table->t = t; diff --git a/src/val.h b/src/val.h index fe35aad9ae..fc098fda97 100644 --- a/src/val.h +++ b/src/val.h @@ -69,5 +69,7 @@ union value int compare_values (const union value *a, const union value *b, int width); +unsigned hash_value(const union value *v, int width); + #endif /* !val_h */ diff --git a/src/vars-atr.c b/src/vars-atr.c index cf4ef4e32c..0b6877dbf7 100644 --- a/src/vars-atr.c +++ b/src/vars-atr.c @@ -45,6 +45,22 @@ compare_values (const union value *a, const union value *b, int width) return memcmp (a->s, b->s, width); } +/* Create a hash of v */ +unsigned +hash_value(const union value *v, int width) +{ + unsigned id_hash; + + if ( 0 == width ) + id_hash = hsh_hash_double (v->f); + else + id_hash = hsh_hash_bytes (v->s, width); + + return id_hash; +} + + + /* Discards all the current state in preparation for a data-input command like DATA LIST or GET. */ void -- 2.30.2