COMPUTERISED INFORMATION-RETRIEVAL DATABASE SYSTEMS

Abstract: A natural language query interface system where the user enters English words or phrases and the system transforms the query into a formal computer language. The transformation is achieved by solving the Steiner graph with the help of a look up table. The Steiner problem consists in evaluating the cost of the different paths between the entity relations in the database conceptual graph to solve the natural language ambiguity.

Description: COMPUTERISED INFORMATION-RETRIEVAL DATABASE SYSTEMS This invention relates to the retrieval of information from computerised database systems. Query languages for systems of this type are already known, falling into two broad classes. In one class the user is required to input a query requesting retrieval of information stored in the computer system in a specialised formal computer language (such as SQL or QUEL) in which case the user is restricted to that group of persons conversant with the relevant formal computer language. In the second class the user may use his own natural language to input his query and in this case the computerised system incorporates an 'intelligent interface' to transform the natural language query to the relevant formal computer language. Unfortunately when a user is free to present a particular query in his own natural language he can do so in a variety of different ways each of which the user perceives as being correct. Due to volume it is not a practical matter for the 'intelligent interface' simply to store in memory each possible query and each possible manner of presentation of each query to provide a corresponding codified output to the remainder of the computer system. For a wide range of problems however and in semantic terms for a particular query, each manner of presentation contains in common a list of items of interest to the user and which are also contained in the computer system data base, and if these items are mapped onto the schema, the nodes of a conceptual graph representing the database, the shortest possible connection, having the minimum total length, effectively represents the query. The problem of finding the shorrest connection is known as the Steiner Problem in Graphs and the answer (being the shortest connection) is known as the Steiner Tree. Identification of the Steiner Tree is known to be a problem of great computational complexity because it is exponential with the number of nodes in the query set, and with the square of the number of nodes in the data base. A known form of 'intelligent interface' as described in the article entitled' Resolving the Query Inference Problem using Steiner Trees' by J.A. ald and P.G. Sorenson, published in ACM Transactions on Database Systems, Vol.9, No. 3, September 1984 incorporates a means of solving the Steiner Problem only where the conceptual graph is restricted in extent. It is an object of the present invention to provide a computerised information-retrieval database system incorporating an intelligent interface having a new and improved means of solving the Steiner Problem. According to the present invention there is provided a computerised information-retrieval system comprising a data base from which information is to be retrieved, an input device at which a user may use his own natural language to input a query, and an intelligent interface to transform the natural language query to the formal computer language of the system, said interface comprising means of solving the steiner Problem where the conceptual graph representing the database is unrestricted, said solving means comprising: means for identifying the number and identity of nodes (N) in the query set, means for assigning to a store at each node of the conceptual graph of the data base a collection of N triple-part markers, each marker first part being an initial node identity, each marker second part being an indication of cost to that marker in traversing between adjacent nodes, and each marker third part being a pointer to the link between adjacent nodes immediately previously traversed by that marker, and each collection of N markers initially comprising a marker having an identity part for each of the N nodes the respective cost and pointer parts for N-l of the markers being undefined and for the remaining marker being set to zero, said remaining marker having its initial node identity identifying the node at which the pertaining collection is stored, means for storing global values which are available to each node, said global values being a node identifier to identify that node (current root) with all cost parts being defined wherein the accumulated value for the second part of the markers is minimum (current minimum) , means for initialising current root to null and current minimum to a maximal value, means for asynchronously passing messages from each of said nodes to each of its immediate neighbours via the respective links therebetween the traverse cost of which is proportional to the length thereof, each received message containing the duplicate of all markers stored in the transmitting node with respective cost parts equal to the previous cost of the respective markers and with respective pointer parts identifying the link traversed by that marker, means at each node for receiving said messages, for iteratively comparing received and stored markers according to initial node identity, the comparison being between the respective cost parts of the markers, to identify and discard those received markers of greater cost than the stored markers, other than when the cost part of the stored markers is undefined, and with the number of received markers in each iterative comparison equally sharing the on-cost of the traverse of the particular link, and on termination of the iteration, for storing the collection of N markers which are respectively of least defined cost, means for updating current minimum and current root when any one node has N markers each having a defined cost part and having an aggregate cost value less than the previously stored value of current minimum, means for initiating message transmission from a node on each occurrence of a change in the stored collection of markers at that node and when that node has an accumulated cost value less than current minimum, means for identifying when the message-passing process has been completed for every node, and, means for tracing the traverse of each marker in the finally identified current root node back to its originating node in order to determine the Steiner Tree. The undefined value for the cost part of a marker is conveniently a large value whereas the undefined value for the pointer part of a marker is conveniently zero. By virtue of the present invention identification of the Steiner Tree is solved at least to provide an approximately accurate answer with substantially reduced computational complexity so that access to the data base is achieved substantially quicker than hitherto, with substantially less computational power, and independently of the size of the data base. The intelligent interface may be physically associated with the or each input device to the computer system, and/or with the or each computer system. A computerised information-retrieval system 10 is schematically illustrated in Fig. 1 and comprises a workstation 11 having an input device 11A and an output device 11B. The workstation 11 is connected to an intelligent interface 12 which in turn is connected to a date base memory system 13. The interf ce 12 comprises a lexical analyser 12A, a semantic associator 12B, a query code generator 12C and an output formatter 12D. The user operates at the workstation 11 by inputing a query via the keyboard input device 11A as an ordered -D - stream of characters in natural language (e.g., English or German) . The lexical analyser 12A partitions the character stream into sections consisting of word streams and, if appropriate, number streams. The word streams (which may be multi-word phrases or single words) are then compared with and matched against the contents of a look-up table held in a local memory and if a match is located, converted to a single token record for transmission to the Semantic Associator 12B. In the absence of a match, the analyzer 12A attempts an approximate match according to predetermined known rules to produce an appropriate token and if this fails the system displays a suitable legend on the workstation VDU device 11B requesting the user to try again with different language. When the complete character stream has been converted to tokens these are passed to Semantic Associator 11B where all the node/link records are stored and those records corresponding to the received tokens are identified. The identified node/link record forms the initial starting point of the conceptual graph which is operated on by a Steiner Matcher within Associator 12B (as will be explained) in order to identify the Steiner Tree which is then interpreted link by link by the code generator 12C. Generator 12C takes each link of the Steiner Tree and compares and matches it with the contents of a look-up table held in a still further local memory in order effectively to reproduce the users query in formal computer language. The query in coded form is then delivered to the DBMS 13 which delivers its consequential output to the formatter 12D which formats the answer to the query for presentation to the user on the VDU 11B of the workstation 11. The lexical analyzer 12A may be a standard analyzer as is used in compiler construction such as 'LEX' (which is supplied with the UNIX operating system) but preferably, as described, additionally provides approximate string matching as described in "Fast Approximate String Matching" by Owolabi and McGregor, published in Software Practice and Experience Vol. 18(4), 1988 for spelling corrections which may be further enhanced by the semantic associator 12B as ollows: As an example of this consider the query, "I've just had such a wered experience". The approximate lexical matcher is arranged to produce several alternative possibly valid matches to 'wered', each being associated with a value indicative of confidence of correctness. For example such approximate matches are 'were' and 'weird' etc and the approximation matcher creates a single new node which represents all of the alternative valid matches, this new node being connected by an arc or link to each of the actual nodes representing 'were' and 'weird' etc, the length of the arc being representative of confidence of correctness. Thus a high degree of confidence that the alternative match is actually correct is represented by a short arc length. The new node is then introduced as one of the nodes that must be present in the Steiner Tree. When the semantic associator 12B has identified the Steiner Tree it contains the new node and only one of the alternative possibly valid match nodes. The associator 12B is then arranged to process out the new node from the Steiner Tree to leave the alternative possibly valid match node and it is this form of the Steiner Tree which is delivered to the code generator 12C. The semantic associator 12B is a modified version (to be explained) of devices which are already known and which operate on a semantic basis, and it is the modified method or algorithm used for solving the Steiner Problem which facilitates the substantial reduction in computational complexity provided by the intelligent interface 12 as previously referred to. The associator 12B may additionally operate at least partly on a syntactic basis, see for example "Implementing Natural Language Parsers" by Thomson and Ritchie published in Artificial Intelligence (Eisenstadt and O'Shea) , 1984. Thus, in solving the Steiner Problem, the number and identity of the nodes N in the query set are first identified and at least the corresponding nodes of the conceptual graph are each subjected to identical local processes involving data held in a store local to each node. Each node store of the conceptual graph is initially assigned a collection of N triple part markers, each marker's first part identifying the marker's initial node, the second part identifying the cost to that marker of having traversed between adjacent nodes, and the third part being a pointer to the link immediately previously traversed by the marker. The initial collection of N markers has respective markers with an identity part for each of the N nodes and, for all except one marker all the cost parts and pointer parts are undefined, so that for convenience they are set to a suitable value. In the remaining marker the identity part identifies the node containing the collection and the cost and pointer parts of the marker are set to zero since that marker having not traversed even notionally has neither a pointer nor a cost. The local processor also has access to two global values namely a node identifier (referred to as current root) to identify that node wherein the accumulated value for the second part of the markers is minimum (referred to as current minimum) provided that each second or cost part of these markers is defined. Initially, current root is set to null and current minimum is set to a maximal value. After this initialisation process, each node passes a message to each of its immediate neighbours via communication channels mimicking the respective links. The traverse cost for the link is proportional to link length and each message issued by a node contains the duplicate of all the markers already stored in that node. When a node receives a message it is locally processed by iterative comparison of the respective cost parts of the markers in the message with the cost parts of the stored markers. The on-cost for the traverse of the link is shared equally by the number of markers in the message and which are used in the comparison process at each iteration. This received message is dismantled by the receiving node and its markers individually compared in (augmented) cost value with the markers of corresponding identity stored at the receiving node to identify and discard those received markers of greater cost than the stored markers other than when the cost part of the stored markers is undefined. The local store is up-dated to hold for each marker identity that marker having least defined cost value. By way of example, after the system has been operating for some time following initialisation each stored marker at a node will have a defined cost value as will each received marker in the transmitted message. If N equals 6 units and the on-cost value for the traverse is 12 units then in the first iteration the comparison is between the stored cost value and the received cost value plus 2 units for each of N(=6) markers. Suppose this identifies that two received markers have greater cost than their stored equivalents, then these two received markers- are discarded from future comparisons. Thus in the second iteration the comparison is between the stored cost value of N-2 (=4) markers and the received cost value plus 12/4 = 3 units for each of the 4 markers. Suppose this identifies that a further one received marker has greater cost than its stored equivalent, then this further one marker is discarded from future comparisons. Thus in the third iteration the comparison is between the stored cost value of 3 markers and the received cost value plus 12/3 = 4 units for each of the 3 markers, and so on. When the comparison process has been completed at a node and there has been a change in the stored collection of markers that node initiates message transmission to each of its adjacent nodes provided the accumulated cost value of the stored markers in less than 'current minimum'. When a node has N defined cost markers it locally computes the aggregate cost value of these markers and if this aggregate cost value is greater than the value of 'current minimum' the node is inhibited from transmitting further messages. Since 'current minimum' is initially set to a maximal value such as infinity the first node to compute an aggregate cost value must have this value less than 'current minimum'. Consequently 'current minimum' is updated to this new value and this particular node is identified as 'current root'. This process is repeated for each subsequent node to collect N defined cost markers. Finally, when message transmission, which is asynchronous, ceases, the node which is then 'current root' is identified and, from the set of N markers therein stored, the traverse route of each stored marker is traced back, to its originating node and it is this traverse route which constitutes the Steiner Tree. Intermittently and prior to cessation of message transmission the Steiner Tree instantaneously represented by Current Root may be evaluated to establish its actual value of Current Minimum and if this is different from the instantaneous Current Minimum value the latter is replaced by the former. The system which has been described with reference to Fig. 1 utilises a parallel arrangement with a processor at or for each node. This function may be achieved with a single processor operating in a serial mode in which case the data-processing for each node is effected sequentially. To speed the operation of a serial processor it is preferred to queue the processing operation by node rather than by individual messages. Thus when the messages for a particular node are processed all received messages are processed at the same time irrespective of the receipt time of the message and message transmission from that node occurs only following the processing. The system and algorithm for solving the Steiner Problem which has been described is sufficiently accurate to the particular problem of natural language queries to provide a unique solution and it is believed that this is because, in formulating a query, human users of the system have a strong preference for avoiding ambiguity. The known method of solving the Steiner Problem only where the conceptual graph is limited in extent is capable of providing a unique solution even where the query contains an ambiguity but is very substantially more complex and requires substantially more computational power than the system and algorithm which has been described. The known method and algorithm operates by sending individual marker records from node to node, carrying a single token from a single named node (i.e., the marker is a single part marker) , via all possible paths to all other nodes. The present invention may be software implemented for example as set forth hereafter under the heading 'Appendix A' which is a program designed for use with a make utility and compilers for C++, C, lex, and YACC. For interrogation of a particular Data Base a Browser program for example as set forth hereafter under the heading 'Appendix B' is used in conjunction with the Data Base to establish two data files ('Words' and 'snet' which characterise the Data Base in short form and which are used by the program of Appendix A to establish direct access with the particular Data Base. APPENDIX A - PAGE 1 Appendix A - Listing of Intelligent Interface • Makefile • graph.c • grap . • gtest.c • itp.c • siist.c • slist.h • . tab.c • stab.h • synt.c • lex. I • q-y APPENDIX A - PAGE 2 Makefile gh: synt.o itp.o graph.o stab.o slist.o gtest.o q.o lex.o CC -g -o gh synt.o itp.o gtβst.o graph.o stab.o slist.oX q.o lex.o striplβ.o -11 graph.o:graph.c graph. slist.h stab.h CC -c -g graph.c stab.o: stab.c stab.h CC -c -g stab.c slist.o: slist.c slist.h CC -c -g slist.c q.o: q.y graph.h slist.h yacc -d q.y; CC -c -g y.tab.c; mv y.tab.o q.o lex.o: lex.l lex.c lex lex.l; CC -c lex.c itp.o: graph.h slist.h stab.h CC -g -c itp.c synt.o: graph.h slist.h stab.h CC -g -c synt.c APPENDIX A - PAGE 3 graph.c typedef void* ent; typedef void (*PFC)(char*); // pointer to function type #include <stream.h> #include <string.h> #include "slist.h" #include "graph. " ^define nl « "\n" Sdefine sp « " " Pedge node: :edgefrom(Pnode pn) gslist_iterator(Pedge) e(in_edges) ; Pedge p; while (p=eO) if (p->source_node==pn) return p; return 0; Pedge node: :edgeto(Pnode pn) ■ C gslist_iterator(Pedge) e(out_edges) ; Pedge p; while (p=e()) if (p->destin_node==pn) return p; return 0; J- edge: :edge(Pnode s_node,Pnode d.node,float 1, float rl, Pedgeinfo ip) source_node = s_node; destin.node = d_node; up_mesg = 0; dora_mesg = 0; len = 1; rlen = rl; pinfo β ip; } ■ void node: :print() cerr «"node_id= " « node_id.major() « ","; cerx << node.id.minorO << "\n"; pinfo->print() ; > APPENDIX A - PAGE 4 Nodelist* node : : send_up(Pmesg m) { gslist_iterator(Pedge) in_iter(in_edges) ; Nodelist* p = new NodelistQ; Pedge e; Pnode n; while (e=in_iter()) { m->count++; if ( _his==e->destin_node) { v e->up_mesg = m; n = e->source_node; > else { n = e->destin_node; e->down.mesg = m; > if (n->channels.empt ()) p->append(n) ; n->channels. ppend(e) ; > return p; > Nodelist* node: :send_down(Pmesg m) . gslist_iterator(Pedge) out_iter(out_edges) ; Nodelist* p = new Nodelist(); Pedge e; Pnode n; while (e=out_iter()) { m->count++; if (this==e->source_node) \ n = e->destin_node; e->down_mesg = m; > else _ n - e->source_node; e->up_mesg = m; > if (n->channels.empty()) p->append(n) ; n->channels.append(e) ; > return p; } Nodelist* node: :sendmesg() { Pmesg p; if (state==STATIC) return 0; APPENDIX A - PAGE 5 p = new mesg(&n_mesg) ; return send_down(p) ; Pnode digraph: :addnode(int id) t Pnode pn = new node(id, π±.num) nodes. ppend(pn) ; return pn; Pedge digraph: :addedge(Pnode s_node, Pnode d_node, float 1, float rl, Pedgeinfo i Pedge newedge = s_node->edgeto(d_node) ; if (newedge) _ if (newedge->rlen==INF && rl !=INF) _ newedge->rlen = rl; s_node->addinedge(newedge) ; d_node->addoutedge(newedge) ; } else if (ip) { if (newedge->pinfo==0) newedge->pinfo = ip; else if (!newedge->pinfo->sameas(ip)) { newedge = new edge(s_node,d_node,l,rl,ip) ; edges.append(newedge) ; > else delete ip; } return newedge; _. else if (newedge=s_node->edgefrom(d_node)) -C if (newedge->len==INF && 1!=INF) { newedge->len = 1; s_node->addoutedge(newedge); d_node->addinedge(newedge) ; x _ else if (ip) _ if (newedge->pinfo==0) newedge->pinfo = ip; else if (!newedge->pinfo->sameas(ip)) -( newedge = new edge(s_node,d_node,l,rl,ip) ; edges.append(newedge) ; } else delete ip; > return newedge; > newedge = new edge(s_node,d_node,l,rl . ip) ; APPENDIX A - PAGE 6 if (rl!=INF) _ s_node->addinedge(newedge) ; d_node->addoutedge(newedge) ; > if (1!=INF) { s_node->addoutedge(newedge) ; d_node->addinedge(newedge) ; > edges. ppend(newedge) ; return newedge; int digraph: :deledge(Pnode s_node, Pnode d_node) { if (s_node==0) return 0; Pedge e = s_node->edgeto(d_node) ; if (e==0) e = s_node->edgefrom(d_node) ; if (e==0) return 0; s_node->deledge(e) ; d_node->deledge(e) ; edges.del(e) ; delete e; return 1; void digraph: :reset() { Pnode p; gslist.iterator(Pnode) next(nodes) ; while (p=next()) p->state=0; > int mesg: :setmar__er(int m, float initcost) ■ c if (m>m_num II m<l) return -1; marker[m-1]=1; cos [m-1]=initcost; channel[m-1]=0; m_c=m; return m; int node: :receivemes (int& n, float& av_cost) APPENDIX A - PAGE 7 int changed = 0; Pedge channel ; n = 0 ; for (channel = channels. et();channel!=0; channel = channels. et()) if (channel->merge(this)) changed =1; if (ichanged) return 0; n = n_mesg.__k_.numO; *• av_cost = n_mesg.total_cost()/n; return changed; int mesg: :__k_num() _ register int n = 0, i = 0; for (;i<m_c;i++) n += (marker[i])?1:Q; return n; > float mesg: :total_cost() { register float total = 0; for (int i=0; i<m_c;i++) total += (marker[i])?cost[i]:0; return total; mesg: :mesg(int n) -* count = 0; m_num = n; m_c = 0; if (n==0) -C marker = 0; cost = 0; channel = 0; return; > marker = new in [n] ; for (int i=O;i<n;marker[i++_}=0) ; cost = new floa [n] ; channel = new Pedge [n] ; mesg: :mesg(Pmesg p) { int n = p->m_num; this = new mesg(n) ; m_c = p->m_c; for (int i=0 ;i<m_c; i++) _ APPENDIX A - PAGE 8 .his->marker[i] = p->marker[i] ; this->cost [i] = p->cost[i]; > } void mesg: :clear() count = 0 ; m_c =0; for (int i = 0;i<m_num;i++) { marker [i] = 0; channel [i] =0 ; > void mesg: : " mesg() delete marker; delete cost; delete channel; > void mesg: :print0 { cerr « "m_n_m= " « m.num << "m_c= "« m_c « "\n"; for (int i=0; i<m_num;i++) cerr << marker[i] << " "; cerr << "\n"; for (i = 0; i<m_num ;i++) cerr << cost[i] << " "; cerr << "\n"; void digraph: : ollect(Pnode np.int* marks,Edgelistfc stree) { Pmesg p; if (m_id==0| |np==0) return; int* m = new int[π__id] ; int i , j ; p = δ(np->n_mesg) ; for (i = 0; i < m_id ; i++) { if (marks [i] sβ 0 ) _ continue; > if (p->marker[i] -- 0) { continue ; > APPENDIX A - PAGE 9 if (p->channel[i]==0) {marks [i] =0 ; continue; > for (j = 0 ; j < m_id ; j++) if (marks [j] && p->channel[i] == p->channel[j]) _ Cj] = 1; marks [j] =0; J- else mCj] = 0; Pedge q = p->channel_i] ; stree. append (q) ; collect ( (q->source_node==np) ?q->destin_node : ->source_node , , stree) ; } delete m; > Pnode digraph: :stei ree(Edgelist__ stree.Nodelist.- activelist) Pnode center = 0; float bound = 999999.0; Pnode pn; int n; int* marks = new int[m_id]; float av_cost; for (int i=0 ;i<m_id;marks[i++]=l) ; Nodelist receiverlist ; gs list .i era or (Pnode) ni (receiverlist) ; if (m_id==l) return center=activelist.get() ; while (pn=activelist.get())-_ do -C receiverlist. ppendl(pn->sendmesg()); } while( pn=activelist.get()) ; while (pn=receiverlist.ge O) { n=0; int changed = pn->receivemesg(n,av_cost) ; if (Jchanged) continue; if (av_cost<bound) _ if (n==m_id) { bound=av_cost; center = pn; } else activelist.append(pn) ; } } APPENDIX A - PAGE 10 > collect(center,marks,stree) ; return center; s void digraph: :clearmesgs() i m_id = 0; v Pnode p; gslist.iterator(Pnode) it(nodes) ; while (p=it()) p->clear(); > int edge: :merge(Pnode nd) 4 _ * register float increment; register float 1; Pmesg oldm = &(nd->n_mesg) ; Pmesg new ; if (nd ss =source-.node) { newm=up_mesg; l=rlen; else { new__ :s down_mesg; l=len; } int changed, i; int choice[MAXMARKS] ; int markernum, nu o arks, marksmerged; changed = 0; if (oldm->m_c<newm->m_.c) { for (int i=oldm->m_c; i<newm->m_c; i++) { oldm->marker[i]=0; oldm->cost[i]=0; oldm->channel[i]=0; } oldm->m_c = newm->m_c; > markernum = numof marks = oldm->m_c ; marksmerged = 0 ; for (i=0 ; i<markernum; choice [i++] = 0) ; increment s l/numo marks ; for (i=0 ; i<markern___; i++) _ if ( ! oldm->mειrker [i] tt newm->marker[i] ) _ choice [i] = 1 ; marksπ_erged++ ; > else if (oldm->marker [i] k newm->mεurkerCi] ) if ( (oldm->cost _i_ - (newm->cos Ci] +increment) ) > SMALLNUHBER) { choice [i] = -1 ; APPENDIX A - PAGE 11 marksmerged- * -- * - ; while (numof marks != marksmerged && marksmerged !=0) _ numof marks = marksmerged; increment = 1/numof marks ; for (i = 0; i < markernum ; i++) if (choice [i] == -1 && (oldm->cost [i] - (newm->cost[i]+increment))<=SMALLNUHBE <choice [i] = 0; marksmerged — ; > } if (marksmerged) _ increment=l/marksmerged ; for (i - 0; i < markernum; i++) if (choice [i]) _ oldm->marker[i] = newm->marker[i] ; oldm->channel [i] = this; oldm->cost[i] = newm->cost [i] +increment ; changed = 1; } } if ( — (newm-> count )==0) delete newm; retur (changed) ; edgeinfo: :edgeinfo(int typ) etype = typ; edgeinfo: :edgeinfo(Pedgeinfo p) -c etype = p->etype; } nodeinfo: :nodeinfo(int typ) 4 " ntype = typ; tablename = 0; range_var = 0; ftyp[03 = J \0 > ; mark = 0; APPENDIX A - PAGE 12 nodeinfo : :nodeinfo(int typ , strlistfe keys , Pchar tabname = O . char* ft = " " ) s ntype = typ; tablename=tabname; range_var = 0; mark = 0; strcpy( typ, ft) ; Pchar key; v while (key=keys.getO) nkeys.aopend(key) ; > extern char* copyo (char*) ; nodeinfo: :nodeinfo(nodeinfo* ip) range.var - 0; mark = 0; if (ip) _ ntype = ip->ntype; tablename = ip->tablename; nkeys.duplicate(ip->nkeys) ; strcpy(ftyp, ip->f yp) ; char* p; while (p=ip->val.get()) val.append(p) ; while (p=ip->op.get()) op.append(p) ; > else { ntype = 0; tablename = 0; > nodeinfo: :print() { cerr << ntype ; cerr << " tablename*" << tablename sp; cerr << " range_var="; putchar(range_var) ; cerr sp; cerr << "mark=" << mark; cerr « "KEYS: " ; char* key; gslist_iterator(Pchar) cit(nkeys); while (key=cit()) cerr <<key sp; cerr « "ATT: " ; char* att; gslist_iterator(Pchar) ait(attributes) ; APPENDIX A - PAGE 13 while (att=ait () ) cerr <<att sp ; cerr nl ; void node: :up(Nodelis fe parents , int linktyp) s gslist.iterator(Pedge) eit(out_edges) ; Pedge pe; Pnode p; while (pe=eit()) { if (pe->pinfo->etype!=linktyp) continue; if (pe->destin_node==this) p=pe->source_node; else continue; if (parents.lookfor(p)) continue; parents.append(p) ; p->u (parents, linktyp); * _. eit.reset(in_edges) ; while (pe=eit()) { if (pe->pinfo->etypei=linktyp) continue; if (pe->destin_node==this) p=pe->source_node; else continue; if (parents.lookfor(p)) continue; parents.append(p) ; p->up(parents, linktyp) ; } } /* downward closure, link type is linktyp */ void node: :dow (Nodelistfe children, int linktyp) { gslist_.iterator(Pedge) eit(out.edges) ; Pedge pe; Pnode p; while (pe=eit()) _ if (pe->pinfo->etype!=linktyp) continue; if (pe->so_rce_node==this) p=pe->destin_node; else continue; if (children.lookfor(p)) continue; children. ppend(p); p->down(children, linktyp) ; } eit.reset(in_edges) ; while (pe=eitO) _ if (pe->pinfo->etype!=linktyp) continue; APPENDIX A - PAGE 14 if (pe->source_node==this) p=pe->destin_noάe; else continue; if (children.lookfor(p)) continue; children. append(p) ; p->down(children, linktyp); } void nodeinfo: :set_val (strlistfe conditions) . if (range.var-s-sO I I val.numO—0) return; char s[360], *v, ss[64]; sprintf(ss, '"/,c.7,s", range.var, nkeys.f irstO) ; s[0] = '( * ; s[l] = '\0'; int count = 0; while ( v=val . get ( ) ) _ char t [60] ; count++ ; char* p = op . get ( ) ; sprintf ( , '"/.s'/.s'/.s" ,ss,p,v) ; if (count>l) strcat(s, " or "); strcat(s, t) ; } strcat(s, ")"); conditions.append(cop of(s)) ; > void node: :bindtable(char var, strlistfe c ds, strlistfe conditions) r i Pnodeinfo pi = pinfo; if (pi->range_var) return; pi->range_var = var; char s [360] ; sprintf (s, "range of '.c is */,s", pi->range_var ,pi->tablename) ; cmds.append(copyo (s)) ; pi->set_val( conditions) ; APPENDIX A - PAGE 15 graph, h #de ine STATIC 1 Sdefine SHALLNUHBER 0.0000001 #define MAXMARKS 200 #define DQ NH 1 #define UPH -1 _ #define INF -1 #include "ident.h" struct node; struct edge; struct mesg; struct edgeinfo; struct nodeinfo; typedef node* Pnode; typedef edge* Pedge; typedef mesg* Pmesg; typedef edgeinfo* Pedgeinfo; typedef nodeinfo* Pnodeinfo; declare( slis ,Pnode) ; aeclare.gslist,Pedge) ; declare(gslist . Pmesg) ; typedef gslist(Pnode) Nodelist; typedef gslist(Pedge) Edgelist; class mesg . int m_num; int count; publi : int _c; int* marker; float* cost; Pedge* channel; friend class edge; friend class node; float total.cost() ; int mk_nu__() ; void clearO ; sβtmarker(int m, float initcost = 0) ; mesg(int n) ; APPENDIX A - PAGE 16 esg(Pmesg) ; " esgO ; void print () ; > ; class node { ident node. id ; Edgelist in.edges ; Edgelist out.edges ; Edgelist channels ; public : mesg n.mesg ; int state ; Pnode info pinfo ; friend class destin.iter ; friend class source.iter ; inline void addinedge (Pedge e) { in. edges . append( e) ;} inline void addoutedge (Pedge e) { out.edges . append(e) ; } inline int getidO { return node.id.ma orO ; > Pedge edgefrom(Pnode pn) ; Pedge edgeto (Pnode pn) ; Pedge neighbour (Pnode pn) { Pedge p = edgef rom(pn) ; if (p) return p ; else return edgeto (pn) ; void deledge(Pedge e) _ in.edges.del(e) ; out.edges.del(e) ; > int receivemesg(int&, floatfe) ; void bindtable(char var, strlistfe cmd, strlistfe conditions); Nodelist* send.u (Pmesg) ; Nodelist* send.down(Pmesg) ; Nodelist* sendmesgO ; void prin () ; void clearO -C state=0; n.mesg.clear() ; > void up(Nodβlist& parents, int linktyp); void down(Nodelistfe children, int linktyp) ; nodednt id, int marker.number = 10) : n.mesg(marker.number) , node.id(id) _ - 23 - APPENDIX A - PAGE 1 " state=0 ; >- " nodeO ; > ; class edge { publi : Pnode source.node ; Pnode destin_node ; Pmesg up_mesg; Pmesg down.mesg; float len; float rlen; Pedgeinfo pinfo; int merge(Pnode) ; int setmarker(int) ; edge(Pnode,Pnode,float 1 = 1.0,float rl=1.0, Pedgeinfo ip = 0) class source.iter : gslist.iterator(Pedge) _ Pnode destin; public: source.i er(Pnode pn) : (pn->in_edges) _ destin = pn; } Pnode operatαrOO . Pedge p= gslist.iterator(Pedge) : :operator() () ; if (p->destin_node==destin) return p->source_node; else return p->destin_node; > class destin.iter : gslist_iterator(Pedge) _ Pnode source; public: destin_iter(Pnode pn) : (pn->out_edges) . source = pn; > Pnode o era orOO -C Pedge p= gslist.iterator(Pedge) : :operator() () ; if (p->source_node==source) return p->destin_node; else return p->source_node; enu dirct -[up,down,indir>; class nodeinfo . Dublic: APPENDIX A - PAGE 18 int ntype; char* tablename; char range.var; strlist val; strlist op; char ftyp[6] ; int mark; strlist attributes; strlist nkeys; prin (); void set.vaKstrlistfe conditions) ; nodeinfodnt typ) ; nodeinfo(int typ , strlistfe keys , Pchar tabname , char* f ieldtype) ; nodeinf o(nodeinf o*) ; " nodein o ( ) ; struct digraph { Nodelist nodes; Edgelist edges; float limit; int mk.num; int m.id; Pedge addedge(Pnode, Pnode, float 1 = 1.0, float rl=1.0, Pedgeinfo ip = 0) ; Pnode addnode(int) ; Pnode addnode(Pnode p) { Pnode pn; if (p= s 0) return 0; else pn = addnode(p->getid()) ; pn->pinfo = new nodeinfo(p->pinfo) ; return pn; > > int deledge(Pnode, Pnode); Pnode steitree(Edgelistfe stree,Nodelistfe actlist); void collect(Pnode,int*,Edgelistfe ); void clearmesgsO ; void reset() ; digraph(int m * 10) { mk.num = m;m_id = 0; * _ "digrap (){} APPENDIX A - PAGE 19 class edgeinfo t public: int etype; int sameas(Pedgeinfo pei) _ return etype==pei->etype; > edgeinfo(int typ) ; edgeinfo(Pedgeinfo) ; " edgeinfo(){>; APPENDIX A - PAGE 20 gtest.c typedef void* ent; typedef void (*PFC) (char*) ; // pointer to function type #define nl << "\n" #define sp << " " #include <strea__.h> #include <string.h> • .include <stdio.h> #include "slist.h" • .include "graph.h" ".include "stab.h" ♦.include <ctype.h> filebuf fl; istream* from; extern long mytimeO; declare(gstab,Pnode) ; extern gstab(Pnode)* diet; extern digraph* g; extern FILE* yyin; main(int argc,char* argv[]) nt m = 500,1 =10; f (argOl) sscanf (argv [1] ," * /.d", fern); f (argc>2) sscanf (argv[2],"7.d",fcl); diet = new gstab (Pnode) (m) ; g = new digraph(l) ; yyin=_.open("Words","r") ; yyparseO ; yyin=fopen("snet","r") ; yyparseO ; yyin = stdin; syntO ; #inciude <sys/time.h> struct timeval tt; struct timezone ttt; long mytimeO APPENDIX A - PAGE 21 getti__eofday(&tt ,fettt) ; return tt.tv_sec*1000000 + tt.tv.usec; skipblankO int c; while (isspace(c=getchar())) if (c—'Xn * ) return 1; ungetc(c,stdin) ; return 0; int quotedword(char* s) int c, ct = 0; while ((_=get_har()) _=" ■■ ) { *s++ = c; ct++;> return ct; int idword(char* s) r int c, d, ct = 0; while Oisspace(c=getchar())) { if (isalnum(c) II c== * _') { *s++ = c; ct++;} else if (c==' . * I I c== * -') { if (isalnum(d=getchar())) _ *s++ = c; *s++ = d; ct += 2; else ungetc(d, stdin) ; break; > y else break; _. mgetc(c, stdin); return ct; int punctword(char* s) int c, ct = 0; while (ispunct(c=getchar())) _ if (c!="" && ϋsspace(c)) *s++ = c; ct++;> APPENDIX A - PAGE 22 else break; > ungβtc(c, stdin); return ct ; int getword(char* s) _ i int c; while (skipblankO) ; if ((c=ge char ())=="") { s += quotedword(s) ; *s = 0; return 0; > *s++ = c; if (isalnum(c)) { s += idword(s) ; } else if (c=* , - J ) { if (isdigit(c=getchar())) { *s++ = c; s += idword(s) ; > else _ if (ispunct(c)) { *s++ = c; s += punctword(s) ;> else ungetc(c, stdin); > j- else if (ispunct(c)) { s += punctword(s) ; > *s = '\0'; if (c==E0F) return 0; else return 1; APPENDIX A - PAGE 23 itp.c typedef void* ent; typedef void (*PFC) (char*) ; //pointer to f nction type #include <stdio.h> #include <strea___.h> ..include <string.h> Sinclude "slist.h" #include "graph. " #include "stab.h" #include "y.tab.h" #define nl « "\n" #define sp « " " #define nodename(x) dict->getname(x->getid()) declare(gstab,Pnode) ; extern gstab(Pnode)* diet; extern gslist(Pedge) s.tree; extern char* quote(char*) ; extern char* copyof(char*) ; tdefine ZERO 0 char c = " a " ; char getvar if (c== , i 5 ) c= J * ; return C++: strlist cmds, conditions ; char* bind(char c, char* table) _ char s [64] ; sprintf (s , "7,c. , /.s" ,c, table) ; return strcpy(new char[strlen(s) + l] , s) ; char* ccnd(char* a, char* b) ■ f char s [128] ; sprintf (s , , a, b) ; return strcpy .new char [strlen(s)+l] , s) ; ** - APPENDIX A - PAGE 24 void buildtree (gslist (Pedge) & stree , Pnode oldc , Pnode newc , digraphfe newg ) { Pedge e ; Pnode n ; while (e ■ stree.get0) _ if (oldc==e->source_node) _ n = newg.addnode(e->destin_node) ; newg.addedge(newc, n, ZERO, INF, new edgeinfo(e->pinfo)) ; buildtree(stree,e->destin_node,n,newg) ; > else if (oldc s =e->destin_node) { n = newg.addnode(e->source_node) ; newg.addedge(n, newc, ZERO, INF, new edgβin o(e->pinfo)) ; buildtree(stree,β->source_node,n,newg) ; else tstree.insert(e) ;return;} ϊ void printedges(gslist(Pedge)fe tree) { gslist.iterator(Pedge) it(tree) ; Pedge pe; while (pe=it()) { cerr << "source_node="<<nodena_ae(pe->source.node) sp; cerr << pe->source_node->pinfo->ntype sp; cerr << "destin_node="<<nodename(pe->destin_node) sp; cerr << pe->destin_node->pinfo->ntype nl; void mkcond(Pnodeinfo psi, Pnodeinfo pdi) Pchar pc; gslist.iterator(Pchar) t(pdi->nkeys) ; while (pc=t()) { Pchar s = bind(psi->range_var,pc) ; Pchar r = bind(pdi->range_var,pc) ; conditions.append(s = cond(s.r)); } void printlist(Nodelist& nodes, char* delim = ", ") APPENDIX A - PAGE 25 Pnode pn; int first = 1 ; while (pn=nodes . get O ) _ if ( ifirst) cerr « delim; else first = 0; cerr << nodename (pn) ; void quel( digraphfe q.g) i gslist.iterator(Pnode) nit (q.g. nodes) ; Pnode pn; Pnodeinfo pi ; Pchar pc ; char query [500] , *p ; p=quer ; while (pc=cmds.get()) _ cerr << pc nl; sprintf(p,"7.s\n",pc) ; p=query+strlen(query) ; * > cerr << "retrieve( "; sprintf(p_"retrieve(") ; p=query+strlen(query) ; int first = 1; while (pn=nit()) . pi = pn-> mfo; //if (pi->mark == 1) _ while (pc=pi->attributes.geτ()) _ if Clfirst -Ccerr « ",";*p= , 1 -; ?++;> else first = 0; Pchar s = bind(pi->range_var,pc) ; cerr « s; sorintf( ,"7.s",s) ; p=query+strlen(αuery) ; > //> //else pi->attributes.clear() ; > cerr « ")" nl; *p=')'; p++; *p='\n J ; *(++p) = '\0'; if (conditions. um()>0)-( cerr « "where "; sprintf( ,"where ") ; p=query+strlβn(query) ; firsτ = 1 ; while (pc"conάitions _get O ) { APPENDIX A - PAGE 26 if .first) { cerr << " and "; sprintf (p," and ") ; p-query+strlen(query) ; > else first = 0; cerr << pc; sprintf (p,"7.s",pc) ; p=query+strlen(query) ; * > cerr << "\\g" nl; puts (query) ; cout << "\\g" nl; fflush(stdout) ; int printable (Pnode pn, digraphfe queryg) Pnode ncenter = queryg. addnode ( pn ) ; ncenter->bindtable(getvar() , cmds, condi ions) ; ncenter->pinfo->attributes.append("all") ; ncenter->pinf o->mark = 1; quel( queryg) ; return 1 ; void sort (gslist (Pedge)fe edges) gslist (Pedge) t p; Pedge pe; while (pe=edges.get()) { pβ->source_node->state++; t p.append(pe) ; while (pe=tmp.get()) if (pe->destin_node->state==0) { pβ->source_node->state—; edges.append(pe) ; > else tmp.append(pe) ; } interpret(Pnode center, gslist(Pedge)* stree) 1 digraph queryg; Pedge pe; Pnodβinfo psi, pdi, pi; gslist(Pedge) hasatt, supset, tmp; if (stree->num()<l) -C //print the table; pi = center->pinfo; APPENDIX A - PAGE 27 if (pi->ntype==TABLE) _ return printable(center, queryg); else if (pi->ntype==VALUE) _ Nodelist nodes; center->u (nodes, TYPEOF); cerr « nodename(center) << " is a "; printlis (nodes, " and a ") ; cerr nl « "* "; return 1; else if (pi->ntype==ATTRI) _ Nodelist nodes; center->up(nodes, HASATT) ; cerr << nodena_me(center) << " is an attribute of "; printlist(nodes," and "); cerr nl « "* "; return 1; else if (pi->ntype==0) _ Nodelist nodes; center->up(nodes, 0); if (nodes. umO==l) { if (nodes.firs ()->pinfo->ntype==TABLE) prin ble(nodes.ge () , queryg); else return 0; -_ else \ cerr << "Which of the following do you want:\n"; printlis (nodes, ". "); cerr nl << "* "; return 1; return 0; } Pnode ncenter = queryg.addnode(center) ; buildtree(*stree, center,ncenter, queryg); gslist.iterator(Pnode) nit(queryg.nodes) ; gslis (Pnode) vals; gslist.iterator(Pedge) eit(queryg.edges) ; while (pe=eit()) { if (pe->pinfo->etype==HASATT) hasatt . append(pe) ; if (pe->pinfo->etype==SUPSET) APPENDIX A - PAGE 28 supset . append (pe) ; if (pe->pinfo->etype==TYPEOF) { char* s = quote (nodename(pe->destin_node) ) ; pe->source_node->pin o->val.append(s) ; pe->source_node->pinfo->o .append("=") ; > eit.reset(hasatt) ; while (pe=eit()) { psi = pe->source_node->pinfo; pdi = pe->destin_node->pinfo; if (psi->range_var==0) { pe->source_node->bindtable(getvar() ,cmds,conditions) ; psi->attributes.duplicate(psi->nkeys) ; psi->mark = 1; psi->set_val(conditions) ; psi->attributes.minus(pdi->nkeys) ; _ eit.reset(hasatt) ; while (pe=eit()) _ psi = pe->source_node->pinfo; pdi = pe->destin_node->pinfo; if (pdi->π_ark"0)-_ pdi->range_var - psi->range_var; pαi->attributes.duplicate(pdi->nkeys) ; pdi->mark = 1; pdi->set_val(conditions) ; else mkcond(psi, pdi); sor (supset) ; eit.reset(supset) ; while (pe=eit()) pe->source_node->state++; while (pe=supset.getO) { Pnode ps = pe->source_node; if (Dβ->destin_node->pinfo->val.numO) ps->pinfo->val.appβndl(fe(pe->destin_node->pinfo->val)) ; ps->pinfo->op.appendl(fe(pβ->destin_node->pinfo->op)) ; APPENDIX A - PAGE 29 else _ if (pe->destin_node->pinfo->range_var==0) pe->destin_node->bindtable(getvar() , cmds, conditions); tmp.append(pe) ; if (— (ps->state) == 0) _ if (ps->pinfo->range_var) ps->pinfo->set_val (conditions) ; else ps->bindtable(getvar() , cmds , conditions) ; if (ps->pinf o->attributes .num()==0) ps->pinfo->attributes .duplicate (ps->pinfo->nkeys) ; if (pe->destin_node->pinfo->attributes .num() !=0) pe->destin_node->pinf o->attributes . ciearO ; while (pe=tmp.get()) {. psi = pe->destin_node->pinfo; pdi = pe->source_node->pinfo; gslist.iterator(Pchar) t(pdi->nkeys) , v(psi->nkeys) ; Pchar pc.qc; while (pc=t()) { qc = v(); Pchar s = bind(psi->range_var,qc) ; Pchar r = bind(pdi->range_var,pc) ; conditions.aDDend(cond(s,r)) ; if (cmds.num()>0) ■ [ quel(queryg) ; return(l);} else return(O) ; APPENDIX A - PAGE 30 slist.c #include <s_rea_t_.h> #include <string.h> typedef void* ent; typedef void (*PFC) (char*) ; //pointer to function type ..include "slist.h" int slist: :insert(ent a) -r if (last) last->next = new slink(a,last->next) ; else { last = new slink(a,0); last->next = last; count++; return 0; > int slist: :aDpend(ent a) _ if (last) last = last->nβxt = new slink(a,last->next) ; else { last = new slink(a.O); last->next = last; _ count++; return 0; \ slist* slist: :appendl(slist* s) . if (s»=0 II s->last s =0) return this; if (last) _ slink* p = last->next; last->next = (s->last)->nβxt; (s->last)->next = p; > last = s->last; s->last=0; count += s->num() ; delete s; return this; APPENDIX A - PAGE 21 ent slist : :get() if (last == 0) return 0; slink* f = last->next; ent r = f->e; if (f == last) last = 0; else last->next = f->next; delete f; count—; return r; ant slist: :ge (int n) if (last == 0) return 0; slink* p = last->next; for (int i = 1; i<n fefe p!=Iast; i++, p = p->next) ; if (i==n) return p->e; else return 0; ent slist: :replace(int n, ent x) if (last == 0) return 0; slink* p = last->next; for (int i = 1; i<n fefe p!=last; i++, p = p->next) ; if (i—n) -C p->e = x; return x; \ else return 0; void slist: :clear() slink* 1 = last; if (1.== 0) return; do -C slink * 11 = 1; 1 = l->next; delete 11; APPENDIX A - PAGE 32 > while (l ! =last) ; last = 0 ; count=0 ; void default.error(char* s) cerr << s << "\n"; exit(l) ; > ?FC slist.handler = default.error; ?FC set_slist_handler(PFC handler) ?FC rr = slist.handler; slist.handler = handler; return rr; ent slist: :del(ent x) if (last==0) return 0; slink* p = last->next; slink* q = last; while (p->e!=x fefe p!=last) { p = p->next; q = q->next; if (p->e==x) { if (p!*last) q->next = p->next; else if (p! β q) i last = q; else last = 0; delete p; count—; return x; else return 0; ent slist::lookfor(ent x) _ if (last==0) return(O) ; APPENDIX A - PAGE 33 slink* p = last->nβxτ; while (p->e !=x) if (p == last) return 0; else p = p->next ; return x; y void slist : .-duplicate (slistfe si) clear 0 ; slist.iterator it(si); ent e; while (e=it()) append(e) ; char* strlist: :lookfor(char* x) if ,last==0) return(O) ; slink* p = last->next; while (strcmp(Pchar(p->e) , x)) if (p == last) return 0; else p = p->next; return x; char* strlist: :del(char* x) r if (last==0) return 0; slink* p = last->next; slink* q = last; while (strcmp(Pchar(p->e) ,x) fefe p!=last) _ p = p->next; q = q->nexτ; > if (strcmp _Pchar(p->e) ,x)==0) _ if (p!=last) q->next = p->next; else if (p!=q) __ q->next = p->next; last = q; • else last = 0; delete p; coun —; return x; APPENDIX A - PAGE 34 else return 0 ; > void strlist: :minus(strlistfe x) { strlist.iterator it(x) ; char* p; while (p=it()) del(p); 6 - 46 - APPENDIX A - PAGE 35 slist.h extern PFC slist.handler; extern PFC set.slist.handler(PFC) ; class slink { friend class slist; friend class slist.iterator; friend class strlist; slink* next; ent e; slink(ent a, slink* p) _ e=a; nexτ=p;> x - class slist • £ friend class slist.iterator; friend class strlist; int count; slink* last; //last->next is head of list public: int inser (ent a); // add at head of list int append(ent a) ; // add at tail of list slist* appendl(slist*) ; // append a slist; ent ge O; // return and remove head of list ent ge (int); // return ith element; do not remove it ent replace(int, ent);// replace ith element ent deKent a) ; // delete a from the list ent lookfor(ent a) ; // search the list or a void clearO; // remove all links int empty(H return lasτ==0;> // true if empty int numO . return count; } II return number of elements in slist ent first() -C return last->next->e;} void duplicate(slistfe) ; slist() -Clasτ=0; count=0; > slistCent a) _last=new slink(a.O); last->nexτ=last; count=l;} "slistO -C clearO ; > class slist.iterator { slink* ce; slist* cs; public: slist_iterator(slistfe s) _ cs = fes; ce = cs->last; ent operatorO0 _ // return to indicate end of iteration APPENDIX A - PAGE 36 // not ideal for all types; fine for pointers ent ret = ce ? (ce=ce->next)->e : 0; if (ce == cs->last) ce = 0; return ret; ". int atstartO { return ce==cs->last; > // true if at the start void reset0 { ce = cs->last; } void reset(slist& s) {cs = &s; ce = cs->last; } ; #ifndef GENERICH #includa <generic.h> #endif #define gslist(type) name2(type,gslist) #define gslist.iterator(type) name2(type,gslist.iterator) -.define gslistdeclare(type) \ struct gslist(type) : slist { \ int insert(type a) \ { return slist: :insert( ent(a) ); > \ int append(type a) \ { return slist: :append( ent(a) ); > \ gslist(type)* appendl(gslist(type)* s) { \ return (gslist(type)*)(slist: :appendl((slist*)(s))) ;}\ type getO -(return type( slist: :get() ); } \ type get(int i) { \ return type(slist: :get(i)) ; \ > \ type del(type a) { \ return type( slist: :del( ent(a) ) ); \ > \ type replace(int i, type x) { \ return type(slist::replaced, ent(x))); \ > \ type lookfor(type a) { \ return type( slist: :lookfor(ent(a))) ; \ } \ int empty() {return slist: :empty() ; \ int numO {return slist: :num() ;> \ type firstO { return type( slist: :first()) ; > \ gslist(type)():() {} \ gslist(type)(type a) : (ent(a)) {> \ "gslist(type)O {clearO;} \ APPENDIX A - PAGE 37 \ \ struct gslist.iterator(type) : slist.iterator { \ gslist.iterator(type) (gslist(type)fe s) \ : ( (slistfe)s ) {} \ type operator0O \ ■ C return type( slist.iterator: :operator() 0) ;} \ void reset() { slist.iterator: :reset0 ; } \ v void reset( gslist( ype)fe s) { \ slist.iterator: :reset((slistfe) s) ; \ } \ class stack : slist { public: void push(ent a) {slist: :insert(a) ;} ent popO { return slist::get() ;} int emptyO { return slist: :empty() ;} stac 0 {} stack(ent a) : (a) {} void clearO { slist::clearO ; } • .define gstack(type) name2(type,gstack) Sdefine gstackdeclare(type) \ struct gstack(type) : stack { \ void push(type a) \ stack::push( ent(a) ); } \ type popO { return type( stack: :pop() ); } \ int empt () {return stack: :empt () ;} \ gstack(type)() {} \ gsτack(type)(type a) : (ent(a)) {} \ "gstack(typβ)O { clear0 ; } \ typedef char* Pchar; declare(gslist,Pchar) ; typedef gslist(Pchar) Charlist; class strlist : public gslis (Pchar) { public: char* lookfor(char* s) ; char* del(char* s) ; void minus(strlistfe) ; APPENDIX A - PAGE 38 } ; class strlist.iterator : public gslist.iterator(Pchar) { APPENDIX A - PAGE 39 stab.c // Symbol table class typedef void* ent ; # include < string. h> # include <stream.h> # include "stab .h" #define S 1 #define ED 2 #define ING 3 #define 14 #define E 5 endin (char* bp, char* ep) if (bp>ep) return 0; switch (*ep) { case * s J : return S; case * d 5 : if (ep>bp fefe *(ep-l) == * e') return ED; else return 0; case 'i': return I; case * g' : if (ep-bp>3 fefe *(ep-l)==-n * fefe *(ep-2)== " i * ) return ING; else return 0; case " e' : return E; char de[4][100]; int suffi (char* word, char* deriv□) char* ep ; char* w; int count = 0 ; int word.len = strlen(word) ; w = new char[word_len+l] ; strcpy(w, word) ; ep = w + word.len - 1; // point to the last char of w. switch (ending(w.ep)) { case S: *ep = * \0 J ; —ep; deri [count] = strcpyCde[count] ,w) ; count++; if (ending(w,ep)==E) { *ep = } \0' ; —ep; deriv [count] = strcpy(de [count] ,w) ; APPENDIX A - PAGE 40 count++; if (ending(w,ep)==I) { *ep = * y * ; dβriv[count] =strcpy(de[count] ,w) ; count++; } } deriv[count] =0; return S; case ED: *ep = '\0'; —ep; deriv[count] = strcpy(de [count] ,w) ; count++ ; *ep = ' \0' ; — ep; deriv[count] = strcpy(de [count] ,w) ; count++; if (ending(w,ep)==I) { *ep = * y " ; deriv[count] =strcpy(de[count] ,w) ; count++; } else if (word_len>3 fefe *ep == *(ep-l) ) { *ep = * \0 * ; ~ep; deriv[count] = strcpy(de [count] ,w) ; count++ ; I deriv[count] =0; return ED; case ING: —ep; *ep = * \0 " ; —ep; *ep = * e * ; deriv[count] = strcpy(de [count] ,w) ; count++ ; *ep = * \0 * ; --ep; deriv [count] = strcpy(de [count] ,w) ; count ++ ; if (word_len>4 fefe *ep == *(ep-l) ) { deriv[count] = strcpy(de [count] ,w) ; count++ ; } deriv [count] =0; return ING; j- deri [count] = 0; return 0; APPENDIX A - PAGE 41 int hash(char* string,int range) if (*string==0) return 0; int tail = strlen(string)-!; int head = 0; unsigned long hashval = 0; unsigned long longint = 0; char* s = (char*) felongint; while (head<=tail) { longint = 0; s[2-((hβadfel)«l)] |= string[head] ; longint <<= 4; if (head.=tail) s[3-((headfel)«D] += string[tail] & "0400; if (head & 1) longint »= 4; else longint >>= 6; hashval += longin ; } return hashval7.range+l ; } int ap .mai (char* , char**) ; int add. str( char*) ; int stab: :get_id(char* p , int ins =0) int notfound =1 ; int pos = hash(p ,hashrange) ; while (table [pos] !=0 fefe (notfound= strcmp (table [pos] ,p) ) fefe links [pos] ! =0 ) pos = links [pos] ; if ( .notfound) return pos ; if (ins) { if (table [pos] ==0) { table [pos] = new char[strlen(p)+l] ; strcpy (table [pos] , p) ; add_str(table[pos]) ; return pos ; else if (freepos>0) { // insert at freepos table [freepos] = new char[strlen(p)+l] ; strcpy.(table [freepos] ,p) ; int id = freepos ; links [pos] = freepos; freepos = links [ reepos] ; links [id] = 0; APPENDIX A - PAGE 42 add_str(table[id]); return id; else return 0;// table is full. } return 0; ϊ int stab: :getid(char* p, int ins =0) { int id - 0; char* derives [5] ; int suffix(char*, char**); if (*p==0) return 0; if (ins) return get.id(p.ins) ; if (id=get_id(p)) return id; else { suffi (p, derives); for (int i =0; derives [i] fefe ! (id=get_id(derives[i])) ;i++) ; if (id) return id; else { char* ap.matchs [5] ; if (ap.mainCp, ap.matchs)) { cerr << p << " = " << ap.matchs [0] « "\n"; return get_id(ap____atchs[0] , 0); y } return 0; ent stab: :get al(char* name) { int id - getid(name) ; if (legalid(id)) return vals[id]; else return 0; int stab: :del (int id) r if ( ϋegalid(id)) return -1; if (vals[id]) return 1; int pos - hash(table [id] .hashrange) ; if (pos==id) { if ((id=links[id])!*0) { APPENDIX A - PAGE 43 table [pos] = table [id] ; vals [pos] = vals [id] ; links [pos] =links [id] ; delete table [id] ; table [id] = 0; return 0; _ * else while (links [pos] !=id) pos = links [pos]; links [pos] = links [id] ; links [id] = freepos; freepos = id; delete table [id] ; table [id] = 0; return 0; stab: : stab (int sz) r if (sz<20) sz = 20; table = new char* [sz] ; for (int i=0; i<sz;table[i++]=0) ; vals = new ent [sz] ; for (i=0; i<sz;vals[i++]=0) ; links = new int [sz] ; for (i=0; i<sz ; links [i++] =0) ; tabsz = sz; hashrange = 1; int count =0; while ((hashrange<<2)<sz){ hashrange <<= 1; count++ ; y (count7.2)? hashrange — :hashrange++; links [sz-1] = 0; for (i = sz-2 ; i>hashrange ; i — ) links [i] = i+1; freepos = hashrange+1; void stab::print() for (int i=l ;i <tabsz; i++) if ( table [i]) cout « i « " " « table [i] « « (int) (vals [i]) \ - no - APPENDIX A - PAGE 44 << " " « links [i]« "\n' APPENDIX A - PAGE 45 stab.h class stab { char** table; ent* vals; int* links; // links for duplicate hash value entries. int tabsz; // table size. int hashrange; //return value of hash function is in [1..hashrange] v int freepos; // head of free store chain. inline int legalid(int id) {return (0<id fefe id<tabsz) ; } int get_id(char* p, int ins =0); // if ins>0, may insert p. public: int getid(char* p, int ins =0); // if ins>0, may insert p. inline int insert(char* p) { // return negative when table is full. return getid(p.l); ** - inline ent setvaKint id, ent val) { if (legalid(id)) { vals[id] = val; return val; else return 0; } inline ent setvaKchar* p , ent val) { return setval (insert (p) , val) ; inline ent getvaKint id) { return (legalid(id) )?vals [id] :0 ;} ent getvaKchar* p) ; // cannot automatically insert . inline char* getname(int id) {return (legalid(id) )?table[id] :0;} int del(int id) ; // can only delete nil value entry inline int del (char* name) { return deKgetid(na e) ) ;} void print () ; stab (int sz) ; "stab(){}; #ifndef GENERICH #include <genβric.h> Sendif #define gstab (type) name2 (type, stab) #define gstabdeclare(type) \ struct gstab(type) : stab { \ int ge id ( char *p , int ins =0) { APPENDIX A - PAGE 46 return stab: :getid(p,ins) ; \ } \ int insert(c__ar* p) { return stab: :insert(p) ; } \ inline type setvaKint id, type val) { \ return (type)stab: -.setvaldd, ent(val)); \ } \ inline type setvaKchar* p, type val) { \ return (type)stab: :setval(insert(p) , ent(val)); \ . > \ type getvaKint id){ return type(stab: :getval(id)) ;} \ type getvaKchar* p) { return type(stab: :getval(p)) ; } \ char* getnamednt id) { return stab: :getname(id) ;} \ int deldnt id) { return stab: :del(id) ;} \ int deKchar* name) {return stab: :del(name) ;} \ void print0 { stab: :print() ;} \ gstab(type)(int sz) : (sz) {} \ " gstab(type)0 {} \ APPENDIX A - PAGE 47 synt.c typedef void* ent; typedef void (*PFC)(char*); // pointer to function type #define nl « "\n" #define sp « " " #include <stream.h> #include <string.h> #include "slist.h" #include "graph.h" #include "stab.h" gslist(Pchar) query; gslis (Pchar) lastquery; extern char* copyo (char*) ; #include <ctype.h> Nodelist activelist; digraph* g; declare(gstab,Pnode) ; gstab(Pnode)* diet; Edgelist s.tree; extern int getword(char*); extern long mytimeO; extern char* copyof(char*) ; printwords(gslist(Pchar)fe 1, int linenum = 0) gslist.iterator (Pchar) it(l) ; char* p ; cerr « linenum << ": "; while (p=itO) cerr « p « "I"; cerr nl; char * quote(char* s) Pchar ss = new char[strlen(s)+3] ; sprintf (ss . "V .s\"" . s) ; return ss ; display 0 ■r APPENDIX A - PAGE 48 char* s ; Pnode last = 0 , pn; gslist.iterator (Pchar) qit (query) ; gslist.iterator (Pchar) lit (lastquery) ; g->clea_rmesgsO ; s = query . get ( ) ; if (*s » ' / ' { while (s = query.get()) lastquery.append(copyof(s)) ; lit.resetO ; while (s = litO) query.append(copyof(s)) ; } else { lastquery.clearO ; query.insert(s) ; qit.reset() ; while (s ■ qitO) lastquery.append(copyof(s)) ; *ι while (s β query.get ) { pn = dict->getval(s) ; if (!pn) { if (*s«- * = * || *s « ■ < ■ II *s == * > * ) { nodeinfo* p = last->pinfo; p->op.append(s) ; if (p->ftyp[0]-== " c " ) p->val.append(s * quote(query.gβt())) ; else p->val.append(s = query.get()); continue; } else { pn->n_mesg.setmarker(++g->m_id) ; activelist.append(pn) ; last = pn; } delete s; > if (activelist.numO) { int t = mytimeO ; float time; Pnode center = g->steitree(s_tree, activelist); time = (mytime()-t)/1000000.0; cerr << "time used: " « time << " seconds" nl; if (center) { Edgelist tree; treβ.duplicate(s.tree) ; if (interpret(center, &s_tree)==0) { APPENDIX A - PAGE 49 if { printtree(fetreβ , center, 0) ; cerr « " continue \n\07* " ; else cerr << "null tree" nl; eise cerr « "Sorry, I don't understand a word" nl << "* " ; syntO char buffer[40] [64] ; short cate[40] ; char s[90] ;char* p=s;char c;int i; cerr << "Entering query mode." nl; cerr « "* "; query.clearO; for (i=0;;i++) { getword(s) ; c = *s; if (c== * ; * ||c=='? , |l c=='.') { displayO; continue; } if (c==-! 1 ) { if (i>l) cerr « "Don't shout at me!" nl; cerr « "Exit query mode" nl; cerr « "> "; break; quer .append(copyof(s)) ; APPENDIX A - PAGE 50 lex.l C [a-zA-Z0-9_.] N [ " a-zA-Z0-9_] D [0-9] NAME {C}+ u typedef void* ent; typedef void (*PFC) (char*) ; //pointer to function type ..include "slist.h" #include "graph.h" ..include "y.tab.h" char name [50] ; 7.} '//. -?{D}+(".")?{D}* {yylval . val=atof (yytext); returo(NUMBER) ; } upclo {return(UP.CLOSURE) ;} downclo {return(DOWN.CLOSURE);} Word {retum(WORD);} up {return (UP) ;} down {retr (DOWN) ;} connect {return (CONNECT) ;} for {return(FOR) ;} all {return (ALL) ;} edge {return(EDGE) ;} display {reτurn(DISPLAY) ;} ingres {retura{INGRES) ;} query {return(QUERY) ;} len= {return(LEN) ;} rlen= {retu n(RLEN) ;} TABLE= {retum(TABNAHE);} FTYP= {return(FTYP);} do {return(DO) ;} isa {re ______ (ISA) ;} subset {return(SUBSET) ;} supset {return(SUPSET) ;} hasatt {return(HASATT);} attof {retur (ATTOF) ;} typeof {return(TYPEOF) ;} table {return(TABLE) ;} attri {return(ATTRI) ;} value {return(VALUE) ;} node {return(NODE) ;} \"[-"]*\" { yytext [strlen(yytext)-l] = '\0'; strcpy(yylval.str,yytext+l) ;return(NAME) ; .PPENDIX A - PAGE 51 {NAME} {strcpy(yyl val. str, yytext) ; return (NAME) ;} > {return ( ' > ' ) ; } [,_. {return ('.');} = {return ('=');} ; {return (';');} [\n] ; \\[\n] ; U APPENDIX A - PAGE 52 q-y y.{ typedef void* ent; typedef void (*PFC) (char*) ; //pointer to function type ..include <stdio.h> #include <stre____.h> #include <string.h> .. include <ctype .h> Sϋnclude "slist .h" #include "graph.h" #include "stab.h" #dβfine nl << "\n" #define sp << " " strlist names; strlist keys; extern digraph* g; declare(gstab,Pnode) ; extern gstab(Pnode)* diet; extern Nodelist activelist; extern char* yytext; extern FILE* yyin; int recover = 0; int state = 0; struct destin { char name[64] ; float length; float rlength; Pedgeinfo pei; typedef destin* Pdestin; declare(gslist, Pdestin); gslist(Pdestin) destins; extern void printedges(Edgelistfe) ; Sde ine nodename(x) dict->getname(x->getid()) 7.} 7.union { float val; int ival; char* pstr; char str[60] ; Pedgeinfo pei; Pnodeinfo pni; } APPENDIX A - PAGE 53 /.token <void> OF FOR DISPLAY EDGE UP DOWN ALL CONNECT LEN RLEN DO NODE /.token <void> DOWN.CLOSURE UP.CLOSURE TABNAME WORD FTYP /.token <val> NUMBER INGRES QUERY /.token <void> ISA SUBSET SUPSET HASATT ATTOF TYPEOF TABLE ATTRI VALUE /.token <str> NAME /.type <pstr> tabname /.type <ival> etype ntype keys /.type <str> fieldtyp tftype <val> length rlength '/type <nni> nodeinfo /./. commandlist: {if (yyin==stdin) cerr « "> ";} I commandlist command ' ; ' { if (yyin==stdin) cerr « "> "; command : EDGE NAME destinlist { Pdestin pd; Pnode s,d; int id = dict->insert($2) ; s = dict->getval(id) ; if (s==0) dict->setval(id, s=g->addnode(id)) ; while (pd=destins.get()) { d = dict->getval(id=dict->insert(pd->name)) ; if (d==0) { d=g->addnode(id) ; dict->setval(id, d) ; if (pd->pei->etype==TYPEOF) d->pinfo = new nodeinfo(VALUE) ; if (pd->pei->etype==HASATT I I pd->pei->etype== SUPSET) { pd->rlength = 1.0; if (d->pinfo->ntype==TABLE) pd->length = 3.0; else pd->length = -1.0; x else { pd->length = -1.0; pd->rlength = 0.3; _- g->addedge(s, d, pd->length, pd->rlength, pd->pei) ; x } I NODE NAME nodeinfo { int id = dict->insert($2) ; Pnode pn = dict->getval(id) ; APPENDIX A - PAGE 54 if (pn—0) { pn = new node(id ,g->mk_num) ; dict->setval(id ,pn) ; g->nodes . append (pn) ; } pn->pinfo = $3 ; pn->pinf o->tablename=dict->getnamedd) ; } I DO NAME { if (yyin=fopen($2 , "r" ) ) { yyparseO; recover = 1; yyin=stdin; x I DOWN NAME { down_nbs(dict->getval($2)) ; I UP NAME { up_nbs(dict->getval($2)) ; } I ALL { Pnode p; gslist.iterator(Pnode) it(g->nodes) ; while (p=it()) cerr << nodename(p) << ", "; cerr nl; I INGRES { char c; while ((c s getchar )! » '!') { putchar(c) ; if (c==-'\n " ) fflush(stdout) ; } while (getcharO !='\n * ) ; ungetc(' ; ' , stdin) ; } I QUERY { syntO ; while (getcharO !='\n') ; ungetc(' ; ' , stdin) ; } I WORD NAME { dict->insert($2) ; } - DO - APPENDIX A - PAGE 55 I UP.CLOSURE NAME etype { gsIist(Pnode) ancestors; (dict->getval ($2) )->up (ancestors, $3) ; Pnode pn; while (pn=ancestors . get () ) cout « nodename(pn) << " , " ; cout nl; [ v D0WN_CL0SURE NAME etype { gslist(Pnode) decendents; (dict->getval($2))->down(decendents , $3) ; Pnode pn; while (pn=decendβnts.get()) cout << nodename(pn) << ", "; cout nl; na elist: I namelist NAME { char *str = new char[strlen($2)+l] ; strcpy(str, $2); names. append (str) ; } length : { $$ = 1; } I LEN NUMBER {$$=$2;} rlength : $$ = !; } I RLEN NUMBER {$$=$2;} » destinlist: I destinlist NAME length rlength etype { Pdestin p = new destin; strcpy(p->name, $2); p->length = $3; p- * rlength =$4; p->pei = new edgeinfo($5) ; destins . append (p) ; fieldtyp: { strcpy($$, "c");} I FTYP NAME { strcpy($$, $2); APPENDIX A - PAGE 56 nodeinfo: ntype keys tabname fieldtyp { $$ = new nodeinfo($l, keys, $3, $4); etype : { $$=0;} I ISA { $$ = ISA; } J SUBSET { $$ = SUBSET; } I SUPSET { $$ = SUPSET; } I HASATT { $$ = HASATT; } I ATTOF { $$ = ATTOF; } I TYPEOF { $$ = TYPEOF; } tabname : {$$ = 0;} I TABNAME NAME { $$ = copyof($2) ; ntype : TABLE { $$ » TABLE; I ATTRI { $$ « ATTRI; I VALUE { $$ = VALUE; } keys : { $$ = 0; } I keys NAME { keys.appβnd(copyof($2)) ; $$ = $1+1; } APPENDIX A - PAGE 57 int yyerror(char* s) if (! recover) cerr « s; else recover=0; void printtree (Edgelist* stree, Pnode center, int tabc = 0) Pedge e; static int linect = 0; if (tabc==0) linect=0; cerr « ++linect « "\t"; for (int i=0;i<tabc;i++) cerr « "\t"; cerr << nodename (center) nl; while (e = stree->get()) { if (center==e->source_node) { printtree ( str ee,e->destin_node,tabc+l) ; else if (center==e->destin_node) { printtree(stree,e->source_node,tabc+l) ; else {stree->insert(e) jreturn;} char* copyof (char* s) char* p = new char[strlen(s)+l] ; return strcpy.p, s); void down.nbs(Pnode pn) x Pnode p; if (pn==0) return; destin_iter iter(pn) ; while (p = iterO) cerr << nodename(p) sp; cerr « "\n"; return; void up.nbs(Pnode pn) Pnode D; APPENDIX A - PAGE 58 if (pn= s 0) return; source.iter iter(pn) ; while (p = iterO) cerr << nodename(p) sp; cerr « "\n"; return; APPENDIX B - PAGE 1 Appendix B - Listing of Browser • Makefile • matci c • netman.c • snet.c • trans.c • dbinfo.q • browse.h • Dif. APPENDIX B - PAGE 2 Makefile snet: db.info.q snet.o trans.o match.o netman.o equel db.info.q; cc -o snet -g db.info.c snet.o trans.o match.o netman.o -lq APPENDIX B - PAGE 3 match,c /* # This module contains # Matching and Pruning Routines __ # forming part of the .= *" GRAND - System # (Using some PIF routines) * # Deon Oosτhuizen x # February 1987 - September 1988 */ t include ''net.h" # include <stdio.h> # unάef TRACE /* Definitions used with real PIF # define TRUE 1 # define FALSE 0 # define OFFEND -6 # define RECSIZΞ 15 « / # define LSIZE 150 # define RSIZE 12 # define LAGSIZE 30 int succl=0,succ2=0; /* counters of no. of successes during inference */ int errc=0,primary.secondary; /* pruning window sizes */ int prim,sec; /* pruning window sizes */ int shrink=2,last_cut=2,lagptr=0; /* store positions up to which pruning has been applied */ extern int ccache.dirty; /* closure caching flag */ extern int count; /* concept number counter */ extern int list[LSIZE] [2] ; /* stores intersections of DCs of 3i and A */ int rules [RSIZE] ; /* used to store 12 most general 'rules ' for use in most .general */ APPENDIX B - PAGE 4 extern char buffer[RECSIZE] ; /* general purpose buffer, usually used for printing strings */ extern struct body *tab[MAX_ENT] ; /* table with link info. */ int arity.mat[2] ; /* dummy array - not used currently */ int lag[LAGSIZE] ; /* window addresses for pruning */ int lowlimit=0; /* ent. no. of first sample */ int pos*1,predict=FALSE; float sum=0.0,avemat=0.0,aveconf=0.0; /* average values and sums */ ixxτ upper=0; /* used during compression */ extern int current; /* current last ent. no. (global var) */ int avepop=0; /* average population size */ set.reg *candi,*candi_store; /* attributes to which sample has to be linked */ set.reg *classes; /* list of classes */ set.reg *avail_ent; /* deleted nodes */ set.reg *shield; /* union of DCs of classes */ trans(X,A,num_classes,in er) int X.A.num.classes.infer; /* This routines calls real.trans once all the attributes of a given sample is known, thereby enabling the system to match the new example against previous examples first before insertion */ :at i; if (pos < num.classes) { if (candi == NULL) { candi β newset() ; shield=newset() ; classes=newset() ; initialiseset(classes,0) ; /* record.classβs(classes) ; */ _. if (A > 0) puton(candi.A) ; pos++; } else { predict=infer; if (A > 0 fefe !infer) puton(canάi.A) ; else { APPENDIX B - PAGE 5 if (A>0) display (A, buffer, ECSIZE) ; printf C true class='/,s " .buffer) ; /* if ( ipredict) printf C X='/.d " ,X) ; */ if (errc == 0) { errc=l ; primary = 14-num_ classes ; if (primary < 3) primary=3 ; sgcondary=pr___ary*2+2 ; if (secondary >= LAGSIZE) secondary=LAGSIZE-2; prim=primar ; sec=secondary; } /* pre.trans(X.A) ; */ if (A > 0 && in er) { puton(candi.A); puton(classes,.A) ; y if (!infer) { for d=first(candi);i != OFFEND;i=succ(candi,i)) if (i == A) real.trans(X.i,TRUE); else real.trans(X.i,FALSE) ; initialiseset(candi.O) ; /* pruneO; */ if (current >= MAX_ENT-num_classes*num_classes/2-250) if (upper < current) upper=current; current=comDress(current) ; eise ι I* printf("\ntrue atts: ") ; for (i=first(candi) ;i != OFFEND;i=succ(candi,i)) { display(i,buffer,RECSIZE) ; printfC 7,s ".buffer); } */ initialiseset(candi.O); /* printf("\n"); */ } pos=l; retur (count) ; } /* */ APPENDIX B - PAGE 6 pre.trans(X.A) int X,A; /* Marker propagating routine */ { j.nt i, j ,k,m,_____=0, markers [MAX.ENT] ; set.reg *DCi=newset() ; set.reg *total=newset () ; candi_store=newset() ; orsetreg(candi,candi_storβ,candi_store) ; for (i=l;i< MAX_ENT;i++) markers [i] =0 ; if (predict) for (i=0;i<RSIZE;i++) rules[i]=0; /* DC for each class */ for (i=first(candi);i != OFFEND jiesuccdandi.i)) { closure (LOAD, i.DCi, ALL, DOWNWARD) ; for (j=first(DCi);j != OFFEND; j=succ(DCi, j)) { if (j < MAX.ENT) { /* accumulate markers */ markers [j]++; if (markers[j] > 2) puton(total, j) ; } else { print ("MAX.ENT too small"); return ; } if (markers [j] > max) max=markers[j] ; /* keep record of most general 'rules' */ if (predict) if (j >- lowlimit fefe markers[j]>l) upd.rule(j); } } disposeset(DCi); if (max > 1) select(X,A,max,markers,total) ; disposeset(total) ; disDoseset(candi.store) ; } - / D - APPENDIX B - PAGE 7 /* */ select(X, ,max,markers.total) int X, ,max.markers[MAX.ENT] ; set.reg *τotal; /* Identifies the meet spanning the largest number of features */ /* During insertion: link example to the meet */ /* During inference: determine if the meet implies contradicting features, and if not, perform inference */ int Y.secY,i,check=0,add_class=FALSE,class,fclass, max; set.reg *max_set=newset() ,*UCY=newset() ,*secUCY=newset() ,*result; for (i=i ; i < MAX_ENT; i++) if (markers [i] == max) { puton(max.set . i) ; /* putoff (total, !) ; */ } Y=focus( ,max,markers,max_set,UCY,total) ; if (lowlimit != 0 fefe Y >= lowlimit) { closure(LOAD,Y,secUCY,ALL,UPWARD); notsetreg(secUCY) ; andsetreg(secUCY,total,total) ; notsetreg(secUCY) ; if (!inseτ(max_set,A)) add_class=TRUE; else if (inset(secUCY,A)) andsetreg(secUCY,UCY,UCY) ; if (predict) { result=newse () ; andsetreg(classes,secUCY,result) ; if ((class=first(result)) != OFFEND) { fclass=class; display.fclass,buffer,RECSIZE) ; printf("\nPredicted classl: */,s ".buffer); predict=2; else { if (strength(Y) > 2) uncβrtain(A,Y.fefclass) ; if (predict==l) add_class-=TRUE; } disnoseset(result); APPENDIX B - PAGE 8 } for (i=first(secUCY) ;i != OFFEND;i=succ(secUCY,i)) { if (i < lowlimit I I lowlimit == 0) { check++; puto (max.se ,i) ; } } fmax=check; avemat* (avemat*sum+max)/++sum; aveconf* (aveconf*—sum+check)/++sum; printf("conf 7.d(7.d) av:conf 7.2.If\n".check,strength(Y) .aveconf) ; if (ipredict) { if (max >= 11 && (check >= 8)) { /* I I strength(Y) > 16 */ /* if (max >= (int) (0.5*num_classes+1.9) fefe (check >= 0.5*num_classes */ /* check >= (((float)classes)/10.0)*7 */ andsetreg(candi,max.set,candi) ; if (add.class) puton(candi.A) ; /* ensure that X bcomes a member of A (in spite of approximation) */ printfC SCORE 7.d " ,card(candi)) ; } else for (i=first(sβcUCY);i != OFFEND;i=succ(secUCY,i)) if (i < lowlimit I I lowlimit == 0) putoff(candi,i; ; add_class=FALSE; _ X else add_class=TRUE; if (ipredict II predict == 1) { if (add.class) { max=0; for (i=first (total) ; i ! = OFFEND; i=succ (total , i) ) if (markers [i] > max) max=markers [i] ; if (max > 1) { initialisese (max.set , 0) ; for (i=f irst(total) ; i ! = OFFEND ; i=succ (total , !) ) if (markers [i] == max) { puton(max_se , i) ; /* putof f (total , i) ; */ } APPENDIX B - PAGE 9 secY=f ocus (A .max .markers .max.set , secUCY .total) ; if (lowlimit ! = 0 fefe secY >= lowlimit) { closure (LOA , s ecY , secUCY , ALL .UPWARD ) ; notsetreg(secUCY) ; andsetreg(secUCY, total , total) ; notsetreg(secUCY) ; if (inse (max.set , A) ) { add. cl as s=FALSE ; if (inset(secUCY.A) ) { orsetregOJCY, secUCY, UCY) ; if (predict) { result=newset() ; andsetreg(classes,secUCY,result) ; if ((class=first(result)) != OFFEND) { fclass=class; display(fclass,buffer,RECSIZE) ; printfC\nPredicted class2: 7.s ".buffer); predict=2; eise _ if (strength(secY) > 2) -__ncertain(A,secY,fefclass) ; if (predict==l) add_class=TRUE; disposeset(result) ; printfC 2nd ") ; if (Y < lowlimit) { check=0; for (i=first(secUCY);i != OFFEND;i=succ(secUCY,i)) if (i < lowlimit I I lowlimit == 0) { check++; putoff(candi,i) ; printfCconf 7.d ".check); Y=secY; * v APPENDIX B - PAGE 10 } } if (predict == 1 I I add.class) { max=ύ; » for (i=first(total) ;i != OFFEND;i=succ(total,i)) if(markers[i] > max) max=markers[i] ; if (max > 1) { initialiseset(max.set,0) ; for d=first(total) ;i != OFFEND;i=succ(total,i)) if (markers[i] == max) { puton(max_sβt,i) ; /* putoff(total,i) ; */ } secY= ocus(A,ma .markers.max.set,secUCY.total) ; if (lowlimit != 0 fefe secY >= lowlimit) if (Y < lowlimit I I inset(max.set,A) II predicτ==l) { closure(LOAD,secY,secUCY,ALL,UPWARD); notsetreg(secUCY) ; andsetreg(secUCY,total,total) ; notsβtreg(secUCY) ; if (inset(secUCY,A)) orsetreg(UCY,secUCY,UCY) ; if (predict) { result=newset() ; andsetreg(classes,secUCY,result) ; if ((class=first(result)) != OFFEND) { fclass=class; display(fclass,buffer,RECSIZE) ; print ("\nPredicted class3: 7,s ".buffer); predict=2; } else if (strength(secY) > 2) uncertain(A,secY,fe class) ; disposeset(result) ; } printf C 3rd " ) ; if (Y < lowlimit) { check=0 ; APPENDIX B - PAGE 11 for (i=first(secUCY);i != OFFEND { if (i < lowlimit I | lowlimit == 0) { check++; putoff(candi,i) ; X printf("conf 7.d ".check); Y=secY; } } } y if (lowlimit ! = 0 fefe Y >= lowlimit) if ( ipredict) add (UCY) ; display (Y. buffer, ECSIZE) ; if (buffer [0] == ' * ' | | (buffer [0] ! = 'S ' fefe buffer [0] ! = ' C' ) ) link(X.Y) ; else merge (X,Y) ; if (predict) { if (predict ==1) most.general(A) ; if (fclass == A) printfC success = 7.d\n",++succl) ; disposeset(UCY) ; disposeset(secUCY); disposeset(max.set) ; } /* */ focus(A,max,markers,max.set,UCY.total) int A.ma .markers[MAX.ENT] ; set.reg *max_set,*UCY,*total; /* finds the meet or 'focus' of a number of given features */ APPENDIX B - PAGE 12 { int Y . i ; set.reg *DCi=newset() ; Y= irst (max.set) ; root (&Y, max.set , DOWNWARD) ; initialiseset (max.set , 0) ; /* this is not neax - I am using max.set for a second purpose , not to create a new set */ closure (LOAD , Y, UCY, ALL, DOWNWARD) ; notsetreg(UCY) ; andsetreg(UCY,total,total) ; closure(LOAD.Y,UCY,ALL,UPWARD) ; for (i=first(UCY) ;i != OFFEND;i=succ(UCY,i)) { /* putoff(total,i); */ if (i < lowlimit I I lowlimit == 0) { if (!inset(candi_store,i)) { if (predict) { if (iinset(classes.i)) closure ( OR, i .DCi, ALL, DOWNWARD) ; } else closure(OR, i ,DCi , ALL, DOWNWARD) ; } else puton(max_set ,i) ; } } notsetreg(DCi) ; andsetreg(DCi,UCY,UCY) ; Y=first(UCY); for (i=succ(UCY,Y);i != OFFEND;i=succ(UCY,i)) if (markers[i] > markers[Y]) Y=i; disposeset(DCi) ; return(Y) ; } / * „/ uncertain(A , Y , class ) int A ,Y , *class ; APPENDIX B - PAGE 13 /* Performs probabilistic inference if no absolute inference is possible */ int c=0 ,i , j .best , hich. one; set.reg *DCA=newset() ; closure(LOAD , A, DCA, ALL, DOWNWARD) ; cIosure(AND ,Y,DCA,ALL, DOWNWARD) ; if ((i=firs (DCA)) != OFFEND) { root(&i,DCA,DOWNWARD) ; c=best=strength(i) ; printfC (evid=7.d/7.d) " .c.strength(Y)) ; _- putoff(classes, ) ; for (i=first(classes) ;i != OFFEND;i=succ(classes,!)) { displayd,buffer,RECSIZE) ; closure(LOAD,i,DCA,ALL,DOWNWARD) ; closure(AND,Y,DCA, LL,DOWNWARD) ; if ((j=first(DCA)) != OFFEND) { root(fe ,DCA,DOWNWARD) ; printf(" (evid_y.s=7.d/7.d) ".buffer,strength(j) ,strength(Y)) ; if (strength( )>= best) { if (strength^ j ) == best) printf C (=) ") ; else best=s-_rength(j ) ; which_one=i ; If (c>0 I I best > 0) { if (c>=best) { *class=A; display(*class , buffer, RECSIZE) ; printf C\nPredicted class : '/,s " .buffer) ; predict=3 ; else { *class=which_one ; display (*class , buffer, RECSIZE) ; printf C\nPredicted class : y.s " .buffer) ; predict=2 ; } } puton( classes , A) ; APPENDIX B - PAGE 14 disposeset (DCA) ; /* merge (X .Y) int X.Y ; /* If the meet of a number of features is an example node, create a new concept node and link that example node AND the current example to the new node */ int new ,j ; set.reg *UClY=newset() ; newY=get_entity() ; /* if (lowlimit == 0) lowlimit=newY-l; */ count++; sprintf(buffer,"*7.d" .count) ; # ifdef TRACE printf("\n new name 7.s:7.d " .buffer,count) ; # endif TRACE while(setstringforentity(newY,buffer,ENTTYPE) < 0) { count++; sprintf(buffer,"*7.d" .count) ; closure(LOAD,Y,UClY,l,UPWARD); putoff(UC1Y.Y) ; for (j=first(UClY);j != OFFEND;j=succ(UClY,j)) { link(newY,j) ; deletelink(Y,j ,1,13) ; } link(X.newY); link(Y.newY); disposeset(UC1Y) ; } /* */ APPENDIX B - PAGE 15 /* Code below added on 20/4/88 */ /» */ prune O /* Performs pruning starting at last.cut and applying three different degrees of severity over three different windows */ int i.temp,lagpos,popsize=0,accum=0,tune=0; int a=0,b=0,c=0,d=0,e=0; if (avail.ent != NULL) popsize=current-lowlimit-card(avail.ent) ; lagptr = ++lagptr 7. LAGSIZE; temp = lag[lagptr] ; lag[lagptr]=current+l; tune=ave_conf() ; tune=popsize/100 + tune/20; if (popsize > 400) { if (prιmary>5) { primary—; secondary-=2; } else if (primary < prim) { primary++; secondary+=2; } else if (primary > prim) { primary—; secondary-=2; } /* printfC p=7.d s=7.d ",primary,secondary) ; */ lagpos= lag[(lagptr+LAGSIZE-secondary) 7. LAGSIZE] ; for (i=last.cut;i<lagpos;i++) if (strength(i) < ll+tune*3) { accum += cut(i.l); if (accum >0) printf("+3+(7,d) ",accum); accum=0; if (last.cut < lagpos) last_cut=lagpos; lagpos= lag[(lagptr+LAGSIZE-primary) */, LAGSIZE] ; for (;Klagpos;i++) if (strength(i) < 3+tune/3) { accum += cut(i ,2) ; if (accum >0) printf C+l+C/.d) " , accum) ; accum=0 ; APPENDIX B - PAGE 16 if (popsize > 100) { for (i=shrink;i<temp ; i++) { if (strength(i) < 26+tune*5) accum += cut (i ,3) ; } if (accum >0) print ("+0+(7,d) ",accum); shrink=i; x "if (avail.ent != NULL) popsize s current-lowlimit-card(avail_ent) ; for d !S lowlimit;i<current;i++) if (strength(i) > 0 fefe strength(i) < 5) a++; else if (strength(i) >= 5 fefe strength(i) < 9) b++; else if (strength(i) >= 9 fefe strength(i) < 16) C++; else if (strength(i) >= 16 fefe strength(i) < 1000) d++; printf (" -5=7.d 5-8=7.d 9-16=7.d 16+=7.d ".a.b.c.d); if (popsize > 0) { /* avepop= (avepop*(sum-l)+popsize)/sum; printfC popsize*7,d ave=7.d\n",popsize,avepop) ; */ printfC popsize=7.d\n",popsize) ; } /* int node.cc; /* delete nodes provided the deletion does not violate lattice properties */ { int d,e=0,f=0,winner,response s 0; set.reg *clsr,*clsrd; /* Nodes falling in the downward closure of classes are more likely to play a role in inference and thus deserve a little more protection. Pruning is therefore not applied so severly on them */ if (inset(shield,node)) { if (cc =« 2) if (strength(node) > 3) return(O) ; /* if (cc == 1) { APPENDIX B - PAGE 17 if (strength(node) > 10) return(O) ; _. else if (strength(node) > 27) return(0) ; */ clsr=newset() ; /* Determine if the node f lls on the edge of the network */ display(node,buffer,RECSIZE) ; If (buffer[0] == '*') { closure(LOAD,node,clsr.l,DOWNWARD) ; putoff(clsr,node) ; winner=first(clsr) ; for (d=winner;d != OFFEND;d=succ(clsr,d)) { if (boun ary(d)) { e++; } else if (strength(d) > strength(winner) fefe e == 0) { winner=d; *. f++: if (e >= 2) { disposeset(clsr) ; return(O) ; if (f >= 2) -; putoff(clsr, inner) ; for (d=first(clsr) ;d != OFFEND;d=succ(clsr,d)) deletelink(d,node,0,15) ; response=l; if (winner != OFFEND) { transfer.classes(winner,node) ; response=l; x else { closure(LOAD,node,clsr,1,UPWARD) ; putoff(clsr,node) ; for (d=firs (clsr) ;d != OFFEND;d=succ(clsr,d)) APPENDIX B - PAGE 18 deletelin (node , d , 0 , 14) ; response=l ; } mendlist(node.O) ; freeze(nodβ) ; } } else { slosure(LOAD,node,clsr,1,UPWARD) ; putoff(clsr,node) ; for (d=first(clsr) ;d != OFFEND;d=succ(clsr,d)) { deletelink(node,d,0,14) ; response=1; /* superior(d) ; */ } mendlist(node,0) ; freeze(node) ; } disposese (clsr) ; return(response) ; } /* boundary(d) int d; /* Deletion of boundary concepts with more than two parents may violate lattice properties */ { int i,e=0,response s FALSE; set.reg *clsrd=newset() ; display(d,buffer,RECSIZE) ; if (buffer[0] == '*') { closure(LOAD,d.dsrd.l.DOWNWARD) ; putoff(clsrd.d) ; if (first(clsrd) != OFFEND) response=TRUE; x APPENDIX B - PAGE 19 if (iresponse) { closure(LOAD,d.clsrd.l,UPWARD) ; putoff(clsrd.d); for (i=first(clsrd);i != OFFEND;i=succ(clsrd,i)) e++; if (e > 2) response=TRUE; x disposeset(clsrd) ; return(response) ; } /* */ transfer.classes(Y,3i) int Y,3i; /* Link the one child of a redundant concept to its superiors */ /* Eliminate the concept */ { int h,class,enough=FALSE; int i.ferqueϊlO] [2] ; set.reg *UClBi=newset() ,*UCY=newset() ,*DC1; deletelink(Y,Bi, l, 12) ; for (i=0; i<10 ;i++) { f erque [i] [0] =0 ; ferque[i] [l]=0; } mendlist(Bi.Y) ; if ((strength(Bi) > strength(Y)) fefe Y<MAX_ENT) set.strength(Y,(strength(Bi) +strength(Y))/2) ; closure(LOAD,Y,UCY,ALL,UPWARD) ; closure(LOAD,B!,UClBi,l,UPWARD) ; putoff(UClBi.Bi) ; for (class=firs (UClBi) ;class != OFFEND;class=succ(UClBi,class)) { deletelink.Bi,class,0,0) ; /* was 1 */ if (unset(UCY,class)) { link(Y,class) ; enough=TRUE; " 5- else { /* display(class,buffer, ECSIZE); if (buffer[0] == '*') { DCl=newset() ; closure(LOAD,class,DC1,1,DOWNWARD) ; APPENDIX B - PAGE 20 putof (DC 1, class) ; if ((h=first(DCD) != OFFEND) { putof f(DCl.h) ; if (first (DC1) • »■ OFFEND fefe strength(h) > strength (class)) { for(i=0;i<10;i++) if (ferque[i][0] == 0) { f erque[i] [0] =h; f * erque[i] [1] =class; break; } if (i«10) { printfC QUE SMALL "); transfer_classes(h,class) ; _> *• } } disposeset(DC1) ; } * ' / } # ifdef TRACE printfC condense CLASS"); # endif TRACE } freeze(Bi) ; /* for(i=0;ferque[i] [0] != 0 fefe i<10;i++) trans er_classes(ferquβ[i] [0] ,ferque[i] [1]) ; */ if ( ! enough) { display (Y, buffer, RECSIZE) ; if (buffer[0] «= '*') { closure(LOAD, Y, UCY, 1, UPWARD) ; putoff(UCY,Y); if ((h=first(UCY)) != OFFEND) { /* done for 'prune' */ putoff(UCY,h=first(UCY)) ; if (first(UCY) == OFFEND) transfer.members(Y.h) ; } } } APPENDIX B - PAGE 21 disposeset (UCY) ; disposeset (UCIBi) ; /* */ add(tree) set_reg *tree; /* Increase strength of nodes taking part in the 'schema' spanned by the strongest meet which spans no contradicting features */ :nt :or (i=first(tree) ;i != OFFEND;i=succ(tree,i)) { if (i < MAX.ENT) inc.strength(i) ; else printf("ERROR !!! * /,d ",i); /* temporary */ /* superior(d, ode) int d.node; /* if d has only one other child left, transfer.classes */ /* Not used currently */ int f; set.reg *clsrd; if (d < lowlimit) return; clsrd=newset() ; closure(LOAD,d,clsrd.l,DOWNWARD) ; putoff(clsrd.d) ; if ((f=first(clsrd)) != OFFEND) { pu off(clsrd.f) ; APPENDIX B - PAGE 22 if (first (clsrd) " OFFEND) { if (strength(f) >« strength(d) ) { printfC STRONGER "); transfer.classes(f.d) ; } } } disposeset(clsrd) ; } /* */ significant(lowest,father) int lowest,father; /* determine if "father" has any other children apart from "lowest" */ /* Not used currently */ { int d,response=FALSE; set.reg *clsr; display (father , buffer , RECSIZE) ; if (buffer CO] ! = ' * ' ) { clsr=newset () ; closure(LOAD, ather,clsr,1,DOWNWARD) ; putoff(clsr, ather) ; putoff(clsr,lowest) ; if ((d=first(clsr)) == OFFEND) response=TRUE; disposeset(clsr) ; } /* printfC response=7.d ".response); */ return(response) ; } /* confidence(node) int node; APPENDIX 3 - PAGE 23 return (strength (node) ) ; /* */ get.entityO /* Gets a new entity and does a little bit more */ int i, _ ; C = new.entityO ; if (lowlimit == 0) { if ((Iowlimit=entity("Sl")) < 0) { Iowlimit=C; /* for (i=0;i< MAX.ENT;i++) { arity.mat [i] =0 ; } */ else update.avail ; shrink=last_cut=lowlimit ; if (C>0) set_strength(C,2) ; else { printf("\n ENTITY SPACE TOO SMALL.n"); scanf("7.d",&C); C=0; return(C) ; /* */ card(set) set.reg *set; APPENDIX B - PAGE 24 /* Determines the cardinality of a set */ { int i.ja-O; for (i=first(set) ;i != OFFEND;i=succ(set,i)) j++; return(j) ; } /* ave.conf() /* Computes the average confidence level (strength) of the nodes */ { for (i=l ; i<=current; i++) if (tab[i] != NULL) { tot += tab [i] ->con ; num++ ; } if (num == 0) return(O) ; return(tot/num) ; } /* dump (f name) char fname[RECSIZE] ; /* Stores all the link information as well as a few other parameters */ /* I.e. Stores the database */ { int i.j; FILE *df; set.reg *clos=newsβt() ; if ((df=fopen( name,"w")) == NULL) return(FALSE) ; APPENDIX B - PAGE 25 fprintf (df ,"7.d 7,d Y,ά 7.d\n", current, upper, lagptr, lowlimit) ; for d=0;i<LAGSIZE;i++) fprintf (df,"7.d ",lag[i]); for (i=l;i<=current;i++) { if (tab[i] != NULL) fprintf (df ,"7.d 7.s 7.d\n" ,tab[i]->id,tab[i]->name,tab[i]->conf ) ; else f printf (df,"-l\n"); for (i=l;i<=current;i++) { if (tab[i] != NULL) { closure (LO AD, i.clos.l, UPWARD) ; putof f(clos.i) ; for(j=first(clos) ; j !=OFFEND;j=succ(clos,j)) f printf (df,"7.d ".j); printf (df,"-l\n"); closure (LOAD, i.clos.l, DOWNWARD) ; putof f(clos.i) ; for(j=first(clos);j !=0FFEND; j=succ(clos, j)) printf (df,"7.d ",j); fprint (df ,"-l\n"); } else fprintf (df ,"-i\n-l\n") ; f close (df); return (TRUE) ; /* */ restore(fname) char fname[RECSIZE] ; /* Recreates the network in memory if an existing database is used */ i int i.f,ent.number; FILE *df; avail_ent=newset() ; if ((df=fopen(fname,"r")) == NULL) return(FALSE) ; fscanf(df,"7.d '/.d 7.d 7.d" .fenumber.feupper.&lagptr.felowlimit) ; for (i=0;i<LAGSIZE;i++) fscanf(df,"7.d",felag[i]) ; APPENDIX B - PAGE 26 for (i=l;i<=number;i++) { fscanf (df ,"7.d",fef); ent=new_ entity () ; if (f >= 0) { fscanf(df,"7.s 7_d" ,tab[ent]->name,fetab[ent]->conf) ; } else puton(avail.ent.i) ; } for (i=l;i<=number;i++) { fsca__f(df,"7.d",fef); while (f >= 0) { link(i.f); fscanf(df,"7.d",fef); } fscanf(df,"7.d".fef); while (f >= 0) { link(f.i); fscanf(df,"7.d",fef); } } shrink=last_cut=lowlimit; fclose(df) ; printf("restore") ; return(TRUE) ; } /* */ update.availO { int i; if (avail.ent ~~ - ~ NULL) avail.enfnewsetO ; for (i=2;i < MAX.ENT ;i++) if (strength(i) -- 1000) puton(avail_ent,i) ; } */ APPENDIX B - PAGE 27 arity(node) int node; /* Not used */ int e,k=0 ; set.reg *UC; if ( (k=arit .mat [node] ) == 0) { UC=newset() ; closure(LOAD,node,UC,ALL,UPWARD) ; for (e=first(UC);e != OFFEND;e=succ(UC,e)) If (e < lowlimit) k++; else break; disposese (UC) ; arity.ma [node] = k; y return(k) ; } /* */ upd.rule(j) / * Keeps track of the most general 'rules' - to be used in most.general */ int k,m; if (rules[0] ϊ= 0) if (strength(j) <= strength(rules[0])) return; for (k=0;k<RSIZE;k++) _ if (j == rules[k]) return; if (rules [k] != 0 fefe strength(j ) <= strength(rules [k] )) { for (m=l ;m<k;m++) rules [m-1] =rules [m] ; rules [k-l] =j ; return; for (m-l ;m<k;m++) rules [m-1] =rules [m] APPENDIX B - PAGE 28 rules[k-l]=j ; } /* */ most.general(A) int A; { int i,e,contra; set.reg *UC=newset() ; printfC here "); notsetreg(candi.store) ; for (i=RSIZE-l;i>=0 fefe rules[i]>0;i~) { closure(LOAD,rules[i] ,UC,ALL,UPWARD) ; for (e=first(UC) ;e != OFFEND;e=succ(UC,e)) if (e > lowlimit) putoff(UC,e) ; andsetreg(candi.store.UC.UC) ; contra=0; for (e=first(UC);e != OFFEND;e=succ(UC,e)) { /* display(e,buffer,RECSIZE) ; printfC 7.s ".buffer); */ if (contra<2) { if (e == A) { printf("evid=7,d success2=7.d contra=7.d\n" , strength(rules[i]) ,++succ2,contra) ; break; } if (inset(classes,e)) break; } contra++; __ if (e != OFFEND) { predict=2; /* if (e != A) printfCevid=7,d\n",strength(rules[i])) ; */ break; } } notsetreg(candi.store) ; disposeset(UC) ; } APPENDIX B - PAGE 29 /* */ freeze(node) int node; /* Delete a node by removing its entry from the table */ if (sweep(node) < 0) { printfC CANNOT freeze 7.d ".node); return; /* ccache_dirty=TRUE; */ if (avail.ent == NULL) avail_ent=newset() ; put on (avail. ent , node) ; /* printf C freeze Xd " ,node) ; */ f ree(tab [node] ->ptrs_up) ; free(tab [node] ->ptrs_down) ; free (tab [node] ) ; tab [node] =NULL; /* compress(upper.limit) int upper.limit; /* Compress the link table by shifting up all entities to make use of deleted entries. The purpose of this excercise is the retain the ORDER of entities, i.e. later created entities have higher entity numbers than 'older' entities. The algorithm depends on this (at least, that was the case when I last tested this aspect in Feb. 1988) */ { int i,j,k; ccache_dirty=TRUE; APPENDIX B - PAGE 30 shrink=last_cut=lowlimit ; /* if (d=f irst (avail.ent)) == OFFEND) return (upper .limit) ; */ for (i=lowlimit;i <= current fefe tab[i] != NULL;i++) /* empty loop */ if (i > current) return(upper_limit) ; print ("\n compressing..."); j-i+1; for (;j <= upper.limit ;i++) { w ile(tab[j] == NULL) { for (k=0;k< LAGSIZE ;k++) if (lag[k] == j) { lag [k] -i; break; } 1 for (k=0;k< LAGSIZE;k++) if (lagik] == j) { lag[k]=i; break; } /* printfC 7.d->7.d ",j,i); */ if (inset(shield,j)) { putof (shield,j) ; puton(shield.i) ; } tab[i]=tab[j] ; tab[j]=NULL; tab[i]->id=i; X initialiseset(avail.ent,0) ; lag[lagptr]=i; printf("finished\n") ; return(i-l) ; } /* */ APPENDIX B - PAGE 31 netman.c /* * This module contains * * Network management routines * forming part of the * . * GRAND - System * (Using some customised PIF routines) * * Deon Oosthuizen * February 1987 - September 1988 */ # include "net.h" # include <stdio.h> # undef TRACE # define cur.ent current # define CHAINS 5 # define CCACHESIZE (2001) # define SMALLCC (CCACHESIZE / 4) # define INTSHIFT 5 # define OUTOFMEMORY -9 # define INT.BITS 32 # define 0FFSTART (-1) # define bitno(pos) (1 « (pos & (INT.BITS-1))) # define traceval 0 typedef struct body **bp; struct body *tab[MAX.ENT] ; int setcounter=0; /* 24/9/87 */ int cur_ent=0; int bl.size* ((MAX.ENT + (INT.BITS - 1)) / INT.BITS) ; char buf [RECSIZE] ; /* SMcI 24-3-87 */ typedef struct ccel { int old; APPENDIX B - PAGE 32 int used; int cent; /* ent of closure */ int cdir; /* direction of closure */ int cdepth; /* depth of closure */ set.reg *set; /* ptr to a bitlist containing a closure result */ struct ccel *nextcel; /* ptr to next location in hashtable. */ } cchashel; /* closure hash table element */ ςchashel *intable[CCACHESIZE] ; /* hash index table points to ccache */ cchashel ccache[SMALLCC] ; /* small hashtable large hash range */ cchashel *lru = ccache; cchashel *get_lru(); set.reg *ccache_match() ; int ccache.dirty = FALSE; /* boolean clean out cache if TRUE */ int next.cbl - 0 ; /* the next cache element for use if not full */ int ccfull = FALSE; /* if every ccache el has a bl allocated then TRUE */ int insplit = FALSE; /* «/ bp hunt(chain.ptr,what,add) struct chain *chain_ptr; struct body *what; int add; /* runs along chain of pointers looking for 'what' */ { int i; register bp body.ptr.tβmp; body.ptr = (bp)chain.ptr; for d=0;i .CHAINS;i++) { if (*body_ptr == what) break; /* search further to see if there is already a link to this class/member further on in the list */ if (add) if (*body_ptr == NULL) { temp=hunt(chain.ptr,what,FALSE) ; if (temp != NULL) return(tem ) ; APPENDIX B - PAGE 33 break; body_ptr++ ; x if (i == CHAINS) { if (chain_ptr->cont ! = NULL) { retur ( (bp )hunt ( chain_ptr-> conτ , hat , add) ) ; -* _ else if (add) { cha__n_ptr->cont = (struct chain *)(calloc(l,sizeof(struct chain))); return((bp)chain_ptr->cont) ; x _. else re_urn((bp)NULL) ; else return((bp)body_ptr) ; /* */ linkdπember,class) register int member, class; /* establish links */ int !, err=0 ; register struct body **where , **here ; ccache.dirty = TRUE; /* boolean clean out cache if TRUE */ where = (bp)hunt(tab[member] ->ptrs_up ,tab [class] .TRUE) ; here = (bp)hunt(tab [class] ->ptrs_dow__ ,tab [member] .TRUE) ; if (*here == NULL) *here = tab [member] ; else err=l ; if (*where == NULL) { *where = tab [class] ; if (err==l) printf C incons . ") ; } else if (err==0) printfC too little ") ; APPENDIX B - PAGE 34 # ifdef TRACE printf ("\n »link ") ; printf C * /,d " .member) ; display (member , buf , 15) ; printf ("7.s-" ,buf) ; display (class , buf , 15) ; printf C7.s " ,buf ) ; # endif TRACE } /* */ struct body *fill.up(base) struct chain **base; /* Not used */ int i; struct body **gap,*temp; gap= (bp)*base; if ((*base)->cont — NULL) { for (i=0;i<CHAINS fefe *gap == NULL;i++) gap++; if (i == CHAINS) { free(*base) ; *base=(struct chain *)NULL; return((struct body *)NULL) ; } else { temp= *gap; printf("7.s\n " ,temp->name) ; *gap=NULL; return(temp) ; } > else { if ((t_mps-fill_up( &((*base)->cont) )) != NULL) return(temp) ; else { for (i=0;i<CHAINS fefe *gap == NULL;i++) gap++; APPENDIX B - PAGE 35 if (i == CHAINS) { printf C surprised ") else { temp= *gap; *gap=NULL; retur (temp) ; /* deletelink(member,class.mandatory,leaf) int memoer,elass; int mandatory,leaf; int err=0; register struct body **pos,*temp; register struct chain *base; ccache.dirty = TRUE; /* boolean clean out cache if TRUE */ base=tab[member]->ptrs_up; if ((pos=hunt(base,tab[class] .FALSE)) == NULL) { err=l; else { *pos = NULL; /* if (base->cont == NULL) *pos = NULL; else { *pos=fill_up(fe _base->cont)) ; } * */ base=tab [class] ->ptrs_down; if ( (pos=h___t (base, tab [member] .FALSE) ) == NULL) { /* if (err == 0) { printf (" ERR0R2 ") ; APPENDIX B - PAGE 36 } */ return(-2) ; } else if (err==l) printf C INCONS "); *pos - NULL; /* if (base->cont == NULL) *pos = NULL; else { } */ # ifdef TRACE printf("\n »deletelink "); display(member,bu ,15) ; printfC7.S-",buf); display(class,buf,15) ; print C7.s ",buf); # endif TRACE retura(O) ; } /* new.entityO /* Create an entry for a new entity in the table */ { if (++cur_ent > MAX.ENT-2) { retura(-l) ; } tab[cur_ent] = (struct body *) (callocd.sizeof (struct body))); tab [cur.ent] ->id s cur_ent ; tab [cur.ent] ->conf =0 ; tab [cur.ent] ->ptrs_up= (struct chain *) (callocd.sizeof (struct chain))); (tab [cur.ent] ->ptrs_up)->cont= (struct chain *)NULL; tab [cur.ent] ->ptrs_down= (struct chain *) (callocd.sizeof (struct chain))); (tab [cur.ent] ->ptrs_down)->cont= (struct chain *)NULL; # ifdef TRACE /* printfC ent:7.d ".cur.ent); */ # endif TRACE return (cur.ent) ; a P-ENT-TX B - PAGE 37 /* - displa (ent,string.maxstringsize) register int ent; register char *string; register int maxstringsize; /* Read the string associated with 'ent' and put it in 'string' */ if (tab[ent] == NULL) /* printfC DISPLAY ERROR "); */ return(-l) ; x strcpy( string, tab [ent] ->name) ; # ifdef TRACE /* printfC\n string 7.s",string) ; */ # endif TRACE retum(l) ; } entity(string) char string[RECSIZE] ; /* gets entity no. corresponding to the string */ int i; char strl[RECSIZE+l] ; # ifdef TRACE if (traceval) printf("\nenti y : string 7.s\n",string) ; # endif TRACE if(strlenCstring) > RECSIZE) re_ura(-5) ; for (i=l;i<=cur_ent;i++) if(tab[i] != NULL) APPENDIX B - PAGE 38 i (strcmp(string,tab[i]->name) == 0) break; /* printfC intab 7.d ",i); */ if (i<s»cur_ent) return(i); else return(-l) ; } /* */ _ setstringforentity(ent,string,type) int ent,type; char string[RECSIZE] ; /* Associate a string with an entity no. */ { # ifdef TRACE if (traceval) printfC sent:7.d 7.s\n" , ent, string) ; # endif TRACE if (strlen(string) > RECSIZE) return(-5) ; if (entity(string) > 0) return(-4) ; strcpy (tab [ent] -.name, string) ; return (0) ; /* */ new_member(class) int class; { return(new.entityθ) ; /* temporary */ } /* */ new.class (member) int member; APPENDIX B - PAGE 39 return(new.entityθ ) ; /* temporary */ >- /* */ set.reg *newset() { register set.reg *s = (int *)calloc(MAX_ENT,sizeof(int)) ; # ifdef TRACE if(traceval) fprintf(stderr,"\nnewset :\n"); # endif TRACE setcounter++; if (setcounter > 74) printf("-7.d-",setcounter) ; if (s == NULL) printfC not enough space for registers ' ■ ); return(s) ; /* disposeset(S) int *S; # ifdef TRACE if(traceval) fprintf(stderr,"\ndisposeset : setreg Xd\n",S) ; # endif TRACE if(S — NULL) return; /* don't free null listsi! */ cfree(S,bl_size,sizeof(int)) ; setcounter—; /* */ initialisese (bitlist,value) register set.reg *bitlist; register int value; /* set all elements of set register to value of 'value' */ APPENDIX B - PAGE 40 { register int nwords = bl_size;/*FIND*/ # ifdef TRACE if(traceval) fprintf(stderr,"\ninitialiseset : setreg 7.d value = Xd.n" , bitlist,value) ; # endif TRACE if(value) value = "0; while(nwords—) *bitlist++ = value; * !- /* puton(bitlist.bitposition) register set.reg *bitlist; register int bitposition; /* This function will make the bit in bitlist indicated by bitposition = 1. */ { # ifdef TRACE i (traceval) fprintf(stderr,"puton : setreg Xd ent = Xo\n" .bitlist,bitposition) ; # endif TRACE i (bitposition > MAX.ENT) { /* whoa!! */ # ifdef DIAGS printf("puton bitlist error bit = Xd (Xo)\n" .bitposition,bitposition) ; # endif DIAGS abort() ; } bitlist[bitposition >> INTSHIFT] 1= bitno(bitposition) ; } /* */ putoff(bitlist,bitposition) register set.reg *bitlist; register int bitposition; /* This function clears the bit in bitlist id'd by bitposition. */ APPENDIX B - PAGE 41 # ifdef TRACE if (traceval) fprintf(stderr,"putoff : ent = ; # endif TRACE if(bitposition > MAX.ENT) { /* whoa!! */ # ifdef DIAGS printf("putoff bitlist error bit = 7,d (7.o)\n",bitposition,bitposition) ; # endif DIAGS abor () ; } bitlist[bitposition >> INTSHIFT] fe= " bitno(bitposition) ; inset(S,e) set.reg *S; int e; r # ifdef TRACE if(traceval) fprintf(stderr,"\ninset : setreg Xd ent = 7.o\n",S,e); # endif TRACE return(brεinbitlist(S,e)) ; bitinbitlist(bitlist,bitposition) register set.reg *bitlist; register int bitposition; /* Returns TRUE if the bitpos'th bit is set */ x_ return( (bitlist [bitposition » INTSHIFT] fe bitno (bitposition) ) ! = 0) ; /* */ first (S) - Ill - APPENDIX B - PAGE 42 set.reg *S ; { # ifdef TRACE i (traceval) fprintf(stderr,"first : setreg 7,d\n",S); # endif TRACE return(nextbitinbitlist(S,OFFSTART)) ; /* */ succ(S.i) set.reg *S, i; { # ifdef TRACE if(traceval) fprint (stderr,"succ : setreg Xd i = Xo\n",S,i); # endif TRACE return(nextbitinbitlist(S,i)) ; } /* nextbitinbitlist(bitlist,currentbit) register set.reg *bitlist; register int currentbit; /* " * This fn will look for the next bit (after currentbit) which * is set & return the address in bitlist as a result if it * runs off the end it returns "OFFEND" . * It treats OFFSTART as a special case as this indicates that * the search should begin from the very first element of the * bitlist. */ r register int top; if(currentbit == OFFSTART) { currentbit = 0; } else { currentbit++; /* don't return input element as answer */}; while(currentbit fe (INT.BITS-D) { /* while there are elements in a split byte */ if(bitinbitlist(bitlist,currentbit)) { APPENDIX B - PAGE 43 if dnsplit) { return(currentbit) ; } currentbit++; " i- while(currentbit < cur_ent+l) { if(bitlist[currentbit » INTSHIFT]) { v /* now find 1st bit in word set */ top = currentbit+(INT_BITS-l) ; for(currentbit = currentbit; currentbit <= top; currentbit++) {/*FIND SMcI change < to <= 16-7-85*/ if(bitinbitlist(bitlist,currentbit)) { return(currentbit) ; y } } else { currentbit = currentbit+INT.BITS; } X return(OFFEND) ; /» */ closure(opcode,es.result,depth,direction) int opcode, es; set.reg *result; int depth, direction; /* This fn will perform a closure in the direction 'direction' to a depth 'depth' on entity 'es' and put the result in bitlist 'result' . mt i; int *tmp; if(es <= 0 [| es > MAX.ENT) return(-l) ; switch(opcode) { case FULLLOAD: real.closure(direction,result,es,depth) ; break; /* everything, tables fe all */ case LOAD: APPENDIX B - PAGE 44 case NOT: real.closure(direction,result.es,depth) ; /* ifζspecset != NULL) andsetreg(result,specset,result) ; */ if(opcode == NOT) notsetreg(result) ; break; case AND: case ANDNOT: tmp = newsetO ; i (tmp == NULL) retur (OUTOFMEMORY) ; real.closure(direction, mp,es,depth) ; if(opcode == ANDNOT) notsetreg(tmp) ; andsetreg(result, mp,result) ; disposeset(tmp) ; break; case OR: tmp = newsetO ; if(tmp == NULL) return(OUTOFMEMORY) ; real_closure(direction,tmp.es.depth) ; orsetrβg(result,tmp,result) ; disposeset(tmp) ; break; de ault: return(-2) ; } retura(O) ; notsetreg(bitlist) register set.reg *bitlist; /* negates all bits in bitlist. */ { register int el = bl.size; # ifdef TRACE i (traceval) fprintf(stderr,"notsetreg : setl 7,d\n" .bitlist) ; # endif TRACE while(el--) { *bitlist = " (*bitlist); bitlist++; _ } /* */ APPENDIX B - PAGE 45 orsetreg(bll ,bl2, result) register set.reg *bll, *bl2 , *result ; /* logical OR op on bll fe bl2. */ register int el = ( (cur.ent + INT.BITS ) / INT.BITS) ; # ifdef TRACE if (traceval) fprint ( st derr. 'Orsetr eg : setl Xd set2 Xd set3 = Xd\n" , bll , bl2, result) ; # endif TRACE while(el—) *result++ = *bl2++ I *bll++; /* andseτreg(bll,bl2,result) register set.reg *bll, *bl2, *result; /* Logical AND operation beettween bits of bll fe bl2. */ X register int el = bl.size; # ifdef TRACE if(traceval) fprint (stderr,"andsetreg : setl Xd set2 Xd set3 = Xd\n",bll,bl2,result) ; # endif TRACE while(el—) *result++ = *bl2++ fe *bll++; /« */ /* This section contains Steve Mclnnes' closure caching routines */ sys.initialiseset(bitlist,value) register set.reg *bitlist; register int value; /* set all elements of set register to value of 'value' */ register int nwords = ((MAX.ENT + (INT.BITS - D) / INT.BITS); if(value) value = " 0; while(nwords—) *bitlist++ = value; */ APPENDIX B - PAGE 46 set.reg *ccache_match(ent , direction, depth) int ent , direction , depth; /* * Fn will determine whether there is a previously computed * closure which will satisfy the current closure requirement. * It must satisfy 3 requirements: -. 1. It must be for the correct entity. * 2. It must be in the correct direction. * 3. It must be to the correct depth. */ { register cchashel **ccptr, **cstart; register cchashel *ptr; register int count = 0; if (ccache.dirty) { /* clean it out */ ptr = ccache; for (; ptr < ~ ~ &(ccache[SMALLCC]) ;) { /* D.O 9/7/88 = */ ptr->old = TRUE; ptr->used = FALSE; ptr->cent = 0; ptr->cdir = 0; ptr->cdepth = 0; ptr->nextcel = NULL; ptr++; ccache.dirty = FALSE; } else { ccptr - cstart = &(intable [cchash( ent , direction, depth)] ) ; for ( ; ; ) { if ((*ccptr) == NULL) break; /* no match */ if ((ent ■■ (*ccptr)->cent) fefe (direction == (*ccptr)->cdir) fefe (depth — (*ccptr)->cdepth)) { if ((*ccptr)->set == NULL) abortO; /* got a hit */ (*ccptr)->old = FALSE; return((*ccptr)->set) ; } ccptr - &((*ccptr)->nextcel) ; if (++count >= SMALLCC) break; /* right round */ } } /* end if cache dirty */ APPENDIX B - PAGE 47 return((set.reg *) NULL); cchash(ent,direc ion,depth) int ent, direction, depth; return(((((ent + depth) * (direction + 10)) « 5) + direction) X CCACHESIZE); _ /* */ saveset(set,ent,direction,depth) set.reg *set; int ent, direction; int depth; /* * closure result lies in set. * now find a place in the cache to store it. * Unless it is already in the cache already. */ { register set.reg *nset; register cchashel **ccptr, **start, **oldptr, *el; nset = ccache_match(ent,direction,depth) ; if (nset != NULL) { /* we have already saved it */ retur ; /* find a place to put it */ start = ccptr = &(intable[cchash(ent,direction,depth)]) ; if (((*ccptr) != NULL) kk (*ccptr)->used == FALSE) { /* found a spot */ if ((*ccptr)->set == NULL) (*ccptr)->set = (int *)malloc(bl_size * sizeof(int)) ; initialiseset((*ccptr)->set,0) ; orsetreg(set,(*ccptr)->se ,(*ccptr)->set) ; (*ccptr)->cent = ent; (*ccptr)->cdir = direction; (*ccptr)->cdepth = depth; (*ccptr)->used = TRUE; (*ccptr)->old = FALSE; (*ccptr)->nextcel = NULL; } else { APPENDIX B - PAGE 48 /* no match, so put a new element on at start of list */ el = get.lruO ; if (el->used == TRUE) { oldptr = 4(intable [cchash(el->cent , el->cdir , el->cdepth)] ) ; while ( ( (*oldptr) ! = NULL) fefe ((*oldptr) ! = el) ) { oldptr * &( (*oldptr)->nextcel) ; } /* we now have ptr to pointer to location of block for reuse */ /* so use it and change ptrs */ if ((*oldptr) « NULL) { fprint (stderr."POINTER PROBLEM.n") ; fflush(stderr) ; } (*oldptr) = (*oldptr)->nextcel; } el->nextcel = (*start); (*start) = el; if (el->set == NULL) el->set = (int *)malloc(bl_size * sizeof (int) ) ; initialiseset(el->set.O) ; orsetreg(set,el->set,el->set) ; el->cent = ent; el->cdir = direction; el->cdepth = depth; el->old = FALSE; el->used = TRUE; } return; /* */ cchashel *get_lru() /* find the least recently used ccache element */ { for (;;) { if (lru->old == TRUE) { return(lru) ; } lru->old = TRUE; lru++; if (lru > &(ccachθ [SMALLCC] ) ) lru = ccache ; APPENDIX B - PAGE 49 /* */ real_cIosure(direction,bll,ent, epth) int direction; , set.reg *bll; int ent, depth; /* This function will perform a closure on either the up mstable or the down mstable on bs (fed in as first parameter) . It will use do the closure to a depth == parameter 4 and using entity to initiate the req'd set we are working with. set.reg *b!2, *bl3; int complete, currentdepth, nbitinbitlist; set.reg *nset; /* check cache */ /* nset = ccache_match(ent,direction,depth) ; if (nset != NULL) { initialiseset(bll.O) ; orsetreg(nset,bll,bll) ; return; } */ /* we have now returned a bl, or we need to do a closure */ bl2 = newsetO ; if(bl2 == NULL) retur (OUTOFMEMORY) ; bl3 = newsetO ; if(bl3 == NULL) { disposeset(bl2) ; return(OUTOFMEMORY) ; sys_initialiseset(bl2,0) ; sys_initialiseset(bll,0) ; puton(bl2,ent) ; puton(bll,ent) ; complete = FALSE; currentdepth = 1; while(currentdepth <= depth fefe !complete) { APPENDIX B - PAGE 50 nbitinbitlist = OFFSTART; complete = TRUE ; sys_initialiseset(bl3 , 0) ; nbitinbitlist = nextbitinbitlist (bl2 , bitinbitlist) ; while (nbitinbitlist ! = OFFEND) { complete = FALSE; closeKdirection.bll ,bl3 , nbitinbitlist) ; nbitinbitlist = nextbitinbitlist (bl2 , nbitinbitlist) ; } orsetreg(bll ,bl3 ,bll) ; sys_initialiseset (bl2 ,0) ; orsetreg(bl2 ,bl3 ,bl2) ; currentdepth++ ; } disposeset (bl2) ; disposeset (bl3) ; saveset (bll , ent , direction, depth) ; /* put it in the cache */ /* closel(direction,bll,bl2,ent) int direction; set.reg *bll, *bl2; /* bl2 is updated as a result of this function. It will also contain the results of any previous closures done at the same level for a different entity. */ register int ent; /* Entity is the number of the entity about which we wish to know either: a. The sets to which it is directly linked as a member i.e the things it is explicitly linked to in the up table. b. The sets to which is is directly linked as superset of. i.e. the things it is explicitly linked to in the down table, */ /* This function will perform a one level closure for the given entity number . */ APPENDIX B - PAGE 51 register int i; register bp pos; register struct chain *zap; if (tab[ent] == NULL) { printfC FROZEN NODE VISITED cll=7.d\n ",ent); apO ; return; Λ if ( direc ion==UPWARD) zap=tab[ent]->ptrs_up; else zap=tab [ent] ->ptrs .down; while (zap != NULL) { pos=(bp)zap; for (i=0;i<CHAINS;i++) { if (*pos != NULL) { /* if ((*pos)->id <= cur.ent) */ if (!bitinbitlist(bll,(*pos)->id)) puton(bl2, (*pos)->id ); " . pos++; t- zap= zap->cont; } /* closer .ent ,bl , dir) int ent, dir; set.reg *bl; register int i; register struct chain *zap; register bp pos; initialiseset(bl.O) ; if (dir == UPWARD) zap=tab[ent]->ptrs_up; else zap- * =tab[ent]->ptrs_down; while (zap != NULL) { pos=(bp)zap; for (i=0;i<CHAINS;i++) { if (*pos != NULL) puton(bl, (*pos)->id ); pos++ ; APPENDIX B - PAGE 52 zap s zap->cont ; } /* */ initdb(a,b,c,d,e,f.g.h.j) int a.b.c.d.e.f.g.h.j; {} opendb(name) char name[] ; {} closedbO {} entdelete(buf) char bufQ ; {} /* */ strength(node) int node; { if (tab [node] ! = NULL) return(tab [node] ->conf ) ; else return (2000) } /* */ set.strength(node,val) int node,val; { tab[node]->conf=val; } /* */ APPENDIX B - PAGE 53 inc.strength(node) int node; tab [node] ->conf ++ ; /* */ disτabO printf ("\n DISPLAY:"); for (i=cur_ent-5;i<=cur_ent fefe i>0;i++) printfC Xd 7,s\n",tab[i]->id,tab[i]->name) ; /* */ sweep (ent) int ent; register int i,err=0; register bp pos; register struct chain *zap,*temp; if (tab [ent] == NULL) { printf ( " already rozen " ) ; return(-l) ; zap=tab [ent] ->ptrs_up ; while (zap != NULL fefe err==0) { pos=(bp)zap; for (i=0;i<CHAINS;i++) { if (*pos != NULL) err=l; pos++; } if (ierr) { temp=zap->cont; free(zap) ; zap=temp; x APPENDIX B - PAGE 54 else tab [ent] ->ptrs_up=zap ; } zap=tab [ent] ->ptrs_down; while (zap != NULL fefe err==0) { pos=(bp)zap; for (i=0;i<CHAINS;i++) { if (*pos != NULL) err»l; if (!err) { temp=zap->con ; free(zap) ; zap=temp ; } else tab[ent]->ptrs_down=zap; } if (err) return(-l); else return (0) ; } yap() { printfC "); } APPENDIX B - PAGE 55 snet.c /* * Semantic network synthesizer, « to be used with the natural language * database interface system written by Lin Dekang * * Deon Qosthuizen October 1987 * #include "pif.h" #include "browse. " #include <stdio.h> * define OFFEND -6 extern struct s.table schema_table [TABLSIZE] ; extern struct table symbol_table[SYMSIZE] ; extern int symbol_no ,cur_end; int rel_no= EMPTY, /* table record no . of current relation */ next_lihk=0; char cur.relatnCFSIZE] ; Field. el [TABLSIZE] [SAMPLESIZE] ; /* entity numbers of symbols belonging to a field */ /* */ /* insert.name(symbol,field,table) char symbol[FSIZE] ,field[FSIZE] .table[FSIZE] ; insert symbolic name (obtained from user) into schema table _nt i; for (i=l ; i<=symbol_no fefe ( strcm (field,schema_table [i] .fld_namβ) I - 0) ; i++) ; str cpy(schema_t able [i] . sym_na__e, symbol) ; } */ APPENDIX B - PAGE 56 /* tab_link(origin,destin,relation) int origin,destin,relation; /* establish a link between two nodes (entities) */ v int i; for (i=0;i<MAXLINKS fefe schema.table [origin] .dest[i] != EMPTY;i++); schema.table [origin] .dest[i] = destin; schema.table [origin] . link.type [i] = relation; schema.table [origin] .role [i] = destin; for (i=0 ; schema.table [destin] .org[i] != EMPTY ;i++) ; schema.table [destin] .org[i] = origin; /* */ tab.deletelink(origin,destin,relation) int origin,destin,relation; /* delete a link between two nodes */ mt I; for (i=0;i<MAXLINKS fefe ( (schema.table [origin] .dest[i] != destin) II (schema.table [origin] .link.type [i] != relation) );i++); if (i ■- MAXLINKS) { printf("\nLink does not exist "); return; } for ( ; KMAXLINKS fefe schema.table [origin] .dest[i+l] !« EMPTY ;i++) { schema.table [erigin] .dest[i] = schema.table [origin] .dest[i+l] ; schema.table [origin] .link.type [i] = schema.table [origin] .link.type [i+l] ; APPENDIX B - PAGE 57 schema_table [origin] .dest [i] = EMPTY; schema.table [origin] .link.type [i] = EMPTY; /* i is now pointing to the last (redundant) position in the array */ for (i=0; i<MAXLINKS fefe schema.table [destin] . org[i] ! = origin; i++) ; for ( ; i<MAXLINKS fefe schema.table [destin] .org[i+l] ! = EMPTY; i++) schema.table [destin] .org[i] = schema.table [destin] . org[i+l] ; schema.table [destin] . org[i] = EMPTY; > /* */ unite(node.duplic) int node,duplic; /* unite two nodes, i.e. delete the duplicate node and transfer its relationships to the first one */ r int i.j.k; schema.table [duplic] .fid. type = DELETED ; /* if lexical names has already been captured, transfer it to new node */ if (schema.table [node] . sym.name [0] == ' \0 ' ) strcpy( schema.table [node] .sym.name , schema.table [duplic] . sym.name) ; for (i=0 ; i<MAXLINKS fefe (k=schema_table [duplic] .dest [i] ) != EMPTY ; i++) { /* change origin pointer of nodes involved */ for (j=0 ; <MAXLINKS fefe schema.table [k] .org[j] ! = duplic; j++) ; schema.table [k] . org [j] =node ; /* transfer the links to 'node' */ for (j=0;j<MAXLINKS fefe schema_table [node] .des [j] != EMPTY; j++) ; s chema_t able [node] . dest [ j ] = ; schema.table [node] .link.type [j] = schema.table [duplic] .link.type [i] ; schema.table [node] .role [j] = schema.table [duplic] .role [i] ; APPENDIX B - PAGE 58 /* change destination pointer of nodes involved */ for (i=0;i<MAXLINKS fefe (k=schema.table [duplic] .org[i]) != EMPTY;i++) { for (j=0;j<MAXLINKS fefe schema.table [k] .dest [j] ~~ duplic; j++); schema.table [k] . dest [j] =node ; /* printfC trans: j.k Xd Xd Xs's (Xs) link to Xs (Xs) is changed to Xs (Xs)",j,k, schema.table [k] .fld.name, schema.table [k] .sym.name, schema.table [duplic] .fld.name, schema.table [duplic] .sym.name, schema.table [node] .fld.name, schema.table [node] . sym.name) ; */ /* transfer the links to 'node' */ for (j=0;j -MAXLINKS fefe schema.table [node] .orgCj] != EMPTY; j++); schema.table [node] .org[j] = k; } } /* */ inser .record(relatn.name) char relatn.name[FSIZE] ; /* read schema in ormation from DBMS (or whatever) and store in table format */ -Γ /* create an entry in the schema table for each entity */ if (strcmp(relatn.name,cur.relatn) != 0) { symbol_no++; rel_no=symbol_no; next_link=0; strcpy( cur .relatn, relatn.name) ; strcp (schema.table [rel.no] .fld.name, relatn.name) ; schema.table [rel.no] . status*RELATION ; APPENDIX B - PAGE 59 /* HAS.ATTR link between relation name and field name */ schema.table [rel.no] . dest [nexτ.l nk] =symbol_no+l ; schema.table [rel.no] .role [next_link] =symbol_no+l ; schema_tablβ [rel.no] . link.type [next.link] =HAS_ATTR ; next_link++; /* printf C .nRecord Xd: Xs Xs Xd Xd Xd " .symbol.no , 5chema_table [symbol.no] .fld.name , schema_table Csymbol.no] .fld.type , schema.table [symbol.no] . dest [0] , schema.table [symbol.no] . org[0] , schema.table [symbol.no] . link.type [0] ) ; */ symbol_no++; schema.table [symbol.no] . status=ENTITY ; schema.table [symbol.no] . org[0] =rel_no ; else { symboI_no++; schema.table [symbol.no] . status=ENTITY ; schema.table [symbol.no] . org [0] =rel_no ; schema.table [rel.no] . dest [next.link] =symbol_no ; schema.table [rel.no] .role [next.link] =symbol_no ; /* HAS.ATTR link between relation name and field name */ schema.table [rel.no] . link.type [next.link] =HAS_ATT ; next_link++; /* */ insert.symboKsymbol, field, relation) char symbol [FSIZE] ,field[FSIZE] .relation [FSIZE] ; /* inspect actual database records and analyse contents */ mt ι,j ; for (i=l ; i<=cur_end fefe strcmp ( symbol, symbol_table [i] . sym.name) != 0 ; i++) ; if (i > cur. end) { APPENDIX B - PAGE 60 cur_end++; strcpy(symbol_table[cur_end] .sym.name, symbol) ; strcpy(symbol_t able [cur. end] .dupl[0] .fld.name, field) ; strcpy(symbol_table[cur_end] .dupl[0] .rel.name, relation) ; /* printf ("\n symbol: Xs ".symbol); */ } else { for (j=0;symbol.table[i] .dupl[j] .fld_name[0] != '\0';j++); if (strcmp (symbol_table[i] .dupl[j-l] .fld_name, field) != 0 11 strcm (symbol_table[i] .dupl[j-l] .rel.name, relation) != 0) { /* printfC actually added "); */ strcpy(symbol_table[i] .dupl[j] .fld.name, field) ; strcpy(symbol_table[i] .dupl[j] .rel.name, relation) ; } } /* */ insert.tuple(table.no,rec.no,rec.ptr.numfIds) int table.no,rec.no,numfIds; tabstr rec_ptr[MAXFIELD] ; { int cc, obj.no, ent .no, i, j ; char obj .name [FSIZE] , ent .name [STRMAX] ; char buff er [FSIZE] ; sprintf (obj .name , "X . Obj Xd" .table.no , rec.no) ; setstringf orentity(obj_no=new_entity() , obj .name, ENTTYPE) ; for (i=l;i<=numflds;i++) { sprintf(ent.name,"Xd.Xd.X.14s" ,table.no,i,rec_ptr[i-l]) ; /* printf("entname: Xs \n" ,ent.name) ; */ if ((ent.nonentity(ent.name)) < 0) { cc=setstringforentity(ent_no=new_entity() ,ent.name,ENTTYPE) ; if (cc<0) { printf("cc=Xd, ent_no=7.d record=Xd\n" .cc.ent.no,rec.no) ; display(ent.no,buf er,FSIZE) ; printf("entity to be stored: Xs ".buffer); } for (j=0;j<SAMPLESIZE fefe Field_cl[table_no+i] [j] !=0;j++); Field.cl [table_no+i] [j] =ent_no ; } APPENDIX B - PAGE 61 /* printf ("\n= Xd Xd ".obj_no.ent.no); */ trans(obj_no,ent_no,numflds,0) ; /* */ in erpret.dependencies(rec.no) int rec_no; { int i , , last .member , f ound=FALSE ; char buff er [FSIZE] ; set.reg *UCli; /* if there is only one field, then it is a key field */ if (schema.table [rec.no+2] .status == RELATION II schema_table [rec_no+2] .status == EMPTY) { schema.table [rec_no+l] .status = KEY; return; } for (i=rec_no+l; (i <= symbol.no) fefe ifound fefe schema_table[i] .status ! = RELATION; i++) if (schema.table [i] . f Id.type ! = ' i ' ) if (key (.rec.no, i)) f ound=TRUΞ ; last=i-l; if (ifound) { /* first delete object nodes belonging to only one class */ UCli=newset(); for (1=1;display(i,buffer,FSIZE) > 0;i++) { * -<= . (buffer[l] == '.' fefe buffer [2] == '0') (buffer [2] == '.' fefe buffer [3] == '0')) { closure(LOAD,i,UCli,l, UPWARD) ; putof f (UCli.i) ; putof f (UCli , (member=f irst (UCli) ) ) ; if ( (first (UCli) ) == OFFEND) deletelink(i,member,0,0) ; } APPENDIX B - PAGE 62 /* else printf ("buffer: 7.s ".buffer); */ \ disposeset (UCli) ; for (i=rec_no+l;(i < last) fefe ifound; i++) if (schema.table [i] .fld.type != 'i') /* not of type INTEGER */ for (j=i+l;(j <= last) fefe ! ound;j++) if (schema.table [j] .fld.type != 'i') f Qund=multiple_key (rec.no, i, j) ; if (ifound) { printf ("\nWhich are the the key fields of the table Xs?\n", schema.table [rec.no] .fld.name) ; printf ("select numbβr(s) separated by spaces and terminated by 0) "); for (i=rec_no+l; (i <= symbol.no) fefe schema.table [i] .status != RELATION;i++) print C\n(Xd) Xs" ,i-rec_no, schema.table [i] .fld.name) ; j=0; scanf("7.d",fej); while (j > 0 fefe j <= i-rec_no) { schema.table [rec_no+j] . statuss-KEY; j=0; scanf("7.d",fej); } gets (buffer) ; } 1- * */ key(table,rec.no) int table.rec.no; { int i, ; char answer [3] ; set.reg *DC=newset() ; if (Field.cl [rec.no] [0] — 0) return(FALSE) ; DC=newset() ; for (i=l;(j=Field_cl [rec.no] [i] ) != 0;i++) { closure (LOAD. j , DC, ALL, DOWNWARD) ; putof f (DC, ) ; putof (DC, first (DC)) ; APPENDIX B - PAGE 63 if (first (DC) != OFFEND) { disposeset (DC) ; return ( FALSE) ; } } disposeset (DC) ; printf C.nThe field Xs seems to be a key field for the table Xs\ \n\nls this correct? y_", schema.table [rec.no] .fld_name, schema.table [table] .fld.name) ; gets (answer) ; if (answer [0] != '__' fefe answer [0] != 'N') { schema_table [rec.no] .status = KEY; return (TRUE) ; else return (FALSE) ; / ♦ */ multiple.key (rec.no ,f ldl ,fld2) int rec.no, f ldl, fld2; /* Inspect table for multiple keys */ mt i , j , k ,mat e=0 , meet , ent ; int response=TRUE; char answer [3] ; char buff er [FSIZE] ; set_reg *DClent=newset() ,*UClj=newset() ,*DCmeet=newset() ; /* if there are only two fields - assume that they are joint keys */ for (k=0;k<4 ;k++) printf ("Xd Xd " ,Field_d _________ [k] . Field. cl[fld2] [k]) ; if (fldl != rec_no+l I I Field_cl[fld2+1] [0] != 0) for d=l;((ent=Field_cl[fldl] [i]) != 0) fefe response ;i++) { closure(LOAD , ent .DClent , 1.DOWNWARD) ; putof f (DClen , ent) ; display(ent, buf er, FSIZE) ; printf ( " \nent : Xs " , buff er ) ; APPENDIX B - PAGE 64 for (j=first(DClent);j != OFFEND; j=succ (DClent, j)) { closure (LOAD, j ,UClj ,1, UPWARD) ; for (k=0;k<SAMPLESIZE fefe (mate=Field_cl[fld2] [k ) != 0;k++) if (inset (UClj .mate)) break; if (mate «= 0) { response=FALSE; break; display(mate,buffer, SIZE) ; print ("mate1: Xs ".buffer); closure(LOAD,j .DCmeet.l,DOWNWARD) ; putoff(DCmeet,j) ; if ((meet=first(DCmeet)) != OFFEND) { display(meet,buf er,FSIZE) ; printf("meet: Xs ".buffer); response=FALSE; break; } display(j .buffer,FSIZE) ; printf("multiple1: Xs ".buffer); } } disposeset(DClent) ; disposeset(UClj) ; disposeset(DCmeet) ; if (response) { printf ("\nThe fields Xs and Xs \n" , schema.table [f ldl] .fld.name, schema.table [fld2] .fld.name) ; print ("seem to form a joint key for the table\ 7,s Is this correct? yφ" , schema.table [rec.no] .fld.name) ; gets (answer) ; if (answer[0] != 'n' fefe answer[0] != 'N') { schema.table [f ldl] .status = KEY; schema.table [fld2] .status = KEY; return (TRUE) ; ι else return(FALSE) ; return(response) ; } APPENDIX B - PAGE 65 setup.net () /* synthesize semantic net from database information */ /* print semantic net information in table format */ FILE *output, *fopenO ; int i.j; static char arc[3] [9]={"\0","is_a "."has.attr"}; if ((output=fopenCsemnet.txt","w")) == NULL) { printf("file open error"); return; } printf("\n\n Tabular representation of semantic network\n"); printfC\n Source node Destination node"); printfC\nField name Lexical name Type Link type Field name. Lexical name Type"); n .. ); for (i=l;i<=symbol_no;i++) { if (schema.table [i] .fld.type != DELETED) { for (j=0;j<MAXLINKS fefe schema_table[i] .dest[j] != EMPTY; ++) { if (schema_table [i] . sym.name [0] == ' \0 ' ) strcpy (schema.table [i] .sym_name, schema.table [i] .fld.name) ; if (schema.table [schema_table[i] .des [j]] .sym.name [0] == '\0') strcpy (schema.table [schema_t able [i] .dest[j]] . sym_name , schema.table [schema.table [i] .dest[j]] .fld_name); f printf (output, "Xs X-13s Xc Xs Xs 7.-13s 7.c\n" , schema.table [i] .fld.name, schema.table [i] . sym_name , schema.table [i] . status , arc [schema.table [i] .link.type [j]] , schema.table [schema.table [i] .role[j]] .fld.name, schema.table [schema.table [i] .dest[j]] .sym.name, schema.table [schema.table [i] .role[j]] .status) ; } APPENDIX B - PAGE 66 x f close (output) ; systemCmore -17 semnet.txt"); APPENDIX B - PAGE 67 trans,c /* * Knowledge Transformation / Learning Routine X * forming part of the _•_ GRAND - System __ * (Using some PIF routines) * Deon Qosthuizen February 1987 - September 1988 « / # include "net.h" # include <stdio.h> # undef TRACE /* int restrict; */ int list[LSIZE] [2] ; /* store meets of Bi and A to avoid repeated computation */ char buffer[RECSIZE] ; /* general purpose buffer for string printing */ int count=0; /* new entity name counter * / extern int lowlimit; /* temporary ? */ extern struct body *tab[MAX.ENT] ; /* table with link info. */ set.reg *global, /* list of all 3i to be inspected or already inspected */ *done, /* Bi already processed */ *origDCA; /* initial DCA before Cs were added in local.context */ set.reg *avail_ent, /* list of deleted entities (the numbers are now 'available' again */ *shield; /* set of all members of CLASSES, as opposed to features */ /* real.trans( ,A.non.attr) int X, ,non.attr; APPENDIX B - PAGE 68 /* Member X has to be inserted into class A */ { int k=0 ; set.reg *DCA=newset() ; s et _r eg *UC lX=news et ( ) , *UCX=news et () ; set.reg *que= _ιewset( ) , * sjet.reg *store=newset() ; /* registers for storing upward and downward closures */ for (k=0 ;k<LSIZE ;k++) { list [k] [0] =0 ; list [k] [l] =0 ; } k=0 ; /* restrict=TRUE; */ # ifdef TRACE display(X,buf er,RECSIZE) ; printf("\n\n======= trans X-A Xs-" .buffer) ; display(A,buffer,RECSIZE) ; printf("Xs",buffer); # endif TRACE /* compute downward closure of A (DCA) and determine if X is in it */ closure(LOAD,A,DCA, LL.DOWNWARD) ; putoff(DCA,A) ; if (origDCA == NULL) origDCA=newset() ; else initialisese (origDCA,0) ; orsetreg(DCA,origDCA,origDCA) ; /* if already present, RETURN */ if (inset(DCA.X) == TRUE) { display(X,buffer,RECSIZE) ; printf ("\n Xs already a member of ".buffer); displayU,buffer,RECSIZE) ; printf("Xs - no action",buffer) ; } else { /* otherwise, compute the upward closure (UCIX) of X to 1 level to determine all sets to which it belongs already */ if (global == NULL) global=newset() ; else initialiseset(global,0) ; if (done »■ NULL) done=newset() ; APPENDIX B - PAGE 69 else initialiseset (done, 0) ; closure(LOAD,X,UClX, l , UPWARD) ; putoff (UC1X.X) ; cIosure(LOAD ,X,UCX,ALL ,UPWARD) ; putoff (UCX.X) ; if (conjunct(X, , DCA. UCX. UCIX, store , que , TRUE) ) { link(X.A) ; else if (first(que) != OFFEND) try(A,DCA,UCX,que) ; if (non.attr) orsetreg(DCA,shield,shield) ; disposese (que) ; disposeset(store) ; disposese (DCA) ; disposeset(UClX) ; disposeset(UCX) ; /* */ conjunct(X, .DCA,UCX,UC1,store,que.XmemBi) int X, ,XmemBi; set.reg *que; set.reg *DCA,*UCX; set.reg *UCl,*sτore; /* Inspect parents of Bi which do not meet with A */ int Bi,linkX=TRUE; set.reg *not_used=newset() ,*DClBi; /* for each of the sets Bi in store to which it belongs already ... */ # ifdef TRACE printf("\n"); for (Bi=first(UCl);Bi != 0FFEND;Bi = succ(UCl,Bi)) { display(Bi,buffar.RECSIZE) ; printf("Bis=7.s ".buffer) ; # endif TRACE for (Bi=first(UCl);Bi != 0FFEND;Bi = succ(UCl.Bi)) { APPENDIX B - PAGE 70 if ( iXmemBi) { /* isn't there an easier way ? */ DClBi=newset () ; closure(LOAD,Bi.DClBi,ALL,DOWNWARD) ; putof (DClBi.Bi) ; andsetreg(UCl,DClBi,DClBi) ; if (first(DCIBi) != OFFEND) { disposeset(DCIBi) ; puton(not_used,Bi); continue; /* Bi's related */ λ disposeset(DCIBi) ; } /* if (inset(que,Bi)) { putoff(que,Bi) ; > */ if (Bi == A) { linkX=FALSE; continue; } if (!next_parent(X,A,Bi,DCA,UCX,que,XmemBi)) { linkX=FALSE; closure(OR,Bi,store,ALL,UPWARD) ; } else { closure(0R,Bi,not.used,1,UPWARD) ; putoff(not.used,Bi) ; puton(store,Bi) ; notsetreg(store) ; andsetreg(not.used,store,not.used) ; notsetreg(store) ; if (first(not.used) != OFFEND) { if (!conjunct(X,A,DCA,UCX,not.used,store,que,FALSE)) linkX=FALSE; } disposeset(not.used) ; return(linkX) ; APPENDIX B - PAGE 71 /* tr (A,DCA,UCX, ue) int A; set.reg *DCA,*UCX; set_reg *que; /* Inspect Bi for meeting with A - level by level */ int i. ,k,Bi, memBi,linkA; set_reg *UCl=newset() ; set_reg *used=newset() ; set.reg *not_used=newset() ; set.reg *new_que=newset() ; set_reg *DCi=newset() ; orsetreg(que,new_que,new_que) ; for (i=first(que) ;i != OFFEND;i=succ(que,i)) { /* if (inset(avail.ent,i)) continue; */ if (tab[i] == NULL) continue; closure(LOAD,i,DCi,ALL,DOWNWARD) ; putoff(DCi.i) ; andsetreg(que,DCi,DCi) ; /* code added 6/5/88 */ if (first(DCi) != OFFEND) { putoff(que,i) ; continue; /* i's related */ Xm_mBi=TRUE; linkA=TRUE; closure(LOAD,i.UCl.l,UPWARD) ; putoff(UCl.i) ; # ifdef TRACE displayd,buffer,RECSIZE) ; printf("\nque=Xs,".buffer); # endif TRACE for (Bi=first(UCl);Bi != 0FFΞND;Bi = succ(UCl.Bi)) { /* if (inset(DCA,Bi)) { would make IntABi absurd linkA=FALSE; continue; } */ APPENDIX B - PAGE 72 if ( inset (que, Bi)) { /* printf ("i's related"); continue; */ putoff (que, Bi) ; } /* else if (inset(DCA,Bi)) { would make IntABi absurd linkA=FALSE; continue ; >*/ # ifdef TRACE printf ( " que2=7.s , " ,buf f er) ; # endif TRACE if (Bi «= A) { /* printf ("B=A"); */ linkA=FALSE; continue ; if (inext.parentd, A, Bi, DCA, UCl.new.que, memBi)) { closure(OR,Bi, used, ALL, UPWARD) ; linkA=FALSE; } else { closure(OR,Bi,not.used,1,UPWARD) ; putoff(not.used,Bi) ; x } notsetreg(used) ; andsetreg(not.used,used,not.used) ; notsetreg(used) ; if (first(not.used) != OFFEND) { i (iconjunctd,A,DCA,UC1,not.used,used,new.que,FALSE)) linkA=FALSE; initialiseset(not_used,0) ; } if (linkA) { link(i.A) ; puton(origDCA.i) ; puto (new.que,i) ; } else puton(done.i) ; initialiseset(used,0) ; } APPENDIX B - PAGE 73 disposeset(DCi) ; notsetreg(que) ; andsetre (que,new.que,new.que) ; disposese (que) ; if (first(new.que) != OFFEND) try(A,DCA, CX,new.que) ; disposeset(new.que); disposeset(not.used) ; disposeset(used) ; disposeset(UC1) ; } /* */ next_parent(X,A,Bi,DCA,UCX, ue, memBi) int X,A,Bi, memBi; set.reg *DCA,*UCX; set.reg *que; /* test if A and the next parent of X has a gib */ int Y,keep,iter,linkX=TRUE; set.reg *Clsr,*IntABi,*DCX=NULL; # ifdef TRACE dispIay(X,buffer,RECSIZE) ; printf("\n next.parent,X=7.s,",buffer) ; display(Bi,buffer,RECSIZE) ; printf("Bi=7.s".buffer); # endif TRACE if ((Y » gety(Bi)) == OFFEND I! Y==X) { /* compute the downward closure of Bi */ Clsr=newset() ; IntABi=newset() ; closure(LOAD,Bi,Clsr,ALL.DOWNWARD) ; /* compute the intersection of DCA and Clsr, i.e. the APPENDIX B - PAGE 74 intersection of the downward closures of A and Bi */ putoff (Clsr, Bi) ; andsetreg(DCA , Clsr, IntABi) ; if ( (Y = firs (IntABi) ) ! = OFFEND) { root (&Y , Int ABi , DOWNWARD) ; if (X ! = Y) puty(Bi .Y) ; else { andsetreg(IntABi,origDCA,IntABi) ; if ((Y = first(IntABi)) != OFFEND) { root(&Y,IntABi,DOWNWARD) ; } } > disposeset(Clsr) ; disposeset(IntABi) ; if (Y != OFFEND) { if (X == Y || inset(UCX.Y)) return(1ink ) ; /* if A and Bi have a member Y in common */ /* determine the maximal common member of A and Bi */ /* ( as a result of the application of this algorithm, */ /* there WILL be only one maximal common member (if any) */ DCX=newset() ; if (iYmemX(X.Y.DCX)) { iocal_context(X,Y,A,Bi,DCA,DCX,que, memBi) ; linkX=FALSE; } disposeset(DCX) ; T- return(linkX) ; } /* */ local_contβxt(X,Y,A,Bi,DCA,DCX,que,XmemBi) int X,Y,A,Bi, memBi; set.reg *DCA,*DCX; set.reg *que; APPENDIX B - PAGE 75 /* Having found a meet, perform the necessary transformation */ int h,C,D,linkX=TRUE,prevY; set.reg *Clsr,*UCl; # ifdef TRACE display (X, bu fer, RECSIZE); printfC commonX : Xs ".buffer); displa (A, buffer, RECSIZE); print ("A: Xs ".buffer); display(Bi, buffer, RECSIZE); printfC Bi:'/.s ".buffer); display (Y, buffer, RECSIZE) ; printfC Y:7.s ".buffer;; # endif TRACE C=co_nmon(X,Y, , Bi, que, XmemBi) ; if (C != Y) puton(DCA,C); if (C != Bi) { if (C == Y) { if (first (DCX) != OFFEND) { Clsr=newset() ; closure(LOAD,Y,Clsr,1,DOWNWARD) ; andsetreg(Clsr,DCX,Clsr) ; if ((prevY=first(Clsr)) != X) /* if ((prevY=first(Clsr)) == X) printfC XamoY"); else */ /* strictly speaking there should be only one of these (prevY) , but under certain very peculiar circumstances, there may be two, caused by the dynamics of the algorithm and condensing */ for (prevY=first(Clsr) ;prevY != OFFEND;prevY=succ(Clsr,prevY)) { deletelink(prevY,Y,0.1) ; display (prevY, buffer, RECSIZE) ; if (buffer [0] ~ '*') { UCl=newset() ; closure(LOAD,prevY,UCl,l,UPWARD) ; putof (UCl.prevY) ; putof (UCl,X); if ((h=first(UCD) == OFFEND) transfer.members(prevY,X) ; APPENDIX B - PAGE 76 disposeset (UC1 ) ; * . X disposeset(Clsr) ; } if (strength(X) > strength(Y)) set_strength(Y,strength(X)) ; link(X.C); puton(origDCA.X) ; j if (XmemBi) { deletelink(X,Bi,0,2) ; mendlist(X.O) ; /* unnecessary ?? */ } x /* YmemX(X.Y.Clsr) int X,Y; set.reg *Clsr; /* In the case where X is not an object: determine if Y is a member or implicit member of X */ display(X,buffer,RECSIZE) ; if (buffer[0] == '*') { closure(LOAD, ,Clsr,ALL,DOWNWARD); if (inset(Clsr,Y)) { return(TRUE) ; } } return(FALSE) ; /* */ co_nmon(X,Y,A,Bi,que,XmemBi) APPENDIX B - PAGE 77 int X,Y, A, Bi, XmemBi; set_reg *que; /* Determine if quad is Y-simple or not and treat accordingly */ int C=0,p, /* entity */ YmemA,YmemBi,cpτY=FALSΞ; /* Boolean variables */ feet.reg *UC1Y = newsetO; /* find the sets of which Y is a direct member */ closure.LOAD.Y.UClY.l,UPWARD) ; putoff(UC1Y.Y) ; YmemA = inset(UClY.A) II (inset(que.Y) fefe !inset(done,Y)) ; YmemBi = inset(UC1Y,Bi) ; putoff(UC1Y, ) ; putoff(UClY.Bi) ; display(Y,buffer, ECSIZE) ; if (buffer[0] == '*') cptY=TRUE; /* if Y is a member of A itself, and of Bi itself, i.e. not a member of one of their members */ if (YmemA fefe YmemBi) C=simple_quad(X,Y,A,Bi.UClY,que,cptY,XmemBi) ; /* otherwise .... */ else { /* if Y is a member of A itself OR Bi itself, then .. */ /* remove Y from A or Bi as applicable */ if (XmemBi fefe YmemBi) if (condense(X,Y,A,Bi,feC,que,l)) { puton(que.Bi) ; puton(global,Bi) ; mendlist(Y.O) ; if (C == 0) { /* create a new set C and make C a member of A and of Bi */ if (YmemBi) { deletelink(Y,Bi,l,3) ; APPENDIX B - PAGE 78 p=inset(que,Y) ; if (YmemA fefe ip) { delθtelink(Y,A,l,4) ; C=insert(X,Y,A.Bi,0); else { C=insert(X,Y,A,Bi,l) ; display (Bi, buffer, RECSIZE) ; if (buf er[0] == '*') { puton ( que, C) ; puton(global,C) ; /* if (p) putoff (que, Y) ; */ else { link(C.A) ; puton(origDCA.C) ; /* if (p) putoff(que,Y) ; */ } * . disposeset(UC1Y) ; return(C) ; simple_quad(X,Y,A,Bi.UClY,que,cptY,XmemBi) int X,Y,A,Bi,cptY,XmemBi; set.reg *UC1Y; set.reg *que; int p.Yonly,C=0,untouched=TRUE; /* if Y is an induced node, and it is a member of A and 3i ONLY, then make X a member of Y */ if (cptY) untouched=ignore(A,Bi,X,Y,UCiY) ; APPENDIX B - PAGE 79 if (cptY fefe first (UC1Y) == OFFEND) { C=Y; /* if Y is an induced node , try to condense graph */ if (XmemBi) if (Yonly=condense(X,Y,A,Bi,feC,que,p=inset(que,Y)) ) { if (untouched) { if (ip) deletelink(Y,A.0,5); transfer___embers(Y,Bi) ; else if (Yonly > 1) { transfer.classes(Y.Bi) ; printf("CLASSES"); C=Y; else deletelink(Y,A,l,6); mendlist(Y.O) ; if (p) { if (untouched) { putoff(que,Y) ; puton(que.Bi) ; puton(gIobaI,Bi) ; eise { p=inset(que,Y) ; if (XmemBi fefe condense(X,Y,A,Bi ,&C ,que,p) ) { if (?) { /* putof f ( ue , Y) ; 14/6 */ puton(que.Bi) ; puton(giobal,3i) ; else deletelink(Y,A,l,7) ; mendlist(Y.O) ; else { /* create a new set C and make C a member of A and of Bi */ deletelink(Y,Bi,l,8) ; APPENDIX B - PAGE 80 if (p) { C=insert(X,Y,A,Bi,l) ; display (Bi, buffer, RECSIZE) ; if (buffer [0] == '*') { /* putoff (que, Y); 14/6 */ puton(que.C) ; puton(global.C) ; } else { link(C.A); puton(origDCA.C) ; } * _ else { deletelink(Y,A,l,9); C=insert(X.Y,A.Bi,0) ; * . x } return(C) ; x I* — */ ignore(A,B,X,Y,UClY) int A.B.X.Y; set.reg *UC1Y; /* Exception: when Y is a member of another set D, BUT this X is in any case already a member of D (22/8) */ x int D,mem,ok,response=TRUE; set.reg *UCX; if ((D- irst(UClY)) != OFFEND) { # ifdef TRACE for (mem=first(UClY);mem != OFFEND;mem=succ(UClY,mem)) { display(mem,buffer, ECSIZE) ; printfC Yl's=7.s",buffer); } # endif TRACE APPENDIX B - PAGE 81 UCX=newset(); closure(LOAD,X,UCX, LL,UPWARD); /* reduce to 2 levels?*/ ok=inset(UCX,Y) ; for (;D != OFFEND;D=succ(UClY,D)) { if (inse (UCX,D)) { if (iok) { printfC CORRUPT "); display(X,buffer,RECSIZE) ; printf(" X=7.s",buffer) ; display(Y,buffer,RECSIZE) printf(" Y=7.s",buffer) ; display(D.buffer.RECSIZE) printfC D=7.s",buffer) ; display(A,buffer.RECSIZE) printf(" A=7.s",buffer) ; display(B.buffer,RECSIZE) printfC B=7.s",buffer); putoff(UClY.D); response=FALSE; /* redundant? */ disposeset(UCX) ; return(response) ; /* */ condense(X,Y,A,Bi,C,que,deep) int X,Y,Bi,A,*C,deep; set.reg *que; /* Handle cases where it is relevant to determine whether Bi is a class or not and whether it has other members apart from X and Y */ { int k,response=FALSE; APPENDIX B - PAGE 82 display (Bi, buffer, RECSIZE) ; /* if Y is an induced node, try to condense graph */ if (buffer [0] == '*') { if (response=no_others(X,Y,Bi,que)) { *C=Bi; if (ideep) { link(Bi.A) ; puton ( origDCA, Bi) ; } return(response) ; } retura(response) ; /* no.others(X,Y,Bi,que) int X.Y.Bi; set.reg *que; /* determine if Bi has any other members apart from X and Y */ /* What if Bi's 'other' children are indirectly linked to A in any case ?? */ { int B; set.reg *DClBi=newset() ; closure(LOAD,Bi,DCIBi,1.DOWNWARD) ; putoff(DCIBi,Bi) ; puto f(DCIBi,Y); putoff(DCIBi,X); if(first(DCIBi) == OFFEND) { disposeset(DCIBi) ; retura(2) ; # ifdef TRACE APPENDIX B - PAGE 83 /* printf("\n"); for (B=first(global) ;B != 0FFEND;B = succ(global.B)) { display(B,buffer,RECSIZE) ; printf * ("Bs=7,s ".buffer); :- */ # endif TRACE notsβtreg(global) ; andsetreg(global,DCIBi,DCIBi) ; notsetreg(global) ; if(first(DCIBi) == OFFEND) { disposeset(DCIBi) ; return(TRUE) ; else { # ifdef TRACE printf("\n"); for (B=firs (DCIBi);B != 0FFEND;B = succ(DClBi.B)) < display(B,buffer,RECSIZE) ; printf("Sos-Xs ",buffer) ; # endif TRACE disposeset(DCIBi) ; return(FALSE) ; /* */ insert(X,Y,A,Bi,deep) int X.Y.A.Bi.deep; /* create a new set C and make C a member of A and of Bi */ _ ..—__,.._-> r •. C = get.entityO ; count++; APPENDIX B - PAGE 84 sprintf (buffer . "*7.d" , count) ; # ifdef TRACE printf("\n new name Xs:7.d " .buffer,count) ; # endif TRACE while(setstringforentity(C,buffer,ENTTYPE) < 0) { count++; sprintf(buffer,"*7.d" .count) ; } * # ifdef TRACE display(A.buffer,RECSIZE); printfC A:7.s ".buffer); display(Bi,buffar,RECSIZE); printfC Bi:7.s ".buffer); display.Y,buffer,RECSIZE); printfC Y:7.s ".buffer); # endif TRACE s et .strength (C , (strength(X) > strength(Y) ? strength(X) : strength(Y) ) ) link(Y .C) ; /* make Y a member of C */ link(C .Bi) ; link(X,C) ; /* set_strength(C,strength(X) + strength(Y)) ; */ if (ideep) { link(C,A); puton(origDCA,C) ; puty(Bi.C); /* this can be done provided none if Bi's parents are processed before itself */ /* mendlist(Y.O) ; alternative way .... */ return(C) ; } /* */ root(node.tree.updown) int *node,updown; set.reg *tree; /* find the root of a tree, i.e. find the MAXIMAL common descendant, */ /* or the MINIMAL common ancestor, which ever may apply */ APPENDIX B - PAGE 85 mt cur_noαe; set.reg *CRoot=newset() ; /* closure(LOAD,*node,CRoot,1,1-updown) ; */ closer(*node,CRoot,1-updown) ; andsetreg(CRoot,tree,CRoot) ; putoff(CRoot,*node) ; for (cur_node=first(CRoot);cur.node != OFFEND;cur_node=first(CRoot)) { *node=cur_node; /* closure(LOAD,cur_node,CRoot,1,1-updown) ; */ closer(*node,CRoot,1-updown) ; andsetreg(CRoot,tree,CRoot) ; putoff(CRoot,*node) ; disposeset.(CRoot) ; /* */ puty(Bi.Y) int Bi.Y; /* Store a meet computed */ int j.next; next = Bi 7. LSIZE; for (j = 0;j<LSIZE;j++) if (list[next] [0] == 0 | | list[next] [0] == Bi) { list[next] [0]=Bi; list[next] [l]=Y; return; else next = ++next X LSIZE; /* printf("Y not stored, LSIZE too smallXn") ; */ printfC."); APPENDIX B - PAGE 86 /* gety(Bi) int Bi; /* Retrieve a meet computed earlier */ { int j.next; next = Bi X LSIZE; for (j = 0; j<LSIZE; j++) /* doesn't this run further than nee?*/ if (list [next] [0] == Bi) { /* printfC read "); */ returadist [next] [l] ) ; } else next = ++next X LSIZE; return(OFFEND) ; } endlist(Y.newY) int Y.newY; /* Update the list of computed meets */ j * int j.next; for (j = 0;j<LSIZE;j++) if (list[j][l] =* Y) { if (newY ~~ - ~ 0 I I list[j][0] == newY) list[j] [0]=0; else list[j] [l]=newY; APPENDIX B - PAGE 87 show . mem, direction, buf ) int mem, direction; char buf [RECSIZE] ; /* Print a one level closure - used for debugging */ x _. s et _r eg *UC lY=news et ( ) ; closure(LOAD,mem,UClY.l,direction) ; showset(UClY,buf); disposese (UC1Y); /* */ showset(UClY.buf) set.reg *UC1Y; char buf[RECSIZE] ; /* Print a set - used for debugging */ nt mem; printf("XnShow Xs: ",buf); for (mem=first(UClY) ;mem != OFFEND;mem=succ(UClY,mem)) { isplayύnem,buffer,RECSIZE) ; printfC Xs",buf er); } } /* */ transfer.members(Y,Bi) /* transfer Y's members to Bi and remove Y J is a redundant node) */ APPENDIX B - PAGE 88 int mem , h , i , enough=F ALSE ; int ferque[10] [2] ; set.reg *DClY=newset() ,*DCBi=newset() ,*UC1; for (i=0;i<10;i++) { ferque[i] [0]=0; f erque[i] [l]=0; } if (strength(Y) > strength(Bi) fefe Bi < MAX.ENT) { set_strength(Bi,strength(Y)) ; } endlist(Y.Bi); mem=deletelink(Y,Bi,0,10) ; if (mem < 0) { printfC trn.mem YBi: 7.d" ,mem) ; } closure(LOAD,Y,DClY,l,DOWNWARD); putoff(DC1Y.Y) ; closure(LOAD,Bi,DCBi,ALL,DOWNWARD) ; putoff(DCBi.Bi) ; for (mem=first(DClY) ;mem != OFFEND;mem=succ(DClY,mem)) { if ((h=deletelink(mem,Y,0,ll)) == 0) if (!inset(DCBi,mem)) { link(mem.Bi) ; enough-TRUE; } else { display(mem, buff er, RECSIZE) ; if (buffer[0] =- '*') { UCl=newset() ; closure(LOAD, mem, UCl.l, UPWARD) ; putoff (UC1, mem) ; putof f(UCl,(h=f irst (UC1))); if (h == OFFEND) printfC'!!!"); else /* *** */ if (first (UC1) == OFFEND fefe ! (inset (global, mem) fefe Unset (done, mem))) { for(i=0;i <10;i++) if (ferque[i][0] *■ 0) { ferque[i][0] =mem; ferque[i] [l] =h; break; } if (i==10) { printfC QUE TOO SMALL "); transf er .members (mem, h) ; } APPENDIX B - PAGE 89 disposeset (UC1) # ifdef TRACE display(Y, buff er, RECSIZE) ; printf C condense Xs-" , buf er) ; display (Bi , buffer, RECSIZE) ; printf ("Xs" .buffer) ; # endif TRACE y I* Y removed */ freeze(Y) ; /* temporary code - should have routine trfr.later which record the h' s above *** */ if (tab [ ferque [i] [0] ] ! = NULL fefe tab [ ferque [i] [l] ] ! = NULL) transfer_members(ferαue[i] [0] ,ferαue [i] [1] ) ; } /* if ( ! enough) { */ /* it should be possible to eliminate this DC by passing on information from NO-OTHERS */ /* display(Bi,buffer,RECSIZE); if (buffer[0] == '*') { closure(LOAD,Bi.DCBi.l,DOWNWARD) ; putoff(DCBi.Bi) ; if ((h=first(DCBi)) != OFFEND) putoff(DCBi.h) ; if (first(DCBi) == OFFEND fefe strength(h) > strength(Bi)) { display.h,buffer,RECSIZE) ; if (buffer[0] == '*') { transfer_classes(h.Bi) ; printfC N. E. ") ; X X X */ disposeset(DC1Y) ; APPENDIX B - PAGE 90 disposeset (DCBi) ; /* APPENDIX B - PAGE 91 dbJnfo.q /* * This module contains part of the semantic network * synthesizer, to be used with the natural language * database interface system written by Lin Dekang * * Deon Oosthuizen October 1987 * • / #include <stdio.h> #include "browse.h" struct s.table schema.table [TABLSIZΞ] ; struct table symbol.table [SYMSIZE] ; int symbol_no= EMPTY, cur_end= EMPTY; char lex [3] ; mainO int i,j,k; char table[FSIZE] .field[FSIZE] ; tabstr fld_ptr[MAXFIELD] ; printfC\nXn\n\n\n\n\n\n\n\n\n\n\n Semantic Network GeneratorXn") print C -======-==--= : -==-=-==-=--==\n\n\n\n\nXn\n\n") ; /* get db information */ openbaseO ; get.schemaO ; printfC Analysing data"); i=l; while (i<=symbol_no) { if (schema.table [i] .status == RELATION) { printf C . ") ; fflush(stdout) ; fld_ptr[0]= schema.table [i] .fld.name ; k=0 ; for (j=i+l ;j<=symbol_no fefe schema.table [j] .status == ENTITY;j++) if (schema.table [j] .fld.type == ' c ' ) { APPENDIX B - PAGE 92 k++; fld_ptr[k]~ schema.table [j] .fld.name; } get_symbols(i,fld_ptr,k) ; i = j; } else { printf ("Warning: unexpected field type encountered") ; . ++ ; } } for (i=l;i<=cur_end;i++) { if (symbol.table[i].dupl[l].fld.name[0] != '\0') { synonyms (i) ; X } for (i=l;i<=cur_end;i++) { if (symbol.table[i].dupl[l].fld.name[0] != '\0') { veri y(i); } } for (i=l;i<=symbol_no;i++) if (schema.table [i] .status == RELATION) interpret.dependencies(i) ; setup.net 0 ; closedbO ; systemCr dbkey"); /* */ par ange (var , table) char var.tableD; { char s [100] ; sprintf (s, "range of Xc=Xs", var, table); IΙwrite(s); IΙsync(O) ; APPENDIX B - PAGE 93 /* */ openbaseO { * int rcode; fILE *fp, *fopen() ; ## char dbname[32]; /* open database for key analysis */ fopenCdbkey'V'w"); /* initialise database */ /* initdb("dbkey",500,40,20,5,7,70,10,3,8); rcode-=opendb("dbkey") ; if (rcode < 0) { printf("Unable to open data base used for key analysesXnX Please create an empty file called 'dbkey' and start againXn"); abort(); } */ printf("XnName of data base to be used for generation of semantic net: "); gets(dbname) ; printfCXn"); printfC Reading sche aXn") ; ## ingres dbname /* get.schemaO /* read schema information from database */ -r int i=0 ,sys_tabs=6 ; char table [40] ; char relatn_name [FSIZE] . cur.table [FSIZE] , field [FSIZE] . type [FSIZE] ; APPENDIX B - PAGE 94 char string [200] ; char *tl_vector[MAXFIELD] ; /* printf CXnHow many system tables? (related to INGRES DBMS) "); scanf C7.d",fesys_tabs); */ sprintf (table, "attribute" ) ; parangeCa', table); sprintf (string, "XXc is a.attrelid, XXc = a.attname, XXc = a.attfrmt"); fl_vector[0] = relatn.name; tl_vector[l] = field; tl_vector[2] = type; ## param retrieve (string, tl.vector) ##{ /* print ("\n field etc. Xs Xs Xs " .relatn.name, ield, type) ; */ if (strcmp (relatn.name, cur.table) != 0) { strcpy (cur.table, relatn.name) ; i++; if (i > sys.tabs) { insert.record(relatn.name) ; strcpy (schema.table [symbol.no] .fld.name, field) ; schema.table [symbol.no] .fld_type=type[0] ; /* print ("XnRecord Xd: Xs Xs Xd Xd Xd ".symbol.no, schema.table [symbol.no] .fld.name, schema.table [symbol.no] .fld.type, schema.table [symbol.no] .dest[0] , schema.table [symbol.no] .org[0] , schema.table [symbol.no] . link.type [0]) ; */ } ##} /* get.symbols(table.no,fld.ptr.numfIds) int table.no,numfIds; tabstr fld_ptr[MAXFIELD] ; /* extract the first section of every table and store the symbols a common symbol table in order to establish relationships between fields. The symbols are also used for a key-field anayses */ APPENDIX B - PAGE 95 int i,j=0; char symbol [MAXFIELD] [FSIZE] ; char string [120] .temp [50] ; char *tl_vector [MAXFIELD] ; parange ( ' a ' , f Id.ptr [0] ) ; sprintf (string, "XXc is a.7.s",fld_ptr[l]) ; for (i=l;i<numflds;i++) { sprintf (temp,", XXc is a. Xs",f Id.ptr [i+ 1] ) ; tl_vector[i-l] = symbol[i-l]; strca (string.temp) ; } for (i=0;i<numflds;i++) { tl_ vector [i] = symbol [i] ; ## param retrieve (string, tl.vector) ##{ for (i=0;i<numflds;i++) { /* printfC Xs Xs Xs\n", symbol [i] ,fld_ptr[i+l] ,f Id.ptr [0]) ;*/ insert.symbol (symbol [i] ,fld_ptr[i+l] ,fld_ptr[0]) ; } insert.tuple(table.no, ,tl_vector,numfIds) ; if (j == SAMPLESIZE) break; ##} /* consolidate(rec.no.new,old) int rec.no,new,old; /* consolidate references to same fields by different symbols */ { int i.j.k,found; for (j=l;j<=cur_end; ++) { if (symbol_table[j] .dupl[l]. rel.name [0] != '\0') { for (k=0;k<MAXDUP fefe symbol_table[j] .dupl[k] .fld.name [0] != '\0';k++) { if (strcmp (symbol. table [rec.no] .dupl[old] .fld.name, APPENDIX B - PAGE 96 symbol_table[j] .dupl[k] .fld.name) == 0 fefe strcmp (symbol.table [rec.no] .dupl[old] .rel.name, symbol_table[j] .dupl[k] .rel.name) == 0) { /* determine if the new name is already present in the record. If it is, just delete old otherwise replace old by new */ found=FALSE; for (i=0;i<MAXDUP fefe symbol.table [j] .dupl[i] .fld.name [0] != θ';i++) { if (strcm (symbol.table [rec.no] .dupl[new] .fld.name, symbol.table [j] .dupl[i] .fld.name) == 0 fefe strcmp (symbol.table [rec.no] .dupl[new] .rel.name, symbol.table [j] .dupl[i] .rel.name) — 0) { symbol.table [j] .dupl[k] .fld.name [0] = '\0'; found=TRUE; break; } } if (ifound) { strcpy (symbol.table [j] .dupl[k] .fld.name, symbol.table [rec.no] .dupl[new] .fld.name) ; strcpy (symbol.table [j] .dupl[k] .rel.name, symbol.table [rec.no] .dupl[new] .rel.name) ; } break; } /* synonyms(rec.no) int rec.no; /* establish relationships between fields with identical names */ { int i, j ,k, vacant, ound; APPENDIX B - PAGE 97 /* find irst empty field in current record */ for (vacant=2;vacant<MAXDUP fefe symbol.table [rec.no] . dupl [vacant] . fld.name [0] ! = ' \0 ' ; vacant++) ; for (i=0;i<MAXDUP;i++) { if (symbol.table [rec.no] .dupl [i] .fld.name [0] == '\0') continue; found=FALSE; for (j=rec_no;j<=cur_end fefe !found;j++) { if (symbol.table [j] . dupl [1] .fld.name [0] != '\0') { for (k=0;k<MAXDUP ;k++) { if (symbol.table [j] .dupl [k] .fld.name [0] == '\0') continue; /* only consider records containing duplicates */ if (strcmp (symbol.tablβ [rec.no] .dupl[i] .fld.name, symbol.table [j] .dupl[k] .fld.name) == 0) /* if the same field name appears in a different table, determine if the field names are synonomous*/ if (strcmp (symbol.table [rec.no] .dupl[i] .rel.name, symbol.table [j] .dupl [k] .rel.name) != 0) { strcpy (symbol.table [rec.no] . dupl [vacant] .fld.name, symbol.table [j] .dupl[k] .fld.name) ; strcpy (symbol.table [rec.no] . dupl [vacant] .rel.name, symbol.table [j] ,dupl[k] .rel.name) ; if (test (rec.no, i, vacant)) call.i (rec.no , i , vacant , ask(rec_no , i , vacant) ) ; symbol.table [rec.no] .dupl [vacant] .fld.name [0]='\0' ; /* done always */ ound=TRUE ; x /* anaivse(rec_no,occl,occ2) APPENDIX B - PAGE 98 int rec.no, occl,occ2; /* analyse given fields in the database and determine how they are related */ . int fieldl,field2; fieldl=-searc__(rec_no,occl) ; ield2=searc (rec_no, occ2) ; if (schema.table [fiβldl] .fld.type != schema.table [fiβld2] .fld.type) return(UNRELATED) ; else return(IN) ; /* temporary response */ X j* */ verify(rec.no) int rec.no; /* symbol appearing in more than one table */ /* inspect all the fields in which a particular name occurred, determine their relationships and alter the schema table accordingly */ t char store[MAXDUP] [FSIZE] ; /* use this buffer to communicate lexical names τo user */ int h.i, j ,k=0,m,s,sym [MAXDUP] ,pos; char buffer [50] , symbol 1 [FSIZE] , symbol2 [FSIZE] ; for (j=0;j<MAXDUP;j++) if (symbol.table [rec.no] .dupl [j] .fld.name [0] != '\0') { for (m=0;m<FSIZE fefe (store [k] [m] *symbol_table [rec.no] . dupl [ j] . fld.name [m] ) ! = ' ' ;m++) ; store [k][m] = '\0'; /* insert NULL into table strings to enable match with shorter strings below */ sym[k]=j; k++; } if (k < 2) return; if (k « 2) { call.it (rec.no, sym[0] ,sym[l]) ; APPENDIX B - PAGE 99 return; printf ("XnWhat are the IS.A relations between the following fields ? "); for (j=0;j<k;j++) if (s_h_ma_table[pos=search(rec_no,sym_j])] . sym_name [0] -- '\0') • * _* printf C\n (Xd) Xs ",j+l, symbol.table [rec.no] .dupl[sym[j]] .fld.name) ; }_ else { printf ("\n (Xd) Xs ", +l,schema_t able [pos] .sym.name) ; (store [sym[j]] [m]=schema_table [pos] . sym_name [m] ) != ' ' ;m++) if (schema.table [pos] .sym.name [m] == 'XO') break; store [sym [j] ] [m]= 'XO'; printf "XnType 'namel name2' for 'namel' is a 'name2' (just CR to stop)\n"); gets (buffer) ; while (buffer [0] != '\0') { for (j=0;j<FSIZE fefe buffer[j] != ' ' fefe buffer[j] != '\0';j++) symbol! [j]=buffer[j] ; symboll[j]='\0'; m=0; for (++j;m<FSIZE fefe (symbol2[m] = buffer[j]) != '\0';j++) { __++; /* printf ("bu fers. Xs.Xs.Xh", symbol l,symbol2) ; */ for (j=0;j<k && strcmp (store [sym [j] ] .symboll) != 0;j++); for (m=0;m<k fefe strcmp (store [sym[m] ] ,symbol2) != 0;m++); if (j==k I I m==k) printf("Input not recognised, please try againXn"); else if (test (rec.no, sym[j] ,sym[m])) { /* printfC test not failed "); */ call.it (rec.no, sym[jj ,sym[m] ,IN) ; gets(buff er) ; } APPENDIX B - PAGE 100 /* delete remaining records containing the same combination of fields */ for (i _ rec_no+l;i< !S cuτ_end;i++) { h=0; s=0; for (j-0; j MAXDUP fefe h>0;j++) if (symbol.table [i] . dupl [j] .fld.name [0] != '\0') { s++; * for (m β 0;m<k ;__.++) if (strcmp(symbol_table [rec.no] .dupl[sym[m]] .fld.name, symbol.table [i] .dupl [j] .fld.name) == 0) h++; } ' if (h >= s) for (j=0;j<MAXDUP;j++) symbol.table [i] . dupl [j] . ld.name [0] = ' \0 ' ; /* search(rec_no,pos) int rec.no,pos; /* scan schema.table for the correct record number */ ■* TIT " • for (i=l;i<=symbol_no fefe strcmp (schema.table [i] .fld.name, symbol.table [rec.no] .dupl [pos] .rel.name) - 0;i++) ; for (++i;i<=symbol_no fefe strcm ( schema.table [i] .fld.name, symbol.tablβ [rec.no] .dupl [pos] .fld.name) != 0;i++); returnd) ; /* */ testd, j ,k) int i.j.k; APPENDIX B - PAGE 101 int source , destm ; destin=search(i,k) ; if (related(source , destin) ) return(FALSE) ; if (schema.table [source] .fld.type ~ DELETED) { symbol.table [i] . dupl [j] .fld.name [0] = ' \0 ' ; return (FALSE) ; if (schema.table [destin] .fld.type == DELETED) { symbol.table [i] .dupl [k] .fld.name [0] = ' \0' ; return (FALSE) ; if ( analyse (i ,j ,k) == UNRELATED) return(FALSE) ; /* */ call.i (i ,j ,k, relation) int i,j ,k, relation; int source,destin; source=search(i, ) ; destin=search(i,k) ; switch (relation) { case SAME: unite (source, destin) ; consolidated , j ,k) ; symbol.table [i] . dupl [k] .fld.name [0] = ' \0 ' ; break ; case IN: /* printf("XnXn LINK Xd Xd \n\n"); */ tab_link(source,destin,IS.A) ; break; case CONTAIN: tab_link(destin,source,IS.A) ; APPENDIX B - PAGE 102 /* */ ask(i,fieldl,fiβld2) int i. ieldl,field2; /* get confirmation of findings, as well as symbolic names from user */ { int pos,pos2; char choice [9] ; char symbol [FSIZE] ,symbol2 [FSIZE] .answer [3] ; pos=search(i,f ieldl) ; if (schema.table [pos] .sym.name [0] == '\0') if (schema.table [pos2=search(i,field2)] .sym.name [0] != '\0') pos*pos2; if (schema.table[pos] .sym.name[0] — '\0') { printf("XnXn What is the lexical name for the field name XsXnXnX in the table Xs ?\n\n (default: Xs) ", symbol.table [i] . dupl [f ieldl] .fld.name, symbol.table [i] . dupl [f ieldl] .rel.name, symbol.table [i] . dupl [f ieldl] .fld_name) ; gets (symbol) ; if (symbol [0] == '\0') strcpy(symbol,symbol_table[i] .dupl[fieldl] .fld.name) ; strcpy (schema_table [pos] .sym.name, symbol) ; x else strcpy (symbol, schema.table [pos] .sym.name) ; if (lex[0] != 'y') { printf ("XnAre Xsas in the table XsXnand Xsas in the table XsXnXnX both referring to the same set of Xs's ? ", symbol.table [i] . dupl [f ieldl] .fld.name, symbol.tablβ [i] . dupl [f ieldl] .rel_name, symbol.table [i] .dupl[fiβld2] .fld_namβ, symbol.tablβ [i] .dupl[ iβld2] .rel_namβ, symbol) ; APPENDIX B - PAGE 103 gets (answer) ; * >.• else rβturn(SAME) ; if (answβr[0] == 'y' 1 1 answer[0] == 'Y' ) { if ClβxCO] ! = 'y' fefe lex[0] ! = 'n' ) { printf ("XnDoes identical field names always imply identical fields? " ) ; gets (lex) ; if (lex[0] ~ 'Y' ) lex[0] = 'y' ; } return(SAME) ; } pos=sβarch(i,field2) ; if ( schema.table [pos .sym.name [0] == ' \0 ' ) { printf ("XnIn that case , please give me a suitable lexical name XnX for the field name Xsin the table Xs " , symbol.table [i] .dupl [field2] .fld.name , symbol.table [i] .dupl [field2] .rel_name) ; gets(symbol2) ; if (symbol2[0] == '\0 5 ) strcpy(symbol2,symbol.table[i] .dupl[field2] .fld.name) ; strcpy(schema.table[pos] .sym_name,symbol2) ; else strcpy _symbol2,schema_table[pos] .sym_name) ; printf("XnWhat is the relation between 'Xs' and 'Xs' ? ",symbol,symbol2) ; printfCXnXn(l) every Xs is a Xs, \n(2) every Xs is a Xs, \n\ (3) they are unrelated \n\nplease select a number: ", symbol,symbol2, symbol2,symbol) ; gets(choice) ; switch (choice[0]) { case '1': return(IN); case '2': return(CONTAIN) ; case '3': return(UNRELATED) ; } * , related(nodel ,node2) APPENDIX B - PAGE 104 int node 1, node_!; /* determine if 2 nodes are already related */ r i int i; for d=0;i<MAXLINKS;i++) if (schema.table [node 1] .dest [i] =■ node2 II schema.table [node 1] .org[i] == node2) return(TRUE) ; return (FALSE) ; > /* */ APPENDIX B - PAGE 105 browse,h /* * This file contains part of the semantic network * synthesizer, to be used with the natural language * database interface system written by Lin Dβkang * Deon Qosthuizen October 1987 */ # define FSIZE 25 # define TABLSIZE 500 # define SYMSIZE 2000 # define MAXLINKS 20 # define MAXDUP 12 # define MAXFIELD 12 # define SAMPLESIZE 40 # define TRUE 1 # define FALSE 0 # define IS.A 1 # define HAS.ATTR 2 # define DELETED 'd' # define EMPTY '\0' # define ENTITY 'e' # define KEY 'k* # define RELATION 'r' # define SYNONYM 4 # define SAME 5 # define IN 6 # define CONTAIN 7 # define UNRELATED 8 struct tab.field.pair { char rel.name[FSIZE] ; char fld.name[FSIZE] ; /* field name */ struct table { char sym.name[FSIZE] ; /* symbol name */ APPENDIX B - PAGE 106 struct tab.field.pair dupl [MAXDUP] ; } ; struct s.table { char sym.name[FSIZE] ; /* symbol name */ char fld.name[FSIZE] ; /* field or relation name */ char status; char fld.type; /* field type 'i'nt or 'c'har */ int org[MAXLINKS] ; /* pointers to soursce nodes */ int des [MAXLINKS] ; /* pointers to destination nodes */ int link.type[MAXLINKS] ; /* type of outgoing arc */ int role[MAXLINKS] ; /* pointer to original record (node) to get access to role information (status,fld.name) */ }; typedef char *tabstr; APPENDIX B - PAGE 107 pif.h /* * β(#)pif.h 3.0 Deductive Systems Limited 10/06/85 * Procedural Interface to Fact include file for applications * This file contains define statements etc for use in calling * PIF functions */ _ /* ..include "gamdef.h" */ /* type descriptors for entities, attributes etc. */ # define L0WBITS 15 /* bottom 4 bits of type */ # define L0W3BITS 7 /* bottom 3 bits of type */ # define MIDBITS 240 /* next 4 bits of type */ # define HIGHBITS (15 « 28) /* top 4 bits in a 32 bit word */ # define ENTTYPE 0 /* basic types occupy lowest 4 bits of type flag */ # define ATTTYPE 1 # define GL0TYPE 2 # define SYNTYPE 3 # define RELTYPE 4 # define SEQTYPE 5 ..ifdef FACTAPP # define TERMINAL 8 /* terminal member flag */ # define JOINTED 256 /* disjoint flag */ # define N0TTERM 0 #endif # define INTTYPE (1«4) /* definition types occupy next 4 bits */ # define DECTYPE (2«4) # define DATTYPE (3«4) # define TXTTYPE (4«4) # define STRMAX 20 /* maximum length of entity name */ /* closure parameter definitions */ # define LOAD 0 # define AND 1 APPENDIX B - PAGE 108 # define OR 2 # define NOT 3 # define ANDNOT 4 # define FULLLOAD 5 # define ALL 1000 # define UPWARD 0 # define DOWNWARD 1 # define ANY 1 /* relational opcodes for restriction operator */ define EQ define NE define LT define LE define GT define GE typedef int set.reg; struct xx { char * blockaddr; /*Address of i/o buffer store block */ struct xx * next; /*Nβxt element */ int fileblkno; /*Blockno of file block allocated for this entry*/ char status; /* Block DIRTY or CLEAN */ }; typedef struct xx blklistelem; typedef struct { int rstart; /* BSA (Backing Store Address) of register start */ int rcursor; /* current read/write position */ int renddata; /* end of information in register */ int rend; /* absolute (possible) end of register capacity */ int rfull; /* boolean - used to give only 1 error message on overflow */ int rtuplen; /* the number of tuples wide each record stored is */ int rseekaddr; /*simulated seek address for dynamic reg file*/ char rsorted; /* Boolean marker is register sorted?*/ unsigned int rlasttimβstamp; /* Timestamp of last record written*/ blklistelem * rblocklist; /* head of list - one per i/o buffer and fileblock */ APPENDIX B - PAGE 109 } tuple.reg; set.reg *newset(); tuple.reg *newreg(); typedef struct { int baddr; int startaddr; } table.desc; typedef struct { int opcode; /* and, or, not, to allow different query types */ int qfield; /* field of tuple which qualifier applies to */ int qqual; /* qualifier of a field */ } qtriple; /* query triple */ /* # define TRACE trace facility inclusion switch */ /* table structure definition */ typedef struct { int flags; /* deleted/active; single valued etc data */ char name[STRMAX] ; /* table name */ int linkno; /* next table if any with same mainsubject */ int mainent; /* first subject entity number */ int Sequent; /* sequence number entity token, if any */ int subno; /* number of additional subject entities */ int propno; /* number of property items */ int where; /* data address */ int width; /* width of tuples associated with this table */ int rblksize; /*size of block containing the restriction*/ table.desc desc; /* plate descriptor */ } table; /* note that the width is in units of integers. It allows for the necessary expansion of decimals into integer components. */ /* if no sequence number is defined for this table, sequent is set to zero */ /* rule structure definition */ typedef struct { int flags; /* enabled, disabled, checked etc */ int svcount; /* number of single value rules in the set */ APPENDIX B - PAGE 110 int mvcount; /* number of multi value rules in the set */ char name[STRMAX] ; /* rule name */ int svstart; /* single value start address */ int mvstart; /* multi value start address */ int svwhβre; /* single valued data address */ int mvf-here; /* multi valued data address */ } rule; # define ENABLESET 1 # define CHECKSET 2 # define SV 0 # define MV 1 # define RNEXT 0 /* rule data held in the first integers in the rule buf er */ # define RPOINT 1 # define RFLAG 2 # define RDATA 3 /* start of actual data */ /* defines to work with rule block */ # define DIRTY (TS.BLOCKSIZE - 6) # define COUNT (TS.BLOCKSIZE - 5) # define SIZE (TS.BLOCKSIZE - 4) # define FREE (TS.BLOCKSIZE - 3) # define NEXIΕLOCK (TS.BLOCKSIZE - 2) # define FWDBLOCK (TS.BLOCKSIZE - 1) # define RS.INFO (6) # define UNKNOWN (1 « 31) # define BLANK (UNKNOWN + 1) # define FILLER (BLANK + 1) # define DECUNKNOWN HUGE # define DECBLANK (DECUNKNOWN -1) /* maximum sizes of things */ # define TABSIZE 31 /* table size, other than first subject entity */ # define FULLTABSIZE 32 /* table size, including first subject entity */ # define TABLIMIT 64 /* maximum size of virtual table */ # define TEXTSIZE 256 /* maximum text length */ # define MAXTUPLESIZE (TABSIZE * (sizeof(double)/sizeof(int))) /* status types for table entry */ APPENDIX B - PAGE 111 # define OPTIONAL 1 # define MANDATORY 2 # define COMPUTED 3 # define PROTECT 4 # define DELCOLUMN 8 /* column is deleted if this bit is set */ /* table deleted bit */ # define TABDELETED (1 « 10) # define YEARSTART 1900

