? deque.patch
Index: src/data/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/ChangeLog,v
retrieving revision 1.98
diff -u -p -r1.98 ChangeLog
--- src/data/ChangeLog	10 Jan 2007 09:22:42 -0000	1.98
+++ src/data/ChangeLog	15 Jan 2007 05:45:46 -0000
@@ -1,3 +1,22 @@
+Sun Jan 14 21:41:12 2007  Ben Pfaff  <blp@gnu.org>
+
+	* automake.mk: Add casedeque.h to sources.
+	
+	* casedeque.h: New file.
+
+	* procedure.c: (struct dataset) Change lag_count, lag_head,
+	lag_queue member into single struct casedeque member.  Update all
+	users to use the casedeque instead.
+	(lag_case) Removed.
+
+Sun Jan 14 21:43:12 2007  Ben Pfaff  <blp@gnu.org>
+
+	* procedure.c: Simplify lagged cases interface.  Updated all
+	clients--well, the only client--to use the simplified interface.
+	(dataset_n_lag) Removed.
+	(dataset_set_n_lag) Removed.
+	(dataset_need_lag) New function.
+
 Tue Jan  9 07:20:05 WST 2007 John Darrington <john@darrington.wattle.id.au>
 
 	* dictionary.c procedure.c: More changes to ensure that callbacks occur
Index: src/data/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/automake.mk,v
retrieving revision 1.14
diff -u -p -r1.14 automake.mk
--- src/data/automake.mk	4 Jan 2007 08:15:27 -0000	1.14
+++ src/data/automake.mk	15 Jan 2007 05:45:46 -0000
@@ -13,6 +13,7 @@ src_data_libdata_a_SOURCES = \
 	src/data/case-source.c \
 	src/data/case-source.h \
 	src/data/case.c \
+	src/data/casedeque.h \
 	src/data/casefilter.c \
 	src/data/casefilter.h \
 	src/data/casefile.h \
Index: src/data/casedeque.h
===================================================================
RCS file: src/data/casedeque.h
diff -N src/data/casedeque.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/data/casedeque.h	15 Jan 2007 05:45:46 -0000
@@ -0,0 +1,27 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef DATA_CASEDEQUE_H
+#define DATA_CASEDEQUE_H 1
+
+#include <data/case.h>
+#include <libpspp/deque.h>
+
+DEQUE_DECLARE (casedeque, struct ccase)
+
+#endif /* data/casedeque.h */
Index: src/data/procedure.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/procedure.c,v
retrieving revision 1.24
diff -u -p -r1.24 procedure.c
--- src/data/procedure.c	10 Jan 2007 09:22:42 -0000	1.24
+++ src/data/procedure.c	15 Jan 2007 05:45:47 -0000
@@ -26,6 +26,7 @@
 #include <data/case-source.h>
 #include <data/case-sink.h>
 #include <data/case.h>
+#include <data/casedeque.h>
 #include <data/casefile.h>
 #include <data/fastfile.h>
 #include <data/dictionary.h>
