1
/* Copyright (C) 2006, 2007 William McCune
3
This file is part of the LADR Deduction Library.
5
The LADR Deduction Library is free software; you can redistribute it
6
and/or modify it under the terms of the GNU General Public License,
9
The LADR Deduction Library is distributed in the hope that it will be
10
useful, 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.
14
You should have received a copy of the GNU General Public License
15
along with the LADR Deduction Library; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
#include "../ladr/banner.h"
24
/*****************************************************************************/
25
/* Variables -- most are used (extern) by other source files */
27
/* Options and Statistics */
30
struct mace_stats Mstats;
32
/* List of symbols and associated data */
36
/* This maps OPS symbol IDs to MACE symbol IDs, which start with 0. */
38
static int Sn_map_size;
39
Symbol_data *Sn_to_mace_sn;
41
/* Cell table is indexed by eterm IDs. */
44
struct cell *Cells; /* the table of cells (dynamically allocated) */
45
struct cell **Ordered_cells; /* (pointers to) permutation of Cells */
46
int First_skolem_cell;
51
Term *Domain; /* array of terms representing (shared) domain elements */
54
Plist Ground_clauses; /* Mclauses (see ground.h) */
56
int Relation_flag; /* term flag */
57
int Negation_flag; /* term flag */
63
static int Max_domain_element_in_input; /* For Least Number Heuristic */
65
static Plist Models; /* in case we collect models as terms */
69
/* stats for entire run */
71
unsigned Total_models;
73
static double Start_seconds;
74
static double Start_domain_seconds;
75
static int Start_megs;
77
/* end of variables */
78
/*****************************************************************************/
80
/* search return codes */
83
SEARCH_GO_MODELS, /* continue: model(s) found on current path */
84
SEARCH_GO_NO_MODELS, /* continue: no models found on current path */
85
SEARCH_MAX_MODELS, /* stop */
86
SEARCH_MAX_MEGS, /* stop */
87
SEARCH_MAX_TOTAL_SECONDS, /* stop */
88
SEARCH_MAX_DOMAIN_SECONDS, /* stop */
89
SEARCH_DOMAIN_OUT_OF_RANGE /* stop */
92
/* Ground terms. MACE4 operates on ground clauses, which are
93
represented by the structure mclause. Ground terms (and
94
atoms) are represented with ordinary LADR terms. There are
97
(1) We use upward pointers from terms to superterms
98
and from atoms to clauses.
100
(2) We need to mark atoms with a termflag, so that we know
101
when to stop when following the upward pointers. Also,
102
a termflag is used to indicate that an atom is negated.
104
(3) Domain elements are represented by variables (sorry,
105
but it is very convenient to do so). Also, there is only
106
one actual copy of each domain element (structure sharing
107
of domain elements). Global array *Domain contains them.
109
IDs. If all of the arguments of a term (including atoms) are
110
domain elements, that term is called an eterm. For example,
111
f(3,4), a, P(0), and Q.
113
Each eterm has a unique ID which is used as an index into
114
the cell table, for example, when a new eterm is obtained by
115
evaluating a subterm to a domain element, we have to quickly
116
check if this new eterm can be evaluated. We do this by
117
calculating its ID and looking up in Cells[ID].value.
118
And when we have a new assignment, say f(3,4)=2, we find the
119
list of occurrences of f(3,4) by looking in Cells[ID].occurrences.
124
* init_mace_options()
132
void init_mace_options(Mace_options opt)
134
opt->domain_size = init_parm("domain_size", 0, 0, INT_MAX);
135
opt->start_size = init_parm("start_size", 2, 2, INT_MAX);
136
opt->end_size = init_parm("end_size", -1, -1, INT_MAX);
137
opt->iterate_up_to = init_parm("iterate_up_to", -1, -1, INT_MAX);
138
opt->max_models = init_parm("max_models", 1, -1, INT_MAX);
139
opt->max_seconds = init_parm("max_seconds", -1, -1, INT_MAX);
140
opt->max_seconds_per = init_parm("max_seconds_per", -1, -1, INT_MAX);
141
opt->selection_order = init_parm("selection_order", 2, 0, 2);
142
opt->selection_measure = init_parm("selection_measure",4, 0, 4);
143
opt->increment = init_parm("increment", 1, 1, INT_MAX);
144
opt->max_megs = init_parm("max_megs", 200, -1, INT_MAX);
145
opt->report_stderr = init_parm("report_stderr", -1, -1, INT_MAX);
147
opt->print_models = init_flag("print_models", TRUE);
148
opt->print_models_tabular = init_flag("print_models_tabular", FALSE);
149
opt->lnh = init_flag("lnh", TRUE);
150
opt->trace = init_flag("trace", FALSE);
151
opt->negprop = init_flag("negprop", TRUE);
152
opt->neg_assign = init_flag("neg_assign", TRUE);
153
opt->neg_assign_near = init_flag("neg_assign_near", TRUE);
154
opt->neg_elim = init_flag("neg_elim", TRUE);
155
opt->neg_elim_near = init_flag("neg_elim_near", TRUE);
156
opt->verbose = init_flag("verbose", FALSE);
157
opt->integer_ring = init_flag("integer_ring", FALSE);
158
opt->order_domain = init_flag("order_domain", FALSE);
159
opt->arithmetic = init_flag("arithmetic", FALSE);
160
opt->iterate_primes = init_flag("iterate_primes", FALSE);
161
opt->iterate_nonprimes = init_flag("iterate_nonprimes", FALSE);
162
opt->skolems_last = init_flag("skolems_last", FALSE);
163
opt->return_models = init_flag("return_models", FALSE);
165
opt->iterate = init_stringparm("iterate", 5,
174
flag_flag_dependency(opt->print_models_tabular,TRUE,opt->print_models,FALSE);
175
flag_flag_dependency(opt->print_models,TRUE,opt->print_models_tabular,FALSE);
177
flag_flag_dependency(opt->iterate_primes, TRUE,opt->iterate_nonprimes,FALSE);
178
flag_flag_dependency(opt->iterate_nonprimes, TRUE,opt->iterate_primes,FALSE);
180
parm_parm_dependency(opt->domain_size, opt->start_size, 1, TRUE);
181
parm_parm_dependency(opt->domain_size, opt->end_size, 1, TRUE);
183
parm_parm_dependency(opt->iterate_up_to, opt->end_size, 1, TRUE);
185
flag_stringparm_dependency(opt->iterate_primes,TRUE,opt->iterate,"primes");
186
flag_stringparm_dependency(opt->iterate_nonprimes,TRUE,opt->iterate,"nonprimes");
188
flag_flag_dependency(opt->integer_ring, TRUE, opt->lnh, FALSE);
189
flag_flag_dependency(opt->order_domain, TRUE, opt->lnh, FALSE);
190
flag_flag_dependency(opt->arithmetic, TRUE, opt->lnh, FALSE);
192
flag_parm_dependency(opt->arithmetic,TRUE,opt->selection_order,0);
194
} /* init_mace_options */
203
char *exit_string(int code)
207
case MAX_MODELS_EXIT: message = "max_models"; break;
208
case ALL_MODELS_EXIT: message = "all_models"; break;
209
case EXHAUSTED_EXIT: message = "exhausted"; break;
210
case MAX_MEGS_YES_EXIT: message = "max_megs_yes"; break;
211
case MAX_MEGS_NO_EXIT: message = "max_megs_no" ; break;
212
case MAX_SEC_YES_EXIT: message = "max_sec_yes"; break;
213
case MAX_SEC_NO_EXIT: message = "max_sec_no"; break;
214
case MACE_SIGINT_EXIT: message = "mace_sigint"; break;
215
case MACE_SIGSEGV_EXIT: message = "mace_sigsegv"; break;
216
default: message = "???";
227
void mace4_exit(int exit_code)
229
if (Opt && flag(Opt->verbose))
232
if (Opt && parm(Opt->report_stderr) > 0)
233
fprintf(stderr, "Domain_size=%d. Models=%d. User_CPU=%.2f.\n",
234
Domain_size, Total_models, user_seconds());
236
printf("\nUser_CPU=%.2f, System_CPU=%.2f, Wall_clock=%d.\n",
237
user_seconds(), system_seconds(), wallclock());
239
if (Total_models == 0)
240
printf("\nExiting with failure.\n");
242
printf("\nExiting with %d model%s.\n",
243
Total_models, Total_models == 1 ? "" : "s");
245
fprintf(stderr, "\n------ process %d exit (%s) ------\n",
246
my_process_id(), exit_string(exit_code));
247
printf("\nProcess %d exit (%s) %s",
248
my_process_id(), exit_string(exit_code), get_date());
250
printf("The process finished %s", get_date());
256
* initialize_for_search()
258
* This is the initialization that has to be done only once
259
* for a given set of clauses. It is independent of the
265
void initialize_for_search(Plist clauses)
270
Mace4_clock = clock_init("Mace4");
272
/* In ground clauses, VARIABLEs represent domain elements,
273
so from here on, print variables as integers. */
275
// set_variable_style(INTEGER_STYLE);
277
/* These flags are for ground clause (mclause) literals. */
279
Relation_flag = claim_term_flag();
280
Negation_flag = claim_term_flag();
282
/* Cache some symbol numbers. */
284
Eq_sn = str_to_sn(eq_sym(), 2);
285
Or_sn = str_to_sn(or_sym(), 2);
286
Not_sn = str_to_sn(not_sym(), 1);
288
/* Set up Symbols list. */
290
init_built_in_symbols(); /* =/2 (and maybe others) */
292
/* Maybe initialize for arithmetic. */
294
if (flag(Opt->arithmetic))
297
Skolems_last = flag(Opt->skolems_last);
299
/* Collect data for each symbol. */
301
Max_domain_element_in_input = -1;
302
i = collect_mace4_syms(clauses, flag(Opt->arithmetic));
303
Max_domain_element_in_input = MAX(Max_domain_element_in_input, i);
305
if (Max_domain_element_in_input == -1)
306
printf("\n%% There are no natural numbers in the input.\n");
308
printf("\n%% The largest natural number in the input is %d.\n",
309
Max_domain_element_in_input);
311
/* Set up map from ordinary symnums to mace symnums. */
316
for (s = Symbols; s != NULL; s = s->next) {
318
/* printf("mace symbol: %s/%d\n", sn_to_str(s->sn), sn_to_arity(s->sn)); */
319
max = (s->sn > max ? s->sn : max);
324
Sn_to_mace_sn = malloc(Sn_map_size * sizeof(void *));
326
for (i = 0; i < Sn_map_size; i++)
327
Sn_to_mace_sn[i] = NULL;
329
for (s = Symbols; s != NULL; s = s->next) {
330
Sn_to_mace_sn[s->sn] = s;
332
} /* initialize_for_search */
336
* init_for_domain_size()
338
* Given the list of (general) clauses, set up the various data
339
* structures that will be needed for a given domain size.
344
void init_for_domain_size(void)
346
int i, j, nextbase, id;
349
/* Give each symbol its "base" value, which is used to index cells. */
352
for (s = Symbols; s != NULL; s = s->next) {
354
nextbase += int_power(Domain_size, s->arity);
357
/* Set up the array of domain terms. All ground terms refer to these. */
359
Domain = malloc(Domain_size * sizeof(void *));
360
for (i = 0; i < Domain_size; i++)
361
Domain[i] = get_variable_term(i);
363
/* Set up the table of cells. */
365
Number_of_cells = nextbase;
366
Cells = malloc(Number_of_cells * sizeof(struct cell));
367
Ordered_cells = malloc(Number_of_cells * sizeof(void *));
369
for (id = 0; id < Number_of_cells; id++) {
370
struct cell *c = Cells + id;
373
c->occurrences = NULL;
375
c->symbol = find_symbol_node(id);
376
c->eterm = decode_eterm_id(id);
377
c->max_index = max_index(id, c->symbol);
378
n = id_to_domain_size(id);
379
c->possible = malloc(n * sizeof(void *));
380
for (j = 0; j < n; j++)
381
c->possible[j] = Domain[j]; /* really just a flag */
384
order_cells(flag(Opt->verbose));
386
if (flag(Opt->negprop))
387
init_negprop_index();
388
} /* init_for_domain_size */
392
* built_in_assignments()
397
void built_in_assignments(void)
400
for (s = Symbols; s != NULL; s = s->next) {
401
if (s->attribute == EQUALITY_SYMBOL) {
403
for (i = 0; i < Domain_size; i++)
404
for (j = 0; j < Domain_size; j++)
405
Cells[X2(s->base,i,j)].value = (Domain[i==j ? 1 : 0]);
408
} /* built_in_assignments */
412
* special_assignments()
417
void special_assignments(void)
419
if (flag(Opt->integer_ring)) {
420
/* Fix [+,-,*] as the ring of integers mod domain_size. */
421
/* If any of those operations doesn't exist, then ignore it.*/
423
for (s = Symbols; s != NULL; s = s->next) {
425
if (is_symbol(s->sn, "+", 2)) {
426
for (i = 0; i < Domain_size; i++)
427
for (j = 0; j < Domain_size; j++)
428
Cells[X2(s->base,i,j)].value = Domain[(i + j) % Domain_size];
430
else if (is_symbol(s->sn, "*", 2)) {
431
for (i = 0; i < Domain_size; i++)
432
for (j = 0; j < Domain_size; j++)
433
Cells[X2(s->base,i,j)].value = Domain[(i * j) % Domain_size];
435
else if (is_symbol(s->sn, "-", 1)) {
436
for (i = 0; i < Domain_size; i++)
437
Cells[X1(s->base,i)].value = Domain[(Domain_size - i) % Domain_size];
439
else if (is_symbol(s->sn, "--", 2)) {
440
for (i = 0; i < Domain_size; i++)
441
for (j = 0; j < Domain_size; j++)
442
Cells[X2(s->base,i,j)].value = Domain[((i + Domain_size) - j) %
447
if (flag(Opt->order_domain)) {
449
for (s = Symbols; s != NULL; s = s->next) {
451
if (is_symbol(s->sn, "<", 2)) {
452
for (i = 0; i < Domain_size; i++)
453
for (j = 0; j < Domain_size; j++)
454
Cells[X2(s->base,i,j)].value = (Domain[i<j ? 1 : 0]);
456
if (is_symbol(s->sn, "<=", 2)) {
457
for (i = 0; i < Domain_size; i++)
458
for (j = 0; j < Domain_size; j++)
459
Cells[X2(s->base,i,j)].value = (Domain[i<=j ? 1 : 0]);
463
} /* special_assignments */
467
* check_that_ground_clauses_are_true()
472
BOOL check_that_ground_clauses_are_true(void)
476
for (g = Ground_clauses; g != NULL; g = g->next) {
479
fprintf(stderr, "ERROR, model reported, but clause not true!\n");
480
fprintf(stdout, "ERROR, model reported, but clause not true! ");
486
} /* check_that_ground_clauses_are_true */
495
int possible_model(void)
497
if (flag(Opt->arithmetic)) {
498
if (!check_with_arithmetic(Ground_clauses))
499
return SEARCH_GO_NO_MODELS;
501
else if (!check_that_ground_clauses_are_true())
502
fatal_error("possible_model, bad model found");
505
static int next_message = 1;
507
Mstats.current_models++;
509
if (flag(Opt->return_models)) {
510
Term modelterm = interp_term();
511
Interp model = compile_interp(modelterm, FALSE);
513
Models = plist_append(Models, model);
516
if (flag(Opt->print_models))
517
print_model_standard(stdout, TRUE);
518
else if (flag(Opt->print_models_tabular))
520
else if (next_message == Total_models) {
521
printf("\nModel %d has been found.\n", Total_models);
525
if (parm(Opt->max_models) != -1 && Total_models >= parm(Opt->max_models))
526
return SEARCH_MAX_MODELS;
528
return SEARCH_GO_MODELS;
530
} /* possible_model */
541
return (megs_malloced() - Start_megs) + (estack_bytes() / (1024*1024));
546
* check_time_memory()
551
int check_time_memory(void)
553
static int Next_report;
555
double seconds = user_seconds();
556
int max_seconds = parm(Opt->max_seconds);
557
int max_seconds_per = parm(Opt->max_seconds_per);
558
int max_megs = parm(Opt->max_megs);
559
int report = parm(Opt->report_stderr);
561
if (max_seconds != -1 && seconds - Start_seconds > max_seconds)
562
return SEARCH_MAX_TOTAL_SECONDS;
563
else if (max_seconds_per != -1 &&
564
seconds - Start_domain_seconds > parm(Opt->max_seconds_per))
565
return SEARCH_MAX_DOMAIN_SECONDS;
566
else if (max_megs != -1 && mace_megs() > parm(Opt->max_megs))
567
return SEARCH_MAX_MEGS;
570
if (Next_report == 0)
571
Next_report = parm(Opt->report_stderr);
572
if (seconds >= Next_report) {
573
fprintf(stderr, "Domain_size=%d. Models=%d. User_CPU=%.2f.\n", Domain_size, Total_models, seconds);
575
while (seconds >= Next_report)
576
Next_report += report;
579
return SEARCH_GO_NO_MODELS;
581
} /* check_time_memory */
585
* mace4_skolem_check()
590
BOOL mace4_skolem_check(int id)
592
/* Should we keep going w.r.t. the Skolem restriction? */
593
if (!flag(Opt->skolems_last))
595
else if (Cells[id].symbol->attribute == SKOLEM_SYMBOL) {
601
} /* mace4_skolem_check */
605
* p_possible_values()
611
void p_possible_values(void)
614
for (i = 0; i < Number_of_cells; i++) {
615
if (Cells[i].symbol->attribute == ORDINARY_SYMBOL) {
617
printf("Cell %d: ", i);
618
for (j = 0; j < id_to_domain_size(i); j++) {
619
if (Cells[i].possible[j] != NULL)
625
} /* p_possible_values */
632
* Max_constrained is the maximum constrained domain element
633
* (or -1 is none is constrained). Greater domain elements
634
* can all be considered symmetric. An element can become
635
* constrained in two ways: (1) it is an index of some selected
636
* cell, or (2) it is the value assigned to some selected cell.
637
* (Propagation does not constrain elements. This might need
638
* careful justification.)
640
* To apply the least number heuristic, we consider values
641
* 0 ... MIN(max_constrained+1, Domain_size-1).
643
* To make this effective, we should keep max_constrained as low as
644
* possible by selecting cells with maximum index <= max_constrained.
648
* SEARCH_GO_NO_MODELS
651
* SEARCH_MAX_TOTAL_SECONDS
652
* SEARCH_MAX_DOMAIN_SECONDS
657
int search(int max_constrained, int depth)
661
int rc = check_time_memory();
662
if (rc != SEARCH_GO_NO_MODELS)
665
id = select_cell(max_constrained);
667
rc = possible_model();
671
int i, last; /* we'll do 0 .. last */
672
int x = Cells[id].max_index;
673
max_constrained = MAX(max_constrained, x);
676
if (flag(Opt->trace)) {
679
/* p_possible_values(); */
682
if (Cells[id].symbol->type == RELATION)
684
else if (flag(Opt->lnh))
685
last = MIN(max_constrained+1, Domain_size-1);
687
last = Domain_size-1;
689
for (i = 0, go = TRUE; i <= last && go; i++) {
691
Mstats.assignments++;
693
if (flag(Opt->trace)) {
695
fwrite_term(stdout, Cells[id].eterm);
696
printf("=%d (%d) depth=%d\n", i, last, depth);
699
stk = assign_and_propagate(id, Domain[i]);
702
/* no contradiction found during propagation, so we recurse */
703
rc = search(MAX(max_constrained, i), depth+1);
704
/* undo assign_and_propagate changes */
705
restore_from_stack(stk);
706
if (rc == SEARCH_GO_MODELS)
707
go = mace4_skolem_check(id);
709
go = (rc == SEARCH_GO_NO_MODELS);
719
* mace4n() -- look for a model of a specific size
724
int mace4n(Plist clauses, int order)
728
Mstate initial_state = get_mstate();
730
Variable_style save_style = variable_style();
731
set_variable_style(INTEGER_STYLE);
733
if (Max_domain_element_in_input >= order) {
734
if (flag(Opt->arithmetic)) {
735
if (!ok_for_arithmetic(clauses, order))
736
return SEARCH_DOMAIN_OUT_OF_RANGE;
739
return SEARCH_DOMAIN_OUT_OF_RANGE;
744
init_for_domain_size();
746
built_in_assignments(); /* Fill out equality table (and maybe others). */
748
special_assignments(); /* assignments determined by options */
750
/* Instantiate clauses over the domain. This also
751
(1) makes any domain element constants into real domain elements,
752
(2) applies OR, NOT, and EQ simplification, and
753
(3) does unit propagation (which pushes events onto initial_state->stack).
754
Do the units first, then the 2-clauses, then the rest. */
756
for (p = clauses; initial_state->ok && p != NULL; p = p->next)
757
if (number_of_literals(p->v) < 2)
758
generate_ground_clauses(p->v, initial_state);
760
for (p = clauses; initial_state->ok && p != NULL; p = p->next)
761
if (number_of_literals(p->v) == 2)
762
generate_ground_clauses(p->v, initial_state);
764
for (p = clauses; initial_state->ok && p != NULL; p = p->next)
765
if (number_of_literals(p->v) > 2)
766
generate_ground_clauses(p->v, initial_state);
768
/* The preceding calls push propagation events onto initial_state->stack.
769
We won't have to undo those initial events during the search,
770
but we can undo them after the search.
773
if (flag(Opt->verbose)) {
774
printf("\nInitial partial model:\n");
781
if (initial_state->ok)
782
rc = search(Max_domain_element_in_input, 0);
784
rc = SEARCH_GO_NO_MODELS; /* contradiction in initial state */
786
/* Free all of the memory associated with the current domain size. */
788
restore_from_stack(initial_state->stack);
789
free_mstate(initial_state);
791
if (flag(Opt->negprop))
792
free_negprop_index();
795
Ordered_cells = NULL;
797
for (i = 0; i < Number_of_cells; i++) {
798
zap_mterm(Cells[i].eterm);
799
free(Cells[i].possible);
804
for (i = 0; i < Domain_size; i++)
809
for (g = Ground_clauses; g != NULL; g = g->next)
811
zap_plist(Ground_clauses);
812
Ground_clauses = NULL;
814
set_variable_style(save_style);
825
BOOL iterate_ok(int n, char *class)
827
if (str_ident(class, "all"))
829
else if (str_ident(class, "evens"))
831
else if (str_ident(class, "odds"))
833
else if (str_ident(class, "primes"))
835
else if (str_ident(class, "nonprimes"))
838
fatal_error("iterate_ok, unknown class");
839
return FALSE; /* to please compiler */
850
int next_domain_size(n)
852
int top = (parm(Opt->end_size) == -1 ? INT_MAX : parm(Opt->end_size));
855
n = parm(Opt->start_size); /* first call */
857
n += parm(Opt->increment);
859
while (!iterate_ok(n, stringparm1(Opt->iterate)))
860
n += parm(Opt->increment);
862
return (n > top ? -1 : n);
863
} /* next_domain_size */
875
Mace_results mace4(Plist clauses, Mace_options opt)
878
Mace_results results = malloc(sizeof(struct mace_results));
880
disable_max_megs(); /* mace4 does its own max_megs check */
881
Start_seconds = user_seconds();
882
Start_megs = megs_malloced();
884
Opt = opt; /* put options into a global variable */
885
initialize_for_search(clauses);
887
n = next_domain_size(0); /* returns -1 if we're done */
888
rc = SEARCH_GO_NO_MODELS;
890
while (n >= 2 && (rc == SEARCH_GO_NO_MODELS || rc == SEARCH_GO_MODELS)) {
892
sprintf(str, "DOMAIN SIZE %d", n);
893
print_separator(stdout, str, TRUE);
895
fprintf(stderr,"\n=== Mace4 starting on domain size %d. ===\n",n);
897
Start_domain_seconds = user_seconds();
898
clock_start(Mace4_clock);
899
rc = mace4n(clauses, n);
900
if (rc == SEARCH_MAX_DOMAIN_SECONDS) {
901
printf("\n====== Domain size %d terminated by max_seconds_per. ======\n",n);
902
rc = SEARCH_GO_NO_MODELS;
904
else if (rc == SEARCH_DOMAIN_OUT_OF_RANGE) {
905
printf("\n====== Domain size %d skipped because domain elememt too big. ======\n",n);
906
rc = SEARCH_GO_NO_MODELS;
908
clock_stop(Mace4_clock);
910
reset_current_stats();
911
clock_reset(Mace4_clock);
912
n = next_domain_size(n); /* returns -1 if we're done */
915
/* free memory used for all domain sizes */
916
free_estack_memory();
918
Sn_to_mace_sn = NULL;
920
results->success = Total_models != 0;
921
results->models = Models; /* NULL if no models or not collecting models */
922
results->user_seconds = user_seconds() - Start_seconds;
924
if (rc == SEARCH_MAX_MODELS)
925
results->return_code = MAX_MODELS_EXIT;
926
else if (rc == SEARCH_GO_MODELS || rc == SEARCH_GO_NO_MODELS)
927
results->return_code = Total_models==0 ? EXHAUSTED_EXIT : ALL_MODELS_EXIT;
928
else if (rc == SEARCH_MAX_TOTAL_SECONDS)
929
results->return_code = Total_models==0 ? MAX_SEC_NO_EXIT : MAX_SEC_YES_EXIT;
930
else if (rc == SEARCH_MAX_MEGS)
931
results->return_code = Total_models==0 ? MAX_MEGS_NO_EXIT : MAX_MEGS_YES_EXIT;
933
fatal_error("mace4: unknown return code");