Claims: CLAIMS 1. A computerised information-retrieval system (10) comprising a data base (13) from which information is to be retrieved, an input device (11A) at which a user may use his own natural language to input a query, and an intelligent interface (12) to transform the natural language query to the formal computer language of the system (10) , said interface (12) comprising means of solving the steiner Problem where the conceptual graph representing the database is unrestricted, said solving means comprising: means for identifying the number and identity of nodes (N) in the query set, means for assigning to a store at each node of the conceptual graph of the data base a collection of N triple-part markers, each marker first part being an initial node identity, each marker second part being an indication of cost to that marker in traversing between adjacent nodes, and each marker third part being a pointer to the link between adjacent nodes immediately previously traversed by that marker, and each collection of N markers initially comprising a marker having an identity part for each of the N nodes the respective cost and pointer parts for N-l of the markers being undefined and for the remaining marker being set to zero, said remaining marker having its initial node identity identifying the node at which the pertaining collection is stored, means for storing global values which are available to each node, said global values being a node identifier to identify that node (current root) with all cost parts being defined wherein the accumulated value for the second part of the markers is minimum (current minimum) , means for initialising current root to null and current minimum to a maximal value, means for asynchronously passing messages from each of said nodes to each of its immediate neighbours via the respective links therebetween the traverse cost of which is proportional to the length thereof, each received message containing the duplicate of all markers stored in the transmitting node with respective cost parts equal to the previous cost of the respective markers and with respective pointer parts identifying the link traversed by that marker, means at each node for receiving said messages, for iteratively comparing received and stored markers according to initial node identity, the comparison being between the respective cost parts of the markers, to identify and discard those received markers of greater cost than the stored markers, other than when the cost part of the stored markers is undefined, and with the number of received markers in each iterative comparison equally sharing the on-cost of the traverse of the particular link, and on termination of the iteration, for storing the collection of N markers which are respectively of least defined cost, means for updating current minimum and current root when any one node has N markers each having a defined cost part and having an aggregate cost value less than the previously stored value of current minimum, means for initiating message transmission from a node on each occurrence of a change in the stored collection of markers at that node and when that node has an accumulated cost value less than current minimum, means for identifying when the message-passing process has been completed for every node, and, means for tracing the traverse of each marker in the finally identified current root node back to its originating node in order to determine the Steiner Tree. 2. A computerised information-retrieval system as claimed in claim 1, wherein the undefined value for the cost part of a marker is a large value and the undefined value for the pointer part of a marker zero. 3. A computerised information-retrieval system as claimed in either preceding claim, wherein the intelligent interface is physically associated with the or each input device to the computer system.

Inventors: MCGREGOR DOUGLAS ROBERT, MCGREGOR, DOUGLAS, ROBERT
Applicants: UNIV STRATHCLYDE, UNIVERSITY OF STRATHCLYDE
Application year: 1992
Main classification: G06F15
IPC codes: G06F17