vestatis.c 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /*
  24. * COMPONENT_NAME: austext
  25. *
  26. * FUNCTIONS: CNCRD_MEMORY_AREA_LIST
  27. * QUERY_STEM_STR
  28. * STAT_STR
  29. * TREENODE
  30. * build_bin_tree
  31. * comp_stat
  32. * descend_tree
  33. * efim_qsort
  34. * fill_stem
  35. * get_next_memory_block
  36. * init_global_memory
  37. * init_memory
  38. * inv_index_bin_tree
  39. * load_ditto_str
  40. * release_shm_mem
  41. * stat_search
  42. * traverse_tree
  43. * ve_statistical
  44. *
  45. * ORIGINS: 27
  46. *
  47. * (C) COPYRIGHT International Business Machines Corp. 1993,1995
  48. * All Rights Reserved
  49. * US Government Users Restricted Rights - Use, duplication or
  50. * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  51. */
  52. /*************************** VESTATIS.C ****************************
  53. * $XConsortium: vestatis.c /main/9 1996/11/25 18:49:04 drk $
  54. * 1993.
  55. * Statistically sorted stems search.
  56. *
  57. * $Log$
  58. * Revision 2.3 1996/02/01 19:35:55 miker
  59. * AusText 2.1.11, DtSearch 0.3: Uses new single word parser/stemmers.
  60. *
  61. * Revision 2.2 1995/10/25 15:00:05 miker
  62. * Added prolog.
  63. *
  64. * Revision 2.1 1995/09/22 22:30:42 miker
  65. * Freeze DtSearch 0.1, AusText 2.1.8
  66. * Revision 1.11 1995/09/07 23:30:15 miker
  67. * ...One last try (sigh).
  68. * Revision 1.10 1995/09/07 19:08:01 miker
  69. * Last fix incorrectly coded.
  70. * Revision 1.9 1995/09/07 16:25:11 miker
  71. * Fixed solaris bus fault caused by TREENODE structure
  72. * not being aligned on machines word boundary. Fault occurred
  73. * only when query contained more than one word.
  74. * Revision 1.8 1995/09/05 19:31:37 miker
  75. * Made usrblk and ausapi_msglist global. Replaced Socrates()
  76. * with calls to parser() and stemmer(). Deleted socblk.
  77. * Numerous name changes. All for DtSearch...
  78. */
  79. #ifndef _ALL_SOURCE
  80. # define _ALL_SOURCE /* to pickup typedefs for shm vnodes */
  81. #endif
  82. #include "SearchE.h"
  83. #include <stdlib.h>
  84. #include <string.h>
  85. #include <ctype.h>
  86. #include <math.h>
  87. #include <sys/stat.h>
  88. #include <sys/types.h>
  89. #include <sys/ipc.h>
  90. #include <sys/shm.h>
  91. #include "vista.h"
  92. /*-------------------------- GLOBALS ----------------------------*/
  93. /**** declaration for the global memory pointers ****/
  94. #define PROGNAME "VESTATIS"
  95. #define MEMORY_SIZE 64000 /* 65536 is 64 KBytes of memory */
  96. #define REC_TYPES 256
  97. #define NORM_VALUE 30
  98. #undef INFINITY /* XXX does GCC's __builtin_inff() work here? */
  99. #define INFINITY 9999.0
  100. #define SORT_MESG 10000
  101. #define CHAR_BITS 8
  102. #define STACKSZ 256
  103. #define MED_3_VALUE 7
  104. #define TIME_ITERATION 1
  105. #define LOG2 0.693147181
  106. #define MS_vestatis 17
  107. #define STRUCT_ALIGN sizeof(char*)
  108. static int SHM_FLAG = IPC_CREAT | S_IRUSR | S_IWUSR | S_IWGRP |
  109. S_IRGRP | S_IROTH | S_IWOTH;
  110. static char *mem_start;
  111. static char *cur_pos;
  112. static long mem_offset;
  113. static long total_memory_size;
  114. typedef struct q_s {
  115. char stem[DtSrMAXWIDTH_HWORD];
  116. int count;
  117. } QUERY_STEM_STR;
  118. typedef struct mem_area {
  119. char *start_of_mem_block;
  120. long block_size;
  121. struct mem_area *next_block;
  122. } CNCRD_MEMORY_AREA_LIST;
  123. typedef struct bintree {
  124. struct bintree *rlink; /* ptr to next node in linked list or
  125. * right link in binary tree */
  126. struct bintree *llink; /* left link in binary tree */
  127. char *word; /* ptr to word in the query */
  128. int count;
  129. } TREENODE;
  130. typedef struct s_a {
  131. DB_ADDR dba;
  132. float wght;
  133. DtSrINT32 num_word_hits;
  134. } STAT_STR;
  135. static STAT_STR *stat_array = NULL;
  136. static TREENODE *root_node;
  137. static TREENODE *top_of_stack;
  138. static TREENODE *stack;
  139. static TREENODE *pres;
  140. static TREENODE *prev;
  141. static TREENODE *next;
  142. static TREENODE *avail_node;
  143. static CNCRD_MEMORY_AREA_LIST *memory_blocks = NULL;
  144. static CNCRD_MEMORY_AREA_LIST *cur_mem_ptr;
  145. static QUERY_STEM_STR *query_stems = NULL;
  146. static DB_ADDR *word_addrs = NULL;
  147. static int num_diff_words = 0;
  148. static char begin_search;
  149. static char begin_sort;
  150. static char begin_load_ditto;
  151. static char begin_qsort;
  152. static char qsort_done;
  153. static DtSrINT32 real_num_rec;
  154. static DtSrINT32 num_hits;
  155. static DtSrINT32 total_num_addrs;
  156. static DtSrINT32 dba_offset;
  157. static unsigned char rec_type_tab[REC_TYPES];
  158. static char vestat_msgbuf[256];
  159. static int mes_search_box;
  160. static int slot_d00;
  161. extern char *chmat ();
  162. extern void find_keyword (char *cur_word, int vista_num);
  163. extern void read_wordstr (struct or_hwordrec * glob_word, int vista_num);
  164. extern void write_wordstr (struct or_hwordrec * glob_word, int vista_num);
  165. static void stat_search (void); /* redefined below */
  166. /********************************/
  167. /* */
  168. /* Release Shared Memory */
  169. /* */
  170. /********************************/
  171. void release_shm_mem (void)
  172. {
  173. if (global_memory_ptr != NULL) {
  174. if (shmdt (global_memory_ptr) == -1) {
  175. DtSearchAddMessage (CATGETS(dtsearch_catd, MS_vestatis, 104,
  176. PROGNAME "104 Cannot detach shared memory "));
  177. OE_flags |= OE_PERMERR;
  178. usrblk.retncode = OE_ABORT;
  179. return;
  180. }
  181. if (shmctl (shm_id, IPC_RMID, NULL) == -1) {
  182. DtSearchAddMessage (CATGETS(dtsearch_catd, MS_vestatis, 110,
  183. PROGNAME "110 Cannot remove shared memory "));
  184. OE_flags |= OE_PERMERR;
  185. usrblk.retncode = OE_ABORT;
  186. return;
  187. }
  188. global_memory_ptr = NULL;
  189. }
  190. return;
  191. } /* release_shm_mem() */
  192. /********************************/
  193. /* */
  194. /* Init Global Memory */
  195. /* */
  196. /********************************/
  197. /* addrs - largest DBA slot in D00 file in the current database
  198. * r_addrs - total records count in the current database.
  199. */
  200. static int init_global_memory (DtSrINT32 addrs, DtSrINT32 r_addrs)
  201. {
  202. long i, j;
  203. size_t k;
  204. i = DtSrMAX_STEMCOUNT * ((addrs >> 3) + 1) * 2 +
  205. addrs * sizeof (int) + sizeof (DB_ADDR) * r_addrs;
  206. j = sizeof (STAT_STR) * addrs + sizeof (DB_ADDR) * r_addrs;
  207. k = (i > j) ? i : j;
  208. shm_id = shmget (IPC_PRIVATE, k, SHM_FLAG);
  209. if ((global_memory_ptr = (char *) shmat (shm_id, (char *) 0, 0)) ==
  210. ((char *) -1)) {
  211. DtSearchAddMessage (CATGETS(dtsearch_catd, MS_vestatis, 115,
  212. PROGNAME "115 No shared memory available"));
  213. OE_flags |= OE_PERMERR;
  214. usrblk.retncode = OE_ABORT;
  215. return FALSE;
  216. }
  217. return TRUE;
  218. } /* init_global_memory() */
  219. /****************************************/
  220. /* */
  221. /* efim_qsort */
  222. /* */
  223. /****************************************/
  224. /* Custom quick sort algorithm (medium-of-3 partitioning).
  225. * Coded for efficiency given our expected data characteristics,
  226. * and for interruptability.
  227. */
  228. int efim_qsort (void)
  229. {
  230. time_t start_time;
  231. double time_dif;
  232. static long left, right;
  233. static long scan_l, scan_r, mid3, pvidx, l_size, r_size;
  234. static long sptr;
  235. static float pivot, temp, stack_l[STACKSZ], stack_r[STACKSZ];
  236. static DB_ADDR dba;
  237. /* Test whether user has pushed STOP button since last call. */
  238. if (usrblk.flags & USR_STOPSRCH) {
  239. if (OE_flags & OE_AUDIT)
  240. oe_write_audit_rec (-1L);
  241. usrblk.retncode = OE_USER_STOP;
  242. release_shm_mem ();
  243. return TRUE;
  244. }
  245. if (begin_qsort) {
  246. sptr = 0;
  247. left = 0;
  248. right = num_hits - 1;
  249. begin_qsort = FALSE;
  250. }
  251. time (&start_time);
  252. for (;;) {
  253. /* check iteration loop */
  254. time_dif = difftime (time (NULL), start_time);
  255. if ((time_dif > TIME_ITERATION
  256. || usrblk.debug & USRDBG_ITERATE) &&
  257. !(usrblk.flags & USR_NO_ITERATE)) {
  258. usrblk.retncode = OE_SEARCHING;
  259. usrblk.workproc = stat_search;
  260. mes_search_box = TRUE;
  261. return TRUE;
  262. }
  263. while (right > left) {
  264. if ((right - left) > MED_3_VALUE) {
  265. /*
  266. * compute value for the median-of-three partitioning
  267. */
  268. mid3 = (left + right) >> 1;
  269. /*
  270. * three-sort left, middle, and right elements
  271. */
  272. if ((stat_array + left)->wght < (stat_array + mid3)->wght) {
  273. temp = (stat_array + left)->wght;
  274. (stat_array + left)->wght =
  275. (stat_array + mid3)->wght;
  276. (stat_array + mid3)->wght = temp;
  277. dba = (stat_array + left)->dba;
  278. (stat_array + left)->dba =
  279. (stat_array + mid3)->dba;
  280. (stat_array + mid3)->dba = dba;
  281. }
  282. if ((stat_array + left)->wght < (stat_array + right)->wght) {
  283. temp = (stat_array + left)->wght;
  284. (stat_array + left)->wght =
  285. (stat_array + right)->wght;
  286. (stat_array + right)->wght = temp;
  287. dba = (stat_array + left)->dba;
  288. (stat_array + left)->dba =
  289. (stat_array + right)->dba;
  290. (stat_array + right)->dba = dba;
  291. }
  292. if ((stat_array + mid3)->wght < (stat_array + right)->wght) {
  293. temp = (stat_array + mid3)->wght;
  294. (stat_array + mid3)->wght =
  295. (stat_array + right)->wght;
  296. (stat_array + right)->wght = temp;
  297. dba = (stat_array + mid3)->dba;
  298. (stat_array + mid3)->dba =
  299. (stat_array + right)->dba;
  300. (stat_array + right)->dba = dba;
  301. }
  302. /* select pivot element index */
  303. pvidx = right - 1;
  304. /* exchange pivot with the middle element */
  305. temp = (stat_array + mid3)->wght;
  306. (stat_array + mid3)->wght = (stat_array + pvidx)->wght;
  307. (stat_array + pvidx)->wght = temp;
  308. dba = (stat_array + mid3)->dba;
  309. (stat_array + mid3)->dba = (stat_array + pvidx)->dba;
  310. (stat_array + pvidx)->dba = dba;
  311. /* setup for partitioning */
  312. scan_l = left + 1;
  313. scan_r = right - 2;
  314. }
  315. else {
  316. /* select pivot element index */
  317. pvidx = right;
  318. /* set scanning indexes */
  319. scan_l = left;
  320. scan_r = right - 1;
  321. }
  322. /* select pivot element */
  323. pivot = (stat_array + pvidx)->wght;
  324. for (;;) {
  325. /* scan from left */
  326. while ((stat_array + scan_l)->wght > pivot) {
  327. scan_l++;
  328. }
  329. /* scan from right */
  330. while ((stat_array + scan_r)->wght < pivot) {
  331. if (scan_r == 0) {
  332. break;
  333. }
  334. scan_r--;
  335. }
  336. /* if scan have met, exit inner loop */
  337. if (scan_l >= scan_r) {
  338. break;
  339. }
  340. /* exchange elements */
  341. temp = (stat_array + scan_r)->wght;
  342. (stat_array + scan_r)->wght = (stat_array + scan_l)->wght;
  343. (stat_array + scan_l)->wght = temp;
  344. dba = (stat_array + scan_r)->dba;
  345. (stat_array + scan_r)->dba = (stat_array + scan_l)->dba;
  346. (stat_array + scan_l)->dba = dba;
  347. /* move scans to next elements */
  348. scan_l++;
  349. scan_r--;
  350. }
  351. if (scan_l != pvidx) {
  352. /* exchange finale element */
  353. temp = (stat_array + pvidx)->wght;
  354. (stat_array + pvidx)->wght = (stat_array + scan_l)->wght;
  355. (stat_array + scan_l)->wght = temp;
  356. dba = (stat_array + pvidx)->dba;
  357. (stat_array + pvidx)->dba = (stat_array + scan_l)->dba;
  358. (stat_array + scan_l)->dba = dba;
  359. }
  360. /* calculate section sizes */
  361. l_size = scan_l - left;
  362. r_size = right - scan_l;
  363. /* place largest section on stack */
  364. if (l_size > r_size) {
  365. /* ignore 1-element sections */
  366. if (l_size > 1) {
  367. sptr++;
  368. if (sptr == STACKSZ) {
  369. fputs (CATGETS(dtsearch_catd, MS_vestatis, 107,
  370. PROGNAME "107 Qsort stack overflow.\n"),
  371. aa_stderr);
  372. OE_flags |= OE_PERMERR;
  373. usrblk.retncode = OE_ABORT;
  374. release_shm_mem ();
  375. return FALSE;
  376. }
  377. *(stack_l + sptr) = left;
  378. *(stack_r + sptr) = scan_l - 1;
  379. }
  380. /* ignore 1-element sections */
  381. if (r_size != 0) {
  382. left = scan_l + 1;
  383. }
  384. else {
  385. break;
  386. }
  387. }
  388. else {
  389. /* ignore 1-element sections */
  390. if (r_size > 1) {
  391. sptr++;
  392. if (sptr == STACKSZ) {
  393. fputs (CATGETS(dtsearch_catd, MS_vestatis, 107,
  394. PROGNAME "107 Qsort stack overflow.\n"),
  395. aa_stderr);
  396. OE_flags |= OE_PERMERR;
  397. usrblk.retncode = OE_ABORT;
  398. release_shm_mem ();
  399. return FALSE;
  400. }
  401. *(stack_l + sptr) = scan_l + 1;
  402. *(stack_r + sptr) = right;
  403. }
  404. /* ignore 1-element sections */
  405. if (l_size != 0) {
  406. right = scan_l - 1;
  407. }
  408. else {
  409. break;
  410. }
  411. }
  412. }
  413. /* iterate with values from stack (if any) */
  414. if (sptr) {
  415. left = *(stack_l + sptr);
  416. right = *(stack_r + sptr);
  417. sptr--;
  418. }
  419. else {
  420. break;
  421. }
  422. }
  423. qsort_done = TRUE;
  424. return TRUE;
  425. } /* efim_qsort() */
  426. /****************************************/
  427. /* */
  428. /* fill_stem */
  429. /* */
  430. /****************************************/
  431. /* "Visit" subroutine of descend_tree(), which is itself subroutine
  432. * of traverse_tree(). Builds query_stems array
  433. * and establishes its size in num_diff_words.
  434. */
  435. static void fill_stem (TREENODE * cur_stem)
  436. {
  437. query_stems[num_diff_words].count = cur_stem->count;
  438. strcpy (query_stems[num_diff_words].stem, cur_stem->word);
  439. num_diff_words++;
  440. return;
  441. } /* fill_stem() */
  442. /****************************************/
  443. /* */
  444. /* descend_tree */
  445. /* */
  446. /****************************************/
  447. /* Subroutine of traverse_tree(), Robson tree traversal algorithm. */
  448. static void descend_tree (void)
  449. {
  450. int not_done = TRUE;
  451. while (not_done) {
  452. /* end of 'descent' subalgorithm? */
  453. if ((pres->llink == NULL) && (pres->rlink == NULL)) {
  454. /* Preorder, Symmetric Order and Postorder */
  455. fill_stem (pres);
  456. avail_node = pres;
  457. return;
  458. }
  459. if (pres->llink != NULL) {
  460. /* Preorder */
  461. fill_stem (pres);
  462. next = pres->llink;
  463. pres->llink = prev;
  464. prev = pres;
  465. pres = next;
  466. }
  467. else {
  468. /* Preorder and Symmetric Order */
  469. fill_stem (pres);
  470. next = pres->rlink;
  471. pres->rlink = prev;
  472. prev = pres;
  473. pres = next;
  474. }
  475. }
  476. return;
  477. } /* descend_tree() */
  478. /********************************/
  479. /* */
  480. /* traverse_tree */
  481. /* */
  482. /********************************/
  483. /* The algorithm is based on the J. M. ROBSON link inversion traversal
  484. * algorithm for binary trees. Ref. Thomas A. STANDISH pp. 77-78.
  485. */
  486. static void traverse_tree (void)
  487. {
  488. int not_done = TRUE;
  489. int descend = TRUE;
  490. /* initialize the variables */
  491. pres = root_node;
  492. prev = pres;
  493. top_of_stack = NULL;
  494. stack = NULL;
  495. while (not_done) {
  496. if (descend) {
  497. descend_tree ();
  498. }
  499. if (pres == root_node) {
  500. return;
  501. }
  502. if (prev->rlink == NULL) {
  503. /* Symmetric Order and Postorder */
  504. /*** fill_stem(prev); ***/
  505. next = prev->llink;
  506. prev->llink = pres;
  507. pres = prev;
  508. prev = next;
  509. descend = FALSE;
  510. }
  511. else {
  512. if (prev->llink == NULL) {
  513. /* Postorder */
  514. /** fill_stem(prev); **/
  515. next = prev->rlink;
  516. prev->rlink = pres;
  517. pres = prev;
  518. prev = next;
  519. descend = FALSE;
  520. }
  521. else {
  522. if (prev == top_of_stack) {
  523. /* Postorder */
  524. /** fill_stem(prev); **/
  525. next = stack;
  526. top_of_stack = stack->rlink;
  527. stack = stack->llink;
  528. next->llink = NULL;
  529. next->rlink = NULL;
  530. next = prev->llink;
  531. prev->llink = prev->rlink;
  532. prev->rlink = pres;
  533. pres = prev;
  534. prev = next;
  535. descend = FALSE;
  536. }
  537. else {
  538. /* Symmetric Order */
  539. /*** fill_stem(prev); ***/
  540. avail_node->llink = stack;
  541. avail_node->rlink = top_of_stack;
  542. stack = avail_node;
  543. top_of_stack = prev;
  544. next = prev->rlink;
  545. prev->rlink = pres;
  546. pres = next;
  547. descend = TRUE;
  548. }
  549. }
  550. }
  551. }
  552. } /* traverse_tree() */
  553. /********************************/
  554. /* */
  555. /* Get Next Memory Block */
  556. /* */
  557. /********************************/
  558. void get_next_memory_block (size_t node_size)
  559. {
  560. CNCRD_MEMORY_AREA_LIST *temp_ptr;
  561. temp_ptr = memory_blocks;
  562. /*
  563. * We run out of pre-allocated memory. Allocate additional block of
  564. * memory
  565. */
  566. if (cur_mem_ptr == NULL) {
  567. total_memory_size += node_size;
  568. mem_start = (char *) malloc (total_memory_size);
  569. mem_offset = 0L;
  570. mem_offset += node_size;
  571. cur_pos = mem_start;
  572. if (mem_start == NULL) {
  573. fprintf (aa_stderr, CATGETS(dtsearch_catd, MS_vestatis, 310,
  574. "%s Out of Memory. Need %ld bytes.\n"),
  575. PROGNAME "310", total_memory_size);
  576. OE_flags |= OE_PERMERR;
  577. usrblk.retncode = OE_ABORT;
  578. release_shm_mem ();
  579. return;
  580. }
  581. /*
  582. * allocate space for the next member of the memory blocks link
  583. * list
  584. */
  585. memory_blocks = (CNCRD_MEMORY_AREA_LIST *)
  586. malloc (sizeof (CNCRD_MEMORY_AREA_LIST) + 2);
  587. if (memory_blocks == NULL) {
  588. fputs (CATGETS(dtsearch_catd, MS_vestatis, 314,
  589. PROGNAME"314 Out of Memory.\n"), aa_stderr);
  590. OE_flags |= OE_PERMERR;
  591. usrblk.retncode = OE_ABORT;
  592. release_shm_mem ();
  593. return;
  594. }
  595. memory_blocks->start_of_mem_block = mem_start;
  596. memory_blocks->next_block = temp_ptr;
  597. memory_blocks->block_size = total_memory_size;
  598. /**** allocation of initial memory blocks is done ****/
  599. }
  600. /* Use next available block of memory */
  601. else {
  602. mem_start = cur_mem_ptr->start_of_mem_block;
  603. total_memory_size = cur_mem_ptr->block_size;
  604. cur_mem_ptr = cur_mem_ptr->next_block;
  605. mem_offset = 0L;
  606. mem_offset += node_size;
  607. cur_pos = mem_start;
  608. }
  609. return;
  610. } /* get_next_memory_block() */
  611. /********************************/
  612. /* */
  613. /* build_bin_tree */
  614. /* */
  615. /********************************/
  616. /* Subroutine of inv_index_bin_tree().
  617. * Called for each stem in query.
  618. * Inserts new stem (already uppercase) into tree
  619. * or increments existing stem's count.
  620. * Returns TRUE and incr num_diff_words if new stem inserted.
  621. * Returns FALSE if existing stem's count merely incremented.
  622. * Returns FALSE and OE_ABORT set on error.
  623. */
  624. static int build_bin_tree (char *cur_word)
  625. {
  626. int i;
  627. int wordlen;
  628. size_t treenode_size;
  629. TREENODE *new;
  630. TREENODE **this_link;
  631. wordlen = strlen (cur_word);
  632. /* Determine the amount of memory needed for the
  633. * new node. Add in a pad amount to align it
  634. * on the machine's word (integer) boundary.
  635. * Some machines aren't happy about misaligned
  636. * structures and we're emulating our own malloc.
  637. * (Thanks, and a tip o' the hat to Takuki Kamiya).
  638. */
  639. treenode_size = sizeof (TREENODE) + wordlen + 2;
  640. treenode_size +=
  641. (STRUCT_ALIGN - treenode_size % STRUCT_ALIGN) % STRUCT_ALIGN;
  642. /* allocate a new node and load its data fields */
  643. mem_offset += treenode_size;
  644. if (mem_offset > total_memory_size) {
  645. /* allocate new chunk of memory */
  646. get_next_memory_block (treenode_size);
  647. if (usrblk.retncode == OE_ABORT)
  648. return FALSE;
  649. }
  650. new = (TREENODE *) cur_pos;
  651. cur_pos = mem_start + mem_offset;
  652. new->llink = NULL;
  653. new->rlink = NULL;
  654. new->word = (char *) new + sizeof (TREENODE);
  655. new->count = 1;
  656. strcpy (new->word, cur_word);
  657. /* Insert current word into binary tree */
  658. for (this_link = &root_node; *this_link != NULL;) {
  659. i = strcmp (new->word, (*this_link)->word);
  660. /* Test for current word already in the binary tree */
  661. if (i == 0) {
  662. mem_offset -= treenode_size;
  663. cur_pos = mem_start + mem_offset;
  664. (*this_link)->count++;
  665. return FALSE; /* no point in continuing descent */
  666. }
  667. /* Descend tree to find correct insertion point */
  668. this_link = (i < 0) ?
  669. &(*this_link)->llink : &(*this_link)->rlink;
  670. } /* end for loop to find tree insertion
  671. * point */
  672. /* Insert new node at current location in tree */
  673. *this_link = new;
  674. num_diff_words++;
  675. return TRUE;
  676. } /* build_bin_tree() */
  677. /************************/
  678. /* */
  679. /* init_memory */
  680. /* */
  681. /************************/
  682. /* Initialize the first block of memory for the binary tree.
  683. * This function is called only once at each run of the offline program.
  684. */
  685. void init_memory (void)
  686. {
  687. mem_start = (char *) malloc (MEMORY_SIZE);
  688. if (mem_start == NULL) {
  689. fprintf (aa_stderr, CATGETS(dtsearch_catd, MS_vestatis, 310,
  690. "%s Out of Memory. Need %ld bytes.\n"), PROGNAME "310", MEMORY_SIZE);
  691. OE_flags |= OE_PERMERR;
  692. usrblk.retncode = OE_ABORT;
  693. release_shm_mem ();
  694. return;
  695. }
  696. total_memory_size = MEMORY_SIZE;
  697. cur_pos = mem_start;
  698. mem_offset = 0L;
  699. /*
  700. * Allocate space for the first member of the memory blocks link list
  701. */
  702. memory_blocks = (CNCRD_MEMORY_AREA_LIST *)
  703. malloc (sizeof (CNCRD_MEMORY_AREA_LIST) + 2);
  704. if (memory_blocks == NULL) {
  705. fputs (CATGETS(dtsearch_catd, MS_vestatis, 314,
  706. PROGNAME "314 Out of Memory.\n"), aa_stderr);
  707. OE_flags |= OE_PERMERR;
  708. usrblk.retncode = OE_ABORT;
  709. release_shm_mem ();
  710. return;
  711. }
  712. memory_blocks->start_of_mem_block = mem_start;
  713. memory_blocks->block_size = total_memory_size;
  714. memory_blocks->next_block = NULL;
  715. cur_mem_ptr = NULL;
  716. return;
  717. } /* init_memory() */
  718. /********************************/
  719. /* */
  720. /* inv_index_bin_tree */
  721. /* */
  722. /********************************/
  723. /* Builds binary tree of all stems in query.
  724. * Returns TRUE and loads num_diff_words with number
  725. * of nodes in tree if tree successfully built,
  726. * or if query is empty.
  727. * Returns FALSE on any error (causing eventual engine abort).
  728. */
  729. static int inv_index_bin_tree (void)
  730. {
  731. char *cptr;
  732. DBLK *dblk = usrblk.dblk;
  733. PARG parg;
  734. /* First time initialize the first block of memory */
  735. if (memory_blocks == NULL) {
  736. /** INITIALIZE MEMORY **/
  737. init_memory ();
  738. if (usrblk.retncode == OE_ABORT)
  739. return FALSE;
  740. root_node = NULL;
  741. }
  742. /* WORD LOOP. Parse and stem each word in query.
  743. * Add each stem to bin tree or incr its count.
  744. */
  745. memset (&parg, 0, sizeof(PARG));
  746. parg.dblk = dblk;
  747. parg.string = usrblk.query;
  748. for ( cptr = dblk->parser (&parg);
  749. cptr;
  750. cptr = dblk->parser (NULL)) {
  751. build_bin_tree (dblk->stemmer (cptr, dblk));
  752. if (usrblk.retncode == OE_ABORT)
  753. return FALSE;
  754. }
  755. return TRUE;
  756. } /* inv_index_bin_tree() */
  757. /************************/
  758. /* */
  759. /* comp_stat */
  760. /* */
  761. /************************/
  762. int comp_stat (void *val1, void *val2)
  763. {
  764. STAT_STR *bkt1;
  765. STAT_STR *bkt2;
  766. bkt1 = (STAT_STR *) val1;
  767. bkt2 = (STAT_STR *) val2;
  768. if ((bkt2->wght) > (bkt1->wght)) {
  769. return 1;
  770. }
  771. else {
  772. return -1;
  773. }
  774. } /* comp_stat() */
  775. /************************************************/
  776. /* */
  777. /* load_ditto_str */
  778. /* */
  779. /************************************************/
  780. /* Last function called from statistical search.
  781. * Builds a real AusText hitlist from the sorted stat_array,
  782. * translating the statistical weights to AusText 'proximity'
  783. * values, and truncating the hitlist at user's maxhits.
  784. * Working variables made static for speeeeeeeed.
  785. */
  786. void load_ditto_str (void)
  787. {
  788. struct or_objrec cur_rec; /* structure taken from austext.h */
  789. struct or_miscrec rec_data;
  790. static time_t start_time;
  791. static double time_dif;
  792. static DB_ADDR dba1;
  793. static DtSrResult *cur_ditto_mem;
  794. static DtSrResult *ditto_llist;
  795. static DtSrResult *temp_ditto;
  796. static int debugging;
  797. static int m;
  798. static DtSrINT32 d0024;
  799. static DtSrINT32 maxhits;
  800. static DtSrINT32 i32, i32_start, j32;
  801. static int fzkeysz, fzkey_remaining, abstrsz, dittosz;
  802. static char *src, *targ, *targend;
  803. static int check_dates = FALSE;
  804. static double sum = 0.0;
  805. static double sum1, sum2, sum3, sum4;
  806. debugging = (usrblk.debug & USRDBG_SRCHCMPL);
  807. maxhits = usrblk.dblk->maxhits;
  808. fzkeysz = usrblk.dblk->dbrec.or_fzkeysz;
  809. abstrsz = usrblk.dblk->dbrec.or_abstrsz;
  810. dittosz = sizeof (DtSrResult) + abstrsz + 16;
  811. if (debugging)
  812. fprintf (aa_stderr, PROGNAME "773 "
  813. "numhits=%ld maxhits=%d numwords=%d abstrsz=%d\n",
  814. (long)num_hits, (int)maxhits, num_diff_words, abstrsz);
  815. if (begin_load_ditto) {
  816. /* test for zero hits */
  817. if (num_hits == 0) {
  818. usrblk.workproc = dummy_workproc;
  819. usrblk.retncode = OE_NOTAVAIL;
  820. if (OE_flags & OE_AUDIT)
  821. oe_write_audit_rec (0L);
  822. release_shm_mem ();
  823. return;
  824. }
  825. check_dates = (usrblk.objdate1 || usrblk.objdate2);
  826. /* In order to translate statistical weight into an AusText
  827. * proximity, basically you have to invert it, then scale it.
  828. * The statistical weight is a similarity measure: the
  829. * larger it is the more similar the document to the query.
  830. * But AusText 'proximity' is like a 'distance' measure,
  831. * the smaller the number the closer the document is to the query.
  832. *
  833. * First 'normalize' each document's statistical
  834. * weight to be a fraction between 0 and 1. Do this
  835. * by calculating a normalization factor (sum1), the
  836. * sqrt of the sum of squares of first NORM_VALUE weights.
  837. * (Trying to make the inversion scheme produce
  838. * reasonable proximity numbers for these first records).
  839. *
  840. * To complete proximity initialization, he uses
  841. * the sum1 factor to determine and keep the first record's
  842. * normalized weight (sum), presumably a fraction close
  843. * to 1.0, and the first record's proximity (sum2),
  844. * basically the percent
  845. * value that the first doc is 'distant' from perfection (1.0 or 100%).
  846. * For example, if the normalized weight of the first record is .931
  847. * then the proximity will be 7 (100% - 93% = 7%). He does this now
  848. * because he's going to use this first proximity (sum2) as a scaling
  849. * factor to stretch out all the subsequent proximities so they
  850. * look reasonable.
  851. */
  852. sum = 0.0;
  853. for (i32 = 0; i32 < num_hits; i32++) {
  854. sum1 = (double) (stat_array + i32)->wght /
  855. (double) num_diff_words;
  856. sum += sum1 * sum1;
  857. if (i32 >= NORM_VALUE)
  858. break;
  859. }
  860. /*
  861. * sum1 = normalization factor.
  862. * sum = normalized weight (betw 0 and 1) of first record.
  863. * sum2 = proximity of first record, proximity scale factor.
  864. */
  865. sum1 = sqrt (sum);
  866. sum = ((stat_array + 0)->wght / num_diff_words) / sum1;
  867. sum2 = (1.0 - sum) * 100.0;
  868. if (debugging)
  869. fprintf (aa_stderr, PROGNAME "844 "
  870. "normfac=%.2lf normwt(#1)=%.2lf prox(#1)=%.2lf\n",
  871. sum1, sum, sum2);
  872. /* Preallocate first hit on ditto_list */
  873. ditto_llist = (DtSrResult *) austext_malloc (dittosz,
  874. PROGNAME "449", NULL);
  875. j32 = 0;
  876. i32_start = 0;
  877. d0024 = OR_D00 << 24;
  878. begin_load_ditto = FALSE;
  879. } /* endif (begin_load_ditto) */
  880. /* Test whether user has pushed STOP button since last call */
  881. if (usrblk.flags & USR_STOPSRCH) {
  882. if (OE_flags & OE_AUDIT)
  883. oe_write_audit_rec (-1L);
  884. usrblk.retncode = OE_USER_STOP;
  885. release_shm_mem ();
  886. if (j32 == 0)
  887. free (ditto_llist);
  888. else
  889. free_llist ((LLIST **) &ditto_llist);
  890. return;
  891. }
  892. time (&start_time);
  893. /**** MAIN DtSrResult LIST BUILD LOOP ****/
  894. for (i32 = i32_start; i32 < num_hits; i32++) {
  895. /* check iteration loop */
  896. time_dif = difftime (time (NULL), start_time);
  897. if ((time_dif > TIME_ITERATION
  898. || usrblk.debug & USRDBG_ITERATE) &&
  899. !(usrblk.flags & USR_NO_ITERATE)) {
  900. i32_start = i32;
  901. usrblk.retncode = OE_SEARCHING;
  902. usrblk.workproc = load_ditto_str;
  903. mes_search_box = TRUE;
  904. return;
  905. }
  906. dba1 = ((stat_array + i32)->dba * slot_d00 - dba_offset)
  907. | d0024;
  908. /*
  909. * Don't use CRSET or RECREAD macros here so we can trap invalid
  910. * dba errs.
  911. */
  912. d_crset (&dba1, saveusr.vistano);
  913. if (db_status < 0) {
  914. fprintf (aa_stderr, CATGETS(dtsearch_catd, MS_vestatis, 437,
  915. "%s: db_status = %d, dba = %d:%ld (x'%08.8lx'), vistano = %d\n"),
  916. PROGNAME "437", db_status, (dba1 & 0xff000000) >> 24,
  917. dba1 & 0xffffff, dba1, saveusr.vistano);
  918. OE_flags |= OE_PERMERR;
  919. usrblk.retncode = OE_ABORT;
  920. release_shm_mem ();
  921. return;
  922. }
  923. d_recread (&cur_rec, saveusr.vistano);
  924. if (db_status < 0) {
  925. fprintf (aa_stderr, CATGETS(dtsearch_catd, MS_vestatis, 437,
  926. "%s: db_status = %d, dba = %d:%ld (x'%08.8lx'), vistano = %d\n"),
  927. PROGNAME "437", db_status, (dba1 & 0xff000000) >> 24,
  928. dba1 & 0xffffff, dba1, saveusr.vistano);
  929. OE_flags |= OE_PERMERR;
  930. usrblk.retncode = OE_ABORT;
  931. release_shm_mem ();
  932. return;
  933. }
  934. swab_objrec (&cur_rec, NTOH);
  935. /* Skip any record with undesired keytype
  936. * char, ie first char of key.
  937. */
  938. if (*(rec_type_tab + cur_rec.or_objkey[0]) == 0)
  939. continue;
  940. /* Skip record if out of date range. */
  941. if (check_dates)
  942. if (!objdate_in_range (cur_rec.or_objdate,
  943. usrblk.objdate1, usrblk.objdate2))
  944. continue;
  945. if (j32 == 0) /* first ditto node already allocated */
  946. cur_ditto_mem = ditto_llist;
  947. else {
  948. cur_ditto_mem = malloc (dittosz);
  949. if (cur_ditto_mem == NULL) {
  950. fputs ( CATGETS(dtsearch_catd, MS_vestatis, 504,
  951. PROGNAME "504 Cannot allocate cur_ditto\n"),
  952. aa_stderr);
  953. OE_flags |= OE_PERMERR;
  954. usrblk.retncode = OE_ABORT;
  955. release_shm_mem ();
  956. return;
  957. }
  958. temp_ditto->link = cur_ditto_mem;
  959. }
  960. /* Load the ditto_list for this dba */
  961. memset (cur_ditto_mem, 0, sizeof(DtSrResult));
  962. cur_ditto_mem->dbn = OE_dbn;
  963. cur_ditto_mem->dba = dba1;
  964. strcpy (cur_ditto_mem->reckey, cur_rec.or_objkey);
  965. cur_ditto_mem->objsize = cur_rec.or_objsize;
  966. cur_ditto_mem->objdate = cur_rec.or_objdate;
  967. cur_ditto_mem->objflags = cur_rec.or_objflags;
  968. cur_ditto_mem->objuflags = cur_rec.or_objuflags;
  969. cur_ditto_mem->objtype = cur_rec.or_objtype;
  970. cur_ditto_mem->objcost = cur_rec.or_objcost;
  971. /*****cur_ditto_mem->flags = 0;****/
  972. cur_ditto_mem->abstractp = (char *) cur_ditto_mem +
  973. sizeof (DtSrResult);
  974. cur_ditto_mem->abstractp[0] = 0;
  975. /* Translate statistical weight into AusText proximity.
  976. * sum3 = normalized weight (betw 0 and 1).
  977. * sum4 = prox = ratio of this normalized weight to
  978. * first rec's weight, scaled by the first rec's proximity.
  979. * No proximity is allowed to exceed some very large number.
  980. */
  981. sum3 = ((stat_array + i32)->wght / num_diff_words) / sum1;
  982. sum4 = sum2 * (sum / sum3);
  983. if (sum4 > INFINITY)
  984. sum4 = INFINITY;
  985. cur_ditto_mem->proximity = sum4;
  986. if (debugging)
  987. fprintf (aa_stderr,
  988. " --> dba=%ld normwt=%.4lf prox=%d key='%s'\n",
  989. dba1, sum3, cur_ditto_mem->proximity,
  990. cur_ditto_mem->reckey);
  991. /*
  992. * The abstract immediately follows the fuzzy key in the FZKABS
  993. * misc recs. It may span several recs.
  994. */
  995. if (abstrsz > 0) {
  996. targ = cur_ditto_mem->abstractp;
  997. targend = targ + abstrsz - 1;
  998. fzkey_remaining = fzkeysz;
  999. SETOR (PROGNAME "2270", OR_OBJ_MISCS, saveusr.vistano);
  1000. FINDFM (PROGNAME "2271", OR_OBJ_MISCS, saveusr.vistano);
  1001. while (db_status == S_OKAY) {
  1002. RECREAD (PROGNAME "549", &rec_data, saveusr.vistano);
  1003. NTOHS (rec_data.or_misctype);
  1004. if (rec_data.or_misctype == ORM_FZKABS) {
  1005. src = (char *) rec_data.or_misc;
  1006. for (m = 0; m < sizeof(rec_data.or_misc); m++) {
  1007. if (fzkey_remaining > 0) {
  1008. src++;
  1009. fzkey_remaining--;
  1010. continue; /* inner for-loop on m */
  1011. }
  1012. *targ = *src;
  1013. if (*src++ == 0 || targ++ >= targend) {
  1014. *targ = 0;
  1015. targ = targend; /* make outer loop end */
  1016. break;
  1017. }
  1018. } /* end for-loop for curr misc rec */
  1019. } /* endif: misctype == FZKABS */
  1020. if (targ >= targend)
  1021. break;
  1022. FINDNM (PROGNAME "545", OR_OBJ_MISCS, saveusr.vistano);
  1023. } /* end while-loop */
  1024. } /* endif: (abstrsz > 0) */
  1025. cur_ditto_mem->link = NULL;
  1026. temp_ditto = cur_ditto_mem;
  1027. /* Increment to next hit.
  1028. * Break loop when we reach user's specified maxhits.
  1029. */
  1030. j32++; /* [j32 same as i] !? */
  1031. if (j32 >= maxhits)
  1032. break;
  1033. } /* i32-loop on each hit in ditto list */
  1034. if (j32 == 0) {
  1035. usrblk.workproc = dummy_workproc;
  1036. usrblk.retncode = OE_NOTAVAIL;
  1037. if (OE_flags & OE_AUDIT)
  1038. oe_write_audit_rec (0L);
  1039. release_shm_mem ();
  1040. return;
  1041. }
  1042. if (num_hits >= maxhits) {
  1043. if (!(usrblk.flags & USR_NO_INFOMSGS)) {
  1044. sprintf (vestat_msgbuf, CATGETS(dtsearch_catd, MS_vestatis, 421,
  1045. "$s Total Number Hits = %ld. Discarded hits beyond maximum number specified."),
  1046. PROGNAME "421", (long)num_hits);
  1047. DtSearchAddMessage (vestat_msgbuf);
  1048. }
  1049. }
  1050. free_llist ((LLIST **) &usrblk.dittolist);
  1051. usrblk.dittolist = ditto_llist;
  1052. usrblk.dittocount = j32;
  1053. usrblk.workproc = dummy_workproc;
  1054. usrblk.retncode = OE_OK;
  1055. if (OE_flags & OE_AUDIT)
  1056. oe_write_audit_rec ((long) num_hits);
  1057. /***** Free shared memory *****/
  1058. release_shm_mem ();
  1059. return;
  1060. } /* load_ditto_str() */
  1061. /****************************************/
  1062. /* */
  1063. /* stat_search */
  1064. /* */
  1065. /****************************************/
  1066. /* Subroutine of ve_statistical() and interruptable workproc.
  1067. */
  1068. static void stat_search (void)
  1069. {
  1070. time_t start_time;
  1071. double time_dif;
  1072. DB_ADDR temp, temp1;
  1073. struct or_hwordrec word1; /* structure taken from austext.h */
  1074. double idf, cur_weight;
  1075. int qs;
  1076. DtSrINT32 int32, j32;
  1077. /*****@@@ size_t size;****/
  1078. static int qs_start;
  1079. /* Test whether user has pushed STOP button since last call */
  1080. if (usrblk.flags & USR_STOPSRCH) {
  1081. if (OE_flags & OE_AUDIT)
  1082. oe_write_audit_rec (-1L);
  1083. usrblk.retncode = OE_USER_STOP;
  1084. release_shm_mem ();
  1085. return;
  1086. }
  1087. if (begin_sort) {
  1088. begin_qsort = TRUE;
  1089. qsort_done = FALSE;
  1090. if (begin_search) {
  1091. qs_start = 0;
  1092. begin_search = FALSE;
  1093. }
  1094. time (&start_time);
  1095. /*
  1096. * For every query stem, read d99. For every dba in d99 for each
  1097. * stem, update object's stat array node with rec count and a
  1098. * weight based on the IDF for this stem. (IDF is described
  1099. * below). Saveusr.stemcount = lesser of DtSrMAX_STEMCOUNT or
  1100. * num_diff_words. All stems are stored in d99 beginning with ^O
  1101. * (decimal 15). Index qs = curr query stem
  1102. */
  1103. for (qs = qs_start; qs < saveusr.stemcount; qs++) {
  1104. word1.or_hwordkey[0] = 15;
  1105. word1.or_hwordkey[1] = '\0';
  1106. strcat (word1.or_hwordkey, query_stems[qs].stem);
  1107. find_keyword (word1.or_hwordkey, saveusr.vistano);
  1108. /*
  1109. * If word is not in the database, ignore it. [ If word
  1110. * not in database, why not take the next stem in query_stems
  1111. * array, if any? ]
  1112. */
  1113. if (db_status != S_OKAY)
  1114. word1.or_hwaddrs = 0;
  1115. else
  1116. read_wordstr (&word1, saveusr.vistano);
  1117. if (word1.or_hwaddrs > 0) {
  1118. fseek (usrblk.dblk->iifile, word1.or_hwoffset,
  1119. SEEK_SET);
  1120. /****@@@size = sizeof (DB_ADDR) * word1.or_hwaddrs;***/
  1121. fread (word_addrs, sizeof(DB_ADDR),
  1122. (size_t)word1.or_hwaddrs, usrblk.dblk->iifile);
  1123. /*
  1124. * Calculate IDF (inverse document frequency) for this
  1125. * word. The IDF is a statistical ratio of the number
  1126. * of documents containing the word and the total
  1127. * number of documents in the entire corpus.
  1128. * It is calculated here on the fly to save space in the
  1129. * database. IDF = {log (totnumdocs / numdocswithword) /
  1130. * log(2)} + 1. Note that an IDF of 1 means the word
  1131. * occurs in every doc (it's meaningless). An IDF of 19
  1132. * means the word occurs once in every 300,000 recs.
  1133. * Note that by dividing by log(2) the IDF also tells
  1134. * us how many binary digits are necessary to discriminate
  1135. * the word. Finally I think 1.0 was added to prevent
  1136. * it ever becoming zero when converted to integer.
  1137. */
  1138. idf = (log ((double) real_num_rec / (double) word1.or_hwaddrs)
  1139. / LOG2) + 1.0;
  1140. /*
  1141. * WEIGHT PASS #1:
  1142. * Update the stat array node for each doc (ie dba) which
  1143. * includes this stem. Specifically,
  1144. * sum the product of the IDF and word-doc weight into
  1145. * the 'wght' bucket, and update the number of query
  1146. * words this doc contains. Note that the d99 dba format
  1147. * is slot# in hi 3 bytes, word-doc weights in lo byte.
  1148. */
  1149. for (j32 = 0; j32 < word1.or_hwaddrs; j32++) {
  1150. NTOHL (word_addrs [j32]);
  1151. temp1 = *(word_addrs + j32); /* d99 dba */
  1152. cur_weight = (double) (temp1 & 0xFF); /* lo byte */
  1153. temp = temp1 >> 8; /* slot# */
  1154. ((stat_array + temp)->num_word_hits)++;
  1155. ((stat_array + temp)->dba) = temp;
  1156. ((stat_array + temp)->wght) += (float) (cur_weight * idf);
  1157. }
  1158. } /* end if (word1.or_hwaddrs > 0), ie
  1159. * query word exists */
  1160. /*
  1161. * If the query words were common, the last double loop may
  1162. * have taken a long time. If so, return now to the user
  1163. * interface to allow the gui to respond to button clicks
  1164. * (like CANCEL buttons).
  1165. */
  1166. time_dif = difftime (time (NULL), start_time);
  1167. if ((time_dif > TIME_ITERATION
  1168. || usrblk.debug & USRDBG_ITERATE) &&
  1169. !(usrblk.flags & USR_NO_ITERATE)) {
  1170. if (qs == saveusr.stemcount - 1) {
  1171. usrblk.retncode = OE_SEARCHING;
  1172. usrblk.workproc = stat_search;
  1173. mes_search_box = TRUE;
  1174. return;
  1175. }
  1176. else {
  1177. qs_start = qs + 1;
  1178. usrblk.retncode = OE_SEARCHING;
  1179. usrblk.workproc = stat_search;
  1180. mes_search_box = TRUE;
  1181. return;
  1182. }
  1183. } /* end if (time_dif > TIME_ITERATION */
  1184. } /* end qs-loop on each query stem */
  1185. /*
  1186. * Entire stat array contains one node for every possible dba
  1187. * (doc). Collapse the records that were actually referenced by
  1188. * the query words into the top portion of the array.
  1189. * Set 'num_hits' to the collapsed stat array size, ie
  1190. * num_hits = the total number of docs that will be on
  1191. * the prelim hitlist, prior to sort and truncation to user's maxhits.
  1192. *
  1193. * WEIGHT PASS #2:
  1194. * While we're at it, finalize the accumulated 'wght' field, which
  1195. * will be our sort field, by multiplying it by the ratio of the
  1196. * number of query words in the document divided by the number of
  1197. * words in the query.
  1198. * Thus the final sort field for each doc is the sum
  1199. * over all the query words in the doc of 3 factors:
  1200. * 1) IDF (relative weight of each query word in corpus), times
  1201. * 2) d99wght (relative weight of each query word in doc), times
  1202. * 3) weight based on number of different query words in this doc.
  1203. */
  1204. num_hits = 0;
  1205. for (int32 = 0; int32 < total_num_addrs; int32++) {
  1206. if (stat_array[int32].wght > 0) {
  1207. (stat_array + num_hits)->num_word_hits =
  1208. (stat_array + int32)->num_word_hits;
  1209. (stat_array + num_hits)->wght = (stat_array + int32)->wght *
  1210. ((double) (stat_array + int32)->num_word_hits /
  1211. (double) num_diff_words);
  1212. (stat_array + num_hits)->dba = (stat_array + int32)->dba;
  1213. num_hits++;
  1214. }
  1215. }
  1216. /*
  1217. * We're about to sort the actual hits. If the number of them
  1218. * exceeds a certain threshold, return to the user interface one
  1219. * more time to again allow the gui to respond to user CANCEL
  1220. * events.
  1221. */
  1222. if (num_hits > SORT_MESG && !(usrblk.flags & USR_NO_ITERATE)) {
  1223. if (!mes_search_box) {
  1224. DtSearchAddMessage (CATGETS(dtsearch_catd, MS_vestatis, 990,
  1225. PROGNAME"990 The system is now sorting. Please wait."));
  1226. }
  1227. usrblk.retncode = OE_SEARCHING;
  1228. usrblk.workproc = stat_search;
  1229. mes_search_box = TRUE;
  1230. begin_sort = FALSE;
  1231. return;
  1232. }
  1233. } /* end if (begin_sort) */
  1234. /* Sort the preliminary hitlist (stat_array)
  1235. * by the calculated statistical weights.
  1236. */
  1237. if (!efim_qsort ())
  1238. return;
  1239. /* Build a real AusText hitlist from the sorted stat_array,
  1240. * translating the statistical weights to AusText 'proximity'
  1241. * values, and truncating the hitlist at user's maxhits.
  1242. */
  1243. if (qsort_done) {
  1244. begin_load_ditto = TRUE;
  1245. load_ditto_str ();
  1246. }
  1247. return;
  1248. } /* stat_search() */
  1249. /****************************************/
  1250. /* */
  1251. /* ve_statistical */
  1252. /* */
  1253. /****************************************/
  1254. void ve_statistical (void)
  1255. {
  1256. void stat_search (void);
  1257. DB_ADDR dba;
  1258. int i, j;
  1259. DtSrINT32 int32;
  1260. mes_search_box = FALSE;
  1261. usrblk.flags &= ~USR_STOPSRCH; /* turn off stop button */
  1262. usrblk.retncode = OE_OK;
  1263. usrblk = usrblk;
  1264. saveusr.vistano = usrblk.dblk->vistano;
  1265. saveusr.dittolist = NULL;
  1266. saveusr.dittocount = 0L;
  1267. saveusr.iterations = 1;
  1268. /****** find total number of records in the database *********/
  1269. RECFRST (PROGNAME "1067", OR_OBJREC, saveusr.vistano);
  1270. CRGET (PROGNAME "1068", &dba, saveusr.vistano);
  1271. real_num_rec = usrblk.dblk->dbrec.or_reccount;
  1272. slot_d00 = usrblk.dblk->dbrec.or_recslots;
  1273. dba_offset = slot_d00 - (dba & 0x00FFFFFF);
  1274. total_num_addrs = (usrblk.dblk->dbrec.or_maxdba -
  1275. (dba & 0x00FFFFFF) + 1) / slot_d00 + 1;
  1276. /* stat_array size = 1 node for every possible object */
  1277. if (usrblk.query[0] == 0) {
  1278. DtSearchAddMessage (CATGETS(dtsearch_catd, MS_vestatis,
  1279. 677, PROGNAME "677 Query field is empty."));
  1280. usrblk.retncode = OE_BAD_QUERY;
  1281. return;
  1282. }
  1283. /*
  1284. * Build binary tree of each stem in query containing count of number
  1285. * of occurrences of stem in query. Loads num_diff_words with number
  1286. * of nodes in tree.
  1287. */
  1288. num_diff_words = 0;
  1289. inv_index_bin_tree();
  1290. if (usrblk.retncode == OE_ABORT)
  1291. return;
  1292. if (num_diff_words < 1) {
  1293. usrblk.retncode = OE_NOTAVAIL;
  1294. return;
  1295. }
  1296. /***** allocate memory for query_stems array *********/
  1297. if (query_stems != NULL) {
  1298. free (query_stems);
  1299. query_stems = NULL;
  1300. }
  1301. query_stems = (QUERY_STEM_STR *) austext_malloc
  1302. (sizeof (QUERY_STEM_STR) * (num_diff_words + 1),
  1303. PROGNAME " 371", NULL);
  1304. /*
  1305. * Traverse tree to build query_stems array, each array node = tree
  1306. * node, ie each unique stem in query and its count in query.
  1307. * Num_diff_words now used as index for growing array.
  1308. */
  1309. num_diff_words = 0;
  1310. traverse_tree ();
  1311. /*
  1312. * For each new query initialize memory offset, current memory start
  1313. * position, and total size for the available memory. Starts from the
  1314. * first member in the link list of memory blocks.
  1315. */
  1316. root_node = NULL;
  1317. mem_start = memory_blocks->start_of_mem_block;
  1318. total_memory_size = memory_blocks->block_size;
  1319. cur_mem_ptr = memory_blocks->next_block;
  1320. cur_pos = mem_start;
  1321. mem_offset = 0L;
  1322. /*
  1323. * Copy first DtSrMAX_STEMCOUNT stems into the saveusr.stems. [So no more
  1324. * than DtSrMAX_STEMCOUNT will be used in search or hiliting!]
  1325. */
  1326. for (i = 0; i < num_diff_words; i++) {
  1327. if (i == DtSrMAX_STEMCOUNT)
  1328. break;
  1329. strcpy (usrblk.stems[i], query_stems[i].stem);
  1330. }
  1331. usrblk.stemcount = i;
  1332. saveusr.stemcount = i;
  1333. /* Prepare a string holding first char of desired record ids */
  1334. for (i = 0; i < REC_TYPES; i++)
  1335. *(rec_type_tab + i) = 0;
  1336. for (i = 0, j = 0; i < usrblk.dblk->ktcount; i++)
  1337. if (usrblk.dblk->keytypes[i].is_selected)
  1338. *(rec_type_tab + usrblk.dblk->keytypes[i].ktchar) = 1;
  1339. saveusr.ktchars[j] = '\0';
  1340. /*
  1341. * New code using shared memory:
  1342. * Allocate global block of shared memory,
  1343. * and assign parts of this memory to each array.
  1344. * Stat array has an element for every possible db object.
  1345. * Set whole stat array to binary zeroes.
  1346. */
  1347. if (!init_global_memory (total_num_addrs, real_num_rec))
  1348. return;
  1349. stat_array = (STAT_STR *) global_memory_ptr;
  1350. word_addrs = (DB_ADDR *) (global_memory_ptr +
  1351. total_num_addrs * sizeof (STAT_STR));
  1352. for (int32 = 0; int32 < total_num_addrs; int32++) {
  1353. (stat_array + int32)->wght = 0.0;
  1354. (stat_array + int32)->num_word_hits = 0;
  1355. }
  1356. /***** end of memory allocation for statistical array *********/
  1357. /* stat_search(): Search d99 and sum the statistical weights.
  1358. * Calls efim_qsort() to sort the hitlist by the weights.
  1359. */
  1360. begin_search = TRUE; /* global initialization and state flags */
  1361. begin_sort = TRUE;
  1362. stat_search ();
  1363. return;
  1364. } /* ve_statistical() */
  1365. /*************************** VESTATIS.C ****************************/