gui: Fix behavior of print preview in psppire-output-window.
[pspp] / src / data / case-matcher.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2008, 2009, 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "data/case-matcher.h"
20
21 #include <stdlib.h>
22
23 #include "data/case.h"
24 #include "data/subcase.h"
25 #include "data/value.h"
26 #include "libpspp/assertion.h"
27
28 #include "gl/xalloc.h"
29
30 struct case_matcher_input
31   {
32     struct subcase by_vars;
33     struct ccase **data;
34     bool *is_minimal;
35   };
36
37 struct case_matcher
38   {
39     struct case_matcher_input *inputs;
40     size_t n_inputs, allocated_inputs;
41     union value *by_values;
42   };
43
44 /* Creates and returns a new case matcher. */
45 struct case_matcher *
46 case_matcher_create (void)
47 {
48   struct case_matcher *cm = xmalloc (sizeof *cm);
49   cm->inputs = NULL;
50   cm->n_inputs = 0;
51   cm->allocated_inputs = 0;
52   cm->by_values = NULL;
53   return cm;
54 }
55
56 /* Adds a new input file to case matcher CM.
57    case_matcher_match() will compare the variables specified in
58    BY in case *DATA and set *IS_MINIMAL appropriately.
59    (The caller may change the case that *DATA points to from one
60    call to the next.)
61
62    All of the BY subcases provided to this function for a given
63    CM must be conformable (see subcase_conformable()). */
64 void
65 case_matcher_add_input (struct case_matcher *cm, const struct subcase *by,
66                         struct ccase **data, bool *is_minimal)
67 {
68   struct case_matcher_input *input;
69
70   if (cm->n_inputs == 0)
71     {
72       cm->by_values = xmalloc (sizeof *cm->by_values
73                                * subcase_get_n_fields (by));
74       caseproto_init_values (subcase_get_proto (by), cm->by_values);
75     }
76   else
77     assert (subcase_conformable (by, &cm->inputs[0].by_vars));
78
79   if (cm->n_inputs >= cm->allocated_inputs)
80     cm->inputs = x2nrealloc (cm->inputs, &cm->allocated_inputs,
81                              sizeof *cm->inputs);
82   input = &cm->inputs[cm->n_inputs++];
83   subcase_clone (&input->by_vars, by);
84   input->data = data;
85   input->is_minimal = is_minimal;
86 }
87
88 /* Destroys case matcher CM. */
89 void
90 case_matcher_destroy (struct case_matcher *cm)
91 {
92   if (cm != NULL)
93     {
94       size_t i;
95
96       if (cm->by_values != NULL)
97         {
98           caseproto_destroy_values (subcase_get_proto (&cm->inputs[0].by_vars),
99                                     cm->by_values);
100           free (cm->by_values);
101         }
102       for (i = 0; i < cm->n_inputs; i++)
103         {
104           struct case_matcher_input *input = &cm->inputs[i];
105           subcase_destroy (&input->by_vars);
106         }
107       free (cm->inputs);
108       free (cm);
109     }
110 }
111
112 static int
113 compare_BY_3way (struct case_matcher_input *a, struct case_matcher_input *b)
114 {
115   return subcase_compare_3way (&a->by_vars, *a->data, &b->by_vars, *b->data);
116 }
117
118 /* Compares the values of the BY variables in all of the nonnull
119    cases provided to case_matcher_add_input() for CM, sets
120    *IS_MINIMAL for each one to true if it has the minimum BY
121    values among those cases or to false if its BY values are
122    greater than the minimum.  Also sets *IS_MINIMAL to false for
123    null cases.  Sets *BY to the BY values extracted from the
124    minimum case.  (The caller must not free *BY.)
125
126    Returns true if at least one of the cases is nonnull, false
127    if they are all null.*/
128 bool
129 case_matcher_match (struct case_matcher *cm, union value **by)
130 {
131   struct case_matcher_input *file, *min;
132
133   min = NULL;
134   for (file = cm->inputs; file < &cm->inputs[cm->n_inputs]; file++)
135     if (*file->data != NULL)
136       {
137         int cmp = min != NULL ? compare_BY_3way (min, file) : 1;
138         if (cmp < 0)
139           *file->is_minimal = false;
140         else
141           {
142             *file->is_minimal = true;
143             if (cmp > 0)
144               min = file;
145           }
146       }
147     else
148       *file->is_minimal = false;
149
150   if (min != NULL)
151     {
152       for (file = cm->inputs; file < min; file++)
153         *file->is_minimal = false;
154       subcase_extract (&min->by_vars, *min->data, cm->by_values);
155       *by = cm->by_values;
156       return true;
157     }
158   else
159     {
160       *by = NULL;
161       return false;
162     }
163 }