@@ -75,11 +76,9 @@ struct dataset {
   /* Time at which proc was last invoked. */
   time_t last_proc_invocation;
 
-  /* Lag queue. */
+  /* Cases just before ("lagging") the current one. */
   int n_lag;			/* Number of cases to lag. */
-  int lag_count;		/* Number of cases in lag_queue so far. */
-  int lag_head;		/* Index where next case will be added. */
-  struct ccase *lag_queue; /* Array of n_lag ccase * elements. */
+  struct casedeque lagged_cases; /* Lagged cases. */
 
   /* Procedure data. */
   bool is_open;               /* Procedure open? */
@@ -100,7 +99,6 @@ static bool internal_procedure (struct d
 static void update_last_proc_invocation (struct dataset *ds);
 static void create_trns_case (struct ccase *, struct dictionary *);
 static void open_active_file (struct dataset *ds);
-static void lag_case (struct dataset *ds, const struct ccase *c);
 static void clear_case (const struct dataset *ds, struct ccase *c);
 static bool close_active_file (struct dataset *ds);
 
@@ -294,9 +292,14 @@ proc_read (struct dataset *ds, struct cc
       if (retval != TRNS_CONTINUE)
         continue;
 
-      /* Write case to LAG queue. */
-      if (ds->n_lag)
-        lag_case (ds, &ds->trns_case);
+      /* Write case to collection of lagged cases. */
+      if (ds->n_lag > 0) 
+        {
+          while (casedeque_count (&ds->lagged_cases) >= ds->n_lag)
+            case_destroy (casedeque_pop_back (&ds->lagged_cases));
+          case_clone (casedeque_push_front (&ds->lagged_cases),
+                      &ds->trns_case);
+        }
 
       /* Write case to replacement active file. */
       ds->cases_written++;
@@ -416,29 +419,8 @@ open_active_file (struct dataset *ds)
   if (ds->proc_sink->class->open != NULL)
     ds->proc_sink->class->open (ds->proc_sink);
 
-  /* Allocate memory for lag queue. */
-  if (ds->n_lag > 0)
-    {
-      int i;
-
-      ds->lag_count = 0;
-      ds->lag_head = 0;
-      ds->lag_queue = xnmalloc (ds->n_lag, sizeof *ds->lag_queue);
-      for (i = 0; i < ds->n_lag; i++)
-        case_nullify (&ds->lag_queue[i]);
-    }
-}
-
-/* Add C to the lag queue. */
-static void
-lag_case (struct dataset *ds, const struct ccase *c)
-{
-  if (ds->lag_count < ds->n_lag)
-    ds->lag_count++;
-  case_destroy (&ds->lag_queue[ds->lag_head]);
-  case_clone (&ds->lag_queue[ds->lag_head], c);
-  if (++ds->lag_head >= ds->n_lag)
-    ds->lag_head = 0;
+  /* Allocate memory for lagged cases. */
+  casedeque_init (&ds->lagged_cases, ds->n_lag);
 }
 
 /* Clears the variables in C that need to be cleared between
@@ -466,16 +448,10 @@ clear_case (const struct dataset *ds, st
 static bool
 close_active_file (struct dataset *ds)
 {
-  /* Free memory for lag queue, and turn off lagging. */
-  if (ds->n_lag > 0)
-    {
-      int i;
-
-      for (i = 0; i < ds->n_lag; i++)
-	case_destroy (&ds->lag_queue[i]);
-      free (ds->lag_queue);
-      ds->n_lag = 0;
-    }
+  /* Free memory for lagged cases. */
+  while (!casedeque_is_empty (&ds->lagged_cases))
+    case_destroy (casedeque_pop_back (&ds->lagged_cases));
+  casedeque_destroy (&ds->lagged_cases);
 
   /* Dictionary from before TEMPORARY becomes permanent. */
   proc_cancel_temporary_transformations (ds);
@@ -504,16 +480,10 @@ close_active_file (struct dataset *ds)
 struct ccase *
 lagged_case (const struct dataset *ds, int n_before)
 {
-  assert (n_before >= 1 );
-  assert (n_before <= ds->n_lag);
+  assert (n_before >= 1 && n_before <= ds->n_lag);
 
-  if (n_before <= ds->lag_count)
-    {
-      int index = ds->lag_head - n_before;
-      if (index < 0)
-        index += ds->n_lag;
-      return &ds->lag_queue[index];
-    }
+  if (n_before <= casedeque_count (&ds->lagged_cases))
+    return casedeque_front (&ds->lagged_cases, n_before - 1);
   else
     return NULL;
 }
@@ -1028,19 +998,12 @@ dataset_set_dict (struct dataset *ds, st
   dict_destroy (old_dict);
 }
 
-int
-dataset_n_lag (const struct dataset *ds)
+void 
+dataset_need_lag (struct dataset *ds, int n_before)
 {
-  return ds->n_lag;
+  ds->n_lag = MAX (ds->n_lag, n_before);
 }
 
-void
-dataset_set_n_lag (struct dataset *ds, int n_lag)
-{
-  ds->n_lag = n_lag;
-}
-
-
 struct casefile_factory *
 dataset_get_casefile_factory (const struct dataset *ds)
 {
Index: src/data/procedure.h
===================================================================
RCS file: /cvsroot/pspp/pspp/src/data/procedure.h,v
retrieving revision 1.11
diff -u -p -r1.11 procedure.h
--- src/data/procedure.h	1 Jan 2007 01:44:33 -0000	1.11
+++ src/data/procedure.h	15 Jan 2007 05:45:47 -0000
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -116,8 +116,6 @@ struct ccase *lagged_case (const struct 
 inline struct dictionary *dataset_dict (const struct dataset *ds);
 inline void dataset_set_dict ( struct dataset *ds, struct dictionary *dict);
 
-inline int dataset_n_lag (const struct dataset *ds);
-inline void dataset_set_n_lag (struct dataset *ds, int n_lag);
-
+void dataset_need_lag (struct dataset *ds, int n_before);
 
 #endif /* procedure.h */
Index: src/language/expressions/parse.c
===================================================================
RCS file: /cvsroot/pspp/pspp/src/language/expressions/parse.c,v
retrieving revision 1.22
diff -u -p -r1.22 parse.c
--- src/language/expressions/parse.c	15 Dec 2006 00:16:02 -0000	1.22
+++ src/language/expressions/parse.c	15 Jan 2007 05:45:49 -0000
@@ -1278,18 +1278,14 @@ parse_function (struct lexer *lexer, str
   n->composite.min_valid = min_valid != -1 ? min_valid : f->array_min_elems; 
 
   if (n->type == OP_LAG_Vn || n->type == OP_LAG_Vs) 
-    {
-      if (dataset_n_lag (e->ds) < 1)
-        dataset_set_n_lag (e->ds, 1);
-    }
+    dataset_need_lag (e->ds, 1);
   else if (n->type == OP_LAG_Vnn || n->type == OP_LAG_Vsn)
     {
       int n_before;
       assert (n->composite.arg_cnt == 2);
       assert (n->composite.args[1]->type == OP_pos_int);
       n_before = n->composite.args[1]->integer.i;
-      if ( dataset_n_lag (e->ds) < n_before)
-        dataset_set_n_lag (e->ds, n_before);
+      dataset_need_lag (e->ds, n_before);
     }
   
   free (args);
Index: src/libpspp/ChangeLog
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/ChangeLog,v
retrieving revision 1.47
diff -u -p -r1.47 ChangeLog
--- src/libpspp/ChangeLog	10 Jan 2007 14:50:16 -0000	1.47
+++ src/libpspp/ChangeLog	15 Jan 2007 05:45:49 -0000
@@ -1,3 +1,9 @@
+Sun Jan 14 21:44:18 2007  Ben Pfaff  <blp@gnu.org>
+
+	* automake.mk: Add deque.h to sources.
+	
+	* deque.h: New file.
+
 Wed Jan 10 06:49:38 2007  Ben Pfaff  <blp@gnu.org>
 
 	* automake.mk: Add heap.c, heap.h to sources.
Index: src/libpspp/automake.mk
===================================================================
RCS file: /cvsroot/pspp/pspp/src/libpspp/automake.mk,v
retrieving revision 1.19
diff -u -p -r1.19 automake.mk
--- src/libpspp/automake.mk	10 Jan 2007 14:50:16 -0000	1.19
+++ src/libpspp/automake.mk	15 Jan 2007 05:45:49 -0000
@@ -15,6 +15,7 @@ src_libpspp_libpspp_a_SOURCES = \
 	src/libpspp/copyleft.c \
 	src/libpspp/copyleft.h \
 	src/libpspp/compiler.h \
+	src/libpspp/deque.h \
 	src/libpspp/float-format.c \
 	src/libpspp/float-format.h \
 	src/libpspp/freaderror.c \
Index: src/libpspp/deque.h
===================================================================
RCS file: src/libpspp/deque.h
diff -N src/libpspp/deque.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/libpspp/deque.h	15 Jan 2007 05:45:50 -0000
@@ -0,0 +1,170 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef LIBPSPP_DEQUE_H
+#define LIBPSPP_DEQUE_H 1
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <libpspp/compiler.h>
+
+#include "xalloc.h"
+
+/* Declares data and functions for a deque whose elements have
+   the given ELEMENT_TYPE.  Instances of the deque are declared
+   as "struct NAME", and each function that operates on the deque
+   has NAME_ as a prefix. */
+#define DEQUE_DECLARE(NAME, ELEMENT_TYPE)                                   \
+/* An instance of the deque. */                                             \
+struct NAME                                                                 \
+  {                                                                         \
+    size_t capacity;    /* Capacity, which must be a power of 2. */         \
+    size_t front;       /* One past the front of the queue. */              \
+    size_t back;        /* The back of the queue. */                        \
+    ELEMENT_TYPE *data; /* Pointer to CAPACITY elements. */                 \
+  };                                                                        \
+                                                                            \
+/* Initializes DEQUE as an empty deque that can store at least              \
+   CAPACITY elements.  (The actual capacity may be larger and is            \
+   always a power of 2.) */                                                 \
+static inline void                                                          \
+NAME##_init (struct NAME *deque, size_t capacity)                           \
+{                                                                           \
+  deque->capacity = 1;                                                      \
+  while (deque->capacity < capacity)                                        \
+    deque->capacity *= 2;                                                   \
+  deque->front = deque->back = 0;                                           \
+  deque->data = xnmalloc (deque->capacity, sizeof *deque->data);            \
+}                                                                           \
+                                                                            \
+/* Destroys DEQUE, which must be empty. */                                  \
+static inline void                                                          \
+NAME##_destroy (struct NAME *deque)                                         \
+{                                                                           \
+  free (deque->data);                                                       \
+}                                                                           \
+                                                                            \
+/* Returns the number of elements currently in DEQUE. */                    \
+static inline size_t                                                        \
+NAME##_count (const struct NAME *deque)                                     \
+{                                                                           \
+  return deque->front - deque->back;                                        \
+}                                                                           \
+                                                                            \
+/* Returns the maximum number of elements that DEQUE can hold at            \
+   any time. */                                                             \
+static inline size_t                                                        \
+NAME##_capacity (const struct NAME *deque)                                  \
+{                                                                           \
+  return deque->capacity;                                                   \
+}                                                                           \
+                                                                            \
+/* Returns true if DEQUE is currently empty (contains no                    \
+   elements), false otherwise. */                                           \
+static inline bool                                                          \
+NAME##_is_empty (const struct NAME *deque)                                  \
+{                                                                           \
+  return NAME##_count (deque) == 0;                                         \
+}                                                                           \
+                                                                            \
+/* Returns true if DEQUE is currently full (cannot take any more            \
+   elements), false otherwise. */                                           \
+static inline bool                                                          \
+NAME##_is_full (const struct NAME *deque)                                   \
+{                                                                           \
+  return NAME##_count (deque) >= NAME##_capacity (deque);                   \
+}                                                                           \
+                                                                            \
+/* Returns the element in DEQUE that is OFFSET elements from its            \
+   front.  A value 0 for OFFSET requests the element at the                 \
+   front, a value of 1 the element just behind the front, and so            \
+   on.  OFFSET must be less than the current number of elements             \
+   in DEQUE. */                                                             \
+static inline ELEMENT_TYPE *                                                \
+NAME##_front (const struct NAME *deque, size_t offset)                      \
+{                                                                           \
+  assert (NAME##_count (deque) > offset);                                   \
+  return &deque->data[(deque->front - offset - 1) & (deque->capacity - 1)]; \
+}                                                                           \
+                                                                            \
+/* Returns the element in DEQUE that is OFFSET elements from its            \
+   back.  A value 0 for OFFSET requests the element at the back,            \
+   a value of 1 the element just ahead of the back, and so on.              \
+   OFFSET must be less than the current number of elements in               \
+   DEQUE. */                                                                \
+static inline ELEMENT_TYPE *                                                \
+NAME##_back (const struct NAME *deque, size_t offset)                       \
+{                                                                           \
+  assert (NAME##_count (deque) > offset);                                   \
+  return &deque->data[(deque->back + offset) & (deque->capacity - 1)];      \
+}                                                                           \
+                                                                            \
+/* Adds and returns the address of a new element at the front of            \
+   DEQUE, which must not be full.  The caller is responsible for            \
+   assigning a value to the returned element. */                            \
+static inline ELEMENT_TYPE *                                                \
+NAME##_push_front (struct NAME *deque)                                      \
+{                                                                           \
+  assert (!NAME##_is_full (deque));                                         \
+  return &deque->data[deque->front++ & (deque->capacity - 1)];              \
+}                                                                           \
+                                                                            \
+/* Adds and returns the address of a new element at the back of             \
+   DEQUE, which must not be full.  The caller is responsible for            \
+   assigning a value to the returned element. */                            \
+static inline ELEMENT_TYPE *                                                \
+NAME##_push_back (struct NAME *deque)                                       \
+{                                                                           \
+  assert (!NAME##_is_full (deque));                                         \
+  return &deque->data[--deque->back & (deque->capacity - 1)];               \
+}                                                                           \
+                                                                            \
+/* Pops the front element off DEQUE (which must not be empty) and           \
+   returns its address.  The element may be reused the next time            \
+   an element is pushed into DEQUE or when DEQUE is expanded. */            \
+static inline ELEMENT_TYPE *                                                \
+NAME##_pop_front (struct NAME *deque)                                       \
+{                                                                           \
+  assert (!NAME##_is_empty (deque));                                        \
+  return &deque->data[--deque->front & (deque->capacity - 1)];              \
+}                                                                           \
+                                                                            \
+/* Pops the back element off DEQUE (which must not be empty) and            \
+   returns its address.  The element may be reused the next time            \
+   an element is pushed into DEQUE or when DEQUE is expanded. */            \
+static inline ELEMENT_TYPE *                                                \
+NAME##_pop_back (struct NAME *deque)                                        \
+{                                                                           \
+  assert (!NAME##_is_empty (deque));                                        \
+  return &deque->data[deque->back++ & (deque->capacity - 1)];               \
+}                                                                           \
+                                                                            \
+/* Expands DEQUE, doubling its capacity. */                                 \
+static inline void                                                          \
+NAME##_expand (struct NAME *deque)                                          \
+{                                                                           \
+  struct NAME old_deque = *deque;                                           \
+  NAME##_init (deque, deque->capacity * 2);                                 \
+  while (!NAME##_is_empty (&old_deque))                                     \
+    *NAME##_push_front (deque) = *NAME##_pop_back (&old_deque);             \
+  free (old_deque.data);                                                    \
+}
+
+#endif /* libpspp/deque.h */
