Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

rete.c

Go to the documentation of this file.
00001 /*************************************************************************
00002  *
00003  *  file:  rete.c
00004  *
00005  * =======================================================================
00006  *  
00007  * All_wmes_in_rete is the header for a dll of all the wmes currently
00008  * in the rete.  (This is normally equal to all of WM, except at times
00009  * when WM changes have been buffered but not yet done.)  The wmes
00010  * are linked via their "rete_next" and "rete_prev" fields.
00011  * Num_wmes_in_rete counts how many wmes there are in the rete.
00012  *
00013  * Init_rete() initializes the rete.  It should be called at startup time.
00014  *
00015  * Any_assertions_or_retractions_ready() returns TRUE iff there are any
00016  * pending changes to the match set.  This is used to test for quiescence.
00017  * Get_next_assertion() retrieves a pending assertion (returning TRUE) or
00018  * returns FALSE is no more are available.  Get_next_retraction() is
00019  * similar.
00020  *
00021  * Add_production_to_rete() adds a given production, with a given LHS,
00022  * to the rete.  If "refracted_inst" is non-NIL, it should point to an
00023  * initial instantiation of the production.  This routine returns one
00024  * of NO_REFRACTED_INST, REFRACTED_INST_MATCHED, etc. (see below).
00025  * Excise_production_from_rete() removes the given production from the
00026  * rete, and enqueues all its existing instantiations as pending
00027  * retractions.
00028  *
00029  * Add_wme_to_rete() and remove_wme_from_rete() inform the rete of changes
00030  * to WM.
00031  *
00032  * P_node_to_conditions_and_nots() takes a p_node and (optionally) a
00033  * token/wme pair, and reconstructs the (optionally instantiated) LHS
00034  * for the production.  The firer uses this to build the instantiated
00035  * conditions; the printer uses it to reconstruct the LHS for printing.
00036  * Get_symbol_from_rete_loc() takes a token/wme pair and a location
00037  * specification (levels_up/field_num), examines the match (token/wme),
00038  * and returns the symbol at that location.  The firer uses this for
00039  * resolving references in RHS actions to variables bound on the LHS.
00040  *
00041  * Count_rete_tokens_for_production() returns a count of the number of 
00042  * tokens currently in use for the given production.
00043  *
00044  * Print_partial_match_information(), print_match_set(), and
00045  * the API function soar_ecPrintReteStatistics() do printouts for various
00046  * interface routines.
00047  *
00048  * Save_rete_net() and load_rete_net() are used for the fastsave/load
00049  * commands.  They save/load everything to/from the given (already open)
00050  * files.  They return TRUE if successful, FALSE if any error occurred.
00051  *
00052  * =======================================================================
00053  *
00054  * Copyright 1995-2003 Carnegie Mellon University,
00055  *                                                                               University of Michigan,
00056  *                                                                               University of Southern California/Information
00057  *                                                                               Sciences Institute. All rights reserved.
00058  *                                                                              
00059  * Redistribution and use in source and binary forms, with or without
00060  * modification, are permitted provided that the following conditions are met:
00061  *
00062  * 1.   Redistributions of source code must retain the above copyright notice,
00063  *              this list of conditions and the following disclaimer. 
00064  * 2.   Redistributions in binary form must reproduce the above copyright notice,
00065  *              this list of conditions and the following disclaimer in the documentation
00066  *              and/or other materials provided with the distribution. 
00067  *
00068  * THIS SOFTWARE IS PROVIDED BY THE SOAR CONSORTIUM ``AS IS'' AND ANY EXPRESS OR
00069  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00070  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00071  * EVENT SHALL THE SOAR CONSORTIUM  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00072  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00073  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00074  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00075  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00076  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00077  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00078  * The views and conclusions contained in the software and documentation are
00079  * those of the authors and should not be interpreted as representing official
00080  * policies, either expressed or implied, of Carnegie Mellon University, the
00081  * University of Michigan, the University of Southern California/Information
00082  * Sciences Institute, or the Soar consortium.
00083  * =======================================================================
00084  */
00085 
00086 /* ======================================================================
00087 
00088                       Rete Net Routines for Soar 6
00089    
00090    TABLE OF CONTENTS (each part is labeled "SECTION" in the code)
00091 
00092     1:  Rete Net Structures and Declarations
00093     2:  Match Set Changes
00094     3:  Alpha Portion of the Rete Net
00095     4:  Beta Net Initialization and Primitive Construction Routines
00096     5:  Beta Net Primitive Destruction Routines
00097     6:  Variable Bindings and Locations
00098     7:  Varnames and Node_Varnames
00099     8:  Building the Rete Net:  Condition-To-Node Converstion
00100     9:  Production Addition and Excising
00101    10:  Building Conditions (instantiated or not) from the Rete Net
00102    11:  Rete Test Evaluation Routines
00103    12:  Beta Node Interpreter Routines: Mem, Pos, and MP Nodes
00104    13:  Beta Node Interpreter Routines: Negative Nodes
00105    14:  Beta Node Interpreter Routines: CN and CN_PARTNER Nodes
00106    15:  Beta Node Interpreter Routines: Production Nodes
00107    16:  Beta Node Interpreter Routines: Tree-Based Removal
00108    17:  Fast, Compact Save/Reload of the Whole Rete Net
00109    18:  Statistics and User Interface Utilities
00110    19:  Rete Initialization
00111 
00112 ====================================================================== */
00113 
00114 #include "soarkernel.h"
00115 #include <ctype.h>
00116 #include "rete.h"
00117 
00118 /* ----------- basic functionality switches ----------- */
00119 
00120 /* Set to FALSE to preserve variable names in chunks (takes extra space) */
00121 bool discard_chunk_varnames = TRUE;
00122 
00123 /* ----------- debugging switches ----------- */
00124 
00125 /* Uncomment the following line to get pnode printouts */
00126 /* #define DEBUG_RETE_PNODES  */
00127 
00128 /* REW: begin 08.20.97 */
00129 /* For information on the Waterfall processing in rete.c */
00130 /* #define DEBUG_WATERFALL */
00131 /* REW: end   08.20.97 */
00132 
00133 /* ----------- statistics switches ----------- */
00134 
00135 /* Uncomment the following line to get statistics on token counts with and
00136    without sharing */
00137 /* #define TOKEN_SHARING_STATS */
00138 
00139 /* Uncomment the following line to gather statistics on null activations */
00140 /* #define NULL_ACTIVATION_STATS */
00141 
00142 /* Uncomment the following line to gather statistics on beta node sharing */
00143 /* #define SHARING_FACTORS */
00144 
00145 /* ----------- handle inter-switch dependencies ----------- */
00146 
00147 /* --- TOKEN_SHARING_STATS requires SHARING_FACTORS --- */
00148 #ifdef TOKEN_SHARING_STATS
00149 #ifndef SHARING_FACTORS
00150 #define SHARING_FACTORS
00151 #endif
00152 #endif
00153 
00154 /* --- Calculate DO_ACTIVATION_STATS_ON_REMOVALS --- */
00155 #ifdef NULL_ACTIVATION_STATS
00156 #ifndef DO_ACTIVATION_STATS_ON_REMOVALS
00157 #define DO_ACTIVATION_STATS_ON_REMOVALS
00158 #endif
00159 #endif
00160 
00161 /* **********************************************************************
00162 
00163    SECTION 1:  Rete Net Structures and Declarations
00164 
00165 ********************************************************************** */
00166 
00167 /* ----------------------------------------------------------------------
00168 
00169        Structures and Declarations:  Alpha Portion of the Rete Net
00170 
00171 ---------------------------------------------------------------------- */
00172 
00173 /* --- dll of all wmes currently in the rete:  this is needed to
00174        initialize newly created alpha memories --- */
00175 /* wme *all_wmes_in_rete; (moved to glob_vars.h) */
00176 
00177 /* --- structure of each alpha memory --- */
00178 typedef struct alpha_mem_struct {
00179     struct alpha_mem_struct *next_in_hash_table;        /* next mem in hash bucket */
00180     struct right_mem_struct *right_mems;        /* dll of right_mem structures */
00181     struct rete_node_struct *beta_nodes;        /* list of attached beta nodes */
00182     struct rete_node_struct *last_beta_node;    /* tail of above dll */
00183     Symbol *id;                 /* constants tested by this alpha mem */
00184     Symbol *attr;               /* (NIL if this alpha mem ignores that field) */
00185     Symbol *value;
00186     bool acceptable;            /* does it test for acceptable pref? */
00187     unsigned long am_id;        /* id for hashing */
00188     unsigned long reference_count;      /* number of beta nodes using this mem */
00189     unsigned long retesave_amindex;
00190 } alpha_mem;
00191 
00192 /* --- the entry for one WME in one alpha memory --- */
00193 typedef struct right_mem_struct {
00194     wme *w;                     /* the wme */
00195     alpha_mem *am;              /* the alpha memory */
00196     struct right_mem_struct *next_in_bucket, *prev_in_bucket;   /*hash bucket dll */
00197     struct right_mem_struct *next_in_am, *prev_in_am;   /*rm's in this amem */
00198     struct right_mem_struct *next_from_wme, *prev_from_wme;     /*tree-based remove */
00199 } right_mem;
00200 
00201 /* Note: right_mem's are stored in hash table current_agent(right_ht) */
00202 
00203 /* ----------------------------------------------------------------------
00204 
00205        Structures and Declarations:  Beta Portion of the Rete Net
00206 
00207 ---------------------------------------------------------------------- */
00208 
00209 /* --- types of tests found at beta nodes --- */
00210 #define CONSTANT_RELATIONAL_RETE_TEST 0x00
00211 #define VARIABLE_RELATIONAL_RETE_TEST 0x10
00212 #define DISJUNCTION_RETE_TEST         0x20
00213 #define ID_IS_GOAL_RETE_TEST          0x30
00214 #define ID_IS_IMPASSE_RETE_TEST       0x31
00215 #define test_is_constant_relational_test(x) (((x) & 0xF0)==0x00)
00216 #define test_is_variable_relational_test(x) (((x) & 0xF0)==0x10)
00217 
00218 /* --- for the last two (i.e., the relational tests), we add in one of
00219        the following, to specifiy the kind of relation --- */
00220 #define RELATIONAL_EQUAL_RETE_TEST            0x00
00221 #define RELATIONAL_NOT_EQUAL_RETE_TEST        0x01
00222 #define RELATIONAL_LESS_RETE_TEST             0x02
00223 #define RELATIONAL_GREATER_RETE_TEST          0x03
00224 #define RELATIONAL_LESS_OR_EQUAL_RETE_TEST    0x04
00225 #define RELATIONAL_GREATER_OR_EQUAL_RETE_TEST 0x05
00226 #define RELATIONAL_SAME_TYPE_RETE_TEST        0x06
00227 #define kind_of_relational_test(x) ((x) & 0x0F)
00228 #define test_is_not_equal_test(x) (((x)==0x01) || ((x)==0x11))
00229 
00230 /* --- tells where to find a variable --- */
00231 typedef unsigned short rete_node_level;
00232 
00233 typedef struct var_location_struct {
00234     rete_node_level levels_up;  /* 0=current node's alphamem, 1=parent's, etc. */
00235     byte field_num;             /* 0=id, 1=attr, 2=value */
00236 } var_location;
00237 
00238 /* define an equality predicate for var_location structures */
00239 #define var_locations_equal(v1,v2) \
00240   ( ((v1).levels_up==(v2).levels_up) && ((v1).field_num==(v2).field_num) )
00241 
00242 /* --- extract field (id/attr/value) from wme --- */
00243 /* WARNING: this relies on the id/attr/value fields being consecutive in
00244    the wme structure (defined in soarkernel.h) */
00245 #define field_from_wme(wme,field_num) \
00246   ( (&((wme)->id))[(field_num)] )
00247 
00248 /* --- gives data for a test that must be applied at a node --- */
00249 typedef struct rete_test_struct {
00250     byte right_field_num;       /* field (0, 1, or 2) from wme */
00251     byte type;                  /* test type (ID_IS_GOAL_RETE_TEST, etc.) */
00252     union rete_test_data_union {
00253         var_location variable_referent; /* for relational tests to a variable */
00254         Symbol *constant_referent;      /* for relational tests to a constant */
00255         list *disjunction_list; /* list of symbols in disjunction test */
00256     } data;
00257     struct rete_test_struct *next;      /* next in list of tests at the node */
00258 } rete_test;
00259 
00260 /* --- types and structure of beta nodes --- */
00261 /*   key:  bit 0 --> hashed                  */
00262 /*         bit 1 --> memory                  */
00263 /*         bit 2 --> positive join           */
00264 /*         bit 3 --> negative join           */
00265 /*         bit 4 --> split from beta memory  */
00266 /*         bit 6 --> various special types   */
00267 
00268 /* Warning: If you change any of these or add ones, be sure to update the
00269    bit-twiddling macros just below */
00270 #define UNHASHED_MEMORY_BNODE   0x02
00271 #define MEMORY_BNODE            0x03
00272 #define UNHASHED_MP_BNODE       0x06
00273 #define MP_BNODE                0x07
00274 #define UNHASHED_POSITIVE_BNODE 0x14
00275 #define POSITIVE_BNODE          0x15
00276 #define UNHASHED_NEGATIVE_BNODE 0x08
00277 #define NEGATIVE_BNODE          0x09
00278 #define DUMMY_TOP_BNODE         0x40
00279 #define DUMMY_MATCHES_BNODE     0x41
00280 #define CN_BNODE                0x42
00281 #define CN_PARTNER_BNODE        0x43
00282 #define P_BNODE                 0x44
00283 
00284 #define bnode_is_hashed(x)   ((x) & 0x01)
00285 #define bnode_is_memory(x)   ((x) & 0x02)
00286 #define bnode_is_positive(x) ((x) & 0x04)
00287 #define bnode_is_negative(x) ((x) & 0x08)
00288 #define bnode_is_posneg(x)   ((x) & 0x0C)
00289 #define bnode_is_bottom_of_split_mp(x) ((x) & 0x10)
00290 #define real_parent_node(x) ( bnode_is_bottom_of_split_mp((x)->node_type) ? (x)->parent->parent : (x)->parent )
00291 
00292 char *bnode_type_names[256];
00293 
00294 /* --- data for positive nodes only --- */
00295 typedef struct pos_node_data_struct {
00296     /* --- dll of left-linked pos nodes from the parent beta memory --- */
00297     struct rete_node_struct *next_from_beta_mem, *prev_from_beta_mem;
00298 } pos_node_data;
00299 
00300 /* --- data for both positive and negative nodes --- */
00301 typedef struct posneg_node_data_struct {
00302     rete_test *other_tests;     /* tests other than the hashed test */
00303     alpha_mem *alpha_mem;       /* the alpha memory this node uses */
00304     struct rete_node_struct *next_from_alpha_mem;       /* dll of nodes using that */
00305     struct rete_node_struct *prev_from_alpha_mem;       /*   ... alpha memory */
00306     struct rete_node_struct *nearest_ancestor_with_same_am;
00307 } posneg_node_data;
00308 
00309 /* --- data for beta memory nodes only --- */
00310 typedef struct beta_memory_node_data_struct {
00311     /* --- first pos node child that is left-linked --- */
00312     struct rete_node_struct *first_linked_child;
00313 } beta_memory_node_data;
00314 
00315 /* --- data for cn and cn_partner nodes only --- */
00316 typedef struct cn_node_data_struct {
00317     struct rete_node_struct *partner;   /* cn, cn_partner point to each other */
00318 } cn_node_data;
00319 
00320 /* --- data for production nodes only --- */
00321 typedef struct p_node_data_struct {
00322     struct production_struct *prod;     /* the production */
00323     struct node_varnames_struct *parents_nvn;   /* records variable names */
00324     struct ms_change_struct *tentative_assertions;      /* pending MS changes */
00325     struct ms_change_struct *tentative_retractions;
00326 } p_node_data;
00327 
00328 #define O_LIST 0                /* moved here from soarkernel.h.  only used in rete.c */
00329 #define I_LIST 1                /*   values for prod->OPERAND_which_assert_list */
00330 
00331 /* --- data for all except positive nodes --- */
00332 typedef struct non_pos_node_data_struct {
00333     struct token_struct *tokens;        /* dll of tokens at this node */
00334     unsigned is_left_unlinked:1;        /* used on mp nodes only */
00335 } non_pos_node_data;
00336 
00337 /* --- structure of a rete beta node --- */
00338 typedef struct rete_node_struct {
00339     byte node_type;             /* tells what kind of node this is */
00340 
00341     /* -- used only on hashed nodes -- */
00342     /* field_num: 0=id, 1=attr, 2=value */
00343     byte left_hash_loc_field_num;
00344     /* left_hash_loc_levels_up: 0=current node's alphamem, 1=parent's, etc. */
00345     rete_node_level left_hash_loc_levels_up;
00346     /* node_id: used for hash function */
00347     unsigned long node_id;
00348 
00349 #ifdef SHARING_FACTORS
00350     unsigned long sharing_factor;
00351 #endif
00352 
00353     struct rete_node_struct *parent;    /* points to parent node */
00354     struct rete_node_struct *first_child;       /* used for dll of all children, */
00355     struct rete_node_struct *next_sibling;      /*   regardless of unlinking status */
00356     union rete_node_a_union {
00357         pos_node_data pos;      /* for pos. nodes */
00358         non_pos_node_data np;   /* for all other nodes */
00359     } a;
00360     union rete_node_b_union {
00361         posneg_node_data posneg;        /* for pos, neg, mp nodes */
00362         beta_memory_node_data mem;      /* for beta memory nodes */
00363         cn_node_data cn;        /* for cn, cn_partner nodes */
00364         p_node_data p;          /* for p nodes */
00365     } b;
00366 } rete_node;
00367 
00368 /* ----------------------------------------------------------------------
00369 
00370              Structures and Declarations:  Right Unlinking
00371 
00372 ---------------------------------------------------------------------- */
00373 
00374 /* Note: a node is right unlinked iff the low-order bit of
00375    node->b.posneg.next_from_alpha_mem is 1 */
00376 
00377 #define node_is_right_unlinked(node) \
00378   (((unsigned long)((node)->b.posneg.next_from_alpha_mem)) & 1)
00379 
00380 #define mark_node_as_right_unlinked(node) { \
00381   (node)->b.posneg.next_from_alpha_mem = ((void *)1); }
00382 
00383 #define relink_to_right_mem(node) { \
00384   rete_node *rtrm_ancestor, *rtrm_prev; \
00385   /* find first ancestor that's linked */ \
00386   rtrm_ancestor = (node)->b.posneg.nearest_ancestor_with_same_am; \
00387   while (rtrm_ancestor && node_is_right_unlinked(rtrm_ancestor)) \
00388     rtrm_ancestor = rtrm_ancestor->b.posneg.nearest_ancestor_with_same_am; \
00389   if (rtrm_ancestor) { \
00390     /* insert just before that ancestor */ \
00391     rtrm_prev = rtrm_ancestor->b.posneg.prev_from_alpha_mem; \
00392     (node)->b.posneg.next_from_alpha_mem = rtrm_ancestor; \
00393     (node)->b.posneg.prev_from_alpha_mem = rtrm_prev; \
00394     rtrm_ancestor->b.posneg.prev_from_alpha_mem = (node); \
00395     if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node); \
00396     else (node)->b.posneg.alpha_mem->beta_nodes = (node); \
00397   } else { \
00398     /* no such ancestor, insert at tail of list */ \
00399     rtrm_prev = (node)->b.posneg.alpha_mem->last_beta_node; \
00400     (node)->b.posneg.next_from_alpha_mem = NIL; \
00401     (node)->b.posneg.prev_from_alpha_mem = rtrm_prev; \
00402     (node)->b.posneg.alpha_mem->last_beta_node = (node); \
00403     if (rtrm_prev) rtrm_prev->b.posneg.next_from_alpha_mem = (node); \
00404     else (node)->b.posneg.alpha_mem->beta_nodes = (node); \
00405   } }
00406 
00407 #define unlink_from_right_mem(node) { \
00408   if ((node)->b.posneg.next_from_alpha_mem == NIL) \
00409     (node)->b.posneg.alpha_mem->last_beta_node = \
00410       (node)->b.posneg.prev_from_alpha_mem; \
00411   remove_from_dll ((node)->b.posneg.alpha_mem->beta_nodes, (node), \
00412                    b.posneg.next_from_alpha_mem, \
00413                    b.posneg.prev_from_alpha_mem); \
00414   mark_node_as_right_unlinked (node); }
00415 
00416 /* ----------------------------------------------------------------------
00417 
00418              Structures and Declarations:  Left Unlinking
00419 
00420 ---------------------------------------------------------------------- */
00421 
00422 /* Note: an unmerged positive node is left unlinked iff the low-order bit of
00423    node->a.pos.next_from_beta_mem is 1 */
00424 
00425 #define node_is_left_unlinked(node) \
00426   (((unsigned long)((node)->a.pos.next_from_beta_mem)) & 1)
00427 
00428 #define mark_node_as_left_unlinked(node) { \
00429   (node)->a.pos.next_from_beta_mem = ((void *)1); }
00430 
00431 #define relink_to_left_mem(node) { \
00432   insert_at_head_of_dll ((node)->parent->b.mem.first_linked_child, (node), \
00433                          a.pos.next_from_beta_mem, \
00434                          a.pos.prev_from_beta_mem); }
00435 
00436 #define unlink_from_left_mem(node) { \
00437   remove_from_dll ((node)->parent->b.mem.first_linked_child, (node), \
00438                    a.pos.next_from_beta_mem, \
00439                    a.pos.prev_from_beta_mem); \
00440   mark_node_as_left_unlinked(node); }
00441 
00442 /* Note: for merged nodes, we still mark them as left-unlinked, just for
00443    uniformity.  This probably makes little difference in efficiency. */
00444 
00445 #define make_mp_bnode_left_unlinked(node) {(node)->a.np.is_left_unlinked = 1;}
00446 #define make_mp_bnode_left_linked(node) {(node)->a.np.is_left_unlinked = 0;}
00447 #define mp_bnode_is_left_unlinked(node) ((node)->a.np.is_left_unlinked)
00448 
00449 /* ----------------------------------------------------------------------
00450 
00451                  Structures and Declarations:  Tokens
00452 
00453 ---------------------------------------------------------------------- */
00454 /*
00455  * Token structure is moved into rete.h
00456  */
00457 
00458 #define new_left_token(new,current_node,parent_tok,parent_wme) { \
00459   (new)->node = (current_node); \
00460   insert_at_head_of_dll ((current_node)->a.np.tokens, (new), \
00461                          next_of_node, prev_of_node); \
00462   (new)->first_child = NIL; \
00463   (new)->parent = (parent_tok); \
00464   insert_at_head_of_dll ((parent_tok)->first_child, (new), \
00465                          next_sibling, prev_sibling); \
00466   (new)->w = (parent_wme); \
00467   if (parent_wme) insert_at_head_of_dll ((parent_wme)->tokens, (new), \
00468                                          next_from_wme, prev_from_wme); }
00469 
00470 /* Note: (most) tokens are stored in hash table current_agent(left_ht) */
00471 
00472 /* ----------------------------------------------------------------------
00473 
00474             Structures and Declarations:  Memory Hash Tables
00475 
00476    Tokens and alpha memory entries (right memory's) as stored in two
00477    global hash tables.  Unlike most hash tables in Soar, these two tables
00478    are not dynamically resized -- their size is fixed at compile-time.
00479 ---------------------------------------------------------------------- */
00480 
00481 /* --- Hash table sizes (actual sizes are powers of 2) --- */
00482 #ifdef _WINDOWS
00483 #define LOG2_LEFT_HT_SIZE 13
00484 #define LOG2_RIGHT_HT_SIZE 13
00485 #else
00486 #define LOG2_LEFT_HT_SIZE 14
00487 #define LOG2_RIGHT_HT_SIZE 14
00488 #endif
00489 
00490 #define LEFT_HT_SIZE (((long) 1) << LOG2_LEFT_HT_SIZE)
00491 #define RIGHT_HT_SIZE (((long) 1) << LOG2_RIGHT_HT_SIZE)
00492 
00493 #define LEFT_HT_MASK (LEFT_HT_SIZE - 1)
00494 #define RIGHT_HT_MASK (RIGHT_HT_SIZE - 1)
00495 
00496 /* --- Given the hash value (hv), get contents of bucket header cell --- */
00497 #define left_ht_bucket(hv) \
00498   (* ( ((token **) current_agent(left_ht)) + ((hv) & LEFT_HT_MASK)))
00499 #define right_ht_bucket(hv) \
00500   (* ( ((right_mem **) current_agent(right_ht)) + ((hv) & RIGHT_HT_MASK)))
00501 
00502 #define insert_token_into_left_ht(tok,hv) { \
00503   token **header_zy37; \
00504   header_zy37 = ((token **) current_agent(left_ht)) + ((hv) & LEFT_HT_MASK); \
00505   insert_at_head_of_dll (*header_zy37, (tok), \
00506                          a.ht.next_in_bucket, a.ht.prev_in_bucket); }
00507 
00508 #define remove_token_from_left_ht(tok,hv) { \
00509   fast_remove_from_dll (left_ht_bucket(hv), tok, token, \
00510                         a.ht.next_in_bucket, a.ht.prev_in_bucket); }
00511 
00512 /* ----------------------------------------------------------------------
00513 
00514        Structures and Declarations:  Beta Net Interpreter Routines
00515 
00516 ---------------------------------------------------------------------- */
00517 
00518 void (*(left_addition_routines[256])) (rete_node * node, token * tok, wme * w);
00519 void (*(right_addition_routines[256])) (rete_node * node, wme * w);
00520 void remove_token_and_subtree(token * tok);
00521 
00522 /* ----------------------------------------------------------------------
00523 
00524              Structures and Declarations:  Debugging Stuff
00525 
00526    These get invoked at the entry and exit points of all node activation 
00527    procedures.  Good place to put debugging checks.
00528 ---------------------------------------------------------------------- */
00529 
00530 #define activation_entry_sanity_check() {}
00531 #define activation_exit_sanity_check() {}
00532 
00533 /* ----------------------------------------------------------------------
00534 
00535          Structures and Declarations:  Null Activation Statistics
00536 
00537    Counts the number of null and non-null left activations.  Note that 
00538    this only tallies activations of join nodes for positive conditions;
00539    negative nodes and CN stuff is ignored.
00540 ---------------------------------------------------------------------- */
00541 
00542 /* --- To avoid double-counting right activations of nodes when adding
00543    productions and using update_node_with_matches_from_above(),
00544    we set this variable to indicate a particular node whose activations
00545    should not be counted. --- */
00546 rete_node *node_to_ignore_for_activation_stats = NIL;
00547 
00548 #ifdef NULL_ACTIVATION_STATS
00549 
00550 void null_activation_stats_for_right_activation(rete_node * node)
00551 {
00552     if (node == node_to_ignore_for_activation_stats)
00553         return;
00554     switch (node->node_type) {
00555     case POSITIVE_BNODE:
00556     case UNHASHED_POSITIVE_BNODE:
00557         current_agent(num_right_activations)++;
00558         if (!node->parent->a.np.tokens)
00559             current_agent(num_null_right_activations)++;
00560         break;
00561     case MP_BNODE:
00562     case UNHASHED_MP_BNODE:
00563         current_agent(num_right_activations)++;
00564         if (!node->a.np.tokens)
00565             current_agent(num_null_right_activations)++;
00566         break;
00567     }
00568 }
00569 
00570 void null_activation_stats_for_left_activation(rete_node * node)
00571 {
00572     switch (node->node_type) {
00573     case POSITIVE_BNODE:
00574     case UNHASHED_POSITIVE_BNODE:
00575         current_agent(num_left_activations)++;
00576         if (node->b.posneg.alpha_mem->right_mems == NIL)
00577             current_agent(num_null_left_activations)++;
00578         break;
00579     case MP_BNODE:
00580     case UNHASHED_MP_BNODE:
00581         if (mp_bnode_is_left_unlinked(node))
00582             return;
00583         current_agent(num_left_activations)++;
00584         if (node->b.posneg.alpha_mem->right_mems == NIL)
00585             current_agent(num_null_left_activations)++;
00586         break;
00587     }
00588 }
00589 
00590 void print_null_activation_stats()
00591 {
00592     print("\nActivations: %lu right (%lu null), %lu left (%lu null)\n",
00593           current_agent(num_right_activations),
00594           current_agent(num_null_right_activations),
00595           current_agent(num_left_activations), current_agent(num_null_left_activations));
00596 }
00597 
00598 #else
00599 
00600 #define null_activation_stats_for_right_activation(node) {}
00601 #define null_activation_stats_for_left_activation(node) {}
00602 #define print_null_activation_stats() {}
00603 
00604 #endif
00605 
00606 /* ----------------------------------------------------------------------
00607 
00608              Structures and Declarations:  Sharing Factors
00609 
00610    Sharing factors are computed/updated using two simple rules:
00611      (1)  Any time we add a new production to the net, when we get all 
00612      done and have created the p-node, etc., we increment the sharing 
00613      factor on every node the production uses.
00614      (2) Any time we make a brand new node, we initialize its sharing 
00615      factor to 0.  (This will get incremented shortly thereafter, due
00616      to rule #1.)
00617 
00618    Note that there are fancy ways to compute/update sharing factors, 
00619    not requiring extra scanning-up-the-net all the time as rule 1 does.
00620    I went with the ablve way to keep the code small and simple.
00621 ---------------------------------------------------------------------- */
00622 
00623 #ifdef SHARING_FACTORS
00624 
00625 #define init_sharing_stats_for_new_node(node) { (node)->sharing_factor = 0; }
00626 
00627 #define set_sharing_factor(node,sf) { \
00628   long ssf_237; \
00629   ssf_237 = (sf) - ((node)->sharing_factor); \
00630   (node)->sharing_factor = (sf); \
00631   current_agent(rete_node_counts_if_no_sharing)[(node)->node_type]+=ssf_237; }
00632 
00633 /* Scans from "node" up to the top node, adds "delta" to sharing factors. */
00634 void adjust_sharing_factors_from_here_to_top(rete_node * node, int delta)
00635 {
00636     while (node != NIL) {
00637         current_agent(rete_node_counts_if_no_sharing)[node->node_type] += delta;
00638         node->sharing_factor += delta;
00639         if (node->node_type == CN_BNODE)
00640             node = node->b.cn.partner;
00641         else
00642             node = node->parent;
00643     }
00644 }
00645 
00646 #else
00647 
00648 #define init_sharing_stats_for_new_node(node) {}
00649 #define set_sharing_factor(node,sf) {}
00650 #define adjust_sharing_factors_from_here_to_top(node,delta) {}
00651 
00652 #endif
00653 
00654 /* ----------------------------------------------------------------------
00655 
00656            Structures and Declarations:  (Extra) Rete Statistics
00657 
00658 ---------------------------------------------------------------------- */
00659 
00660 #ifdef TOKEN_SHARING_STATS
00661 
00662 /* gets real sharing factor -- converts "0" (temporary sharing factor on
00663    newly created nodes while we're adding a production to the net) to 1 */
00664 #define real_sharing_factor(node) \
00665   ((node)->sharing_factor ? (node)->sharing_factor : 1)
00666 
00667 #define token_added(node) { \
00668   current_agent(token_additions)++; \
00669   current_agent(token_additions_without_sharing) += real_sharing_factor(node);}
00670 
00671 #else
00672 
00673 #define token_added(node) {}
00674 
00675 #endif
00676 
00677 /* --- Invoked on every right activation; add=TRUE means right addition --- */
00678 /* NOT invoked on removals unless DO_ACTIVATION_STATS_ON_REMOVALS is set */
00679 #define right_node_activation(node,add) { \
00680   null_activation_stats_for_right_activation(node); }
00681 
00682 /* --- Invoked on every left activation; add=TRUE means left addition --- */
00683 /* NOT invoked on removals unless DO_ACTIVATION_STATS_ON_REMOVALS is set */
00684 #define left_node_activation(node,add) { \
00685   null_activation_stats_for_left_activation(node); }
00686 
00687 /* --- The following two macros are used when creating/destroying nodes --- */
00688 
00689 #define init_new_rete_node_with_type(node,type) { \
00690   (node)->node_type = (type); \
00691   current_agent(rete_node_counts)[(type)]++; \
00692   init_sharing_stats_for_new_node(node); }
00693 
00694 #define update_stats_for_destroying_node(node) { \
00695   set_sharing_factor(node,0); \
00696   current_agent(rete_node_counts)[(node)->node_type]--; }
00697 
00698 /* **********************************************************************
00699 
00700    SECTION 2:  Match Set Changes
00701 
00702    Match set changes (i.e., additions or deletions of complete production
00703    matches) are stored on two lists.  There is one global list of all
00704    pending ms changes.  Each ms change is also stored on a local list
00705    for its p-node, containing just the ms changes for that production.
00706    The second list is needed for when a match is only temporarily
00707    present during one elaboration cycle -- e.g., we make one change to
00708    working memory which triggers an addition/retraction, but then make
00709    another change to working memory which reverses the previous 
00710    addition/retraction.  After the second change, the p-node gets activated
00711    and has to quickly find the thing being reversed.  The small local
00712    list makes this possible.
00713 
00714    EXTERNAL INTERFACE:
00715    Any_assertions_or_retractions_ready() returns TRUE iff there are any
00716    pending changes to the match set.  This is used to test for quiescence.
00717    Get_next_assertion() retrieves a pending assertion (returning TRUE) or
00718    returns FALSE is no more are available.  Get_next_retraction() is
00719    similar.
00720 ********************************************************************** */
00721 
00722 /* REW: begin 08.20.97 */
00723 
00724 Symbol *find_goal_for_match_set_change_assertion(ms_change * msc)
00725 {
00726 
00727     wme *lowest_goal_wme;
00728     goal_stack_level lowest_level_so_far;
00729     token *tok;
00730 
00731 #ifdef DEBUG_WATERFALL
00732     print_with_symbols("\nMatch goal for assertion: %y", msc->p_node->b.p.prod->name);
00733 #endif
00734 
00735     lowest_goal_wme = NIL;
00736     lowest_level_so_far = -1;
00737 
00738     if (msc->w) {
00739         if (msc->w->id->id.isa_goal == TRUE) {
00740             lowest_goal_wme = msc->w;
00741             lowest_level_so_far = msc->w->id->id.level;
00742         }
00743     }
00744 
00745     for (tok = msc->tok; tok != current_agent(dummy_top_token); tok = tok->parent) {
00746         if (tok->w != NIL) {
00747             /* print_wme(tok->w); */
00748             if (tok->w->id->id.isa_goal == TRUE) {
00749 
00750                 if (lowest_goal_wme == NIL)
00751                     lowest_goal_wme = tok->w;
00752 
00753                 else {
00754                     if (tok->w->id->id.level > lowest_goal_wme->id->id.level)
00755                         lowest_goal_wme = tok->w;
00756                 }
00757             }
00758 
00759         }
00760     }
00761 
00762     if (lowest_goal_wme) {
00763 #ifdef DEBUG_WATERFALL
00764         print_with_symbols(" is [%y]", lowest_goal_wme->id);
00765 #endif
00766         return lowest_goal_wme->id;
00767     }
00768     {
00769         char msg[MESSAGE_SIZE];
00770         print_with_symbols("\nError: Did not find goal for ms_change assertion: %y\n", msc->p_node->b.p.prod->name);
00771         snprintf(msg, MESSAGE_SIZE, "\nError: Did not find goal for ms_change assertion: %s\n",
00772                  symbol_to_string(msc->p_node->b.p.prod->name, TRUE, NIL, 0));
00773         msg[MESSAGE_SIZE - 1] = 0;      /* snprintf doesn't set last char to null if output is truncated */
00774         abort_with_fatal_error(msg);
00775     }
00776     return 0;
00777 }
00778 
00779 Symbol *find_goal_for_match_set_change_retraction(ms_change * msc)
00780 {
00781 
00782 #ifdef DEBUG_WATERFALL
00783     print_with_symbols("\nMatch goal level for retraction: %y", msc->inst->prod->name);
00784 #endif
00785 
00786     if (msc->inst->match_goal) {
00787         /* If there is a goal, just return the goal */
00788 #ifdef DEBUG_WATERFALL
00789         print_with_symbols(" is [%y]", msc->inst->match_goal);
00790 #endif
00791         return msc->inst->match_goal;
00792 
00793     } else {
00794 
00795 #ifdef DEBUG_WATERFALL
00796         print(" is NIL (nil goal retraction)");
00797 #endif
00798         return NIL;
00799 
00800     }
00801 }
00802 
00803 void print_assertion(ms_change * msc)
00804 {
00805 
00806     if (msc->p_node)
00807         print_with_symbols("\nAssertion: %y", msc->p_node->b.p.prod->name);
00808     else
00809         print("\nAssertion exists but has no p_node");
00810 }
00811 
00812 void print_retraction(ms_change * msc)
00813 {
00814 
00815     if (msc->p_node)
00816         print_with_symbols("\nRetraction: %y", msc->p_node->b.p.prod->name);
00817     else
00818         print("\nRetraction exists but has no p_node");
00819 }
00820 
00821 /* REW: end   08.20.97 */
00822 
00823 bool any_assertions_or_retractions_ready(void)
00824 {
00825 
00826     Symbol *goal;
00827 
00828     /* REW: begin 08.20.97 */
00829 #ifndef SOAR_8_ONLY
00830     if (current_agent(operand2_mode) == TRUE) {
00831 #endif
00832 
00833         /* Determining if assertions or retractions are ready require looping over
00834            all goals in Waterfall/Operand2 */
00835 
00836         if (current_agent(nil_goal_retractions))
00837             return TRUE;
00838 
00839         /* Loop from bottom to top because we expect activity at
00840            the bottom usually */
00841 
00842         for (goal = current_agent(bottom_goal); goal; goal = goal->id.higher_goal) {
00843             /* if there are any assertions or retrctions for this goal,
00844                return TRUE */
00845             if (goal->id.ms_o_assertions || goal->id.ms_i_assertions || goal->id.ms_retractions)
00846                 return TRUE;
00847         }
00848 
00849         /* if there are no nil_goal_retractions and no assertions or retractions
00850            for any  goal then return FALSE -- there aren't any productions
00851            ready to fire or retract */
00852 
00853         return FALSE;
00854 #ifndef SOAR_8_ONLY
00855     }
00856 
00857 /* REW: end   08.20.97 */
00858 
00859     else
00860 
00861         return (bool) (current_agent(ms_assertions) || current_agent(ms_retractions));
00862 #endif
00863 
00864 }
00865 
00866 /* RCHONG: begin 10.11 */
00867 
00868 bool any_i_assertions_or_retractions_ready(void)
00869 {
00870     return (bool) (current_agent(ms_i_assertions) || current_agent(ms_retractions));
00871 }
00872 
00873 /* RCHONG: end 10.11 */
00874 
00875 bool get_next_assertion(production ** prod, struct token_struct ** tok, wme ** w)
00876 {
00877     ms_change *msc;
00878 
00879     msc = NIL;                  /* unneeded, but avoids gcc -Wall warn */
00880 
00881 /* REW: begin 09.15.96 */
00882 #ifndef SOAR_8_ONLY
00883     if (current_agent(operand2_mode) == TRUE) {
00884 #endif
00885 
00886         /* REW: begin 08.20.97 */
00887 
00888         /* In Waterfall, we return only assertions that match in the
00889            currently active goal */
00890 
00891         if (current_agent(active_goal)) {       /* Just do asserts for current goal */
00892             if (current_agent(FIRING_TYPE) == PE_PRODS) {
00893                 if (!current_agent(active_goal)->id.ms_o_assertions)
00894                     return FALSE;
00895 
00896                 msc = current_agent(active_goal)->id.ms_o_assertions;
00897                 remove_from_dll(current_agent(ms_o_assertions), msc, next, prev);
00898                 remove_from_dll(current_agent(active_goal)->id.ms_o_assertions, msc, next_in_level, prev_in_level);
00899 
00900             } else {
00901                 /* IE PRODS */
00902                 if (!current_agent(active_goal)->id.ms_i_assertions)
00903                     return FALSE;
00904 
00905                 msc = current_agent(active_goal)->id.ms_i_assertions;
00906                 remove_from_dll(current_agent(ms_i_assertions), msc, next, prev);
00907                 remove_from_dll(current_agent(active_goal)->id.ms_i_assertions, msc, next_in_level, prev_in_level);
00908             }
00909 
00910         } else {
00911 
00912             /* REW: 2003-11-05 */
00913             /* If there is not an active goal, then there should not be any
00914                assertions during apply.  If there are, then we generate and error message
00915                and abort.  In propose, there must not be any i_asssertions.  There
00916                may be o_assertions that are ready to fire, but these are ignored in
00917                propose. */
00918 
00919             if (((current_agent(FIRING_TYPE) == PE_PRODS) && current_agent(ms_o_assertions))
00920                 || ((current_agent(FIRING_TYPE) == IE_PRODS) && current_agent(ms_i_assertions))) {
00921 
00922                 char msg[MESSAGE_SIZE];
00923                 strncpy(msg, "\nrete.c: Error: No active goal, but assertions are on the assertion list.",
00924                         MESSAGE_SIZE);
00925                 msg[MESSAGE_SIZE - 1] = 0;
00926                 abort_with_fatal_error(msg);
00927 
00928             }
00929 
00930             return FALSE;       /* if we are in an initiazation and there are no
00931                                    assertions, just retrurn FALSE to terminate
00932                                    the procedure. */
00933 
00934         }
00935         /* REW: end   08.20.97 */
00936 #ifndef SOAR_8_ONLY
00937     }
00938     /* REW: end   09.15.96 */
00939 
00940     else {
00941         if (!current_agent(ms_assertions))
00942             return FALSE;
00943         msc = current_agent(ms_assertions);
00944 
00945         remove_from_dll(current_agent(ms_assertions), msc, next, prev);
00946     }
00947 #endif
00948 
00949     remove_from_dll(msc->p_node->b.p.tentative_assertions, msc, next_of_node, prev_of_node);
00950     *prod = msc->p_node->b.p.prod;
00951     *tok = msc->tok;
00952     *w = msc->w;
00953     free_with_pool(&current_agent(ms_change_pool), msc);
00954     return TRUE;
00955 }
00956 
00957 bool get_next_retraction(instantiation ** inst)
00958 {
00959     ms_change *msc;
00960 
00961     /* REW: begin 08.20.97 */
00962 #ifndef SOAR_8_ONLY
00963     if (!current_agent(operand2_mode)) {
00964         /* for non-Operand2 modes, just remove the head of the retractions list */
00965         /* REW: end   08.20.97 */
00966         if (!current_agent(ms_retractions))
00967             return FALSE;
00968         msc = current_agent(ms_retractions);
00969         remove_from_dll(current_agent(ms_retractions), msc, next, prev);
00970         if (msc->p_node)
00971             remove_from_dll(msc->p_node->b.p.tentative_retractions, msc, next_of_node, prev_of_node);
00972         *inst = msc->inst;
00973         free_with_pool(&current_agent(ms_change_pool), msc);
00974         return TRUE;
00975 
00976         /* REW: begin 08.20.97 */
00977     } else {
00978 #endif
00979         /* just do the retractions for the current level */
00980 
00981         /* initialization condition (2.107/2.111) */
00982         if (current_agent(active_level) == 0)
00983             return FALSE;
00984 
00985         if (!current_agent(active_goal)->id.ms_retractions)
00986             return FALSE;
00987 
00988         msc = current_agent(active_goal)->id.ms_retractions;
00989 
00990         /* remove from the complete retraction list */
00991         remove_from_dll(current_agent(ms_retractions), msc, next, prev);
00992         /* and remove from the Waterfall-specific list */
00993         remove_from_dll(current_agent(active_goal)->id.ms_retractions, msc, next_in_level, prev_in_level);
00994         if (msc->p_node)
00995             remove_from_dll(msc->p_node->b.p.tentative_retractions, msc, next_of_node, prev_of_node);
00996         *inst = msc->inst;
00997         free_with_pool(&current_agent(ms_change_pool), msc);
00998         return TRUE;
00999 
01000 #ifndef SOAR_8_ONLY
01001     }
01002 #endif
01003     /* REW: end   08.20.97 */
01004 }
01005 
01006 /* REW: begin 08.20.97 */
01007 
01008 /* Retract an instantiation on the nil goal list.  If there are no
01009    retractions on the nil goal retraction list, return FALSE.  This
01010    procedure is only called in Operand2 mode, so there is no need for
01011    any checks for Operand2-specific processing. */
01012 
01013 bool get_next_nil_goal_retraction(instantiation ** inst)
01014 {
01015     ms_change *msc;
01016 
01017     if (!current_agent(nil_goal_retractions))
01018         return FALSE;
01019     msc = current_agent(nil_goal_retractions);
01020 
01021     /* Remove this retraction from the NIL goal list */
01022     remove_from_dll(current_agent(nil_goal_retractions), msc, next_in_level, prev_in_level);
01023 
01024     /* next and prev set and used in Operand2 exactly as used in Soar 7 --
01025        so we have to make sure and delete this retraction from the regular
01026        list */
01027     remove_from_dll(current_agent(ms_retractions), msc, next, prev);
01028 
01029     if (msc->p_node) {
01030         remove_from_dll(msc->p_node->b.p.tentative_retractions, msc, next_of_node, prev_of_node);
01031     }
01032     *inst = msc->inst;
01033     free_with_pool(&current_agent(ms_change_pool), msc);
01034     return TRUE;
01035 
01036 }
01037 
01038 /* REW: end   08.20.97 */
01039 
01040 /* **********************************************************************
01041 
01042    SECTION 3:  Alpha Portion of the Rete Net
01043 
01044    The alpha (top) part of the rete net consists of the alpha memories.
01045    Each of these memories is stored in one of 16 hash tables, depending
01046    on which fields it tests:
01047 
01048       bit 0 (value 1) indicates it tests the id slot
01049       bit 1 (value 2) indicates it tests the attr slot
01050       bit 2 (value 4) indicates it tests the value slot
01051       bit 3 (value 8) indicates it tests for an acceptable preference
01052 
01053    The hash tables are dynamically resized hash tables.
01054 
01055    Find_or_make_alpha_mem() either shares an existing alpha memory or 
01056    creates a new one, adjusting reference counts accordingly.
01057    Remove_ref_to_alpha_mem() decrements the reference count and 
01058    deallocates the alpha memory if it's no longer used.
01059 
01060    EXTERNAL INTERFACE:
01061    Add_wme_to_rete() and remove_wme_from_rete() do just what they say.
01062 ********************************************************************** */
01063 
01064 /* --- Returns TRUE iff the given wme goes into the given alpha memory --- */
01065 #define wme_matches_alpha_mem(w,am) ( \
01066   (((am)->id==NIL) || ((am)->id==(w)->id)) && \
01067   (((am)->attr==NIL) || ((am)->attr==(w)->attr)) && \
01068   (((am)->value==NIL) || ((am)->value==(w)->value)) && \
01069   ((am)->acceptable==(w)->acceptable))
01070 
01071 /* --- Returns hash value for the given id/attr/value symbols --- */
01072 #define alpha_hash_value(i,a,v,num_bits) \
01073  ( ( ((i) ? ((Symbol *)(i))->common.hash_id : 0) ^ \
01074      ((a) ? ((Symbol *)(a))->common.hash_id : 0) ^ \
01075      ((v) ? ((Symbol *)(v))->common.hash_id : 0) ) & \
01076    masks_for_n_low_order_bits[(num_bits)] )
01077 
01078 /* --- rehash funciton for resizable hash table routines --- */
01079 unsigned long hash_alpha_mem(void *item, short num_bits)
01080 {
01081     alpha_mem *am;
01082 
01083     am = item;
01084     return alpha_hash_value(am->id, am->attr, am->value, num_bits);
01085 }
01086 
01087 /* --- Which of the 16 hash tables to use? --- */
01088 #define table_for_tests(id,attr,value,acceptable) \
01089   current_agent(alpha_hash_tables) [ ((id) ? 1 : 0) + ((attr) ? 2 : 0) + \
01090                                      ((value) ? 4 : 0) + \
01091                                      ((acceptable) ? 8 : 0) ]
01092 
01093 #define get_next_alpha_mem_id() (current_agent(alpha_mem_id_counter)++)
01094 
01095 /* --- Adds a WME to an alpha memory (create a right_mem for it), but doesn't
01096    inform any successors --- */
01097 void add_wme_to_alpha_mem(wme * w, alpha_mem * am)
01098 {
01099     right_mem **header, *rm;
01100     unsigned long hv;
01101 
01102     /* --- allocate new right_mem, fill it fields --- */
01103     allocate_with_pool(&current_agent(right_mem_pool), &rm);
01104     rm->w = w;
01105     rm->am = am;
01106 
01107     /* --- add it to dll's for the hash bucket, alpha mem, and wme --- */
01108     hv = am->am_id ^ w->id->common.hash_id;
01109     header = ((right_mem **) current_agent(right_ht)) + (hv & RIGHT_HT_MASK);
01110     insert_at_head_of_dll(*header, rm, next_in_bucket, prev_in_bucket);
01111     insert_at_head_of_dll(am->right_mems, rm, next_in_am, prev_in_am);
01112     insert_at_head_of_dll(w->right_mems, rm, next_from_wme, prev_from_wme);
01113 }
01114 
01115 /* --- Removes a WME (right_mem) from its alpha memory, but doesn't inform
01116    any successors --- */
01117 void remove_wme_from_alpha_mem(right_mem * rm)
01118 {
01119     wme *w;
01120     alpha_mem *am;
01121     unsigned long hv;
01122     right_mem **header;
01123 
01124     w = rm->w;
01125     am = rm->am;
01126 
01127     /* --- remove it from dll's for the hash bucket, alpha mem, and wme --- */
01128     hv = am->am_id ^ w->id->common.hash_id;
01129     header = ((right_mem **) current_agent(right_ht)) + (hv & RIGHT_HT_MASK);
01130     remove_from_dll(*header, rm, next_in_bucket, prev_in_bucket);
01131     remove_from_dll(am->right_mems, rm, next_in_am, prev_in_am);
01132     remove_from_dll(w->right_mems, rm, next_from_wme, prev_from_wme);
01133 
01134     /* --- deallocate it --- */
01135     free_with_pool(&current_agent(right_mem_pool), rm);
01136 }
01137 
01138 /* --- Looks for an existing alpha mem, returns it or NIL if not found --- */
01139 alpha_mem *find_alpha_mem(Symbol * id, Symbol * attr, Symbol * value, bool acceptable)
01140 {
01141     hash_table *ht;
01142     alpha_mem *am;
01143     unsigned long hash_value;
01144 
01145     ht = table_for_tests(id, attr, value, acceptable);
01146     hash_value = alpha_hash_value(id, attr, value, ht->log2size);
01147 
01148     for (am = (alpha_mem *) (*(ht->buckets + hash_value)); am != NIL; am = am->next_in_hash_table)
01149         if ((am->id == id) && (am->attr == attr) && (am->value == value) && (am->acceptable == acceptable))
01150             return am;
01151     return NIL;
01152 }
01153 
01154 /* --- Find and share existing alpha memory, or create new one.  Adjusts
01155    the reference count on the alpha memory accordingly. --- */
01156 alpha_mem *find_or_make_alpha_mem(Symbol * id, Symbol * attr, Symbol * value, bool acceptable)
01157 {
01158     hash_table *ht;
01159     alpha_mem *am, *more_general_am;
01160     wme *w;
01161     right_mem *rm;
01162 
01163     /* --- look for an existing alpha mem --- */
01164     am = find_alpha_mem(id, attr, value, acceptable);
01165     if (am) {
01166         am->reference_count++;
01167         return am;
01168     }
01169 
01170     /* --- no existing alpha_mem found, so create a new one --- */
01171     allocate_with_pool(&current_agent(alpha_mem_pool), &am);
01172     am->next_in_hash_table = NIL;
01173     am->right_mems = NIL;
01174     am->beta_nodes = NIL;
01175     am->last_beta_node = NIL;
01176     am->reference_count = 1;
01177     am->id = id;
01178     if (id)
01179         symbol_add_ref(id);
01180     am->attr = attr;
01181     if (attr)
01182         symbol_add_ref(attr);
01183     am->value = value;
01184     if (value)
01185         symbol_add_ref(value);
01186     am->acceptable = acceptable;
01187     am->am_id = get_next_alpha_mem_id();
01188     ht = table_for_tests(id, attr, value, acceptable);
01189     add_to_hash_table(ht, am);
01190 
01191     /* --- fill new mem with any existing matching WME's --- */
01192     more_general_am = NIL;
01193     if (id)
01194         more_general_am = find_alpha_mem(NIL, attr, value, acceptable);
01195     if (!more_general_am && value)
01196         more_general_am = find_alpha_mem(NIL, attr, NIL, acceptable);
01197     if (more_general_am) {
01198         /* --- fill new mem using the existing more general one --- */
01199         for (rm = more_general_am->right_mems; rm != NIL; rm = rm->next_in_am)
01200             if (wme_matches_alpha_mem(rm->w, am))
01201                 add_wme_to_alpha_mem(rm->w, am);
01202     } else {
01203         /* --- couldn't find such an existing mem, so do it the hard way --- */
01204         for (w = current_agent(all_wmes_in_rete); w != NIL; w = w->rete_next)
01205             if (wme_matches_alpha_mem(w, am))
01206                 add_wme_to_alpha_mem(w, am);
01207     }
01208 
01209     return am;
01210 }
01211 
01212 /* --- Using the given hash table and hash value, try to find a 
01213    matching alpha memory in the indicated hash bucket.  If we find one,
01214    we add the wme to it and inform successor nodes. --- */
01215 void add_wme_to_aht(hash_table * ht, unsigned long hash_value, wme * w)
01216 {
01217     alpha_mem *am;
01218     rete_node *node, *next;
01219 
01220     hash_value = hash_value & masks_for_n_low_order_bits[ht->log2size];
01221     am = (alpha_mem *) (*(ht->buckets + hash_value));
01222     while (am != NIL) {
01223         if (wme_matches_alpha_mem(w, am)) {
01224             /* --- found the right alpha memory, first add the wme --- */
01225             add_wme_to_alpha_mem(w, am);
01226 
01227             /* --- now call the beta nodes --- */
01228             for (node = am->beta_nodes; node != NIL; node = next) {
01229                 next = node->b.posneg.next_from_alpha_mem;
01230                 (*(right_addition_routines[node->node_type])) (node, w);
01231             }
01232             return;             /* only one possible alpha memory per table could match */
01233         }
01234         am = am->next_in_hash_table;
01235     }
01236 }
01237 
01238 #define xor(i,a,v) ((i) ^ (a) ^ (v))
01239 
01240 /* --- Adds a WME to the Rete. --- */
01241 void add_wme_to_rete(wme * w)
01242 {
01243     unsigned long hi, ha, hv;
01244 
01245     /* --- add w to all_wmes_in_rete --- */
01246     insert_at_head_of_dll(current_agent(all_wmes_in_rete), w, rete_next, rete_prev);
01247     current_agent(num_wmes_in_rete)++;
01248 
01249     /* --- it's not in any right memories or tokens yet --- */
01250     w->right_mems = NIL;
01251     w->tokens = NIL;
01252 
01253     /* --- add w to the appropriate alpha_mem in each of 8 possible tables --- */
01254     hi = w->id->common.hash_id;
01255     ha = w->attr->common.hash_id;
01256     hv = w->value->common.hash_id;
01257 
01258     if (w->acceptable) {
01259         add_wme_to_aht(current_agent(alpha_hash_tables)[8], xor(0, 0, 0), w);
01260         add_wme_to_aht(current_agent(alpha_hash_tables)[9], xor(hi, 0, 0), w);
01261         add_wme_to_aht(current_agent(alpha_hash_tables)[10], xor(0, ha, 0), w);
01262         add_wme_to_aht(current_agent(alpha_hash_tables)[11], xor(hi, ha, 0), w);
01263         add_wme_to_aht(current_agent(alpha_hash_tables)[12], xor(0, 0, hv), w);
01264         add_wme_to_aht(current_agent(alpha_hash_tables)[13], xor(hi, 0, hv), w);
01265         add_wme_to_aht(current_agent(alpha_hash_tables)[14], xor(0, ha, hv), w);
01266         add_wme_to_aht(current_agent(alpha_hash_tables)[15], xor(hi, ha, hv), w);
01267     } else {
01268         add_wme_to_aht(current_agent(alpha_hash_tables)[0], xor(0, 0, 0), w);
01269         add_wme_to_aht(current_agent(alpha_hash_tables)[1], xor(hi, 0, 0), w);
01270         add_wme_to_aht(current_agent(alpha_hash_tables)[2], xor(0, ha, 0), w);
01271         add_wme_to_aht(current_agent(alpha_hash_tables)[3], xor(hi, ha, 0), w);
01272         add_wme_to_aht(current_agent(alpha_hash_tables)[4], xor(0, 0, hv), w);
01273         add_wme_to_aht(current_agent(alpha_hash_tables)[5], xor(hi, 0, hv), w);
01274         add_wme_to_aht(current_agent(alpha_hash_tables)[6], xor(0, ha, hv), w);
01275         add_wme_to_aht(current_agent(alpha_hash_tables)[7], xor(hi, ha, hv), w);
01276     }
01277 }
01278 
01279 /* --- Removes a WME from the Rete. --- */
01280 void remove_wme_from_rete(wme * w)
01281 {
01282     right_mem *rm;
01283     alpha_mem *am;
01284     rete_node *node, *next, *child;
01285     token *tok, *left;
01286 
01287     /* --- remove w from all_wmes_in_rete --- */
01288     remove_from_dll(current_agent(all_wmes_in_rete), w, rete_next, rete_prev);
01289     current_agent(num_wmes_in_rete)--;
01290 
01291     /* --- remove w from each alpha_mem it's in --- */
01292     while (w->right_mems) {
01293         rm = w->right_mems;
01294         am = rm->am;
01295         /* --- found the alpha memory, first remove the wme from it --- */
01296         remove_wme_from_alpha_mem(rm);
01297 
01298 #ifdef DO_ACTIVATION_STATS_ON_REMOVALS
01299         /* --- if doing statistics stuff, then activate each attached node --- */
01300         for (node = am->beta_nodes; node != NIL; node = next) {
01301             next = node->b.posneg.next_from_alpha_mem;
01302             right_node_activation(node, FALSE);
01303         }
01304 #endif
01305 
01306         /* --- for left unlinking, then if the alpha memory just went to
01307            zero, left unlink any attached Pos or MP nodes --- */
01308         if (am->right_mems == NIL) {
01309             for (node = am->beta_nodes; node != NIL; node = next) {
01310                 next = node->b.posneg.next_from_alpha_mem;
01311                 switch (node->node_type) {
01312                 case POSITIVE_BNODE:
01313                 case UNHASHED_POSITIVE_BNODE:
01314                     unlink_from_left_mem(node);
01315                     break;
01316                 case MP_BNODE:
01317                 case UNHASHED_MP_BNODE:
01318                     make_mp_bnode_left_unlinked(node);
01319                     break;
01320                 }               /* end of switch (node->node_type) */
01321             }
01322         }
01323     }
01324 
01325     /* --- tree-based removal of all tokens that involve w --- */
01326     while (w->tokens) {
01327         tok = w->tokens;
01328         node = tok->node;
01329         if (!tok->parent) {
01330             /* Note: parent pointer is NIL only on negative node negrm tokens */
01331             left = tok->a.neg.left_token;
01332             remove_from_dll(w->tokens, tok, next_from_wme, prev_from_wme);
01333             remove_from_dll(left->negrm_tokens, tok, a.neg.next_negrm, a.neg.prev_negrm);
01334             free_with_pool(&current_agent(token_pool), tok);
01335             if (!left->negrm_tokens) {  /* just went to 0, so call children */
01336                 for (child = node->first_child; child != NIL; child = child->next_sibling)
01337                     (*(left_addition_routines[child->node_type])) (child, left, NIL);
01338             }
01339         } else {
01340             remove_token_and_subtree(w->tokens);
01341         }
01342     }
01343 }
01344 
01345 /* --- Decrements reference count, deallocates alpha memory if unused. --- */
01346 void remove_ref_to_alpha_mem(alpha_mem * am)
01347 {
01348     hash_table *ht;
01349 
01350     am->reference_count--;
01351     if (am->reference_count != 0)
01352         return;
01353     /* --- remove from hash table, and deallocate the alpha_mem --- */
01354     ht = table_for_tests(am->id, am->attr, am->value, am->acceptable);
01355     remove_from_hash_table(ht, am);
01356     if (am->id)
01357         symbol_remove_ref(am->id);
01358     if (am->attr)
01359         symbol_remove_ref(am->attr);
01360     if (am->value)
01361         symbol_remove_ref(am->value);
01362     while (am->right_mems)
01363         remove_wme_from_alpha_mem(am->right_mems);
01364     free_with_pool(&current_agent(alpha_mem_pool), am);
01365 }
01366 
01367 /* **********************************************************************
01368 
01369    SECTION 4: Beta Net Initialization and Primitive Construction Routines
01370 
01371    The following routines are the basic Rete net building routines.
01372    Init_dummy_top_node() creates the dummy top node (for the current
01373    agent).  Make_new_mem_node(), make_new_positive_node(),
01374    make_new_mp_node(), make_new_negative_node(), make_new_cn_node(), and
01375    make_new_production_node() are the basic node creators.  Split_mp_node()
01376    and merge_into_mp_node() do the dynamic merging/splitting of memory
01377    and positive nodes.
01378 ********************************************************************** */
01379 
01380 #define get_next_beta_node_id() (current_agent(beta_node_id_counter)++)
01381 
01382 /* ------------------------------------------------------------------------
01383                           Init Dummy Top Node
01384 
01385    The dummy top node always has one token in it (WME=NIL).  This is 
01386    just there so that (real) root nodes in the beta net can be handled 
01387    the same as non-root nodes.
01388 ------------------------------------------------------------------------ */
01389 
01390 void init_dummy_top_node()
01391 {
01392     /* --- create the dummy top node --- */
01393     allocate_with_pool(&current_agent(rete_node_pool), &current_agent(dummy_top_node));
01394     init_new_rete_node_with_type(current_agent(dummy_top_node), DUMMY_TOP_BNODE);
01395     current_agent(dummy_top_node)->parent = NIL;
01396     current_agent(dummy_top_node)->first_child = NIL;
01397     current_agent(dummy_top_node)->next_sibling = NIL;
01398 
01399     /* --- create the dummy top token --- */
01400     allocate_with_pool(&current_agent(token_pool), &current_agent(dummy_top_token));
01401     current_agent(dummy_top_token)->parent = NIL;
01402     current_agent(dummy_top_token)->node = current_agent(dummy_top_node);
01403     current_agent(dummy_top_token)->w = NIL;
01404     current_agent(dummy_top_token)->first_child = NIL;
01405     current_agent(dummy_top_token)->next_sibling = NIL;
01406     current_agent(dummy_top_token)->prev_sibling = NIL;
01407     current_agent(dummy_top_token)->next_from_wme = NIL;
01408     current_agent(dummy_top_token)->prev_from_wme = NIL;
01409     current_agent(dummy_top_token)->next_of_node = NIL;
01410     current_agent(dummy_top_token)->prev_of_node = NIL;
01411     current_agent(dummy_top_node)->a.np.tokens = current_agent(dummy_top_token);
01412 }
01413 
01414 /* ------------------------------------------------------------------------
01415                   Remove Node From Parents List of Children
01416 
01417    Splices a given node out of its parent's list of children.  This would
01418    be a lot easier if the children lists were doubly-linked, but that
01419    would take up a lot of extra space.
01420 ------------------------------------------------------------------------ */
01421 
01422 void remove_node_from_parents_list_of_children(rete_node * node)
01423 {
01424     rete_node *prev_sibling;
01425 
01426     prev_sibling = node->parent->first_child;
01427     if (prev_sibling == node) {
01428         node->parent->first_child = node->next_sibling;
01429         return;
01430     }
01431     while (prev_sibling->next_sibling != node)
01432         prev_sibling = prev_sibling->next_sibling;
01433     prev_sibling->next_sibling = node->next_sibling;
01434 }
01435 
01436 /* ------------------------------------------------------------------------
01437                  Update Node With Matches From Above
01438 
01439    Calls a node's left-addition routine with each match (token) from 
01440    the node's parent.  DO NOT call this routine on (positive, unmerged)
01441    join nodes.
01442 ------------------------------------------------------------------------ */
01443 
01444 void update_node_with_matches_from_above(rete_node * child)
01445 {
01446     rete_node *parent;
01447     rete_node *saved_parents_first_child, *saved_childs_next_sibling;
01448     right_mem *rm;
01449     token *tok;
01450 
01451     if (bnode_is_bottom_of_split_mp(child->node_type)) {
01452         char msg[MESSAGE_SIZE];
01453         strncpy(msg, "\nrete.c: Internal error: update_node_with_matches_from_above called on split node",
01454                 MESSAGE_SIZE);
01455         msg[MESSAGE_SIZE - 1] = 0;
01456         abort_with_fatal_error(msg);
01457     }
01458 
01459     parent = child->parent;
01460 
01461     /* --- if parent is dummy top node, tell child about dummy top token --- */
01462     if (parent->node_type == DUMMY_TOP_BNODE) {
01463         (*(left_addition_routines[child->node_type])) (child, current_agent(dummy_top_token), NIL);
01464         return;
01465     }
01466 
01467     /* --- if parent is positive: first do surgery on parent's child list,
01468        to replace the list with "child"; then call parent's add_right 
01469        routine with each wme in the parent's alpha mem; then do surgery 
01470        to restore previous child list of parent. --- */
01471     if (bnode_is_positive(parent->node_type)) {
01472         /* --- If the node is right unlinked, then don't activate it.  This is
01473            important because some interpreter routines rely on the node
01474            being right linked whenever it gets right activated. */
01475         if (node_is_right_unlinked(parent))
01476             return;
01477         saved_parents_first_child = parent->first_child;
01478         saved_childs_next_sibling = child->next_sibling;
01479         parent->first_child = child;
01480         child->next_sibling = NIL;
01481         /* to avoid double-counting these right adds */
01482         node_to_ignore_for_activation_stats = parent;
01483         for (rm = parent->b.posneg.alpha_mem->right_mems; rm != NIL; rm = rm->next_in_am)
01484             (*(right_addition_routines[parent->node_type])) (parent, rm->w);
01485         node_to_ignore_for_activation_stats = NIL;
01486         parent->first_child = saved_parents_first_child;
01487         child->next_sibling = saved_childs_next_sibling;
01488         return;
01489     }
01490 
01491     /* --- if parent is negative or cn: easy, just look at the list of tokens
01492        on the parent node. --- */
01493     for (tok = parent->a.np.tokens; tok != NIL; tok = tok->next_of_node)
01494         if (!tok->negrm_tokens)
01495             (*(left_addition_routines[child->node_type])) (child, tok, NIL);
01496 }
01497 
01498 /* ------------------------------------------------------------------------
01499                      Nearest Ancestor With Same AM
01500 
01501    Scans up the net and finds the first (i.e., nearest) ancestor node
01502    that uses a given alpha_mem.  Returns that node, or NIL if none exists.
01503 ------------------------------------------------------------------------ */
01504 
01505 rete_node *nearest_ancestor_with_same_am(rete_node * node, alpha_mem * am)
01506 {
01507     while (node->node_type != DUMMY_TOP_BNODE) {
01508         if (node->node_type == CN_BNODE)
01509             node = node->b.cn.partner->parent;
01510         else
01511             node = real_parent_node(node);
01512         if (bnode_is_posneg(node->node_type) && (node->b.posneg.alpha_mem == am))
01513             return node;
01514     }
01515     return NIL;
01516 }
01517 
01518 /* --------------------------------------------------------------------
01519                          Make New Mem Node
01520  
01521    Make a new beta memory node, return a pointer to it.
01522 -------------------------------------------------------------------- */
01523 
01524 rete_node *make_new_mem_node(rete_node * parent, byte node_type, var_location left_hash_loc)
01525 {
01526     rete_node *node;
01527 
01528     /* --- create the node data structure, fill in fields --- */
01529     allocate_with_pool(&current_agent(rete_node_pool), &node);
01530     init_new_rete_node_with_type(node, node_type);
01531     node->parent = parent;
01532     node->next_sibling = parent->first_child;
01533     parent->first_child = node;
01534     node->first_child = NIL;
01535     node->b.mem.first_linked_child = NIL;
01536     node->left_hash_loc_field_num = left_hash_loc.field_num;
01537     node->left_hash_loc_levels_up = left_hash_loc.levels_up;
01538     node->node_id = get_next_beta_node_id();
01539     node->a.np.tokens = NIL;
01540 
01541     /* --- call new node's add_left routine with all the parent's tokens --- */
01542     update_node_with_matches_from_above(node);
01543 
01544     return node;
01545 }
01546 
01547 /* --------------------------------------------------------------------
01548                          Make New Positive Node
01549 
01550    Make a new positive join node, return a pointer to it.
01551 -------------------------------------------------------------------- */
01552 
01553 rete_node *make_new_positive_node(rete_node * parent_mem, byte node_type,
01554                                   alpha_mem * am, rete_test * rt, bool prefer_left_unlinking)
01555 {
01556     rete_node *node;
01557 
01558     /* --- create the node data structure, fill in fields --- */
01559     allocate_with_pool(&current_agent(rete_node_pool), &node);
01560     init_new_rete_node_with_type(node, node_type);
01561     node->parent = parent_mem;
01562     node->next_sibling = parent_mem->first_child;
01563     parent_mem->first_child = node;
01564     node->first_child = NIL;
01565     relink_to_left_mem(node);
01566     node->b.posneg.other_tests = rt;
01567     node->b.posneg.alpha_mem = am;
01568     node->b.posneg.nearest_ancestor_with_same_am = nearest_ancestor_with_same_am(node, am);
01569     relink_to_right_mem(node);
01570 
01571     /* --- don't need to force WM through new node yet, as it's just a
01572        join node with no children --- */
01573 
01574     /* --- unlink the join node from one side if possible --- */
01575     if (!parent_mem->a.np.tokens)
01576         unlink_from_right_mem(node);
01577     if ((!am->right_mems) && !node_is_right_unlinked(node))
01578         unlink_from_left_mem(node);
01579     if (prefer_left_unlinking && (!parent_mem->a.np.tokens) && (!am->right_mems)) {
01580         relink_to_right_mem(node);
01581         unlink_from_left_mem(node);
01582     }
01583 
01584     return node;
01585 }
01586 
01587 /* --------------------------------------------------------------------
01588                              Split MP Node
01589 
01590    Split a given MP node into separate M and P nodes, return a pointer
01591    to the new Memory node.
01592 -------------------------------------------------------------------- */
01593 
01594 rete_node *split_mp_node(rete_node * mp_node)
01595 {
01596     rete_node mp_copy;
01597     rete_node *pos_node, *mem_node, *parent;
01598     byte mem_node_type, node_type;
01599     token *t;
01600 
01601     /* --- determine appropriate node types for new M and P nodes --- */
01602     if (mp_node->node_type == MP_BNODE) {
01603         node_type = POSITIVE_BNODE;
01604         mem_node_type = MEMORY_BNODE;
01605     } else {
01606         node_type = UNHASHED_POSITIVE_BNODE;
01607         mem_node_type = UNHASHED_MEMORY_BNODE;
01608     }
01609 
01610     /* --- save a copy of the MP data, then kill the MP node --- */
01611     mp_copy = *mp_node;
01612     parent = mp_node->parent;
01613     remove_node_from_parents_list_of_children(mp_node);
01614     update_stats_for_destroying_node(mp_node);  /* clean up rete stats stuff */
01615 
01616     /* --- the old MP node will get transmogrified into the new Pos node --- */
01617     pos_node = mp_node;
01618 
01619     /* --- create the new M node, transfer the MP node's tokens to it --- */
01620     allocate_with_pool(&current_agent(rete_node_pool), &mem_node);
01621     init_new_rete_node_with_type(mem_node, mem_node_type);
01622     set_sharing_factor(mem_node, mp_copy.sharing_factor);
01623 
01624     mem_node->parent = parent;
01625     mem_node->next_sibling = parent->first_child;
01626     parent->first_child = mem_node;
01627     mem_node->first_child = pos_node;
01628     mem_node->b.mem.first_linked_child = NIL;
01629     mem_node->left_hash_loc_field_num = mp_copy.left_hash_loc_field_num;
01630     mem_node->left_hash_loc_levels_up = mp_copy.left_hash_loc_levels_up;
01631     mem_node->node_id = mp_copy.node_id;
01632 
01633     mem_node->a.np.tokens = mp_node->a.np.tokens;
01634     for (t = mp_node->a.np.tokens; t != NIL; t = t->next_of_node)
01635         t->node = mem_node;
01636 
01637     /* --- transmogrify the old MP node into the new Pos node --- */
01638     init_new_rete_node_with_type(pos_node, node_type);
01639     pos_node->parent = mem_node;
01640     pos_node->first_child = mp_copy.first_child;
01641     pos_node->next_sibling = NIL;
01642     pos_node->b.posneg = mp_copy.b.posneg;
01643     relink_to_left_mem(pos_node);       /* for now, but might undo this below */
01644     set_sharing_factor(pos_node, mp_copy.sharing_factor);
01645 
01646     /* --- set join node's unlinking status according to mp_copy's --- */
01647     if (mp_bnode_is_left_unlinked(&mp_copy))
01648         unlink_from_left_mem(pos_node);
01649 
01650     return mem_node;
01651 }
01652 
01653 /* --------------------------------------------------------------------
01654                            Merge Into MP Node
01655 
01656    Merge a given Memory node and its one positive join child into an
01657    MP node, returning a pointer to the MP node.
01658 -------------------------------------------------------------------- */
01659 
01660 rete_node *merge_into_mp_node(rete_node * mem_node)
01661 {
01662     rete_node *pos_node, *mp_node, *parent;
01663     rete_node pos_copy;
01664     byte node_type;
01665     token *t;
01666 
01667     pos_node = mem_node->first_child;
01668     parent = mem_node->parent;
01669 
01670     /* --- sanity check: Mem node must have exactly one child --- */
01671     if ((!pos_node) || pos_node->next_sibling) {
01672         char msg[MESSAGE_SIZE];
01673         strncpy(msg, "\nrete.c: Internal error: tried to merge_into_mp_node, but <>1 child\n", MESSAGE_SIZE);
01674         msg[MESSAGE_SIZE - 1] = 0;
01675         abort_with_fatal_error(msg);
01676     }
01677 
01678     /* --- determine appropriate node type for new MP node --- */
01679     if (mem_node->node_type == MEMORY_BNODE) {
01680         node_type = MP_BNODE;
01681     } else {
01682         node_type = UNHASHED_MP_BNODE;
01683     }
01684 
01685     /* --- save a copy of the Pos data, then kill the Pos node --- */
01686     pos_copy = *pos_node;
01687     update_stats_for_destroying_node(pos_node); /* clean up rete stats stuff */
01688 
01689     /* --- the old Pos node gets transmogrified into the new MP node --- */
01690     mp_node = pos_node;
01691     init_new_rete_node_with_type(mp_node, node_type);
01692     set_sharing_factor(mp_node, pos_copy.sharing_factor);
01693     mp_node->b.posneg = pos_copy.b.posneg;
01694 
01695     /* --- transfer the Mem node's tokens to the MP node --- */
01696     mp_node->a.np.tokens = mem_node->a.np.tokens;
01697     for (t = mem_node->a.np.tokens; t != NIL; t = t->next_of_node)
01698         t->node = mp_node;
01699     mp_node->left_hash_loc_field_num = mem_node->left_hash_loc_field_num;
01700     mp_node->left_hash_loc_levels_up = mem_node->left_hash_loc_levels_up;
01701     mp_node->node_id = mem_node->node_id;
01702 
01703     /* --- replace the Mem node with the new MP node --- */
01704     mp_node->parent = parent;
01705     mp_node->next_sibling = parent->first_child;
01706     parent->first_child = mp_node;
01707     mp_node->first_child = pos_copy.first_child;
01708 
01709     remove_node_from_parents_list_of_children(mem_node);
01710     update_stats_for_destroying_node(mem_node); /* clean up rete stats stuff */
01711     free_with_pool(&current_agent(rete_node_pool), mem_node);
01712 
01713     /* --- set MP node's unlinking status according to pos_copy's --- */
01714     make_mp_bnode_left_linked(mp_node);
01715     if (node_is_left_unlinked(&pos_copy))
01716         make_mp_bnode_left_unlinked(mp_node);
01717 
01718     return mp_node;
01719 }
01720 
01721 /* --------------------------------------------------------------------
01722                            Make New MP Node
01723 
01724    Make a new MP node, return a pointer to it.
01725 -------------------------------------------------------------------- */
01726 
01727 rete_node *make_new_mp_node(rete_node * parent, byte node_type,
01728                             var_location left_hash_loc, alpha_mem * am, rete_test * rt, bool prefer_left_unlinking)
01729 {
01730     rete_node *mem_node, *pos_node;
01731     byte mem_node_type, pos_node_type;
01732 
01733     if (node_type == MP_BNODE) {
01734         pos_node_type = POSITIVE_BNODE;
01735         mem_node_type = MEMORY_BNODE;
01736     } else {
01737         pos_node_type = UNHASHED_POSITIVE_BNODE;
01738         mem_node_type = UNHASHED_MEMORY_BNODE;
01739     }
01740     mem_node = make_new_mem_node(parent, mem_node_type, left_hash_loc);
01741     pos_node = make_new_positive_node(mem_node, pos_node_type, am, rt, prefer_left_unlinking);
01742     return merge_into_mp_node(mem_node);
01743 }
01744 
01745 /* --------------------------------------------------------------------
01746                          Make New Negative Node
01747 
01748    Make a new negative node, return a pointer to it.
01749 -------------------------------------------------------------------- */
01750 
01751 rete_node *make_new_negative_node(rete_node * parent, byte node_type,
01752                                   var_location left_hash_loc, alpha_mem * am, rete_test * rt)
01753 {
01754     rete_node *node;
01755 
01756     allocate_with_pool(&current_agent(rete_node_pool), &node);
01757     init_new_rete_node_with_type(node, node_type);
01758     node->parent = parent;
01759     node->next_sibling = parent->first_child;
01760     parent->first_child = node;
01761     node->first_child = NIL;
01762     node->left_hash_loc_field_num = left_hash_loc.field_num;
01763     node->left_hash_loc_levels_up = left_hash_loc.levels_up;
01764     node->b.posneg.other_tests = rt;
01765     node->b.posneg.alpha_mem = am;
01766     node->a.np.tokens = NIL;
01767     node->b.posneg.nearest_ancestor_with_same_am = nearest_ancestor_with_same_am(node, am);
01768     relink_to_right_mem(node);
01769 
01770     node->node_id = get_next_beta_node_id();
01771 
01772     /* --- call new node's add_left routine with all the parent's tokens --- */
01773     update_node_with_matches_from_above(node);
01774 
01775     /* --- if no tokens arrived from parent, unlink the node --- */
01776     if (!node->a.np.tokens)
01777         unlink_from_right_mem(node);
01778 
01779     return node;
01780 }
01781 
01782 /* --------------------------------------------------------------------
01783                           Make New CN Node
01784 
01785    Make new CN and CN_PARTNER nodes, return a pointer to the CN node.
01786 -------------------------------------------------------------------- */
01787 
01788 rete_node *make_new_cn_node(rete_node * parent, rete_node * bottom_of_subconditions)
01789 {
01790     rete_node *node, *partner, *ncc_subconditions_top_node;
01791 
01792     /* --- Find top node in the subconditions branch --- */
01793     ncc_subconditions_top_node = NIL;   /* unneeded, but avoids gcc -Wall warn */
01794     for (node = bottom_of_subconditions; node != parent; node = node->parent) {
01795         ncc_subconditions_top_node = node;
01796     }
01797 
01798     allocate_with_pool(&current_agent(rete_node_pool), &node);
01799     init_new_rete_node_with_type(node, CN_BNODE);
01800     allocate_with_pool(&current_agent(rete_node_pool), &partner);
01801     init_new_rete_node_with_type(partner, CN_PARTNER_BNODE);
01802 
01803     /* NOTE: for improved efficiency, <node> should be on the parent's
01804        children list *after* the ncc subcontitions top node */
01805     remove_node_from_parents_list_of_children(ncc_subconditions_top_node);
01806     node->parent = parent;
01807     node->next_sibling = parent->first_child;
01808     ncc_subconditions_top_node->next_sibling = node;
01809     parent->first_child = ncc_subconditions_top_node;
01810     node->first_child = NIL;
01811 
01812     node->a.np.tokens = NIL;
01813     node->b.cn.partner = partner;
01814     node->node_id = get_next_beta_node_id();
01815 
01816     partner->parent = bottom_of_subconditions;
01817     partner->next_sibling = bottom_of_subconditions->first_child;
01818     bottom_of_subconditions->first_child = partner;
01819     partner->first_child = NIL;
01820     partner->a.np.tokens = NIL;
01821     partner->b.cn.partner = node;
01822 
01823     /* --- call partner's add_left routine with all the parent's tokens --- */
01824     update_node_with_matches_from_above(partner);
01825     /* --- call new node's add_left routine with all the parent's tokens --- */
01826     update_node_with_matches_from_above(node);
01827 
01828     return node;
01829 }
01830 
01831 /* --------------------------------------------------------------------
01832                         Make New Production Node
01833 
01834    Make a new production node, return a pointer to it.
01835 
01836    Does not handle the following tasks:
01837      - filling in p_node->b.p.parents_nvn or discarding chunk variable names 
01838      - filling in stuff on new_prod (except does fill in new_prod->p_node)
01839      - using update_node_with_matches_from_above (p_node) or handling
01840        an initial refracted instantiation
01841 -------------------------------------------------------------------- */
01842 
01843 rete_node *make_new_production_node(rete_node * parent, production * new_prod)
01844 {
01845     rete_node *p_node;
01846 
01847     allocate_with_pool(&current_agent(rete_node_pool), &p_node);
01848     init_new_rete_node_with_type(p_node, P_BNODE);
01849     new_prod->p_node = p_node;
01850     p_node->parent = parent;
01851     p_node->next_sibling = parent->first_child;
01852     parent->first_child = p_node;
01853     p_node->first_child = NIL;
01854     p_node->b.p.prod = new_prod;
01855     p_node->a.np.tokens = NIL;
01856     p_node->b.p.tentative_assertions = NIL;
01857     p_node->b.p.tentative_retractions = NIL;
01858     return p_node;
01859 }
01860 
01861 /* **********************************************************************
01862  
01863    SECTION 5:  Beta Net Primitive Destruction Routines
01864 
01865    Deallocate_rete_test_list() deallocates a list of rete test structures,
01866    removing references to symbols within them.  
01867 
01868    Deallocate_rete_node() deallocates a given beta node (which must
01869    not be a p_node), cleaning up any tokens it contains, removing 
01870    references (to symbols and alpha memories).  It also continues
01871    deallocating nodes up the net if they are no longer used.
01872 ********************************************************************** */
01873 
01874 void deallocate_rete_test_list(rete_test * rt)
01875 {
01876     rete_test *next_rt;
01877 
01878     while (rt) {
01879         next_rt = rt->next;
01880 
01881         if (test_is_constant_relational_test(rt->type)) {
01882             symbol_remove_ref(rt->data.constant_referent);
01883         } else if (rt->type == DISJUNCTION_RETE_TEST) {
01884             deallocate_symbol_list_removing_references(rt->data.disjunction_list);
01885         }
01886 
01887         free_with_pool(&current_agent(rete_test_pool), rt);
01888         rt = next_rt;
01889     }
01890 }
01891 
01892 void deallocate_rete_node(rete_node * node)
01893 {
01894     rete_node *parent;
01895 
01896     /* --- don't deallocate the dummy top node --- */
01897     if (node == current_agent(dummy_top_node))
01898         return;
01899 
01900     /* --- sanity check --- */
01901     if (node->node_type == P_BNODE) {
01902         char msg[MESSAGE_SIZE];
01903         strncpy(msg, "Internal error: deallocate_rete_node() called on p-node.\n", MESSAGE_SIZE);
01904         msg[MESSAGE_SIZE - 1] = 0;
01905         abort_with_fatal_error(msg);
01906     }
01907 
01908     parent = node->parent;
01909 
01910     /* --- if a cn node, deallocate its partner first --- */
01911     if (node->node_type == CN_BNODE)
01912         deallocate_rete_node(node->b.cn.partner);
01913 
01914     /* --- clean up any tokens at the node --- */
01915     if (!bnode_is_bottom_of_split_mp(node->node_type))
01916         while (node->a.np.tokens)
01917             remove_token_and_subtree(node->a.np.tokens);
01918 
01919     /* --- stuff for posneg nodes only --- */
01920     if (bnode_is_posneg(node->node_type)) {
01921         deallocate_rete_test_list(node->b.posneg.other_tests);
01922         /* --- right unlink the node, cleanup alpha memory --- */
01923         if (!node_is_right_unlinked(node))
01924             unlink_from_right_mem(node);
01925         remove_ref_to_alpha_mem(node->b.posneg.alpha_mem);
01926     }
01927 
01928     /* --- remove the node from its parent's list --- */
01929     remove_node_from_parents_list_of_children(node);
01930 
01931     /* --- for unmerged pos. nodes: unlink, maybe merge its parent --- */
01932     if (bnode_is_bottom_of_split_mp(node->node_type)) {
01933         if (!node_is_left_unlinked(node))
01934             unlink_from_left_mem(node);
01935         /* --- if parent is mem node with just one child, merge them --- */
01936         if (parent->first_child && (!parent->first_child->next_sibling))
01937             merge_into_mp_node(parent);
01938     }
01939 
01940     update_stats_for_destroying_node(node);     /* clean up rete stats stuff */
01941     free_with_pool(&current_agent(rete_node_pool), node);
01942 
01943     /* --- if parent has no other children, deallocate it, and recurse  --- */
01944     if (!parent->first_child)
01945         deallocate_rete_node(parent);
01946 }
01947 
01948 /* **********************************************************************
01949 
01950    SECTION 6:  Variable Bindings and Locations
01951 
01952    As we build the network for a production, we have to keep track of
01953    where variables are bound -- i.e., at what earlier conditions/fields
01954    (if any) did a given variable occur?  We could do this by scanning
01955    upwards -- look at all the earlier conditions to try to find an 
01956    occurrence of the variable -- but that would take O(C) time, where 
01957    C is the number of conditions.  Instead, we store binding location
01958    information directly on the variables in the symbol table.  Each
01959    variable has a field var.rete_binding_locations, which holds a
01960    stack (yes, a stack) of binding locations, with the most recent (i.e., 
01961    lowest in the Rete) binding on top of the stack.  (It has to be a stack
01962    so we can push and pop bindings during the handling of conjunctive
01963    negations.)
01964 
01965    Whenever a variable is created, the symbol table routines initialize 
01966    var.rete_binding_locations to NIL.  It is important for the stack to 
01967    get completely popped after we're done with each production addition, 
01968    so it gets properly reset to NIL.
01969 
01970    The basic operations on these binding stacks are done with a few
01971    macros below.  A binding location is represented by the CAR of a 
01972    CONS -- the level and field numbers are crammed into the CAR.  
01973    Var_is_bound() returns TRUE iff the given variable has been bound.
01974    Push_var_binding() pushes a new binding of the given variable.
01975    Pop_var_binding() pops the top binding.
01976 ********************************************************************** */
01977 
01978 #define var_is_bound(v) (((Symbol *)(v))->var.rete_binding_locations != NIL)
01979 
01980 #define varloc_to_dummy(depth,field_num) ((void *)(((depth)<<2) + (field_num)))
01981 #define dummy_to_varloc_depth(d)     (((unsigned long)(d))>>2)
01982 #define dummy_to_varloc_field_num(d) (((unsigned long)(d)) & 3)
01983 
01984 #define push_var_binding(v,depth,field_num) { \
01985   void *dummy_xy312; \
01986   dummy_xy312 = varloc_to_dummy ((depth), (field_num)); \
01987   push (dummy_xy312, ((Symbol *)(v))->var.rete_binding_locations); }
01988 
01989 #define pop_var_binding(v) { \
01990   cons *c_xy312; \
01991   c_xy312 = ((Symbol *)(v))->var.rete_binding_locations; \
01992   ((Symbol *)(v))->var.rete_binding_locations = c_xy312->rest; \
01993   free_cons (c_xy312); }
01994 
01995 /* -------------------------------------------------------------------
01996                           Find Var Location
01997 
01998    This routine finds the most recent place a variable was bound.
01999    It does this simply by looking at the top of the binding stack
02000    for that variable.  If there is any binding, its location is stored 
02001    in the parameter *result, and the function returns TRUE.  If no 
02002    binding is found, the function returns FALSE.
02003 ------------------------------------------------------------------- */
02004 
02005 bool find_var_location(Symbol * var, rete_node_level current_depth, var_location * result)
02006 {
02007     void *dummy;
02008     if (!var->var.rete_binding_locations)
02009         return FALSE;
02010     dummy = var->var.rete_binding_locations->first;
02011     result->levels_up = (unsigned short) (current_depth - (rete_node_level) dummy_to_varloc_depth(dummy));
02012     result->field_num = (byte) dummy_to_varloc_field_num(dummy);
02013     return TRUE;
02014 }
02015 
02016 /* -------------------------------------------------------------------
02017                       Bind Variables in Test
02018 
02019    This routine pushes bindings for variables occurring (i.e., being
02020    equality-tested) in a given test.  It can do this in DENSE fashion
02021    (push a new binding for ANY variable) or SPARSE fashion (push a new
02022    binding only for previously-unbound variables), depending on the 
02023    boolean "dense" parameter.  Any variables receiving new bindings 
02024    are also pushed onto the given "varlist".
02025 ------------------------------------------------------------------- */
02026 
02027 void bind_variables_in_test(test t, rete_node_level depth, byte field_num, bool dense, list ** varlist)
02028 {
02029     Symbol *referent;
02030     complex_test *ct;
02031     cons *c;
02032 
02033     if (test_is_blank_test(t))
02034         return;
02035     if (test_is_blank_or_equality_test(t)) {
02036         referent = referent_of_equality_test(t);
02037         if (referent->common.symbol_type != VARIABLE_SYMBOL_TYPE)
02038             return;
02039         if (!dense && var_is_bound(referent))
02040             return;
02041         push_var_binding(referent, depth, field_num);
02042         push(referent, *varlist);
02043         return;
02044     }
02045 
02046     ct = complex_test_from_test(t);
02047     if (ct->type == CONJUNCTIVE_TEST)
02048         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
02049             bind_variables_in_test(c->first, depth, field_num, dense, varlist);
02050 }
02051 
02052 /* -------------------------------------------------------------------
02053              Pop Bindings and Deallocate List of Variables
02054 
02055    This routine takes a list of variables; for each item <v> on the
02056    list, it pops a binding of <v>.  It also deallocates the list.
02057    This is often used for un-binding a group of variables which got
02058    bound in some procedure.
02059 ------------------------------------------------------------------- */
02060 
02061 void pop_bindings_and_deallocate_list_of_variables(list * vars)
02062 {
02063     while (vars) {
02064         cons *c;
02065         c = vars;
02066         vars = vars->rest;
02067         pop_var_binding(c->first);
02068         free_cons(c);
02069     }
02070 }
02071 
02072 /* **********************************************************************
02073 
02074    SECTION 7:  Varnames and Node_Varnames
02075 
02076    Varnames and Node_Varnames (NVN) structures are used to record the names
02077    of variables bound (i.e., equality tested) at rete nodes.  The only
02078    purpose of saving this information is so we can reconstruct the 
02079    original source code for a production when we want to print it.  For
02080    chunks, we don't save any of this information -- we just re-gensym 
02081    the variable names on each printing (unless discard_chunk_varnames
02082    is set to FALSE).
02083 
02084    For each production, a chain of node_varnames structures is built,
02085    paralleling the structure of the rete net (i.e., the portion of the rete
02086    used for that production).  There is a node_varnames structure for
02087    each Mem, Neg, or NCC node in that part, giving the names of variables
02088    bound in the id, attr, and value fields of the condition at that node.
02089 
02090    At each field, we could bind zero, one, or more variables.  To
02091    save space, we use some bit-twiddling here.  A "varnames" represents
02092    zero or more variables:   NIL means zero; a pointer (with the low-order
02093    bit being 0) to a variable means just that one variable; and any
02094    other pointer (with the low-order bit set to 1) points (minus 1, of
02095    course) to a consed list of variables.
02096 
02097    Add_var_to_varnames() takes an existing varnames object (which can
02098    be NIL, for no variable names) and returns a new varnames object
02099    which adds (destructively!) a given variable to the previous one.
02100    Deallocate_varnames() deallocates a varnames object, removing references
02101    to symbols, etc.  Deallocate_node_varnames() deallocates a whole
02102    chain of node_varnames structures, scanning up the net, etc.
02103 ********************************************************************** */
02104 
02105 typedef char varnames;
02106 
02107 #define one_var_to_varnames(x) ((varnames *) (x))
02108 #define var_list_to_varnames(x) ((varnames *) (((char *)(x)) + 1))
02109 #define varnames_is_one_var(x) (! (varnames_is_var_list(x)))
02110 #define varnames_is_var_list(x) (((unsigned long)(x)) & 1)
02111 #define varnames_to_one_var(x) ((Symbol *) (x))
02112 #define varnames_to_var_list(x) ((list *) (((char *)(x)) - 1))
02113 
02114 typedef struct three_field_varnames_struct {
02115     varnames *id_varnames;
02116     varnames *attr_varnames;
02117     varnames *value_varnames;
02118 } three_field_varnames;
02119 
02120 typedef struct node_varnames_struct {
02121     struct node_varnames_struct *parent;
02122     union varname_data_union {
02123         three_field_varnames fields;
02124         struct node_varnames_struct *bottom_of_subconditions;
02125     } data;
02126 } node_varnames;
02127 
02128 varnames *add_var_to_varnames(Symbol * var, varnames * old_varnames)
02129 {
02130     cons *c1, *c2;
02131 
02132     symbol_add_ref(var);
02133     if (old_varnames == NIL)
02134         return one_var_to_varnames(var);
02135     if (varnames_is_one_var(old_varnames)) {
02136         allocate_cons(&c1);
02137         allocate_cons(&c2);
02138         c1->first = var;
02139         c1->rest = c2;
02140         c2->first = varnames_to_one_var(old_varnames);
02141         c2->rest = NIL;
02142         return var_list_to_varnames(c1);
02143     }
02144     /* --- otherwise old_varnames is a list --- */
02145     allocate_cons(&c1);
02146     c1->first = var;
02147     c1->rest = varnames_to_var_list(old_varnames);
02148     return var_list_to_varnames(c1);
02149 }
02150 
02151 void deallocate_varnames(varnames * vn)
02152 {
02153     Symbol *sym;
02154     list *symlist;
02155 
02156     if (vn == NIL)
02157         return;
02158     if (varnames_is_one_var(vn)) {
02159         sym = varnames_to_one_var(vn);
02160         symbol_remove_ref(sym);
02161     } else {
02162         symlist = varnames_to_var_list(vn);
02163         deallocate_symbol_list_removing_references(symlist);
02164     }
02165 }
02166 
02167 void deallocate_node_varnames(rete_node * node, rete_node * cutoff, node_varnames * nvn)
02168 {
02169     node_varnames *temp;
02170 
02171     while (node != cutoff) {
02172         if (node->node_type == CN_BNODE) {
02173             deallocate_node_varnames(node->b.cn.partner->parent, node->parent, nvn->data.bottom_of_subconditions);
02174         } else {
02175             deallocate_varnames(nvn->data.fields.id_varnames);
02176             deallocate_varnames(nvn->data.fields.attr_varnames);
02177             deallocate_varnames(nvn->data.fields.value_varnames);
02178         }
02179         node = real_parent_node(node);
02180         temp = nvn;
02181         nvn = nvn->parent;
02182         free_with_pool(&current_agent(node_varnames_pool), temp);
02183     }
02184 }
02185 
02186 /* -------------------------------------------------------------------
02187      Creating the Node Varnames Structures for a List of Conditions
02188 
02189    Add_unbound_varnames_in_test() adds to an existing varnames object
02190    the names of any currently-unbound variables equality-tested in
02191    a given test.  Make_nvn_for_posneg_cond() creates and returns the
02192    node_varnames structure for a single given (simple) positive or
02193    negative condition.  Get_nvn_for_condition_list() creates the 
02194    whole chain of NVN structures for a list of conditions, returning
02195    a pointer to the bottom structure in the chain.
02196 ------------------------------------------------------------------- */
02197 
02198 varnames *add_unbound_varnames_in_test(test t, varnames * starting_vn)
02199 {
02200     cons *c;
02201     Symbol *referent;
02202     complex_test *ct;
02203 
02204     if (test_is_blank_test(t))
02205         return starting_vn;
02206     if (test_is_blank_or_equality_test(t)) {
02207         referent = referent_of_equality_test(t);
02208         if (referent->common.symbol_type == VARIABLE_SYMBOL_TYPE)
02209             if (!var_is_bound(referent))
02210                 starting_vn = add_var_to_varnames(referent, starting_vn);
02211         return starting_vn;
02212     }
02213 
02214     ct = complex_test_from_test(t);
02215 
02216     if (ct->type == CONJUNCTIVE_TEST) {
02217         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
02218             starting_vn = add_unbound_varnames_in_test(c->first, starting_vn);
02219     }
02220     return starting_vn;
02221 }
02222 
02223 node_varnames *make_nvn_for_posneg_cond(condition * cond, node_varnames * parent_nvn)
02224 {
02225     node_varnames *new;
02226     list *vars_bound;
02227 
02228     vars_bound = NIL;
02229 
02230     allocate_with_pool(&current_agent(node_varnames_pool), &new);
02231     new->parent = parent_nvn;
02232 
02233     /* --- fill in varnames for id test --- */
02234     new->data.fields.id_varnames = add_unbound_varnames_in_test(cond->data.tests.id_test, NIL);
02235 
02236     /* --- add sparse bindings for id, then get attr field varnames --- */
02237     bind_variables_in_test(cond->data.tests.id_test, 0, 0, FALSE, &vars_bound);
02238     new->data.fields.attr_varnames = add_unbound_varnames_in_test(cond->data.tests.attr_test, NIL);
02239 
02240     /* --- add sparse bindings for attr, then get value field varnames --- */
02241     bind_variables_in_test(cond->data.tests.attr_test, 0, 0, FALSE, &vars_bound);
02242     new->data.fields.value_varnames = add_unbound_varnames_in_test(cond->data.tests.value_test, NIL);
02243 
02244     /* --- Pop the variable bindings for these conditions --- */
02245     pop_bindings_and_deallocate_list_of_variables(vars_bound);
02246 
02247     return new;
02248 }
02249 
02250 node_varnames *get_nvn_for_condition_list(condition * cond_list, node_varnames * parent_nvn)
02251 {
02252     node_varnames *new = NULL;
02253     condition *cond;
02254     list *vars;
02255 
02256     vars = NIL;
02257 
02258     for (cond = cond_list; cond != NIL; cond = cond->next) {
02259 
02260         switch (cond->type) {
02261         case POSITIVE_CONDITION:
02262             new = make_nvn_for_posneg_cond(cond, parent_nvn);
02263 
02264             /* --- Add sparse variable bindings for this condition --- */
02265             bind_variables_in_test(cond->data.tests.id_test, 0, 0, FALSE, &vars);
02266             bind_variables_in_test(cond->data.tests.attr_test, 0, 0, FALSE, &vars);
02267             bind_variables_in_test(cond->data.tests.value_test, 0, 0, FALSE, &vars);
02268             break;
02269         case NEGATIVE_CONDITION:
02270             new = make_nvn_for_posneg_cond(cond, parent_nvn);
02271             break;
02272         case CONJUNCTIVE_NEGATION_CONDITION:
02273             allocate_with_pool(&current_agent(node_varnames_pool), &new);
02274             new->parent = parent_nvn;
02275             new->data.bottom_of_subconditions = get_nvn_for_condition_list(cond->data.ncc.top, parent_nvn);
02276             break;
02277         }
02278 
02279         parent_nvn = new;
02280     }
02281 
02282     /* --- Pop the variable bindings for these conditions --- */
02283     pop_bindings_and_deallocate_list_of_variables(vars);
02284 
02285     return parent_nvn;
02286 }
02287 
02288 /* **********************************************************************
02289 
02290    SECTION 8:  Building the Rete Net:  Condition-To-Node Converstion
02291 
02292    Build_network_for_condition_list() is the key routine here. (See
02293    description below.)
02294 ********************************************************************** */
02295 
02296 /* ---------------------------------------------------------------------
02297 
02298        Test Type <---> Relational (Rete) Test Type Conversion Tables
02299 
02300    These tables convert from xxx_TEST's (defined in soarkernel.h for various
02301    kinds of complex_test's) to xxx_RETE_TEST's (defined in rete.c for
02302    the different kinds of Rete tests), and vice-versa.  We might just 
02303    use the same set of constants for both purposes, but we want to be
02304    able to do bit-twiddling on the RETE_TEST types.
02305 
02306    (This stuff probably doesn't belong under "Building the Rete Net",
02307    but I wasn't sure where else to put it.)
02308 --------------------------------------------------------------------- */
02309 
02310 byte test_type_to_relational_test_type[256];
02311 byte relational_test_type_to_test_type[256];
02312 
02313 /* Warning: the two items below must not be the same as any xxx_TEST's defined
02314    in soarkernel.h for the types of complex_test's */
02315 #define EQUAL_TEST_TYPE 254
02316 #define ERROR_TEST_TYPE 255
02317 
02318 void init_test_type_conversion_tables(void)
02319 {
02320     int i;
02321 
02322     for (i = 0; i < 256; i++)
02323         test_type_to_relational_test_type[i] = ERROR_TEST_TYPE;
02324     for (i = 0; i < 256; i++)
02325         relational_test_type_to_test_type[i] = ERROR_TEST_TYPE;
02326 
02327     /* we don't need ...[equal test] */
02328     test_type_to_relational_test_type[NOT_EQUAL_TEST] = RELATIONAL_NOT_EQUAL_RETE_TEST;
02329     test_type_to_relational_test_type[LESS_TEST] = RELATIONAL_LESS_RETE_TEST;
02330     test_type_to_relational_test_type[GREATER_TEST] = RELATIONAL_GREATER_RETE_TEST;
02331     test_type_to_relational_test_type[LESS_OR_EQUAL_TEST] = RELATIONAL_LESS_OR_EQUAL_RETE_TEST;
02332     test_type_to_relational_test_type[GREATER_OR_EQUAL_TEST] = RELATIONAL_GREATER_OR_EQUAL_RETE_TEST;
02333     test_type_to_relational_test_type[SAME_TYPE_TEST] = RELATIONAL_SAME_TYPE_RETE_TEST;
02334 
02335     relational_test_type_to_test_type[RELATIONAL_EQUAL_RETE_TEST] = EQUAL_TEST_TYPE;
02336     relational_test_type_to_test_type[RELATIONAL_NOT_EQUAL_RETE_TEST] = NOT_EQUAL_TEST;
02337     relational_test_type_to_test_type[RELATIONAL_LESS_RETE_TEST] = LESS_TEST;
02338     relational_test_type_to_test_type[RELATIONAL_GREATER_RETE_TEST] = GREATER_TEST;
02339     relational_test_type_to_test_type[RELATIONAL_LESS_OR_EQUAL_RETE_TEST] = LESS_OR_EQUAL_TEST;
02340     relational_test_type_to_test_type[RELATIONAL_GREATER_OR_EQUAL_RETE_TEST] = GREATER_OR_EQUAL_TEST;
02341     relational_test_type_to_test_type[RELATIONAL_SAME_TYPE_RETE_TEST] = SAME_TYPE_TEST;
02342 }
02343 
02344 /* ------------------------------------------------------------------------
02345                          Add Rete Tests for Test
02346 
02347    This is used for converting tests (from conditions) into the appropriate
02348    rete_test's and/or constant-to-be-tested-by-the-alpha-network.  It takes
02349    all sub-tests from a given test, converts them into the necessary Rete 
02350    tests (if any -- note that an equality test with a previously-unbound
02351    variable can be ignored), and destructively adds the Rete tests to
02352    the given "rt" parameter.  The "current_depth" and "field_num" params
02353    tell where the current test originated.
02354 
02355    For any field, we can handle one equality-with-a-constant test in the 
02356    alpha net.  If the "*alpha_constant" parameter is initially NIL, this 
02357    routine may also set *alpha_constant to point to the constant symbol 
02358    for the alpha net to test (rather than creating the corresponding  
02359    rete_test).
02360 
02361    Before calling this routine, variables should be bound densely for
02362    parent and higher conditions, and sparsely for the current condition.
02363 ------------------------------------------------------------------------ */
02364 
02365 void add_rete_tests_for_test(test t,
02366                              rete_node_level current_depth, byte field_num, rete_test ** rt, Symbol ** alpha_constant)
02367 {
02368     var_location where;
02369     cons *c;
02370     rete_test *new_rt;
02371     complex_test *ct;
02372     Symbol *referent;
02373 
02374     if (test_is_blank_test(t))
02375         return;
02376 
02377     if (test_is_blank_or_equality_test(t)) {
02378         referent = referent_of_equality_test(t);
02379 
02380         /* --- if constant test and alpha=NIL, install alpha test --- */
02381         if ((referent->common.symbol_type != VARIABLE_SYMBOL_TYPE) && (*alpha_constant == NIL)) {
02382             *alpha_constant = referent;
02383             return;
02384         }
02385 
02386         /* --- if constant, make = constant test --- */
02387         if (referent->common.symbol_type != VARIABLE_SYMBOL_TYPE) {
02388             allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02389             new_rt->right_field_num = field_num;
02390             new_rt->type = CONSTANT_RELATIONAL_RETE_TEST + RELATIONAL_EQUAL_RETE_TEST;
02391             new_rt->data.constant_referent = referent;
02392             symbol_add_ref(referent);
02393             new_rt->next = *rt;
02394             *rt = new_rt;
02395             return;
02396         }
02397 
02398         /* --- variable: if binding is for current field, do nothing --- */
02399         if (!find_var_location(referent, current_depth, &where)) {
02400             char msg[MESSAGE_SIZE];
02401             print_with_symbols("Error: Rete build found test of unbound var: %y\n", referent);
02402             snprintf(msg, MESSAGE_SIZE, "Error: Rete build found test of unbound var: %s\n",
02403                      symbol_to_string(referent, TRUE, NIL, 0));
02404             msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
02405             abort_with_fatal_error(msg);
02406         }
02407         if ((where.levels_up == 0) && (where.field_num == field_num))
02408             return;
02409 
02410         /* --- else make variable equality test --- */
02411         allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02412         new_rt->right_field_num = field_num;
02413         new_rt->type = VARIABLE_RELATIONAL_RETE_TEST + RELATIONAL_EQUAL_RETE_TEST;
02414         new_rt->data.variable_referent = where;
02415         new_rt->next = *rt;
02416         *rt = new_rt;
02417         return;
02418     }
02419 
02420     ct = complex_test_from_test(t);
02421 
02422     switch (ct->type) {
02423 
02424     case NOT_EQUAL_TEST:
02425     case LESS_TEST:
02426     case GREATER_TEST:
02427     case LESS_OR_EQUAL_TEST:
02428     case GREATER_OR_EQUAL_TEST:
02429     case SAME_TYPE_TEST:
02430         /* --- if constant, make constant test --- */
02431         if (ct->data.referent->common.symbol_type != VARIABLE_SYMBOL_TYPE) {
02432             allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02433             new_rt->right_field_num = field_num;
02434             new_rt->type = (unsigned char) (CONSTANT_RELATIONAL_RETE_TEST +
02435                                             test_type_to_relational_test_type[ct->type]);
02436             new_rt->data.constant_referent = ct->data.referent;
02437             symbol_add_ref(ct->data.referent);
02438             new_rt->next = *rt;
02439             *rt = new_rt;
02440             return;
02441         }
02442         /* --- else make variable test --- */
02443         if (!find_var_location(ct->data.referent, current_depth, &where)) {
02444             char msg[MESSAGE_SIZE];
02445             print_with_symbols("Error: Rete build found test of unbound var: %y\n", ct->data.referent);
02446             snprintf(msg, MESSAGE_SIZE, "Error: Rete build found test of unbound var: %s\n",
02447                      symbol_to_string(ct->data.referent, TRUE, NIL, 0));
02448             msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
02449             abort_with_fatal_error(msg);
02450         }
02451         allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02452         new_rt->right_field_num = field_num;
02453         new_rt->type = (unsigned char) (VARIABLE_RELATIONAL_RETE_TEST + test_type_to_relational_test_type[ct->type]);
02454         new_rt->data.variable_referent = where;
02455         new_rt->next = *rt;
02456         *rt = new_rt;
02457         return;
02458 
02459     case DISJUNCTION_TEST:
02460         allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02461         new_rt->right_field_num = field_num;
02462         new_rt->type = DISJUNCTION_RETE_TEST;
02463         new_rt->data.disjunction_list = copy_symbol_list_adding_references(ct->data.disjunction_list);
02464         new_rt->next = *rt;
02465         *rt = new_rt;
02466         return;
02467 
02468     case CONJUNCTIVE_TEST:
02469         for (c = ct->data.conjunct_list; c != NIL; c = c->rest) {
02470             add_rete_tests_for_test(c->first, current_depth, field_num, rt, alpha_constant);
02471         }
02472         return;
02473 
02474     case GOAL_ID_TEST:
02475         allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02476         new_rt->type = ID_IS_GOAL_RETE_TEST;
02477         new_rt->right_field_num = 0;
02478         new_rt->next = *rt;
02479         *rt = new_rt;
02480         return;
02481 
02482     case IMPASSE_ID_TEST:
02483         allocate_with_pool(&current_agent(rete_test_pool), &new_rt);
02484         new_rt->type = ID_IS_IMPASSE_RETE_TEST;
02485         new_rt->right_field_num = 0;
02486         new_rt->next = *rt;
02487         *rt = new_rt;
02488         return;
02489 
02490     default:
02491         {
02492             char msg[MESSAGE_SIZE];
02493             snprintf(msg, MESSAGE_SIZE, "Error: found bad test type %d while building rete\n", ct->type);
02494             msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
02495             abort_with_fatal_error(msg);
02496         }
02497     }                           /* end of switch statement */
02498 }                               /* end of function add_rete_tests_for_test() */
02499 
02500 /* ------------------------------------------------------------------------
02501                       Rete Test Lists are Identical
02502 
02503    This is used for checking whether an existing Rete node can be 
02504    shared, instead of building a new one.
02505 
02506    Single_rete_tests_are_identical() checks whether two (non-conjunctive)
02507    Rete tests are the same.  (Note that in the case of disjunction tests,
02508    the symbols in the disjunction have to be in the same order; this 
02509    simplifies and speeds up the code here, but unnecessarily reduces
02510    sharing.)
02511 
02512    Rete_test_lists_are_identical() checks whether two lists of Rete tests
02513    are identical.  (Note that the lists have to be in the order; the code
02514    here doesn't check all possible orderings.)
02515 ------------------------------------------------------------------------ */
02516 
02517 bool single_rete_tests_are_identical(rete_test * rt1, rete_test * rt2)
02518 {
02519     cons *c1, *c2;
02520 
02521     if (rt1->type != rt2->type)
02522         return FALSE;
02523 
02524     if (rt1->right_field_num != rt2->right_field_num)
02525         return FALSE;
02526 
02527     if (test_is_variable_relational_test(rt1->type))
02528         return (bool) (var_locations_equal(rt1->data.variable_referent, rt2->data.variable_referent));
02529 
02530     if (test_is_constant_relational_test(rt1->type)) {
02531         return (bool) (rt1->data.constant_referent == rt2->data.constant_referent);
02532     }
02533 
02534     if (rt1->type == ID_IS_GOAL_RETE_TEST)
02535         return TRUE;
02536     if (rt1->type == ID_IS_IMPASSE_RETE_TEST)
02537         return TRUE;
02538 
02539     if (rt1->type == DISJUNCTION_RETE_TEST) {
02540         c1 = rt1->data.disjunction_list;
02541         c2 = rt2->data.disjunction_list;
02542         while ((c1 != NIL) && (c2 != NIL)) {
02543             if (c1->first != c2->first)
02544                 return FALSE;
02545             c1 = c1->rest;
02546             c2 = c2->rest;
02547         }
02548         if (c1 == c2)
02549             return TRUE;
02550         return FALSE;
02551     }
02552     {
02553         char msg[MESSAGE_SIZE];
02554         strncpy(msg, "Internal error: bad rete test type in single_rete_tests_are_identical\n", MESSAGE_SIZE);
02555         msg[MESSAGE_SIZE - 1] = 0;
02556         abort_with_fatal_error(msg);
02557     }
02558     return FALSE;               /* unreachable, but without it, gcc -Wall warns here */
02559 }
02560 
02561 bool rete_test_lists_are_identical(rete_test * rt1, rete_test * rt2)
02562 {
02563     while (rt1 && rt2) {
02564         if (!single_rete_tests_are_identical(rt1, rt2))
02565             return FALSE;
02566         rt1 = rt1->next;
02567         rt2 = rt2->next;
02568     }
02569     if (rt1 == rt2)
02570         return TRUE;            /* make sure they both hit end-of-list */
02571     return FALSE;
02572 }
02573 
02574 /* ------------------------------------------------------------------------
02575                       Extract Rete Test to Hash With
02576 
02577    Extracts from a Rete test list the variable equality test to use for
02578    hashing.  Returns TRUE if successful, or FALSE if there was no such
02579    test to use for hashing.  The Rete test list ("rt") is destructively
02580    modified to splice out the extracted test.
02581 ------------------------------------------------------------------------ */
02582 
02583 bool extract_rete_test_to_hash_with(rete_test ** rt, var_location * dest_hash_loc)
02584 {
02585     rete_test *prev, *current;
02586 
02587     /* --- look through rt list, find the first variable equality test --- */
02588     prev = NIL;
02589     for (current = *rt; current != NIL; prev = current, current = current->next)
02590         if (current->type == VARIABLE_RELATIONAL_RETE_TEST + RELATIONAL_EQUAL_RETE_TEST)
02591             break;
02592 
02593     if (!current)
02594         return FALSE;           /* no variable equality test was found */
02595 
02596     /* --- unlink it from rt --- */
02597     if (prev)
02598         prev->next = current->next;
02599     else
02600         *rt = current->next;
02601 
02602     /* --- extract info, and deallocate that single test --- */
02603     *dest_hash_loc = current->data.variable_referent;
02604     current->next = NIL;
02605     deallocate_rete_test_list(current);
02606     return TRUE;
02607 }
02608 
02609 /* ------------------------------------------------------------------------
02610                        Make Node for Positive Cond
02611 
02612    Finds or creates a node for the given single condition <cond>, which
02613    must be a simple positive condition.  The node is made a child of the
02614    given <parent> node.  Variables for earlier conditions should be bound
02615    densely before this routine is called.  The routine returns a pointer 
02616    to the (newly-created or shared) node.
02617 ------------------------------------------------------------------------ */
02618 
02619 rete_node *make_node_for_positive_cond(condition * cond, rete_node_level current_depth, rete_node * parent)
02620 {
02621     byte pos_node_type, mem_node_type, mp_node_type;
02622     Symbol *alpha_id, *alpha_attr, *alpha_value;
02623     rete_node *node, *mem_node, *mp_node;
02624     alpha_mem *am;
02625     rete_test *rt;
02626     bool hash_this_node;
02627     var_location left_hash_loc;
02628     list *vars_bound_here;
02629 
02630     alpha_id = alpha_attr = alpha_value = NIL;
02631     rt = NIL;
02632     vars_bound_here = NIL;
02633 
02634     /* --- Add sparse variable bindings for this condition --- */
02635     bind_variables_in_test(cond->data.tests.id_test, current_depth, 0, FALSE, &vars_bound_here);
02636     bind_variables_in_test(cond->data.tests.attr_test, current_depth, 1, FALSE, &vars_bound_here);
02637     bind_variables_in_test(cond->data.tests.value_test, current_depth, 2, FALSE, &vars_bound_here);
02638 
02639     /* --- Get Rete tests, alpha constants, and hash location --- */
02640     add_rete_tests_for_test(cond->data.tests.id_test, current_depth, 0, &rt, &alpha_id);
02641     hash_this_node = extract_rete_test_to_hash_with(&rt, &left_hash_loc);
02642     add_rete_tests_for_test(cond->data.tests.attr_test, current_depth, 1, &rt, &alpha_attr);
02643     add_rete_tests_for_test(cond->data.tests.value_test, current_depth, 2, &rt, &alpha_value);
02644 
02645     /* --- Pop sparse variable bindings for this condition --- */
02646     pop_bindings_and_deallocate_list_of_variables(vars_bound_here);
02647 
02648     /* --- Get alpha memory --- */
02649     am = find_or_make_alpha_mem(alpha_id, alpha_attr, alpha_value, cond->test_for_acceptable_preference);
02650 
02651     /* --- Algorithm for adding node:
02652        1.  look for matching mem node; if found then
02653        look for matching join node; create new one if no match
02654        2.  no matching mem node:  look for mp node with matching mem
02655        if found, if join part matches too, then done
02656        else delete mp node, create mem node and 2 joins
02657        if not matching mem node, create new mp node. */
02658 
02659     /* --- determine desired node types --- */
02660     if (hash_this_node) {
02661         pos_node_type = POSITIVE_BNODE;
02662         mem_node_type = MEMORY_BNODE;
02663         mp_node_type = MP_BNODE;
02664     } else {
02665         pos_node_type = UNHASHED_POSITIVE_BNODE;
02666         mem_node_type = UNHASHED_MEMORY_BNODE;
02667         mp_node_type = UNHASHED_MP_BNODE;
02668     }
02669 
02670     /* --- look for a matching existing memory node --- */
02671     for (mem_node = parent->first_child; mem_node != NIL; mem_node = mem_node->next_sibling)
02672         if ((mem_node->node_type == mem_node_type) &&
02673             ((!hash_this_node) ||
02674              ((mem_node->left_hash_loc_field_num == left_hash_loc.field_num) &&
02675               (mem_node->left_hash_loc_levels_up == left_hash_loc.levels_up))))
02676             break;
02677 
02678     if (mem_node) {             /* -- A matching memory node was found --- */
02679         /* --- look for a matching existing join node --- */
02680         for (node = mem_node->first_child; node != NIL; node = node->next_sibling)
02681             if ((node->node_type == pos_node_type) &&
02682                 (am == node->b.posneg.alpha_mem) && rete_test_lists_are_identical(node->b.posneg.other_tests, rt))
02683                 break;
02684 
02685         if (node) {             /* --- A matching join node was found --- */
02686             deallocate_rete_test_list(rt);
02687             remove_ref_to_alpha_mem(am);
02688             return node;
02689         } else {                /* --- No match was found, so create a new node --- */
02690             node = make_new_positive_node(mem_node, pos_node_type, am, rt, FALSE);
02691             return node;
02692         }
02693     }
02694 
02695     /* --- No matching memory node was found; look for MP with matching M --- */
02696     for (mp_node = parent->first_child; mp_node != NIL; mp_node = mp_node->next_sibling)
02697         if ((mp_node->node_type == mp_node_type) &&
02698             ((!hash_this_node) ||
02699              ((mp_node->left_hash_loc_field_num == left_hash_loc.field_num) &&
02700               (mp_node->left_hash_loc_levels_up == left_hash_loc.levels_up))))
02701             break;
02702 
02703     if (mp_node) {              /* --- Found matching M part of MP --- */
02704         if ((am == mp_node->b.posneg.alpha_mem) && rete_test_lists_are_identical(mp_node->b.posneg.other_tests, rt)) {
02705             /* --- Complete MP match was found --- */
02706             deallocate_rete_test_list(rt);
02707             remove_ref_to_alpha_mem(am);
02708             return mp_node;
02709         }
02710 
02711         /* --- Delete MP node, replace it with M and two positive joins --- */
02712         mem_node = split_mp_node(mp_node);
02713         node = make_new_positive_node(mem_node, pos_node_type, am, rt, FALSE);
02714         return node;
02715     }
02716 
02717     /* --- Didn't even find a matching M part of MP, so make a new MP node --- */
02718     return make_new_mp_node(parent, mp_node_type, left_hash_loc, am, rt, FALSE);
02719 }
02720 
02721 /* ------------------------------------------------------------------------
02722                        Make Node for Negative Cond
02723 
02724    Finds or creates a node for the given single condition <cond>, which
02725    must be a simple negative (not ncc) condition.  The node is made a
02726    child of the given <parent> node.  Variables for earlier conditions 
02727    should be bound densely before this routine is called.  The routine 
02728    returns a pointer to the (newly-created or shared) node.
02729 ------------------------------------------------------------------------ */
02730 
02731 rete_node *make_node_for_negative_cond(condition * cond, rete_node_level current_depth, rete_node * parent)
02732 {
02733     byte node_type;
02734     Symbol *alpha_id, *alpha_attr, *alpha_value;
02735     rete_node *node;
02736     alpha_mem *am;
02737     rete_test *rt;
02738     bool hash_this_node;
02739     var_location left_hash_loc;
02740     list *vars_bound_here;
02741 
02742     alpha_id = alpha_attr = alpha_value = NIL;
02743     rt = NIL;
02744     vars_bound_here = NIL;
02745 
02746     /* --- Add sparse variable bindings for this condition --- */
02747     bind_variables_in_test(cond->data.tests.id_test, current_depth, 0, FALSE, &vars_bound_here);
02748     bind_variables_in_test(cond->data.tests.attr_test, current_depth, 1, FALSE, &vars_bound_here);
02749     bind_variables_in_test(cond->data.tests.value_test, current_depth, 2, FALSE, &vars_bound_here);
02750 
02751     /* --- Get Rete tests, alpha constants, and hash location --- */
02752     add_rete_tests_for_test(cond->data.tests.id_test, current_depth, 0, &rt, &alpha_id);
02753     hash_this_node = extract_rete_test_to_hash_with(&rt, &left_hash_loc);
02754     add_rete_tests_for_test(cond->data.tests.attr_test, current_depth, 1, &rt, &alpha_attr);
02755     add_rete_tests_for_test(cond->data.tests.value_test, current_depth, 2, &rt, &alpha_value);
02756 
02757     /* --- Pop sparse variable bindings for this condition --- */
02758     pop_bindings_and_deallocate_list_of_variables(vars_bound_here);
02759 
02760     /* --- Get alpha memory --- */
02761     am = find_or_make_alpha_mem(alpha_id, alpha_attr, alpha_value, cond->test_for_acceptable_preference);
02762 
02763     /* --- determine desired node type --- */
02764     node_type = (bool) (hash_this_node ? NEGATIVE_BNODE : UNHASHED_NEGATIVE_BNODE);
02765 
02766     /* --- look for a matching existing node --- */
02767     for (node = parent->first_child; node != NIL; node = node->next_sibling)
02768         if ((node->node_type == node_type) &&
02769             (am == node->b.posneg.alpha_mem) &&
02770             ((!hash_this_node) ||
02771              ((node->left_hash_loc_field_num == left_hash_loc.field_num) &&
02772               (node->left_hash_loc_levels_up == left_hash_loc.levels_up))) &&
02773             rete_test_lists_are_identical(node->b.posneg.other_tests, rt))
02774             break;
02775 
02776     if (node) {                 /* --- A matching node was found --- */
02777         deallocate_rete_test_list(rt);
02778         remove_ref_to_alpha_mem(am);
02779         return node;
02780     } else {                    /* --- No match was found, so create a new node --- */
02781         node = make_new_negative_node(parent, node_type, left_hash_loc, am, rt);
02782         return node;
02783     }
02784 }
02785 
02786 /* ------------------------------------------------------------------------
02787                       Build Network for Condition List
02788 
02789     This routine builds or shares the Rete network for the conditions in 
02790     the given <cond_list>.  <Depth_of_first_cond> tells the depth of the 
02791     first condition/node; <parent> gives the parent node under which the
02792     network should be built or shared.
02793 
02794     Three "dest" parameters may be used for returing results from this
02795     routine.  If <dest_bottom_node> is given as non-NIL, this routine
02796     fills it in with a pointer to the lowermost node in the resulting
02797     network.  If <dest_bottom_depth> is non-NIL, this routine fills it
02798     in with the depth of the lowermost node.  If <dest_vars_bound> is
02799     non_NIL, this routine fills it in with a list of variables bound
02800     in the given <cond_list>, and does not pop the bindings for those
02801     variables, in which case the caller is responsible for popping theose
02802     bindings.  If <dest_vars_bound> is given as NIL, then this routine
02803     pops the bindings, and the caller does not have to do the cleanup.
02804 ------------------------------------------------------------------------ */
02805 
02806 void build_network_for_condition_list(condition * cond_list,
02807                                       rete_node_level depth_of_first_cond,
02808                                       rete_node * parent,
02809                                       rete_node ** dest_bottom_node,
02810                                       rete_node_level * dest_bottom_depth, list ** dest_vars_bound)
02811 {
02812     rete_node *node, *new_node, *child, *subconditions_bottom_node;
02813     condition *cond;
02814     rete_node_level current_depth;
02815     list *vars_bound;
02816 
02817     node = parent;
02818     current_depth = depth_of_first_cond;
02819     vars_bound = NIL;
02820 
02821     for (cond = cond_list; cond != NIL; cond = cond->next) {
02822         switch (cond->type) {
02823 
02824         case POSITIVE_CONDITION:
02825             new_node = make_node_for_positive_cond(cond, current_depth, node);
02826             /* --- Add dense variable bindings for this condition --- */
02827             bind_variables_in_test(cond->data.tests.id_test, current_depth, 0, TRUE, &vars_bound);
02828             bind_variables_in_test(cond->data.tests.attr_test, current_depth, 1, TRUE, &vars_bound);
02829             bind_variables_in_test(cond->data.tests.value_test, current_depth, 2, TRUE, &vars_bound);
02830             break;
02831 
02832         case NEGATIVE_CONDITION:
02833             new_node = make_node_for_negative_cond(cond, current_depth, node);
02834             break;
02835 
02836         case CONJUNCTIVE_NEGATION_CONDITION:
02837             /* --- first, make the subconditions part of the rete --- */
02838             build_network_for_condition_list(cond->data.ncc.top, current_depth,
02839                                              node, &subconditions_bottom_node, NIL, NIL);
02840             /* --- look for an existing CN node --- */
02841             for (child = node->first_child; child != NIL; child = child->next_sibling)
02842                 if (child->node_type == CN_BNODE)
02843                     if (child->b.cn.partner->parent == subconditions_bottom_node)
02844                         break;
02845             /* --- share existing node or build new one --- */
02846             if (child) {
02847                 new_node = child;
02848             } else {
02849                 new_node = make_new_cn_node(node, subconditions_bottom_node);
02850             }
02851             break;
02852 
02853         default:
02854             new_node = NIL;     /* unreachable, but without it gcc -Wall warns here */
02855         }
02856 
02857         node = new_node;
02858         current_depth++;
02859     }
02860 
02861     /* --- return results to caller --- */
02862     if (dest_bottom_node)
02863         *dest_bottom_node = node;
02864     if (dest_bottom_depth)
02865         *dest_bottom_depth = (unsigned short) (current_depth - 1);
02866     if (dest_vars_bound) {
02867         *dest_vars_bound = vars_bound;
02868     } else {
02869         pop_bindings_and_deallocate_list_of_variables(vars_bound);
02870     }
02871 }
02872 
02873 /* ************************************************************************
02874  
02875    SECTION 9:  Production Addition and Excising
02876 
02877    EXTERNAL INTERFACE:
02878    Add_production_to_rete() adds a given production, with a given LHS,
02879    to the Rete.  Excise_production_from_rete() removes a given production
02880    from the Rete.
02881 ************************************************************************ */
02882 
02883 /* ---------------------------------------------------------------------
02884                              Same RHS
02885 
02886    Tests whether two RHS's (i.e., action lists) are the same (except
02887    for function calls).  This is used for finding duplicate productions.
02888 --------------------------------------------------------------------- */
02889 
02890 bool same_rhs(action * rhs1, action * rhs2)
02891 {
02892     action *a1, *a2;
02893 
02894     /* --- Scan through the two RHS's; make sure there's no function calls,
02895        and make sure the actions are all the same. --- */
02896     /* --- Warning: this relies on the representation of rhs_value's:
02897        two of the same funcall will not be equal (==), but two of the
02898        same symbol, reteloc, or unboundvar will be equal (==). --- */
02899 
02900     a1 = rhs1;
02901     a2 = rhs2;
02902 
02903     while (a1 && a2) {
02904         if (a1->type == FUNCALL_ACTION)
02905             return FALSE;
02906         if (a2->type == FUNCALL_ACTION)
02907             return FALSE;
02908         if (a1->preference_type != a2->preference_type)
02909             return FALSE;
02910         if (a1->id != a2->id)
02911             return FALSE;
02912         if (a1->attr != a2->attr)
02913             return FALSE;
02914         if (a1->value != a2->value)
02915             return FALSE;
02916         if (preference_is_binary(a1->preference_type))
02917             if (a1->referent != a2->referent)
02918                 return FALSE;
02919         a1 = a1->next;
02920         a2 = a2->next;
02921     }
02922 
02923     /* --- If we reached the end of one RHS but not the other, then
02924        they must be different --- */
02925     if (a1 != a2)
02926         return FALSE;
02927 
02928     /* --- If we got this far, the RHS's must be identical. --- */
02929     return TRUE;
02930 }
02931 
02932 /* ---------------------------------------------------------------------
02933                     Fixup RHS-Value Variable References
02934 
02935    After we've built the network for a production, we go through its 
02936    RHS and replace all the variables with reteloc's and unboundvar indices.
02937    For each variable <v> on the RHS, if <v> is bound on the LHS, then
02938    we replace RHS references to it with a specification of where its
02939    LHS binding can be found, e.g., "the value field four levels up".
02940    Each RHS variable <v> not bound on the LHS is replaced with an index,
02941    e.g., "unbound varible number 6".  As we're doing this, we keep track
02942    of the names of all the unbound variables.
02943 
02944    When this routine is called, variables should be bound (densely) for
02945    the entire LHS.
02946 --------------------------------------------------------------------- */
02947 
02948 /* --- names of RHS unbound vars, in reverse order (last on list is #1) --- */
02949 list *rhs_unbound_vars_for_new_prod;
02950 
02951 /* --- number of items in rhs_unbound_vars_for_new_prod --- */
02952 unsigned long num_rhs_unbound_vars_for_new_prod;
02953 
02954 /* --- TC num. for marking previously-encountered RHS unbound variables --- */
02955 tc_number rhs_unbound_vars_tc;
02956 
02957 void fixup_rhs_value_variable_references(rhs_value * rv, rete_node_level bottom_depth)
02958 {
02959     cons *c;
02960     Symbol *sym;
02961     var_location var_loc;
02962     unsigned long index;
02963 
02964     if (rhs_value_is_symbol(*rv)) {
02965         sym = rhs_value_to_symbol(*rv);
02966         if (sym->common.symbol_type != VARIABLE_SYMBOL_TYPE)
02967             return;
02968         /* --- Found a variable.  Is is bound on the LHS? --- */
02969         if (find_var_location(sym, (rete_node_level) (bottom_depth + 1), &var_loc)) {
02970             /* --- Yes, replace it with reteloc --- */
02971             symbol_remove_ref(sym);
02972             *rv = reteloc_to_rhs_value(var_loc.field_num, var_loc.levels_up - 1);
02973         } else {
02974             /* --- No, replace it with rhs_unboundvar --- */
02975             if (sym->var.tc_num != rhs_unbound_vars_tc) {
02976                 symbol_add_ref(sym);
02977                 push(sym, rhs_unbound_vars_for_new_prod);
02978                 sym->var.tc_num = rhs_unbound_vars_tc;
02979                 index = num_rhs_unbound_vars_for_new_prod++;
02980                 sym->var.current_binding_value = (Symbol *) index;
02981             } else {
02982                 index = (unsigned long) (sym->var.current_binding_value);
02983             }
02984             *rv = unboundvar_to_rhs_value(index);
02985             symbol_remove_ref(sym);
02986         }
02987         return;
02988     }
02989 
02990     if (rhs_value_is_funcall(*rv)) {
02991         for (c = rhs_value_to_funcall_list(*rv)->rest; c != NIL; c = c->rest)
02992             fixup_rhs_value_variable_references((rhs_value *) (&(c->first)), bottom_depth);
02993     }
02994 }
02995 
02996 /* ---------------------------------------------------------------------
02997                     Update Max RHS Unbound Variables
02998 
02999    When a production is fired, we use an array of gensyms to store 
03000    the bindings for the RHS unbound variables.  We have to grow the 
03001    memory block allocated for this array any time a production comes 
03002    along with more RHS unbound variables than we've ever seen before.
03003    This procedure checks the number of RHS unbound variables for a new
03004    production, and grows the array if necessary.
03005 --------------------------------------------------------------------- */
03006 
03007 void update_max_rhs_unbound_variables(unsigned long num_for_new_production)
03008 {
03009     if (num_for_new_production > current_agent(max_rhs_unbound_variables)) {
03010         free_memory(current_agent(rhs_variable_bindings), MISCELLANEOUS_MEM_USAGE);
03011         current_agent(max_rhs_unbound_variables) = num_for_new_production;
03012         current_agent(rhs_variable_bindings) = (Symbol **)
03013             allocate_memory_and_zerofill(current_agent(max_rhs_unbound_variables) *
03014                                          sizeof(Symbol *), MISCELLANEOUS_MEM_USAGE);
03015     }
03016 }
03017 
03018 /* ---------------------------------------------------------------------
03019                        Add Production to Rete
03020 
03021    Add_production_to_rete() adds a given production, with a given LHS,
03022    to the rete.  If "refracted_inst" is non-NIL, it should point to an
03023    initial instantiation of the production.  This routine returns 
03024    DUPLICATE_PRODUCTION if the production was a duplicate; else
03025    NO_REFRACTED_INST if no refracted inst. was given; else either
03026    REFRACTED_INST_MATCHED or REFRACTED_INST_DID_NOT_MATCH.
03027 
03028    The initial refracted instantiation is provided so the initial 
03029    instantiation of a newly-build chunk doesn't get fired.  We handle
03030    this as follows.  We store the initial instantiation as a "tentative
03031    retraction" on the new p-node.  Then we inform the p-node of any
03032    matches (tokens from above).  If any of them is the same as the
03033    refracted instantiation, then that instantiation will get removed
03034    from "tentative_retractions".  When the p-node has been informed of
03035    all matches, we just check whether the instantiation is still on
03036    tentative_retractions.  If not, there was a match (and the p-node's
03037    activation routine filled in the token info on the instantiation for
03038    us).  If so, there was no match for the refracted instantiation.
03039 
03040    BUGBUG should we check for duplicate justifications?
03041 --------------------------------------------------------------------- */
03042 
03043 byte add_production_to_rete(production * p,
03044                             condition * lhs_top, instantiation * refracted_inst, bool warn_on_duplicates)
03045 {
03046     rete_node *bottom_node, *p_node;
03047     rete_node_level bottom_depth;
03048     list *vars_bound;
03049     ms_change *msc;
03050     action *a;
03051     byte production_addition_result;
03052 
03053     /* --- build the network for all the conditions --- */
03054     build_network_for_condition_list(lhs_top, 1, current_agent(dummy_top_node),
03055                                      &bottom_node, &bottom_depth, &vars_bound);
03056 
03057     /* --- change variable names in RHS to Rete location references or
03058        unbound variable indices --- */
03059     rhs_unbound_vars_for_new_prod = NIL;
03060     num_rhs_unbound_vars_for_new_prod = 0;
03061     rhs_unbound_vars_tc = get_new_tc_number();
03062     for (a = p->action_list; a != NIL; a = a->next) {
03063         fixup_rhs_value_variable_references(&(a->value), bottom_depth);
03064         if (a->type == MAKE_ACTION) {
03065             fixup_rhs_value_variable_references(&(a->id), bottom_depth);
03066             fixup_rhs_value_variable_references(&(a->attr), bottom_depth);
03067             if (preference_is_binary(a->preference_type))
03068                 fixup_rhs_value_variable_references(&(a->referent), bottom_depth);
03069         }
03070     }
03071 
03072     /* --- clean up variable bindings created by build_network...() --- */
03073     pop_bindings_and_deallocate_list_of_variables(vars_bound);
03074 
03075     update_max_rhs_unbound_variables(num_rhs_unbound_vars_for_new_prod);
03076 
03077     /* --- look for an existing p node that matches --- */
03078     for (p_node = bottom_node->first_child; p_node != NIL; p_node = p_node->next_sibling) {
03079         if (p_node->node_type != P_BNODE)
03080             continue;
03081         if (!same_rhs(p_node->b.p.prod->action_list, p->action_list))
03082             continue;
03083         /* --- duplicate production found --- */
03084         if (warn_on_duplicates)
03085             print_with_symbols("\nIgnoring %y because it is a duplicate of %y ", p->name, p_node->b.p.prod->name);
03086         deallocate_symbol_list_removing_references(rhs_unbound_vars_for_new_prod);
03087         return DUPLICATE_PRODUCTION;
03088     }
03089 
03090     /* --- build a new p node --- */
03091     p_node = make_new_production_node(bottom_node, p);
03092     adjust_sharing_factors_from_here_to_top(p_node, 1);
03093 
03094     /* KJC 1/28/98  left these comments in to support REW comments below
03095        but commented out the operand_mode code  */
03096     /* RCHONG: begin 10.11 */
03097     /*
03098 
03099        in operand, we don't want to refract the instantiation.  consider
03100        this situation: a PE chunk was created during the IE phase.  that
03101        instantiation shouldn't be applied and we prevent this from
03102        happening (see chunk_instantiation() in chunk.c).  we eventually get
03103        to the OUTPUT_PHASE, then the QUIESCENCE_PHASE.  up to this point,
03104        the chunk hasn't done it's thing.  we start the PE_PHASE.  now, it
03105        is at this time that the just-built PE chunk should match and fire.
03106        if we were to refract the chunk, it wouldn't fire it at this point
03107        and it's actions would never occur.  by not refracting it, we allow
03108        the chunk to match and fire.
03109 
03110        caveat: we must refract justifications, otherwise they would fire
03111        and in doing so would produce more chunks/justifications.
03112 
03113        if ((current_agent(operand_mode) == TRUE) && 1)
03114        if (refracted_inst != NIL) {
03115        if (refracted_inst->prod->type != JUSTIFICATION_PRODUCTION_TYPE)
03116        refracted_inst = NIL;
03117        }
03118      */
03119     /* RCHONG: end 10.11 */
03120 
03121     /* REW: begin 09.15.96 */
03122     /* In Operand2, for now, we want both chunks and justifications to be
03123        treated as refracted instantiations, at least for now.  At some point,
03124        this issue needs to be re-visited for chunks that immediately match with
03125        a different instantiation and a different type of support than the
03126        original, chunk-creating instantion. */
03127     /* REW: end   09.15.96 */
03128 
03129     /* --- handle initial refraction by adding it to tentative_retractions --- */
03130     if (refracted_inst) {
03131         insert_at_head_of_dll(p->instantiations, refracted_inst, next, prev);
03132         refracted_inst->rete_token = NIL;
03133         refracted_inst->rete_wme = NIL;
03134         allocate_with_pool(&current_agent(ms_change_pool), &msc);
03135         msc->inst = refracted_inst;
03136         msc->p_node = p_node;
03137 /* REW: begin 08.20.97 */
03138         /* Because the RETE 'artificially' refracts this instantiation (ie, it is
03139            not actually firing -- the original instantiation fires but not the
03140            chunk), we make the refracted instantiation of the chunk a nil_goal
03141            retraction, rather than associating it with the activity of its match
03142            goal. In p_node_left_addition, where the tentative assertion will be
03143            generated, we make it a point to look at the goal value and exrtac
03144            from the appropriate list; here we just make a a simplifying
03145            assumption that the goal is NIL (although, in reality), it never will
03146            be.  */
03147 
03148         /* This initialization is necessary (for at least safety reasons, for all
03149            msc's, regardless of the mode */
03150         msc->level = 0;
03151         msc->goal = NIL;
03152 #ifndef SOAR_8_ONLY
03153         if (current_agent(operand2_mode)) {
03154 #endif
03155 
03156 #ifdef DEBUG_WATERFALL
03157             print_with_symbols("\n %y is a refracted instantiation", refracted_inst->prod->name);
03158 #endif
03159 
03160             insert_at_head_of_dll(current_agent(nil_goal_retractions), msc, next_in_level, prev_in_level);
03161 #ifndef SOAR_8_ONLY
03162         }
03163 #endif
03164 /* REW: end   08.20.97 */
03165 
03166 #ifdef BUG_139_WORKAROUND
03167         msc->p_node->b.p.prod->already_fired = 0;       /* RPM workaround for bug #139; mark prod as not fired yet */
03168 #endif
03169 
03170         insert_at_head_of_dll(current_agent(ms_retractions), msc, next, prev);
03171         insert_at_head_of_dll(p_node->b.p.tentative_retractions, msc, next_of_node, prev_of_node);
03172     }
03173 
03174     /* --- call new node's add_left routine with all the parent's tokens --- */
03175     update_node_with_matches_from_above(p_node);
03176 
03177     /* --- store result indicator --- */
03178     if (!refracted_inst) {
03179         production_addition_result = NO_REFRACTED_INST;
03180     } else {
03181         remove_from_dll(p->instantiations, refracted_inst, next, prev);
03182         if (p_node->b.p.tentative_retractions) {
03183             production_addition_result = REFRACTED_INST_DID_NOT_MATCH;
03184             msc = p_node->b.p.tentative_retractions;
03185             p_node->b.p.tentative_retractions = NIL;
03186             remove_from_dll(current_agent(ms_retractions), msc, next, prev);
03187             /* REW: begin 10.03.97 *//* BUGFIX 2.125 */
03188 #ifndef SOAR_8_ONLY
03189             if (current_agent(operand2_mode)) {
03190 #endif
03191                 if (msc->goal) {
03192                     remove_from_dll(msc->goal->id.ms_retractions, msc, next_in_level, prev_in_level);
03193                 } else {
03194                     remove_from_dll(current_agent(nil_goal_retractions), msc, next_in_level, prev_in_level);
03195                 }
03196 
03197 #ifndef SOAR_8_ONLY
03198             }
03199 #endif
03200             /* REW: end   10.03.97 */
03201 
03202             free_with_pool(&current_agent(ms_change_pool), msc);
03203 
03204         } else {
03205             production_addition_result = REFRACTED_INST_MATCHED;
03206         }
03207     }
03208 
03209     /* --- if not a chunk, store variable name information --- */
03210     if ((p->type == CHUNK_PRODUCTION_TYPE) && discard_chunk_varnames) {
03211         p->p_node->b.p.parents_nvn = NIL;
03212         p->rhs_unbound_variables = NIL;
03213         deallocate_symbol_list_removing_references(rhs_unbound_vars_for_new_prod);
03214     } else {
03215         p->p_node->b.p.parents_nvn = get_nvn_for_condition_list(lhs_top, NIL);
03216         p->rhs_unbound_variables = destructively_reverse_list(rhs_unbound_vars_for_new_prod);
03217     }
03218 
03219 #ifndef FEW_CALLBACKS
03220 
03221     /* --- invoke callback functions --- */
03222     soar_invoke_callbacks(soar_agent, PRODUCTION_JUST_ADDED_CALLBACK, (soar_call_data) p);
03223 #endif
03224 
03225     return production_addition_result;
03226 }
03227 
03228 /* ---------------------------------------------------------------------
03229                       Excise Production from Rete
03230 
03231    This removes a given production from the Rete net, and enqueues all 
03232    its existing instantiations as pending retractions.
03233 --------------------------------------------------------------------- */
03234 
03235 void excise_production_from_rete(production * p)
03236 {
03237     rete_node *p_node, *parent;
03238     ms_change *msc;
03239 
03240 #ifndef FEW_CALLBACKS
03241     soar_invoke_callbacks(soar_agent, PRODUCTION_JUST_ABOUT_TO_BE_EXCISED_CALLBACK, (soar_call_data) p);
03242 #endif
03243 
03244     p_node = p->p_node;
03245     p->p_node = NIL;            /* mark production as not being in the rete anymore */
03246     parent = p_node->parent;
03247 
03248     /* --- deallocate the variable name information --- */
03249     if (p_node->b.p.parents_nvn)
03250         deallocate_node_varnames(parent, current_agent(dummy_top_node), p_node->b.p.parents_nvn);
03251 
03252     /* --- cause all existing instantiations to retract, by removing any
03253        tokens at the node --- */
03254     while (p_node->a.np.tokens)
03255         remove_token_and_subtree(p_node->a.np.tokens);
03256 
03257     /* --- At this point, there are no tentative_assertion's.  Now set
03258        the p_node field of all tentative_retractions to NIL, to indicate
03259        that the p_node is being excised  --- */
03260     for (msc = p_node->b.p.tentative_retractions; msc != NIL; msc = msc->next_of_node)
03261         msc->p_node = NIL;
03262 
03263     /* --- finally, excise the p_node --- */
03264     remove_node_from_parents_list_of_children(p_node);
03265     update_stats_for_destroying_node(p_node);   /* clean up rete stats stuff */
03266     free_with_pool(&current_agent(rete_node_pool), p_node);
03267 
03268     /* --- update sharing factors on the path from here to the top node --- */
03269     adjust_sharing_factors_from_here_to_top(parent, -1);
03270 
03271     /* --- and propogate up the net --- */
03272     if (!parent->first_child)
03273         deallocate_rete_node(parent);
03274 }
03275 
03276 /* **********************************************************************
03277 
03278    SECTION 10:  Building Conditions (instantiated or not) from the Rete Net
03279 
03280    These routines are used for two things.  First, when we want to print
03281    out the source code for a production, we need to reconstruct its
03282    conditions and actions.  Second, when we fire a production, we need to
03283    build its instantiated conditions.  (These are used for run-time 
03284    o-support calculations and for backtracing.)
03285 
03286    Conceptually, we do this all top-down, by starting at the top Rete
03287    node and walking down to the p-node for the desired production.
03288    (The actual implementation starts at the p-node, of course, and 
03289    walks its way up the net recursively.)  As we work our way down, at
03290    each level:
03291       For instantiating a top-level positive condition:  
03292           Just build a simple instantiated condition by looking at the
03293           WME it matched.  Also record any "<>" tests.
03294       For instantiating anything else, or for rebuilding the LHS:  
03295           Look at the Rete node and use it to figure out what the 
03296           LHS condition looked like.  
03297 
03298    EXTERNAL INTERFACE:
03299    P_node_to_conditions_and_nots() takes a p_node and (optionally) a
03300    token/wme pair, and reconstructs the (optionally instantiated) LHS
03301    for the production.  It also reconstructs the RHS actions.
03302    Get_symbol_from_rete_loc() takes a token/wme pair and a location
03303    specification (levels_up/field_num), examines the match (token/wme),
03304    and returns the symbol at that location.
03305 ********************************************************************** */
03306 
03307 /* ----------------------------------------------------------------------
03308                       Add Gensymmed Equality Test
03309 
03310    This routine destructively modifies a given test, adding to it a test
03311    for equality with a new gensym variable.
03312 ---------------------------------------------------------------------- */
03313 
03314 void add_gensymmed_equality_test(test * t, char first_letter)
03315 {
03316     Symbol *new;
03317     test eq_test;
03318     char prefix[2];
03319 
03320     prefix[0] = first_letter;
03321     prefix[1] = 0;
03322     new = generate_new_variable(prefix);
03323     eq_test = make_equality_test(new);
03324     symbol_remove_ref(new);
03325     add_new_test_to_test(t, eq_test);
03326 }
03327 
03328 /* ----------------------------------------------------------------------
03329                      Var Bound in Reconstructed Conds
03330 
03331    We're reconstructing the conditions for a production in top-down
03332    fashion.  Suppose we come to a Rete test checking for equality with 
03333    the "value" field 3 levels up.  In that case, for the current condition,
03334    we want to include an equality test for whatever variable got bound
03335    in the value field 3 levels up.  This function scans up the list
03336    of conditions reconstructed so far, and finds the appropriate variable.
03337 ---------------------------------------------------------------------- */
03338 
03339 Symbol *var_bound_in_reconstructed_conds(condition * cond,      /* current cond */
03340                                          byte where_field_num, rete_node_level where_levels_up)
03341 {
03342     test t;
03343     complex_test *ct;
03344     cons *c;
03345 
03346     while (where_levels_up) {
03347         where_levels_up--;
03348         cond = cond->prev;
03349     }
03350 
03351     if (where_field_num == 0)
03352         t = cond->data.tests.id_test;
03353     else if (where_field_num == 1)
03354         t = cond->data.tests.attr_test;
03355     else
03356         t = cond->data.tests.value_test;
03357 
03358     if (test_is_blank_test(t))
03359         goto abort_var_bound_in_reconstructed_conds;
03360     if (test_is_blank_or_equality_test(t))
03361         return referent_of_equality_test(t);
03362 
03363     ct = complex_test_from_test(t);
03364     if (ct->type == CONJUNCTIVE_TEST) {
03365         for (c = ct->data.conjunct_list; c != NIL; c = c->rest)
03366             if ((!test_is_blank_test((test) (c->first))) && (test_is_blank_or_equality_test((test) (c->first))))
03367                 return referent_of_equality_test((test) (c->first));
03368     }
03369 
03370   abort_var_bound_in_reconstructed_conds:
03371     {
03372         char msg[MESSAGE_SIZE];
03373         strncpy(msg, "Internal error in var_bound_in_reconstructed_conds\n", MESSAGE_SIZE);
03374         msg[MESSAGE_SIZE - 1] = 0;
03375         abort_with_fatal_error(msg);
03376     }
03377     return 0;                   /* unreachable, but without it, gcc -Wall warns here */
03378 }
03379 
03380 /* ----------------------------------------------------------------------
03381                       Add Rete Test List to Tests
03382 
03383    Given the additional Rete tests (besides the hashed equality test) at
03384    a certain node, we need to convert them into the equivalent tests in
03385    the conditions being reconstructed.  This procedure does this -- it
03386    destructively modifies the given currently-being-reconstructed-cond
03387    by adding any necessary extra tests to its three field tests.
03388 ---------------------------------------------------------------------- */
03389 
03390 void add_rete_test_list_to_tests(condition * cond,      /* current cond */
03391                                  rete_test * rt)
03392 {
03393     Symbol *referent;
03394     test new;
03395     complex_test *new_ct;
03396     byte test_type;
03397 
03398     for (; rt != NIL; rt = rt->next) {
03399 
03400         if (rt->type == ID_IS_GOAL_RETE_TEST) {
03401             allocate_with_pool(&current_agent(complex_test_pool), &new_ct);
03402             new = make_test_from_complex_test(new_ct);
03403             new_ct->type = GOAL_ID_TEST;
03404         } else if (rt->type == ID_IS_IMPASSE_RETE_TEST) {
03405             allocate_with_pool(&current_agent(complex_test_pool), &new_ct);
03406             new = make_test_from_complex_test(new_ct);
03407             new_ct->type = IMPASSE_ID_TEST;
03408         } else if (rt->type == DISJUNCTION_RETE_TEST) {
03409             allocate_with_pool(&current_agent(complex_test_pool), &new_ct);
03410             new = make_test_from_complex_test(new_ct);
03411             new_ct->type = DISJUNCTION_TEST;
03412             new_ct->data.disjunction_list = copy_symbol_list_adding_references(rt->data.disjunction_list);
03413         } else if (test_is_constant_relational_test(rt->type)) {
03414             test_type = relational_test_type_to_test_type[kind_of_relational_test(rt->type)];
03415             referent = rt->data.constant_referent;
03416             symbol_add_ref(referent);
03417             if (test_type == EQUAL_TEST_TYPE) {
03418                 new = make_equality_test_without_adding_reference(referent);
03419             } else {
03420                 allocate_with_pool(&current_agent(complex_test_pool), &new_ct);
03421                 new = make_test_from_complex_test(new_ct);
03422                 new_ct->type = test_type;
03423                 new_ct->data.referent = referent;
03424             }
03425         } else if (test_is_variable_relational_test(rt->type)) {
03426             test_type = relational_test_type_to_test_type[kind_of_relational_test(rt->type)];
03427             if (!rt->data.variable_referent.levels_up) {
03428                 /* --- before calling var_bound_in_reconstructed_conds, make sure 
03429                    there's an equality test in the referent location (add one if
03430                    there isn't one already there), otherwise there'd be no variable
03431                    there to test against --- */
03432                 if (rt->data.variable_referent.field_num == 0) {
03433                     if (!test_includes_equality_test_for_symbol(cond->data.tests.id_test, NIL))
03434                         add_gensymmed_equality_test(&(cond->data.tests.id_test), 's');
03435                 } else if (rt->data.variable_referent.field_num == 1) {
03436                     if (!test_includes_equality_test_for_symbol(cond->data.tests.attr_test, NIL))
03437                         add_gensymmed_equality_test(&(cond->data.tests.attr_test), 'a');
03438                 } else {
03439                     if (!test_includes_equality_test_for_symbol(cond->data.tests.value_test, NIL))
03440                         add_gensymmed_equality_test(&(cond->data.tests.value_test),
03441                                                     first_letter_from_test(cond->data.tests.attr_test));
03442                 }
03443             }
03444             referent = var_bound_in_reconstructed_conds(cond,
03445                                                         rt->data.variable_referent.field_num,
03446                                                         rt->data.variable_referent.levels_up);
03447             symbol_add_ref(referent);
03448             if (test_type == EQUAL_TEST_TYPE) {
03449                 new = make_equality_test_without_adding_reference(referent);
03450             } else {
03451                 allocate_with_pool(&current_agent(complex_test_pool), &new_ct);
03452                 new = make_test_from_complex_test(new_ct);
03453                 new_ct->type = test_type;
03454                 new_ct->data.referent = referent;
03455             }
03456         } else {
03457             char msg[MESSAGE_SIZE];
03458             strncpy(msg, "Error: bad test_type in add_rete_test_to_test\n", MESSAGE_SIZE);
03459             msg[MESSAGE_SIZE - 1] = 0;
03460             abort_with_fatal_error(msg);
03461             new = NIL;          /* unreachable, but without it gcc -Wall warns here */
03462         }
03463 
03464         if (rt->right_field_num == 0)
03465             add_new_test_to_test(&(cond->data.tests.id_test), new);
03466         else if (rt->right_field_num == 2)
03467             add_new_test_to_test(&(cond->data.tests.value_test), new);
03468         else
03469             add_new_test_to_test(&(cond->data.tests.attr_test), new);
03470     }
03471 }
03472 
03473 /* ----------------------------------------------------------------------
03474                                Collect Nots
03475 
03476    When we build the instantiated conditions for a production being
03477    fired, we also record all the "<>" tests between pairs of identifiers.
03478    (This information is used during chunking.)  This procedure looks for
03479    any such <> tests in the given Rete test list (from the "other tests"
03480    at a Rete node), and adds records of them to the global variable
03481    nots_found_in_production.  "Right_wme" is the wme that matched
03482    the current condition; "cond" is the currently-being-reconstructed
03483    condition.
03484 ---------------------------------------------------------------------- */
03485 
03486 not *nots_found_in_production;  /* collected <> tests */
03487 
03488 void collect_nots(rete_test * rt, wme * right_wme, condition * cond)
03489 {
03490     not *new_not;
03491     Symbol *right_sym;
03492     Symbol *referent;
03493 
03494     for (; rt != NIL; rt = rt->next) {
03495 
03496         if (!test_is_not_equal_test(rt->type))
03497             continue;
03498 
03499         right_sym = field_from_wme(right_wme, rt->right_field_num);
03500 
03501         if (right_sym->common.symbol_type != IDENTIFIER_SYMBOL_TYPE)
03502             continue;
03503 
03504         if (rt->type == CONSTANT_RELATIONAL_RETE_TEST + RELATIONAL_NOT_EQUAL_RETE_TEST) {
03505             referent = rt->data.constant_referent;
03506             if (referent->common.symbol_type != IDENTIFIER_SYMBOL_TYPE)
03507                 continue;
03508             allocate_with_pool(&current_agent(not_pool), &new_not);
03509             new_not->next = nots_found_in_production;
03510             nots_found_in_production = new_not;
03511             new_not->s1 = right_sym;
03512             symbol_add_ref(right_sym);
03513             new_not->s2 = referent;
03514             symbol_add_ref(referent);
03515             continue;
03516         }
03517 
03518         if (rt->type == VARIABLE_RELATIONAL_RETE_TEST + RELATIONAL_NOT_EQUAL_RETE_TEST) {
03519             referent = var_bound_in_reconstructed_conds(cond,
03520                                                         rt->data.variable_referent.field_num,
03521                                                         rt->data.variable_referent.levels_up);
03522             if (referent->common.symbol_type != IDENTIFIER_SYMBOL_TYPE)
03523                 continue;
03524             allocate_with_pool(&current_agent(not_pool), &new_not);
03525             new_not->next = nots_found_in_production;
03526             nots_found_in_production = new_not;
03527             new_not->s1 = right_sym;
03528             symbol_add_ref(right_sym);
03529             new_not->s2 = referent;
03530             symbol_add_ref(referent);
03531             continue;
03532         }
03533     }
03534 }
03535 
03536 /* ----------------------------------------------------------------------
03537                           Add Varnames to Test
03538 
03539    This routine adds (an equality test for) each variable in "vn" to
03540    the given test "t", destructively modifying t.  This is used for
03541    restoring the original variables to test in a hand-coded production
03542    when we reconstruct its conditions.
03543 ---------------------------------------------------------------------- */
03544 
03545 void add_varnames_to_test(varnames * vn, test * t)
03546 {
03547     test new;
03548     cons *c;
03549 
03550     if (vn == NIL)
03551         return;
03552     if (varnames_is_one_var(vn)) {
03553         new = make_equality_test(varnames_to_one_var(vn));
03554         add_new_test_to_test(t, new);
03555     } else {
03556         for (c = varnames_to_var_list(vn); c != NIL; c = c->rest) {
03557             new = make_equality_test((Symbol *) (c->first));
03558             add_new_test_to_test(t, new);
03559         }
03560     }
03561 }
03562 
03563 /* ----------------------------------------------------------------------
03564                       Add Hash Info to ID Test
03565 
03566    This routine adds an equality test to the id field test in a given
03567    condition, destructively modifying that id test.  The equality test
03568    is the one appropriate for the given hash location (field_num/levels_up).
03569 ---------------------------------------------------------------------- */
03570 
03571 void add_hash_info_to_id_test(condition * cond, byte field_num, rete_node_level levels_up)
03572 {
03573     Symbol *temp;
03574     test new;
03575 
03576     temp = var_bound_in_reconstructed_conds(cond, field_num, levels_up);
03577     new = make_equality_test(temp);
03578     add_new_test_to_test(&(cond->data.tests.id_test), new);
03579 }
03580 
03581 /* ----------------------------------------------------------------------
03582                           Rete Node To Conditions
03583 
03584    This is the main routine for reconstructing the LHS source code, and 
03585    for building instantiated conditions when a production is fired.
03586    It builds the conditions corresponding to the given rete node ("node")
03587    and all its ancestors, up to the given "cutoff" node.  The given
03588    node_varnames structure "nvn", if non-NIL, should be the node_varnames
03589    corresponding to "node".  <tok,w> (if they are non-NIL) specifies the
03590    token/wme pair that emerged from "node" -- these are used only when
03591    firing, not when reconstructing.  "conds_for_cutoff_and_up" should be
03592    the lowermost cond in the already-constructed chain of conditions
03593    for the "cutoff" node and higher.  "Dest_top_cond" and "dest_bottom_cond"
03594    get filled in with the highest and lowest conditions built by this
03595    procedure.
03596 ---------------------------------------------------------------------- */
03597 
03598 /* BUGBUG clean this procedure up somehow? */
03599 
03600 void rete_node_to_conditions(rete_node * node,
03601                              node_varnames * nvn,
03602                              rete_node * cutoff,
03603                              token * tok,
03604                              wme * w,
03605                              condition * conds_for_cutoff_and_up,
03606                              condition ** dest_top_cond, condition ** dest_bottom_cond)
03607 {
03608     condition *cond;
03609     alpha_mem *am;
03610 
03611     allocate_with_pool(&current_agent(condition_pool), &cond);
03612     if (real_parent_node(node) == cutoff) {
03613         cond->prev = conds_for_cutoff_and_up;   /* if this is the top of an NCC, this
03614                                                    will get replaced by NIL later */
03615         *dest_top_cond = cond;
03616     } else {
03617         rete_node_to_conditions(real_parent_node(node),
03618                                 nvn ? nvn->parent : NIL,
03619                                 cutoff,
03620                                 tok ? tok->parent : NIL,
03621                                 tok ? tok->w : NIL, conds_for_cutoff_and_up, dest_top_cond, &(cond->prev));
03622         cond->prev->next = cond;
03623     }
03624     cond->next = NIL;
03625     *dest_bottom_cond = cond;
03626 
03627     if (node->node_type == CN_BNODE) {
03628         cond->type = CONJUNCTIVE_NEGATION_CONDITION;
03629         rete_node_to_conditions(node->b.cn.partner->parent,
03630                                 nvn ? nvn->data.bottom_of_subconditions : NIL,
03631                                 node->parent, NIL, NIL, cond->prev, &(cond->data.ncc.top), &(cond->data.ncc.bottom));
03632         cond->data.ncc.top->prev = NIL;
03633     } else {
03634         if (bnode_is_positive(node->node_type))
03635             cond->type = POSITIVE_CONDITION;
03636         else
03637             cond->type = NEGATIVE_CONDITION;
03638 
03639         if (w && (cond->type == POSITIVE_CONDITION)) {
03640             /* --- make simple tests and collect nots --- */
03641             cond->data.tests.id_test = make_equality_test(w->id);
03642             cond->data.tests.attr_test = make_equality_test(w->attr);
03643             cond->data.tests.value_test = make_equality_test(w->value);
03644             cond->test_for_acceptable_preference = w->acceptable;
03645             cond->bt.wme = w;
03646             if (node->b.posneg.other_tests)     /* don't bother if there are no tests */
03647                 collect_nots(node->b.posneg.other_tests, w, cond);
03648         } else {
03649             am = node->b.posneg.alpha_mem;
03650             cond->data.tests.id_test = make_blank_or_equality_test(am->id);
03651             cond->data.tests.attr_test = make_blank_or_equality_test(am->attr);
03652             cond->data.tests.value_test = make_blank_or_equality_test(am->value);
03653             cond->test_for_acceptable_preference = am->acceptable;
03654 
03655             if (nvn) {
03656                 add_varnames_to_test(nvn->data.fields.id_varnames, &(cond->data.tests.id_test));
03657                 add_varnames_to_test(nvn->data.fields.attr_varnames, &(cond->data.tests.attr_test));
03658                 add_varnames_to_test(nvn->data.fields.value_varnames, &(cond->data.tests.value_test));
03659             }
03660 
03661             /* --- on hashed nodes, add equality test for the hash function --- */
03662             if ((node->node_type == MP_BNODE) || (node->node_type == NEGATIVE_BNODE)) {
03663                 add_hash_info_to_id_test(cond, node->left_hash_loc_field_num, node->left_hash_loc_levels_up);
03664             } else if (node->node_type == POSITIVE_BNODE) {
03665                 add_hash_info_to_id_test(cond,
03666                                          node->parent->left_hash_loc_field_num, node->parent->left_hash_loc_levels_up);
03667             }
03668 
03669             /* --- if there are other tests, add them too --- */
03670             if (node->b.posneg.other_tests)
03671                 add_rete_test_list_to_tests(cond, node->b.posneg.other_tests);
03672 
03673             /* --- if we threw away the variable names, make sure there's some 
03674                equality test in each of the three fields --- */
03675             if (!nvn) {
03676                 if (!test_includes_equality_test_for_symbol(cond->data.tests.id_test, NIL))
03677                     add_gensymmed_equality_test(&(cond->data.tests.id_test), 's');
03678                 if (!test_includes_equality_test_for_symbol(cond->data.tests.attr_test, NIL))
03679                     add_gensymmed_equality_test(&(cond->data.tests.attr_test), 'a');
03680                 if (!test_includes_equality_test_for_symbol(cond->data.tests.value_test, NIL))
03681                     add_gensymmed_equality_test(&(cond->data.tests.value_test),
03682                                                 first_letter_from_test(cond->data.tests.attr_test));
03683             }
03684         }
03685     }
03686 }
03687 
03688 /* -------------------------------------------------------------------
03689              Reconstructing the RHS Actions of a Production
03690 
03691    When we print a production (but not when we fire one), we have to 
03692    reconstruct the RHS actions.  This is because many of the variables
03693    in the RHS have been replaced by references to Rete locations (i.e.,
03694    rather than specifying <v>, we specify "value field 3 levels up"
03695    or "the 7th RHS unbound variable".  The routines below copy rhs_value's
03696    and actions, and substitute variable names for such references.
03697    For RHS unbound variables, we gensym new variable names.
03698 ------------------------------------------------------------------- */
03699 
03700 long highest_rhs_unboundvar_index;
03701 
03702 rhs_value copy_rhs_value_and_substitute_varnames(rhs_value rv, condition * cond, char first_letter)
03703 {
03704     cons *c, *new_c, *prev_new_c;
03705     list *fl, *new_fl;
03706     Symbol *sym;
03707     long index;
03708     char prefix[2];
03709 
03710     if (rhs_value_is_reteloc(rv)) {
03711         sym = var_bound_in_reconstructed_conds(cond,
03712                                                (byte) rhs_value_to_reteloc_field_num(rv),
03713                                                (rete_node_level) rhs_value_to_reteloc_levels_up(rv));
03714         symbol_add_ref(sym);
03715         return symbol_to_rhs_value(sym);
03716     }
03717 
03718     if (rhs_value_is_unboundvar(rv)) {
03719         index = rhs_value_to_unboundvar(rv);
03720         if (!*(current_agent(rhs_variable_bindings) + index)) {
03721             prefix[0] = first_letter;
03722             prefix[1] = 0;
03723             sym = generate_new_variable(prefix);
03724             *(current_agent(rhs_variable_bindings) + index) = sym;
03725             if (highest_rhs_unboundvar_index < index)
03726                 highest_rhs_unboundvar_index = index;
03727         } else {
03728             sym = *(current_agent(rhs_variable_bindings) + index);
03729             symbol_add_ref(sym);
03730         }
03731         return symbol_to_rhs_value(sym);
03732     }
03733 
03734     if (rhs_value_is_funcall(rv)) {
03735         fl = rhs_value_to_funcall_list(rv);
03736         allocate_cons(&new_fl);
03737         new_fl->first = fl->first;
03738         prev_new_c = new_fl;
03739         for (c = fl->rest; c != NIL; c = c->rest) {
03740             allocate_cons(&new_c);
03741             new_c->first = copy_rhs_value_and_substitute_varnames(c->first, cond, first_letter);
03742             prev_new_c->rest = new_c;
03743             prev_new_c = new_c;
03744         }
03745         prev_new_c->rest = NIL;
03746         return funcall_list_to_rhs_value(new_fl);
03747     } else {
03748         symbol_add_ref(rhs_value_to_symbol(rv));
03749         return rv;
03750     }
03751 }
03752 
03753 action *copy_action_list_and_substitute_varnames(action * actions, condition * cond)
03754 {
03755     action *old, *new, *prev, *first;
03756     char first_letter;
03757 
03758     prev = NIL;
03759     first = NIL;                /* unneeded, but without it gcc -Wall warns here */
03760     old = actions;
03761     while (old) {
03762         allocate_with_pool(&current_agent(action_pool), &new);
03763         if (prev)
03764             prev->next = new;
03765         else
03766             first = new;
03767         prev = new;
03768         new->type = old->type;
03769         new->preference_type = old->preference_type;
03770         new->support = old->support;
03771         if (old->type == FUNCALL_ACTION) {
03772             new->value = copy_rhs_value_and_substitute_varnames(old->value, cond, 'v');
03773         } else {
03774             new->id = copy_rhs_value_and_substitute_varnames(old->id, cond, 's');
03775             new->attr = copy_rhs_value_and_substitute_varnames(old->attr, cond, 'a');
03776             first_letter = first_letter_from_rhs_value(new->attr);
03777             new->value = copy_rhs_value_and_substitute_varnames(old->value, cond, first_letter);
03778             if (preference_is_binary(old->preference_type))
03779                 new->referent = copy_rhs_value_and_substitute_varnames(old->referent, cond, first_letter);
03780         }
03781         old = old->next;
03782     }
03783     if (prev)
03784         prev->next = NIL;
03785     else
03786         first = NIL;
03787     return first;
03788 }
03789 
03790 /* -----------------------------------------------------------------------
03791                      P Node to Conditions and Nots
03792                        Get Symbol From Rete Loc
03793 
03794    P_node_to_conditions_and_nots() takes a p_node and (optionally) a
03795    token/wme pair, and reconstructs the (optionally instantiated) LHS
03796    for the production.  If "dest_rhs" is non-NIL, it also reconstructs
03797    the RHS actions, and fills in dest_rhs with the action list.
03798    Note: if tok!=NIL, this routine also returns (in dest_nots) the
03799    top-level positive "<>" tests.  If tok==NIL, dest_nots is not used.
03800 
03801    Get_symbol_from_rete_loc() takes a token/wme pair and a location
03802    specification (levels_up/field_num), examines the match (token/wme),
03803    and returns the symbol at that location.  The firer uses this for
03804    resolving references in RHS actions to variables bound on the LHS.
03805 ----------------------------------------------------------------------- */
03806 
03807 void p_node_to_conditions_and_nots(rete_node * p_node,
03808                                    token * tok,
03809                                    wme * w,
03810                                    condition ** dest_top_cond,
03811                                    condition ** dest_bottom_cond, not ** dest_nots, action ** dest_rhs)
03812 {
03813     cons *c;
03814     Symbol **cell;
03815     long index;
03816     production *prod;
03817 
03818     prod = p_node->b.p.prod;
03819 
03820     nots_found_in_production = NIL;
03821     if (tok == NIL)
03822         w = NIL;                /* just for safety */
03823     reset_variable_generator(NIL, NIL); /* we'll be gensymming new vars */
03824     rete_node_to_conditions(p_node->parent,
03825                             p_node->b.p.parents_nvn,
03826                             current_agent(dummy_top_node), tok, w, NIL, dest_top_cond, dest_bottom_cond);
03827     if (tok)
03828         *dest_nots = nots_found_in_production;
03829     nots_found_in_production = NIL;     /* just for safety */
03830     if (dest_rhs) {
03831         highest_rhs_unboundvar_index = -1;
03832         if (prod->rhs_unbound_variables) {
03833             cell = current_agent(rhs_variable_bindings);
03834             for (c = prod->rhs_unbound_variables; c != NIL; c = c->rest) {
03835                 *(cell++) = c->first;
03836                 highest_rhs_unboundvar_index++;
03837             }
03838         }
03839         *dest_rhs = copy_action_list_and_substitute_varnames(prod->action_list, *dest_bottom_cond);
03840         index = 0;
03841         cell = current_agent(rhs_variable_bindings);
03842         while (index++ <= highest_rhs_unboundvar_index)
03843             *(cell++) = NIL;
03844     }
03845 }
03846 
03847 Symbol *get_symbol_from_rete_loc(unsigned short levels_up, byte field_num, token * tok, wme * w)
03848 {
03849     while (levels_up) {
03850         levels_up--;
03851         w = tok->w;
03852         tok = tok->parent;
03853     }
03854     if (field_num == 0)
03855         return w->id;
03856     if (field_num == 1)
03857         return w->attr;
03858     return w->value;
03859 }
03860 
03861 /* **********************************************************************
03862 
03863    SECTION 11:  Rete Test Evaluation Routines
03864 
03865    These routines perform the "other tests" stored at positive and
03866    negative join nodes.  Each is passed parameters: the rete_test
03867    to be performed, and the <token,wme> pair on which to perform the
03868    test.
03869 ********************************************************************** */
03870 
03871 bool((*(rete_test_routines[256]))
03872      (rete_test * rt, token * left, wme * w));
03873 
03874 #define match_left_and_right(rete_test,left,w) \
03875   ( (*(rete_test_routines[(rete_test)->type])) \
03876     ((rete_test),(left),(w)) )
03877 
03878 #define numeric_comparison_between_symbols(s1,s2,comparator_op) ( \
03879   ( ((s1)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) && \
03880     ((s2)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) ) ? \
03881     (((s1)->ic.value) comparator_op ((s2)->ic.value)) : \
03882   ( ((s1)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) && \
03883     ((s2)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) ) ? \
03884     (((s1)->ic.value) comparator_op ((s2)->fc.value)) : \
03885   ( ((s1)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) && \
03886     ((s2)->common.symbol_type==INT_CONSTANT_SYMBOL_TYPE) ) ? \
03887     (((s1)->fc.value) comparator_op ((s2)->ic.value)) : \
03888   ( ((s1)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) && \
03889     ((s2)->common.symbol_type==FLOAT_CONSTANT_SYMBOL_TYPE) ) ? \
03890     (((s1)->fc.value) comparator_op ((s2)->fc.value)) : \
03891   FALSE )
03892 
03893 /* Note:  "=" and "<>" tests always return FALSE when one argument is
03894    an integer and the other is a floating point number */
03895 
03896 bool error_rete_test_routine(rete_test * rt, token * left, wme * w)
03897 {
03898 
03899     char msg[MESSAGE_SIZE];
03900     strncpy(msg, "Internal error: bad rete test type, hit error_rete_test_routine\n", MESSAGE_SIZE);
03901     msg[MESSAGE_SIZE - 1] = 0;
03902     abort_with_fatal_error(msg);
03903 
03904     rt = rt;                    /* unreachable, but without it, compilers warn here */
03905     left = left;                /* unreachable, but without it, compilers warn here */
03906     w = w;                      /* unreachable, but without it, compilers warn here */
03907     return FALSE;               /* unreachable, but without it, gcc -Wall warns here */
03908 }
03909 
03910 bool id_is_goal_rete_test_routine(rete_test * rt, token * left, wme * w)
03911 {
03912     rt = rt;
03913     left = left;
03914 
03915     return w->id->id.isa_goal;
03916 }
03917 
03918 bool id_is_impasse_rete_test_routine(rete_test * rt, token * left, wme * w)
03919 {
03920 
03921     left = left;
03922     rt = rt;
03923 
03924     return w->id->id.isa_impasse;
03925 }
03926 
03927 bool disjunction_rete_test_routine(rete_test * rt, token * left, wme * w)
03928 {
03929     Symbol *sym;
03930     cons *c;
03931 
03932     left = left;
03933 
03934     sym = field_from_wme(w, rt->right_field_num);
03935     for (c = rt->data.disjunction_list; c != NIL; c = c->rest)
03936         if (c->first == sym)
03937             return TRUE;
03938     return FALSE;
03939 }
03940 
03941 bool constant_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
03942 {
03943     Symbol *s1, *s2;
03944 
03945     left = left;
03946 
03947     s1 = field_from_wme(w, rt->right_field_num);
03948     s2 = rt->data.constant_referent;
03949     return (bool) (s1 == s2);
03950 }
03951 
03952 bool constant_not_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
03953 {
03954     Symbol *s1, *s2;
03955 
03956     left = left;
03957 
03958     s1 = field_from_wme(w, rt->right_field_num);
03959     s2 = rt->data.constant_referent;
03960     return (bool) (s1 != s2);
03961 }
03962 
03963 bool constant_less_rete_test_routine(rete_test * rt, token * left, wme * w)
03964 {
03965     Symbol *s1, *s2;
03966 
03967     left = left;
03968 
03969     s1 = field_from_wme(w, rt->right_field_num);
03970     s2 = rt->data.constant_referent;
03971     return (bool) numeric_comparison_between_symbols(s1, s2, <);
03972 }
03973 
03974 bool constant_greater_rete_test_routine(rete_test * rt, token * left, wme * w)
03975 {
03976     Symbol *s1, *s2;
03977 
03978     left = left;
03979 
03980     s1 = field_from_wme(w, rt->right_field_num);
03981     s2 = rt->data.constant_referent;
03982     return (bool) numeric_comparison_between_symbols(s1, s2, >);
03983 }
03984 
03985 bool constant_less_or_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
03986 {
03987     Symbol *s1, *s2;
03988 
03989     left = left;
03990 
03991     s1 = field_from_wme(w, rt->right_field_num);
03992     s2 = rt->data.constant_referent;
03993     return (bool) numeric_comparison_between_symbols(s1, s2, <=);
03994 }
03995 
03996 bool constant_greater_or_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
03997 {
03998     Symbol *s1, *s2;
03999 
04000     left = left;
04001 
04002     s1 = field_from_wme(w, rt->right_field_num);
04003     s2 = rt->data.constant_referent;
04004     return (bool) numeric_comparison_between_symbols(s1, s2, >=);
04005 }
04006 
04007 bool constant_same_type_rete_test_routine(rete_test * rt, token * left, wme * w)
04008 {
04009     Symbol *s1, *s2;
04010 
04011     left = left;
04012 
04013     s1 = field_from_wme(w, rt->right_field_num);
04014     s2 = rt->data.constant_referent;
04015     return (bool) (s1->common.symbol_type == s2->common.symbol_type);
04016 }
04017 
04018 bool variable_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
04019 {
04020     Symbol *s1, *s2;
04021     int i;
04022 
04023     s1 = field_from_wme(w, rt->right_field_num);
04024 
04025     if (rt->data.variable_referent.levels_up != 0) {
04026         i = rt->data.variable_referent.levels_up - 1;
04027         while (i != 0) {
04028             left = left->parent;
04029             i--;
04030         }
04031         w = left->w;
04032     }
04033     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04034 
04035     return (bool) (s1 == s2);
04036 }
04037 
04038 bool variable_not_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
04039 {
04040     Symbol *s1, *s2;
04041     int i;
04042 
04043     s1 = field_from_wme(w, rt->right_field_num);
04044 
04045     if (rt->data.variable_referent.levels_up != 0) {
04046         i = rt->data.variable_referent.levels_up - 1;
04047         while (i != 0) {
04048             left = left->parent;
04049             i--;
04050         }
04051         w = left->w;
04052     }
04053     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04054 
04055     return (bool) (s1 != s2);
04056 }
04057 
04058 bool variable_less_rete_test_routine(rete_test * rt, token * left, wme * w)
04059 {
04060     Symbol *s1, *s2;
04061     int i;
04062 
04063     s1 = field_from_wme(w, rt->right_field_num);
04064 
04065     if (rt->data.variable_referent.levels_up != 0) {
04066         i = rt->data.variable_referent.levels_up - 1;
04067         while (i != 0) {
04068             left = left->parent;
04069             i--;
04070         }
04071         w = left->w;
04072     }
04073     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04074 
04075     return (bool) numeric_comparison_between_symbols(s1, s2, <);
04076 }
04077 
04078 bool variable_greater_rete_test_routine(rete_test * rt, token * left, wme * w)
04079 {
04080     Symbol *s1, *s2;
04081     int i;
04082 
04083     s1 = field_from_wme(w, rt->right_field_num);
04084 
04085     if (rt->data.variable_referent.levels_up != 0) {
04086         i = rt->data.variable_referent.levels_up - 1;
04087         while (i != 0) {
04088             left = left->parent;
04089             i--;
04090         }
04091         w = left->w;
04092     }
04093     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04094 
04095     return (bool) numeric_comparison_between_symbols(s1, s2, >);
04096 }
04097 
04098 bool variable_less_or_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
04099 {
04100     Symbol *s1, *s2;
04101     int i;
04102 
04103     s1 = field_from_wme(w, rt->right_field_num);
04104 
04105     if (rt->data.variable_referent.levels_up != 0) {
04106         i = rt->data.variable_referent.levels_up - 1;
04107         while (i != 0) {
04108             left = left->parent;
04109             i--;
04110         }
04111         w = left->w;
04112     }
04113     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04114 
04115     return (bool) numeric_comparison_between_symbols(s1, s2, <=);
04116 }
04117 
04118 bool variable_greater_or_equal_rete_test_routine(rete_test * rt, token * left, wme * w)
04119 {
04120     Symbol *s1, *s2;
04121     int i;
04122 
04123     s1 = field_from_wme(w, rt->right_field_num);
04124 
04125     if (rt->data.variable_referent.levels_up != 0) {
04126         i = rt->data.variable_referent.levels_up - 1;
04127         while (i != 0) {
04128             left = left->parent;
04129             i--;
04130         }
04131         w = left->w;
04132     }
04133     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04134 
04135     return (bool) numeric_comparison_between_symbols(s1, s2, >=);
04136 }
04137 
04138 bool variable_same_type_rete_test_routine(rete_test * rt, token * left, wme * w)
04139 {
04140     Symbol *s1, *s2;
04141     int i;
04142 
04143     s1 = field_from_wme(w, rt->right_field_num);
04144 
04145     if (rt->data.variable_referent.levels_up != 0) {
04146         i = rt->data.variable_referent.levels_up - 1;
04147         while (i != 0) {
04148             left = left->parent;
04149             i--;
04150         }
04151         w = left->w;
04152     }
04153     s2 = field_from_wme(w, rt->data.variable_referent.field_num);
04154     return (bool) (s1->common.symbol_type == s2->common.symbol_type);
04155 }
04156 
04157 /* ************************************************************************
04158 
04159    SECTION 12:  Beta Node Interpreter Routines: Mem, Pos, and MP Nodes
04160 
04161 ************************************************************************ */
04162 
04163 void positive_node_left_addition(rete_node * node, token * new, Symbol * hash_referent);
04164 void unhashed_positive_node_left_addition(rete_node * node, token * new);
04165 
04166 void rete_error_left(rete_node * node, token * t, wme * w)
04167 {
04168     char msg[MESSAGE_SIZE];
04169 
04170     w = w;
04171     t = t;
04172 
04173     snprintf(msg, MESSAGE_SIZE, "Rete net error:  tried to left-activate node of type %d\n", node->node_type);
04174     msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
04175     abort_with_fatal_error(msg);
04176 }
04177 
04178 void rete_error_right(rete_node * node, wme * w)
04179 {
04180     char msg[MESSAGE_SIZE];
04181 
04182     w = w;
04183 
04184     snprintf(msg, MESSAGE_SIZE, "Rete net error:  tried to right-activate node of type %d\n", node->node_type);
04185     msg[MESSAGE_SIZE - 1] = 0;  /* snprintf doesn't set last char to null if output is truncated */
04186     abort_with_fatal_error(msg);
04187 }
04188 
04189 void beta_memory_node_left_addition(rete_node * node, token * tok, wme * w)
04190 {
04191     unsigned long hv;
04192     Symbol *referent;
04193     rete_node *child, *next;
04194     token *new;
04195 
04196     activation_entry_sanity_check();
04197     left_node_activation(node, TRUE);
04198 
04199     {
04200         int levels_up;
04201         token *t;
04202 
04203         levels_up = node->left_hash_loc_levels_up;
04204         if (levels_up == 1) {
04205             referent = field_from_wme(w, node->left_hash_loc_field_num);
04206         } else {                /* --- levels_up > 1 --- */
04207             for (t = tok, levels_up -= 2; levels_up != 0; levels_up--)
04208                 t = t->parent;
04209             referent = field_from_wme(t->w, node->left_hash_loc_field_num);
04210         }
04211     }
04212 
04213     hv = node->node_id ^ referent->common.hash_id;
04214 
04215     /* --- build new left token, add it to the hash table --- */
04216     token_added(node);
04217     allocate_with_pool(&current_agent(token_pool), &new);
04218     new_left_token(new, node, tok, w);
04219     insert_token_into_left_ht(new, hv);
04220     new->a.ht.referent = referent;
04221 
04222     /* --- inform each linked child (positive join) node --- */
04223     for (child = node->b.mem.first_linked_child; child != NIL; child = next) {
04224         next = child->a.pos.next_from_beta_mem;
04225         positive_node_left_addition(child, new, referent);
04226     }
04227     activation_exit_sanity_check();
04228 }
04229 
04230 void unhashed_beta_memory_node_left_addition(rete_node * node, token * tok, wme * w)
04231 {
04232     unsigned long hv;
04233     rete_node *child, *next;
04234     token *new;
04235 
04236     activation_entry_sanity_check();
04237     left_node_activation(node, TRUE);
04238 
04239     hv = node->node_id;
04240 
04241     /* --- build new left token, add it to the hash table --- */
04242     token_added(node);
04243     allocate_with_pool(&current_agent(token_pool), &new);
04244     new_left_token(new, node, tok, w);
04245     insert_token_into_left_ht(new, hv);
04246     new->a.ht.referent = NIL;
04247 
04248     /* --- inform each linked child (positive join) node --- */
04249     for (child = node->b.mem.first_linked_child; child != NIL; child = next) {
04250         next = child->a.pos.next_from_beta_mem;
04251         unhashed_positive_node_left_addition(child, new);
04252     }
04253     activation_exit_sanity_check();
04254 }
04255 
04256 void positive_node_left_addition(rete_node * node, token * new, Symbol * hash_referent)
04257 {
04258     unsigned long right_hv;
04259     right_mem *rm;
04260     alpha_mem *am;
04261     rete_test *rt;
04262     bool failed_a_test;
04263     rete_node *child;
04264 
04265     activation_entry_sanity_check();
04266     left_node_activation(node, TRUE);
04267 
04268     am = node->b.posneg.alpha_mem;
04269 
04270     if (node_is_right_unlinked(node)) {
04271         relink_to_right_mem(node);
04272         if (am->right_mems == NIL) {
04273             unlink_from_left_mem(node);
04274             activation_exit_sanity_check();
04275             return;
04276         }
04277     }
04278 
04279     /* --- look through right memory for matches --- */
04280     right_hv = am->am_id ^ hash_referent->common.hash_id;
04281     for (rm = right_ht_bucket(right_hv); rm != NIL; rm = rm->next_in_bucket) {
04282         if (rm->am != am)
04283             continue;
04284         /* --- does rm->w match new? --- */
04285         if (hash_referent != rm->w->id)
04286             continue;
04287         failed_a_test = FALSE;
04288         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04289             if (!match_left_and_right(rt, new, rm->w)) {
04290                 failed_a_test = TRUE;
04291                 break;
04292             }
04293         if (failed_a_test)
04294             continue;
04295         /* --- match found, so call each child node --- */
04296         for (child = node->first_child; child != NIL; child = child->next_sibling)
04297             (*(left_addition_routines[child->node_type])) (child, new, rm->w);
04298     }
04299     activation_exit_sanity_check();
04300 }
04301 
04302 void unhashed_positive_node_left_addition(rete_node * node, token * new)
04303 {
04304     right_mem *rm;
04305     rete_test *rt;
04306     bool failed_a_test;
04307     rete_node *child;
04308 
04309     activation_entry_sanity_check();
04310     left_node_activation(node, TRUE);
04311 
04312     if (node_is_right_unlinked(node)) {
04313         relink_to_right_mem(node);
04314         if (node->b.posneg.alpha_mem->right_mems == NIL) {
04315             unlink_from_left_mem(node);
04316             activation_exit_sanity_check();
04317             return;
04318         }
04319     }
04320 
04321     /* --- look through right memory for matches --- */
04322     for (rm = node->b.posneg.alpha_mem->right_mems; rm != NIL; rm = rm->next_in_am) {
04323         /* --- does rm->w match new? --- */
04324         failed_a_test = FALSE;
04325         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04326             if (!match_left_and_right(rt, new, rm->w)) {
04327                 failed_a_test = TRUE;
04328                 break;
04329             }
04330         if (failed_a_test)
04331             continue;
04332         /* --- match found, so call each child node --- */
04333         for (child = node->first_child; child != NIL; child = child->next_sibling)
04334             (*(left_addition_routines[child->node_type])) (child, new, rm->w);
04335     }
04336     activation_exit_sanity_check();
04337 }
04338 
04339 void mp_node_left_addition(rete_node * node, token * tok, wme * w)
04340 {
04341     unsigned long hv;
04342     Symbol *referent;
04343     rete_node *child;
04344     token *new;
04345     unsigned long right_hv;
04346     right_mem *rm;
04347     alpha_mem *am;
04348     rete_test *rt;
04349     bool failed_a_test;
04350 
04351     activation_entry_sanity_check();
04352     left_node_activation(node, TRUE);
04353 
04354     {
04355         int levels_up;
04356         token *t;
04357 
04358         levels_up = node->left_hash_loc_levels_up;
04359         if (levels_up == 1) {
04360             referent = field_from_wme(w, node->left_hash_loc_field_num);
04361         } else {                /* --- levels_up > 1 --- */
04362             for (t = tok, levels_up -= 2; levels_up != 0; levels_up--)
04363                 t = t->parent;
04364             referent = field_from_wme(t->w, node->left_hash_loc_field_num);
04365         }
04366     }
04367 
04368     hv = node->node_id ^ referent->common.hash_id;
04369 
04370     /* --- build new left token, add it to the hash table --- */
04371     token_added(node);
04372     allocate_with_pool(&current_agent(token_pool), &new);
04373     new_left_token(new, node, tok, w);
04374     insert_token_into_left_ht(new, hv);
04375     new->a.ht.referent = referent;
04376 
04377     if (mp_bnode_is_left_unlinked(node)) {
04378         activation_exit_sanity_check();
04379         return;
04380     }
04381 
04382     am = node->b.posneg.alpha_mem;
04383 
04384     if (node_is_right_unlinked(node)) {
04385         relink_to_right_mem(node);
04386         if (am->right_mems == NIL) {
04387             make_mp_bnode_left_unlinked(node);
04388             activation_exit_sanity_check();
04389             return;
04390         }
04391     }
04392 
04393     /* --- look through right memory for matches --- */
04394     right_hv = am->am_id ^ referent->common.hash_id;
04395     for (rm = right_ht_bucket(right_hv); rm != NIL; rm = rm->next_in_bucket) {
04396         if (rm->am != am)
04397             continue;
04398         /* --- does rm->w match new? --- */
04399         if (referent != rm->w->id)
04400             continue;
04401         failed_a_test = FALSE;
04402         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04403             if (!match_left_and_right(rt, new, rm->w)) {
04404                 failed_a_test = TRUE;
04405                 break;
04406             }
04407         if (failed_a_test)
04408             continue;
04409         /* --- match found, so call each child node --- */
04410         for (child = node->first_child; child != NIL; child = child->next_sibling)
04411             (*(left_addition_routines[child->node_type])) (child, new, rm->w);
04412     }
04413     activation_exit_sanity_check();
04414 }
04415 
04416 void unhashed_mp_node_left_addition(rete_node * node, token * tok, wme * w)
04417 {
04418     unsigned long hv;
04419     rete_node *child;
04420     token *new;
04421     right_mem *rm;
04422     rete_test *rt;
04423     bool failed_a_test;
04424 
04425     activation_entry_sanity_check();
04426     left_node_activation(node, TRUE);
04427 
04428     hv = node->node_id;
04429 
04430     /* --- build new left token, add it to the hash table --- */
04431     token_added(node);
04432     allocate_with_pool(&current_agent(token_pool), &new);
04433     new_left_token(new, node, tok, w);
04434     insert_token_into_left_ht(new, hv);
04435     new->a.ht.referent = NIL;
04436 
04437     if (mp_bnode_is_left_unlinked(node))
04438         return;
04439 
04440     if (node_is_right_unlinked(node)) {
04441         relink_to_right_mem(node);
04442         if (node->b.posneg.alpha_mem->right_mems == NIL) {
04443             make_mp_bnode_left_unlinked(node);
04444             activation_exit_sanity_check();
04445             return;
04446         }
04447     }
04448 
04449     /* --- look through right memory for matches --- */
04450     for (rm = node->b.posneg.alpha_mem->right_mems; rm != NIL; rm = rm->next_in_am) {
04451         /* --- does rm->w match new? --- */
04452         failed_a_test = FALSE;
04453         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04454             if (!match_left_and_right(rt, new, rm->w)) {
04455                 failed_a_test = TRUE;
04456                 break;
04457             }
04458         if (failed_a_test)
04459             continue;
04460         /* --- match found, so call each child node --- */
04461         for (child = node->first_child; child != NIL; child = child->next_sibling)
04462             (*(left_addition_routines[child->node_type])) (child, new, rm->w);
04463     }
04464     activation_exit_sanity_check();
04465 }
04466 
04467 void positive_node_right_addition(rete_node * node, wme * w)
04468 {
04469     unsigned long hv;
04470     token *tok;
04471     Symbol *referent;
04472     rete_test *rt;
04473     bool failed_a_test;
04474     rete_node *child;
04475 
04476     activation_entry_sanity_check();
04477     right_node_activation(node, TRUE);
04478 
04479     if (node_is_left_unlinked(node)) {
04480         relink_to_left_mem(node);
04481         if (!node->parent->a.np.tokens) {
04482             unlink_from_right_mem(node);
04483             activation_exit_sanity_check();
04484             return;
04485         }
04486     }
04487 
04488     referent = w->id;
04489     hv = node->parent->node_id ^ referent->common.hash_id;
04490 
04491     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04492         if (tok->node != node->parent)
04493             continue;
04494         /* --- does tok match w? --- */
04495         if (tok->a.ht.referent != referent)
04496             continue;
04497         failed_a_test = FALSE;
04498         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04499             if (!match_left_and_right(rt, tok, w)) {
04500                 failed_a_test = TRUE;
04501                 break;
04502             }
04503         if (failed_a_test)
04504             continue;
04505         /* --- match found, so call each child node --- */
04506         for (child = node->first_child; child != NIL; child = child->next_sibling)
04507             (*(left_addition_routines[child->node_type])) (child, tok, w);
04508     }
04509     activation_exit_sanity_check();
04510 }
04511 
04512 void unhashed_positive_node_right_addition(rete_node * node, wme * w)
04513 {
04514     unsigned long hv;
04515     token *tok;
04516     rete_test *rt;
04517     bool failed_a_test;
04518     rete_node *child;
04519 
04520     activation_entry_sanity_check();
04521     right_node_activation(node, TRUE);
04522 
04523     if (node_is_left_unlinked(node)) {
04524         relink_to_left_mem(node);
04525         if (!node->parent->a.np.tokens) {
04526             unlink_from_right_mem(node);
04527             activation_exit_sanity_check();
04528             return;
04529         }
04530     }
04531 
04532     hv = node->parent->node_id;
04533 
04534     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04535         if (tok->node != node->parent)
04536             continue;
04537         /* --- does tok match w? --- */
04538         failed_a_test = FALSE;
04539         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04540             if (!match_left_and_right(rt, tok, w)) {
04541                 failed_a_test = TRUE;
04542                 break;
04543             }
04544         if (failed_a_test)
04545             continue;
04546         /* --- match found, so call each child node --- */
04547         for (child = node->first_child; child != NIL; child = child->next_sibling)
04548             (*(left_addition_routines[child->node_type])) (child, tok, w);
04549     }
04550     activation_exit_sanity_check();
04551 }
04552 
04553 void mp_node_right_addition(rete_node * node, wme * w)
04554 {
04555     unsigned long hv;
04556     token *tok;
04557     Symbol *referent;
04558     rete_test *rt;
04559     bool failed_a_test;
04560     rete_node *child;
04561 
04562     activation_entry_sanity_check();
04563     right_node_activation(node, TRUE);
04564 
04565     if (mp_bnode_is_left_unlinked(node)) {
04566         make_mp_bnode_left_linked(node);
04567         if (!node->a.np.tokens) {
04568             unlink_from_right_mem(node);
04569             activation_exit_sanity_check();
04570             return;
04571         }
04572     }
04573 
04574     referent = w->id;
04575     hv = node->node_id ^ referent->common.hash_id;
04576 
04577     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04578         if (tok->node != node)
04579             continue;
04580         /* --- does tok match w? --- */
04581         if (tok->a.ht.referent != referent)
04582             continue;
04583         failed_a_test = FALSE;
04584         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04585             if (!match_left_and_right(rt, tok, w)) {
04586                 failed_a_test = TRUE;
04587                 break;
04588             }
04589         if (failed_a_test)
04590             continue;
04591         /* --- match found, so call each child node --- */
04592         for (child = node->first_child; child != NIL; child = child->next_sibling)
04593             (*(left_addition_routines[child->node_type])) (child, tok, w);
04594     }
04595     activation_exit_sanity_check();
04596 }
04597 
04598 void unhashed_mp_node_right_addition(rete_node * node, wme * w)
04599 {
04600     unsigned long hv;
04601     token *tok;
04602     rete_test *rt;
04603     bool failed_a_test;
04604     rete_node *child;
04605 
04606     activation_entry_sanity_check();
04607     right_node_activation(node, TRUE);
04608 
04609     if (mp_bnode_is_left_unlinked(node)) {
04610         make_mp_bnode_left_linked(node);
04611         if (!node->a.np.tokens) {
04612             unlink_from_right_mem(node);
04613             activation_exit_sanity_check();
04614             return;
04615         }
04616     }
04617 
04618     hv = node->node_id;
04619 
04620     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04621         if (tok->node != node)
04622             continue;
04623         /* --- does tok match w? --- */
04624         failed_a_test = FALSE;
04625         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04626             if (!match_left_and_right(rt, tok, w)) {
04627                 failed_a_test = TRUE;
04628                 break;
04629             }
04630         if (failed_a_test)
04631             continue;
04632         /* --- match found, so call each child node --- */
04633         for (child = node->first_child; child != NIL; child = child->next_sibling)
04634             (*(left_addition_routines[child->node_type])) (child, tok, w);
04635     }
04636     activation_exit_sanity_check();
04637 }
04638 
04639 /* ************************************************************************
04640 
04641    SECTION 13:  Beta Node Interpreter Routines: Negative Nodes
04642 
04643 ************************************************************************ */
04644 
04645 void negative_node_left_addition(rete_node * node, token * tok, wme * w)
04646 {
04647     unsigned long hv, right_hv;
04648     Symbol *referent;
04649     right_mem *rm;
04650     alpha_mem *am;
04651     rete_test *rt;
04652     bool failed_a_test;
04653     rete_node *child;
04654     token *new;
04655 
04656     activation_entry_sanity_check();
04657     left_node_activation(node, TRUE);
04658 
04659     if (node_is_right_unlinked(node))
04660         relink_to_right_mem(node);
04661 
04662     {
04663         int levels_up;
04664         token *t;
04665 
04666         levels_up = node->left_hash_loc_levels_up;
04667         if (levels_up == 1) {
04668             referent = field_from_wme(w, node->left_hash_loc_field_num);
04669         } else {                /* --- levels_up > 1 --- */
04670             for (t = tok, levels_up -= 2; levels_up != 0; levels_up--)
04671                 t = t->parent;
04672             referent = field_from_wme(t->w, node->left_hash_loc_field_num);
04673         }
04674     }
04675 
04676     hv = node->node_id ^ referent->common.hash_id;
04677 
04678     /* --- build new token, add it to the hash table --- */
04679     token_added(node);
04680     allocate_with_pool(&current_agent(token_pool), &new);
04681     new_left_token(new, node, tok, w);
04682     insert_token_into_left_ht(new, hv);
04683     new->a.ht.referent = referent;
04684     new->negrm_tokens = NIL;
04685 
04686     /* --- look through right memory for matches --- */
04687     am = node->b.posneg.alpha_mem;
04688     right_hv = am->am_id ^ referent->common.hash_id;
04689     for (rm = right_ht_bucket(right_hv); rm != NIL; rm = rm->next_in_bucket) {
04690         if (rm->am != am)
04691             continue;
04692         /* --- does rm->w match new? --- */
04693         if (referent != rm->w->id)
04694             continue;
04695         failed_a_test = FALSE;
04696         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04697             if (!match_left_and_right(rt, new, rm->w)) {
04698                 failed_a_test = TRUE;
04699                 break;
04700             }
04701         if (failed_a_test)
04702             continue;
04703         {
04704             token *t;
04705             allocate_with_pool(&current_agent(token_pool), &t);
04706             t->node = node;
04707             t->parent = NIL;
04708             t->w = rm->w;
04709             t->a.neg.left_token = new;
04710             insert_at_head_of_dll(rm->w->tokens, t, next_from_wme, prev_from_wme);
04711             t->first_child = NIL;
04712             insert_at_head_of_dll(new->negrm_tokens, t, a.neg.next_negrm, a.neg.prev_negrm);
04713         }
04714     }
04715 
04716     /* --- if no matches were found, call each child node --- */
04717     if (!new->negrm_tokens) {
04718         for (child = node->first_child; child != NIL; child = child->next_sibling)
04719             (*(left_addition_routines[child->node_type])) (child, new, NIL);
04720     }
04721     activation_exit_sanity_check();
04722 }
04723 
04724 void unhashed_negative_node_left_addition(rete_node * node, token * tok, wme * w)
04725 {
04726     unsigned long hv;
04727     rete_test *rt;
04728     bool failed_a_test;
04729     right_mem *rm;
04730     rete_node *child;
04731     token *new;
04732 
04733     activation_entry_sanity_check();
04734     left_node_activation(node, TRUE);
04735 
04736     if (node_is_right_unlinked(node))
04737         relink_to_right_mem(node);
04738 
04739     hv = node->node_id;
04740 
04741     /* --- build new token, add it to the hash table --- */
04742     token_added(node);
04743     allocate_with_pool(&current_agent(token_pool), &new);
04744     new_left_token(new, node, tok, w);
04745     insert_token_into_left_ht(new, hv);
04746     new->a.ht.referent = NIL;
04747     new->negrm_tokens = NIL;
04748 
04749     /* --- look through right memory for matches --- */
04750     for (rm = node->b.posneg.alpha_mem->right_mems; rm != NIL; rm = rm->next_in_am) {
04751         /* --- does rm->w match new? --- */
04752         failed_a_test = FALSE;
04753         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04754             if (!match_left_and_right(rt, new, rm->w)) {
04755                 failed_a_test = TRUE;
04756                 break;
04757             }
04758         if (failed_a_test)
04759             continue;
04760         {
04761             token *t;
04762             allocate_with_pool(&current_agent(token_pool), &t);
04763             t->node = node;
04764             t->parent = NIL;
04765             t->w = rm->w;
04766             t->a.neg.left_token = new;
04767             insert_at_head_of_dll(rm->w->tokens, t, next_from_wme, prev_from_wme);
04768             t->first_child = NIL;
04769             insert_at_head_of_dll(new->negrm_tokens, t, a.neg.next_negrm, a.neg.prev_negrm);
04770         }
04771     }
04772 
04773     /* --- if no matches were found, call each child node --- */
04774     if (!new->negrm_tokens) {
04775         for (child = node->first_child; child != NIL; child = child->next_sibling)
04776             (*(left_addition_routines[child->node_type])) (child, new, NIL);
04777     }
04778     activation_exit_sanity_check();
04779 }
04780 
04781 void negative_node_right_addition(rete_node * node, wme * w)
04782 {
04783     unsigned long hv;
04784     token *tok;
04785     Symbol *referent;
04786     rete_test *rt;
04787     bool failed_a_test;
04788 
04789     activation_entry_sanity_check();
04790     right_node_activation(node, TRUE);
04791 
04792     referent = w->id;
04793     hv = node->node_id ^ referent->common.hash_id;
04794 
04795     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04796         if (tok->node != node)
04797             continue;
04798         /* --- does tok match w? --- */
04799         if (tok->a.ht.referent != referent)
04800             continue;
04801         failed_a_test = FALSE;
04802         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04803             if (!match_left_and_right(rt, tok, w)) {
04804                 failed_a_test = TRUE;
04805                 break;
04806             }
04807         if (failed_a_test)
04808             continue;
04809         /* --- match found: build new negrm token, remove descendent tokens --- */
04810         {
04811             token *t;
04812             allocate_with_pool(&current_agent(token_pool), &t);
04813             t->node = node;
04814             t->parent = NIL;
04815             t->w = w;
04816             t->a.neg.left_token = tok;
04817             insert_at_head_of_dll(w->tokens, t, next_from_wme, prev_from_wme);
04818             t->first_child = NIL;
04819             insert_at_head_of_dll(tok->negrm_tokens, t, a.neg.next_negrm, a.neg.prev_negrm);
04820         }
04821         while (tok->first_child)
04822             remove_token_and_subtree(tok->first_child);
04823     }
04824     activation_exit_sanity_check();
04825 }
04826 
04827 void unhashed_negative_node_right_addition(rete_node * node, wme * w)
04828 {
04829     unsigned long hv;
04830     token *tok;
04831     rete_test *rt;
04832     bool failed_a_test;
04833 
04834     activation_entry_sanity_check();
04835     right_node_activation(node, TRUE);
04836 
04837     hv = node->node_id;
04838 
04839     for (tok = left_ht_bucket(hv); tok != NIL; tok = tok->a.ht.next_in_bucket) {
04840         if (tok->node != node)
04841             continue;
04842         /* --- does tok match w? --- */
04843         failed_a_test = FALSE;
04844         for (rt = node->b.posneg.other_tests; rt != NIL; rt = rt->next)
04845             if (!match_left_and_right(rt, tok, w)) {
04846                 failed_a_test = TRUE;
04847                 break;
04848             }
04849         if (failed_a_test)
04850             continue;
04851         /* --- match found: build new negrm token, remove descendent tokens --- */
04852         {
04853             token *t;
04854             allocate_with_pool(&current_agent(token_pool), &t);
04855             t->node = node;
04856             t->parent = NIL;
04857             t->w = w;
04858             t->a.neg.left_token = tok;
04859             insert_at_head_of_dll(w->tokens, t, next_from_wme, prev_from_wme);
04860             t->first_child = NIL;
04861             insert_at_head_of_dll(tok->negrm_tokens, t, a.neg.next_negrm, a.neg.prev_negrm);
04862         }
04863         while (tok->first_child)
04864             remove_token_and_subtree(tok->first_child);
04865     }
04866     activation_exit_sanity_check();
04867 }
04868 
04869 /* ************************************************************************
04870 
04871    SECTION 14:  Beta Node Interpreter Routines: CN and CN_PARTNER Nodes
04872 
04873    These routines can support either the CN node hearing about new left
04874    tokens before the CN_PARTNER, or vice-versa.  This makes them a bit
04875    more complex than they would be otherwise.
04876 ************************************************************************ */
04877 
04878 void cn_node_left_addition(rete_node * node, token * tok, wme * w)
04879 {
04880     unsigned long hv;
04881     token *t, *new;
04882     rete_node *child;
04883 
04884     activation_entry_sanity_check();
04885     left_node_activation(node, TRUE);
04886 
04887     hv = node->node_id ^ (unsigned long) tok ^ (unsigned long) w;
04888 
04889     /* --- look for a matching left token (since the partner node might have
04890        heard about this new token already, in which case it would have done
04891        the CN node's work already); if found, exit --- */
04892     for (t = left_ht_bucket(hv); t != NIL; t = t->a.ht.next_in_bucket)
04893         if ((t->node == node) && (t->parent == tok) && (t->w == w))
04894             return;
04895 
04896     /* --- build left token, add it to the hash table --- */
04897     token_added(node);
04898     allocate_with_pool(&current_agent(token_pool), &new);
04899     new_left_token(new, node, tok, w);
04900     insert_token_into_left_ht(new, hv);
04901     new->negrm_tokens = NIL;
04902 
04903     /* --- pass the new token on to each child node --- */
04904     for (child = node->first_child; child != NIL; child = child->next_sibling)
04905         (*(left_addition_routines[child->node_type])) (child, new, NIL);
04906 
04907     activation_exit_sanity_check();
04908 }
04909 
04910 void cn_partner_node_left_addition(rete_node * node, token * tok, wme * w)
04911 {
04912     rete_node *partner, *temp;
04913     unsigned long hv;
04914     token *left, *negrm_tok;
04915 
04916     activation_entry_sanity_check();
04917     left_node_activation(node, TRUE);
04918 
04919     partner = node->b.cn.partner;
04920 
04921     /* --- build new negrm token --- */
04922     token_added(node);
04923     allocate_with_pool(&current_agent(token_pool), &negrm_tok);
04924     new_left_token(negrm_tok, node, tok, w);
04925 
04926     /* --- advance (tok,w) up to the token from the top of the branch --- */
04927     temp = node->parent;
04928     while (temp != partner->parent) {
04929         temp = real_parent_node(temp);
04930         w = tok->w;
04931         tok = tok->parent;
04932     }
04933 
04934     /* --- look for the matching left token --- */
04935     hv = partner->node_id ^ (unsigned long) tok ^ (unsigned long) w;
04936     for (left = left_ht_bucket(hv); left != NIL; left = left->a.ht.next_in_bucket)
04937         if ((left->node == partner) && (left->parent == tok) && (left->w == w))
04938             break;
04939 
04940     /* --- if not found, create a new left token --- */
04941     if (!left) {
04942         token_added(partner);
04943         allocate_with_pool(&current_agent(token_pool), &left);
04944         new_left_token(left, partner, tok, w);
04945         insert_token_into_left_ht(left, hv);
04946         left->negrm_tokens = NIL;
04947     }
04948 
04949     /* --- add new negrm token to the left token --- */
04950     negrm_tok->a.neg.left_token = left;
04951     insert_at_head_of_dll(left->negrm_tokens, negrm_tok, a.neg.next_negrm, a.neg.prev_negrm);
04952 
04953     /* --- remove any descendent tokens of the left token --- */
04954     while (left->first_child)
04955         remove_token_and_subtree(left->first_child);
04956 
04957     activation_exit_sanity_check();
04958 }
04959 
04960 /* ************************************************************************
04961 
04962    SECTION 15:  Beta Node Interpreter Routines: Production Nodes
04963 
04964    During each elaboration cycle, we buffer the assertions (new matches)
04965    and retractions (old no-longer-present matches) in "tentative_assertions"
04966    and "tentative_retractions" on each p-node.  We have to buffer them
04967    because a match could appear and then disappear during one e-cycle
04968    (e.g., add one WME, this creates a match, then remove another WME,
04969    and the match goes away).  A match can also disappear then re-appear
04970    (example case involves an NCC -- create a match fot the NCC by adding
04971    a WME inside it, then remove another WME for a different condition
04972    inside the NCC).  When one of these "stobe" situations occurs,
04973    we don't want to actually fire the production or retract the 
04974    instantiation -- hence the buffering.
04975 ************************************************************************ */
04976 
04977 /* ----------------------------------------------------------------------
04978                          P Node Left Addition
04979 
04980    Algorithm:
04981    
04982    Does this token match (wme's equal) one of tentative_retractions?
04983      (We have to check instantiation structure for this--when an
04984      instantiation retracts then re-asserts in one e-cycle, the
04985      token itself will be different, but all the wme's tested positively
04986      will be the same.)
04987    If so, remove that tentative_retraction.
04988    If not, store this new token in tentative_assertions.
04989 ---------------------------------------------------------------------- */
04990 
04991 void p_node_left_addition(rete_node * node, token * tok, wme * w)
04992 {
04993     ms_change *msc;
04994     condition *cond;
04995     token *current_token, *new;
04996     wme *current_wme;
04997     rete_node *current_node;
04998     bool match_found;
04999 
05000     /* RCHONG: begin 10.11 */
05001 
05002     int prod_type;
05003     token *OPERAND_curr_tok, *temp_tok;
05004 
05005     action *act;
05006     bool operator_proposal, op_elab;
05007     char action_attr[ACTION_ATTR_SIZE];
05008 
05009     int pass;
05010     wme *lowest_goal_wme;
05011 
05012     /* RCHONG: end 10.11 */
05013 
05014     activation_entry_sanity_check();
05015     left_node_activation(node, TRUE);
05016 
05017     /* --- build new left token (used only for tree-based remove) --- */
05018     token_added(node);
05019     allocate_with_pool(&current_agent(token_pool), &new);
05020     new_left_token(new, node, tok, w);
05021 
05022     /* --- check for match in tentative_retractions --- */
05023     match_found = FALSE;
05024     for (msc = node->b.p.tentative_retractions; msc != NIL; msc = msc->next_of_node) {
05025         match_found = TRUE;
05026         cond = msc->inst->bottom_of_instantiated_conditions;
05027         current_token = tok;
05028         current_wme = w;
05029         current_node = node->parent;
05030         while (current_node->node_type != DUMMY_TOP_BNODE) {
05031             if (bnode_is_positive(current_node->node_type))
05032                 if (current_wme != cond->bt.wme) {
05033                     match_found = FALSE;
05034                     break;
05035                 }
05036             current_node = real_parent_node(current_node);
05037             current_wme = current_token->w;
05038             current_token = current_token->parent;
05039             cond = cond->prev;
05040         }
05041         if (match_found)
05042             break;
05043     }
05044 
05045 #ifdef BUG_139_WORKAROUND
05046     /* --- test workaround for bug #139: don't rematch justifications; let them be removed --- */
05047     /* note that the justification is added to the retraction list when it is first created, so
05048        we let it match the first time, but not after that */
05049     if (match_found && node->b.p.prod->type == JUSTIFICATION_PRODUCTION_TYPE) {
05050         if (node->b.p.prod->already_fired) {
05051             return;
05052         } else {
05053             node->b.p.prod->already_fired = 1;
05054         }
05055     }
05056 #endif
05057 
05058     /* --- if match found tentative_retractions, remove it --- */
05059     if (match_found) {
05060         msc->inst->rete_token = tok;
05061         msc->inst->rete_wme = w;
05062 
05063         remove_from_dll(node->b.p.tentative_retractions, msc, next_of_node, prev_of_node);
05064         remove_from_dll(current_agent(ms_retractions), msc, next, prev);
05065         /* REW: begin 08.20.97 */
05066         if (msc->goal) {
05067             remove_from_dll(msc->goal->id.ms_retractions, msc, next_in_level, prev_in_level);
05068         } else {
05069             remove_from_dll(current_agent(nil_goal_retractions), msc, next_in_level, prev_in_level);
05070         }
05071         /* REW: end   08.20.97 */
05072 
05073         free_with_pool(&current_agent(ms_change_pool), msc);
05074 #ifdef DEBUG_RETE_PNODES
05075         print_with_symbols("\nRemoving tentative retraction: %y", node->b.p.prod->name);
05076 #endif
05077         activation_exit_sanity_check();
05078         return;
05079     }
05080 
05081     /* --- no match found, so add new assertion --- */
05082 #ifdef DEBUG_RETE_PNODES
05083     print_with_symbols("\nAdding tentative assertion: %y", node->b.p.prod->name);
05084 #endif
05085 
05086     allocate_with_pool(&current_agent(ms_change_pool), &msc);
05087     msc->tok = tok;
05088     msc->w = w;
05089     msc->p_node = node;
05090     msc->inst = NIL;            /* just for safety */
05091     /* REW: begin 08.20.97 */
05092     /* initialize goal regardless of run mode */
05093     msc->level = 0;
05094     msc->goal = NIL;
05095     /* REW: end   08.20.97 */
05096 
05097 /* RCHONG: begin 10.11 */
05098 
05099     /*  (this is a RCHONG comment, but might also apply to Operand2...?)
05100 
05101        what we have to do now is to, essentially, determine the kind of
05102        support this production would get based on its present complete
05103        matches.  once i know the support, i can then know into which match
05104        set list to put "msc".
05105 
05106        this code is used to make separate PE productions from IE
05107        productions by putting them into different match set lists.  in
05108        non-OPERAND, these matches would all go into one list.
05109 
05110        BUGBUG i haven't tested this with a production that has more than
05111        one match where the matches could have different support.  is that
05112        even possible???
05113 
05114      */
05115 
05116     /* operand code removed 1/22/99 - kjc */
05117 
05118     /* REW: begin 09.15.96 */
05119 #ifndef SOAR_8_ONLY
05120     if (current_agent(operand2_mode) == TRUE) {
05121 #endif
05122 
05123         /* REW: begin 08.20.97 */
05124         /* Find the goal and level for this ms change */
05125         msc->goal = find_goal_for_match_set_change_assertion(msc);
05126         msc->level = msc->goal->id.level;
05127 #ifdef DEBUG_WATERFALL
05128         print("\n    Level of goal is  %d", msc->level);
05129 #endif
05130         /* REW: end 08.20.97 */
05131 
05132         prod_type = IE_PRODS;
05133 
05134         if (node->b.p.prod->declared_support == DECLARED_O_SUPPORT)
05135             prod_type = PE_PRODS;
05136 
05137         else if (node->b.p.prod->declared_support == DECLARED_I_SUPPORT)
05138             prod_type = IE_PRODS;
05139 
05140         else if (node->b.p.prod->declared_support == UNDECLARED_SUPPORT) {
05141 
05142             /*
05143                check if the instantiation is proposing an operator.  if it
05144                is, then this instantiation is i-supported.
05145              */
05146 
05147             operator_proposal = FALSE;
05148 
05149             for (act = node->b.p.prod->action_list; act != NIL; act = act->next) {
05150 
05151                 if ((act->type == MAKE_ACTION) && (rhs_value_is_symbol(act->attr))) {
05152                     if ((strcmp(rhs_value_to_string(act->attr, action_attr, ACTION_ATTR_SIZE),
05153                                 "operator") == NIL) && (act->preference_type == ACCEPTABLE_PREFERENCE_TYPE)) {
05154                         operator_proposal = TRUE;
05155                         prod_type = !PE_PRODS;
05156                         break;
05157                     }
05158                 }
05159             }
05160 
05161             if (operator_proposal == FALSE) {
05162 
05163                 /*
05164                    examine all the different matches for this productions
05165                  */
05166 
05167                 for (OPERAND_curr_tok = node->a.np.tokens;
05168                      OPERAND_curr_tok != NIL; OPERAND_curr_tok = OPERAND_curr_tok->next_of_node) {
05169 
05170                     /*
05171 
05172                        i'll need to make two passes over each set of wmes that
05173                        match this production.  the first pass looks for the lowest
05174                        goal identifier.  the second pass looks for a wme of the form:
05175 
05176                        (<lowest-goal-id> ^operator ...)
05177 
05178                        if such a wme is found, then this production is a PE_PROD.
05179                        otherwise, it's a IE_PROD.
05180 
05181                        admittedly, this implementation is kinda sloppy.  i need to
05182                        clean it up some.
05183 
05184                        BUGBUG this check only looks at positive conditions.  we
05185                        haven't really decided what testing the absence of the
05186                        operator will do.  this code assumes that such a productions
05187                        (instantiation) would get i-support.
05188 
05189                      */
05190 
05191                     op_elab = FALSE;
05192                     lowest_goal_wme = NIL;
05193 
05194                     for (pass = 0; pass != 2; pass++) {
05195 
05196                         temp_tok = OPERAND_curr_tok;
05197                         while (temp_tok != NIL) {
05198                             while (temp_tok->w == NIL) {
05199                                 temp_tok = temp_tok->parent;
05200                                 if (temp_tok == NIL)
05201                                     break;
05202                             }
05203                             if (temp_tok == NIL)
05204                                 break;
05205                             if (temp_tok->w == NIL)
05206                                 break;
05207 
05208                             if (pass == 0) {
05209                                 if (temp_tok->w->id->id.isa_goal == TRUE) {
05210                                     if (lowest_goal_wme == NIL)
05211                                         lowest_goal_wme = temp_tok->w;
05212                                     else {
05213                                         if (temp_tok->w->id->id.level > lowest_goal_wme->id->id.level)
05214                                             lowest_goal_wme = temp_tok->w;
05215                                     }
05216                                 }
05217                             } else {
05218                                 if ((temp_tok->w->attr ==
05219                                      current_agent(operator_symbol)) &&
05220                                     (temp_tok->w->acceptable == FALSE) && (temp_tok->w->id == lowest_goal_wme->id)) {
05221 
05222                                     if (current_agent(o_support_calculation_type) == 3 ||
05223                                         current_agent(o_support_calculation_type) == 4) {
05224 
05225                                         /*
05226                                          * iff RHS has only operator elaborations 
05227                                          * then it's IE_PROD, otherwise PE_PROD, so
05228                                          * look for non-op-elabs in the actions  KJC 1/00
05229                                          */
05230 
05231                                         /* We also need to check reteloc's to see if they 
05232                                            are referring to operator augmentations before determining
05233                                            if this is an operator elaboration 
05234                                          */
05235 
05236                                         for (act = node->b.p.prod->action_list; act != NIL; act = act->next) {
05237                                             if (act->type == MAKE_ACTION) {
05238 
05239                                                 if ((rhs_value_is_symbol(act->id)) &&
05243                                                     (rhs_value_to_symbol(act->id) == temp_tok->w->value)) {
05244 
05245                                                     op_elab = TRUE;
05246 
05247                                                 } else if (current_agent(o_support_calculation_type) == 4 &&
05248                                                            (rhs_value_is_reteloc(act->id)) &&
05249                                                            (temp_tok->w->value ==
05250                                                             get_symbol_from_rete_loc((byte)
05251                                                                                      rhs_value_to_reteloc_levels_up
05252                                                                                      (act->id),
05253                                                                                      (byte)
05254                                                                                      rhs_value_to_reteloc_field_num
05255                                                                                      (act->id), tok, w))) {
05256 
05257                                                     op_elab = TRUE;
05258 
05259                                                 } else {
05260 
05261                                                     /* this is not an operator elaboration */
05262                                                     prod_type = PE_PRODS;
05263                                                 }
05264                                             }   /* act->type == MAKE_ACTION */
05265                                         }       /* foreach action */
05266                                     } else {
05267                                         prod_type = PE_PRODS;
05268                                         break;
05269                                     }
05270                                 }
05271                             }   /* end if (pass == 0) */
05272                             temp_tok = temp_tok->parent;
05273                         }       /* end while (temp_tok != NIL) */
05274 
05275                         if (prod_type == PE_PRODS) {
05276                             if (current_agent(o_support_calculation_type) != 3
05277                                 && current_agent(o_support_calculation_type != 4)) {
05278                                 break;
05279                             }
05280                         } else if (op_elab == TRUE) {
05281 
05282                             /* warn user about mixed actions  */
05283 
05284                             if (current_agent(o_support_calculation_type) == 3 &&
05285                                 current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM]) {
05286                                 print_with_symbols
05287                                     ("\nWARNING:  operator elaborations mixed with operator applications\nget o_support in prod %y",
05288                                      node->b.p.prod->name);
05289                                 prod_type = PE_PRODS;
05290                                 break;
05291                             } else if (current_agent(o_support_calculation_type) == 4 &&
05292                                        current_agent(sysparams)[PRINT_WARNINGS_SYSPARAM]) {
05293                                 print_with_symbols
05294                                     ("\nWARNING:  operator elaborations mixed with operator applications\nget i_support in prod %y",
05295                                      node->b.p.prod->name);
05296                                 prod_type = IE_PRODS;
05297                                 break;
05298                             }
05299 
05300                         }
05301                     }           /* end for pass =  */
05302                 }               /* end for loop checking all matches */
05303 
05304                 /* BUG:  IF you print lowest_goal_wme here, you don't get what
05305                    you'd expect.  Instead of the lowest goal WME, it looks like
05306                    you get the lowest goal WME in the first/highest assertion of
05307                    all the matches for this production.  So, if there is a single
05308                    match, you get the right number.  If there are multiple matches
05309                    for the same production, you get the lowest goal of the
05310                    highest match goal production (or maybe just the first to
05311                    fire?).  I don;t know for certain if this is the behavior
05312                    Ron C. wanted or if it's a bug --
05313                    i need to talk to him about it. */
05314 
05315             }
05316             /* end if (operator_proposal == FALSE) */
05317         }
05318 
05319         /* end UNDECLARED_SUPPORT */
05320         if (prod_type == PE_PRODS) {
05321             insert_at_head_of_dll(current_agent(ms_o_assertions), msc, next, prev);
05322 
05323             /* REW: begin 08.20.97 */
05324             insert_at_head_of_dll(msc->goal->id.ms_o_assertions, msc, next_in_level, prev_in_level);
05325             /* REW: end   08.20.97 */
05326 
05327             node->b.p.prod->OPERAND_which_assert_list = O_LIST;
05328 
05329             if (current_agent(soar_verbose_flag) == TRUE) {
05330                 print_with_symbols("\n   RETE: putting [%y] into ms_o_assertions", node->b.p.prod->name);
05331 
05332             }
05333 
05334         }
05335 
05336         else {
05337             insert_at_head_of_dll(current_agent(ms_i_assertions), msc, next, prev);
05338 
05339             /* REW: end 08.20.97 */
05340             insert_at_head_of_dll(msc->goal->id.ms_i_assertions, msc, next_in_level, prev_in_level);
05341             /* REW: end 08.20.97 */
05342 
05343             node->b.p.prod->OPERAND_which_assert_list = I_LIST;
05344 
05345             if (current_agent(soar_verbose_flag) == TRUE) {
05346                 print_with_symbols("\n   RETE: putting [%y] into ms_i_assertions", node->b.p.prod->name);
05347 
05348             }
05349 
05350         }
05351 #ifndef SOAR_8_ONLY
05352     }
05353 
05354     /* REW: end   09.15.96 */
05355 
05356     else
05357         /* non-Operand* flavor Soar */
05358         insert_at_head_of_dll(current_agent(ms_assertions), msc, next, prev);
05359 #endif
05360 
05361 #ifdef MATCHTIME_INTERRUPT
05362     if (node->b.p.prod->interrupt) {
05363         char *ch;
05364         node->b.p.prod->interrupt++;
05365         current_agent(stop_soar)++;
05366 
05367         /*                              print( "INTERRUPT CALLED! [Phase] (Interrupt, Stop) is [%d] (%d,%d)\n", current_agent(current_phase), node->b.p.prod->interrupt, current_agent(stop_soar) ); */
05368 
05369         /*
05370            Note that this production name might not be completely accurate.
05371            If two productions match, the last matched production name will be
05372            saved, but if this production then gets retracted on the same
05373            elaboration cycle, while the first matching production remains
05374            on the assertion list, Soar will still halt, but the production
05375            named will be inaccurate.
05376          */
05377         strncpy(current_agent(interrupt_source), "*** Interrupt (probably) from production ", INTERRUPT_SOURCE_SIZE);
05378         current_agent(interrupt_source)[INTERRUPT_SOURCE_SIZE - 1] = 0;
05379         ch = current_agent(interrupt_source);
05380         while (*ch)
05381             ch++;
05382         symbol_to_string(node->b.p.prod->name, TRUE, ch,
05383                          INTERRUPT_SOURCE_SIZE - (ch - current_agent(interrupt_source)));
05384         while (*ch)
05385             ch++;
05386         strncpy(ch, " ***", INTERRUPT_SOURCE_SIZE - (ch - current_agent(interrupt_source)));
05387         ch[INTERRUPT_SOURCE_SIZE - (ch - current_agent(interrupt_source)) - 1] = 0;
05388         current_agent(reason_for_stopping) = current_agent(interrupt_source);
05389 
05390     }
05391 #endif
05392 
05393     /* RCHONG: end 10.11 */
05394     insert_at_head_of_dll(node->b.p.tentative_assertions, msc, next_of_node, prev_of_node);
05395     activation_exit_sanity_check();
05396 }
05397 
05398 /* ----------------------------------------------------------------------
05399                          P Node Left Removal
05400 
05401    Algorithm:
05402    
05403    Does this token match (eq) one of the tentative_assertions?
05404    If so, just remove that tentative_assertion.
05405    If not, find the instantiation corresponding to this token
05406      and add it to tentative_retractions.
05407 ---------------------------------------------------------------------- */
05408 
05409 /* BUGBUG shouldn't need to pass in both tok and w -- should have the
05410    p-node's token get passed in instead, and have it point to the
05411    corresponding instantiation structure. */
05412 
05413 void p_node_left_removal(rete_node * node, token * tok, wme * w)
05414 {
05415     ms_change *msc;
05416     instantiation *inst;
05417 
05418     activation_entry_sanity_check();
05419 
05420     /* --- check for match in tentative_assertions --- */
05421     for (msc = node->b.p.tentative_assertions; msc != NIL; msc = msc->next_of_node) {
05422         if ((msc->tok == tok) && (msc->w == w)) {
05423             /* --- match found in tentative_assertions, so remove it --- */
05424             remove_from_dll(node->b.p.tentative_assertions, msc, next_of_node, prev_of_node);
05425 
05426 #ifdef MATCHTIME_INTERRUPT
05427             if (node->b.p.prod->interrupt > 1) {
05428                 node->b.p.prod->interrupt--;
05429                 current_agent(stop_soar)--;
05430