[110] | 1 | /**********************************************************************
|
---|
| 2 | *
|
---|
| 3 | * queryinfo.cpp --
|
---|
| 4 | * Copyright (C) 1999 The New Zealand Digital Library Project
|
---|
| 5 | *
|
---|
[534] | 6 | * A component of the Greenstone digital library software
|
---|
| 7 | * from the New Zealand Digital Library Project at the
|
---|
| 8 | * University of Waikato, New Zealand.
|
---|
[110] | 9 | *
|
---|
[534] | 10 | * This program is free software; you can redistribute it and/or modify
|
---|
| 11 | * it under the terms of the GNU General Public License as published by
|
---|
| 12 | * the Free Software Foundation; either version 2 of the License, or
|
---|
| 13 | * (at your option) any later version.
|
---|
| 14 | *
|
---|
| 15 | * This program is distributed in the hope that it will be useful,
|
---|
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 18 | * GNU General Public License for more details.
|
---|
| 19 | *
|
---|
| 20 | * You should have received a copy of the GNU General Public License
|
---|
| 21 | * along with this program; if not, write to the Free Software
|
---|
| 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
| 23 | *
|
---|
[110] | 24 | *********************************************************************/
|
---|
| 25 |
|
---|
| 26 | #include "queryinfo.h"
|
---|
| 27 |
|
---|
| 28 |
|
---|
| 29 | // query parameters
|
---|
| 30 |
|
---|
[351] | 31 | queryparamclass::queryparamclass () {
|
---|
| 32 | clear ();
|
---|
| 33 | }
|
---|
| 34 |
|
---|
| 35 | void queryparamclass::clear () {
|
---|
| 36 | combinequery.clear();
|
---|
| 37 | collection.clear();
|
---|
| 38 | index.clear();
|
---|
| 39 | subcollection.clear();
|
---|
| 40 | language.clear();
|
---|
[1319] | 41 | level.clear();
|
---|
[351] | 42 | querystring.clear();
|
---|
| 43 | search_type = 0; // 0 = boolean, 1 = ranked
|
---|
[502] | 44 | match_mode = 0; // 0 = some, 1 = all
|
---|
[351] | 45 | casefolding = 0;
|
---|
| 46 | stemming = 0;
|
---|
[12868] | 47 | accentfolding = 0;
|
---|
[351] | 48 | maxdocs = -1; // all
|
---|
[4193] | 49 | maxnumeric = 4; // must default to the same value as mg_passes
|
---|
[12410] | 50 | filterstring.clear();
|
---|
[12276] | 51 | sortfield.clear();
|
---|
[12770] | 52 | fuzziness.clear();
|
---|
[27062] | 53 | sortorder = 0; // 0 = ascending, 1 = descending
|
---|
[12655] | 54 | startresults = 1; // all
|
---|
| 55 | endresults = 10; // all
|
---|
[351] | 56 | }
|
---|
| 57 |
|
---|
| 58 |
|
---|
| 59 | queryparamclass &queryparamclass::operator=(const queryparamclass &q) {
|
---|
| 60 | combinequery = q.combinequery;
|
---|
[110] | 61 | collection = q.collection;
|
---|
[351] | 62 | index = q.index;
|
---|
| 63 | subcollection = q.subcollection;
|
---|
| 64 | language = q.language;
|
---|
[1319] | 65 | level = q.level;
|
---|
[110] | 66 | querystring = q.querystring;
|
---|
| 67 | search_type = q.search_type;
|
---|
[502] | 68 | match_mode = q.match_mode;
|
---|
[110] | 69 | casefolding = q.casefolding;
|
---|
| 70 | stemming = q.stemming;
|
---|
[12868] | 71 | accentfolding = q.accentfolding;
|
---|
[110] | 72 | maxdocs = q.maxdocs;
|
---|
[4193] | 73 | maxnumeric = q.maxnumeric;
|
---|
[12410] | 74 | filterstring = q.filterstring;
|
---|
[12276] | 75 | sortfield = q.sortfield;
|
---|
[12770] | 76 | fuzziness = q.fuzziness;
|
---|
[27062] | 77 | sortorder = q.sortorder;
|
---|
[12655] | 78 | startresults = q.startresults;
|
---|
| 79 | endresults = q.endresults;
|
---|
[110] | 80 | return *this;
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 |
|
---|
[351] | 84 | bool operator==(const queryparamclass &x, const queryparamclass &y) {
|
---|
| 85 | return ((x.combinequery == y.combinequery) &&
|
---|
| 86 | (x.collection == y.collection) &&
|
---|
| 87 | (x.index == y.index) &&
|
---|
| 88 | (x.subcollection == y.subcollection) &&
|
---|
| 89 | (x.language == y.language) &&
|
---|
[1319] | 90 | (x.level == y.level) &&
|
---|
[110] | 91 | (x.querystring == y.querystring) &&
|
---|
| 92 | (x.search_type == y.search_type) &&
|
---|
[502] | 93 | (x.match_mode == y.match_mode) &&
|
---|
[110] | 94 | (x.casefolding == y.casefolding) &&
|
---|
| 95 | (x.stemming == y.stemming) &&
|
---|
[12868] | 96 | (x.accentfolding == y.accentfolding) &&
|
---|
[4193] | 97 | (x.maxdocs == y.maxdocs) &&
|
---|
[12276] | 98 | (x.maxnumeric == y.maxnumeric) &&
|
---|
[12410] | 99 | (x.filterstring == y.filterstring) &&
|
---|
[12388] | 100 | (x.sortfield == y.sortfield) &&
|
---|
[12770] | 101 | (x.fuzziness == y.fuzziness) &&
|
---|
[27062] | 102 | (x.sortorder == y.sortorder) &&
|
---|
[12655] | 103 | (x.startresults == y.startresults) &&
|
---|
| 104 | (x.startresults == y.startresults));
|
---|
[110] | 105 | }
|
---|
| 106 |
|
---|
[351] | 107 | bool operator!=(const queryparamclass &x, const queryparamclass &y) {
|
---|
[110] | 108 | return !(x == y);
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 |
|
---|
[351] | 112 | ostream &operator<< (ostream &outs, queryparamclass &q) {
|
---|
[110] | 113 | outconvertclass text_t2ascii;
|
---|
| 114 |
|
---|
| 115 | outs << "*** queryparamclass\n";
|
---|
[351] | 116 | outs << text_t2ascii << " combinequery = \"" << q.combinequery << "\"\n";
|
---|
[110] | 117 | outs << text_t2ascii << " collection = \"" << q.collection << "\"\n";
|
---|
[351] | 118 | outs << text_t2ascii << " index = \"" << q.index << "\"\n";
|
---|
[1319] | 119 | outs << text_t2ascii << " level = \"" << q.level << "\"\n";
|
---|
[351] | 120 | outs << text_t2ascii << " subcollection = \"" << q.subcollection << "\"\n";
|
---|
| 121 | outs << text_t2ascii << " language = \"" << q.language << "\"\n";
|
---|
[110] | 122 | outs << text_t2ascii << " querystring = \"" << q.querystring << "\"\n";
|
---|
| 123 | outs << " search_type = \"" << q.search_type << "\"\n";
|
---|
[502] | 124 | outs << " match_mode = \"" << q.match_mode << "\"\n";
|
---|
[110] | 125 | outs << " casefolding = \"" << q.casefolding << "\"\n";
|
---|
| 126 | outs << " stemming = \"" << q.stemming << "\"\n";
|
---|
[12868] | 127 | outs << " accentfolding = \"" << q.accentfolding << "\"\n";
|
---|
[110] | 128 | outs << " maxdocs = \"" << q.maxdocs << "\"\n";
|
---|
[4193] | 129 | outs << " maxnumeric = \"" << q.maxnumeric << "\"\n";
|
---|
[12410] | 130 | outs << " filterstring = \"" << q.filterstring << "\"\n";
|
---|
[12276] | 131 | outs << " sortfield = \"" << q.sortfield << "\"\n";
|
---|
[12770] | 132 | outs << " fuzziness = \"" << q.fuzziness << "\"\n";
|
---|
[27062] | 133 | outs << " sortorder = \"" << q.sortorder << "\"\n";
|
---|
[12655] | 134 | outs << " startresults = \"" << q.startresults << "\"\n";
|
---|
| 135 | outs << " endresults = \"" << q.endresults << "\"\n";
|
---|
[110] | 136 | outs << "\n";
|
---|
| 137 |
|
---|
| 138 | return outs;
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 |
|
---|
| 142 |
|
---|
| 143 |
|
---|
| 144 | // term frequencies
|
---|
| 145 |
|
---|
[351] | 146 | termfreqclass::termfreqclass () {
|
---|
| 147 | clear();
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | void termfreqclass::clear() {
|
---|
| 151 | termstr.clear();
|
---|
| 152 | termstemstr.clear();
|
---|
| 153 | utf8equivterms.erase(utf8equivterms.begin(), utf8equivterms.end());
|
---|
| 154 | termfreq = 0;
|
---|
| 155 | }
|
---|
| 156 |
|
---|
| 157 | termfreqclass &termfreqclass::operator=(const termfreqclass &t) {
|
---|
[110] | 158 | termstr = t.termstr;
|
---|
[319] | 159 | termstemstr = t.termstemstr;
|
---|
[326] | 160 | utf8equivterms = t.utf8equivterms;
|
---|
[110] | 161 | termfreq = t.termfreq;
|
---|
| 162 |
|
---|
| 163 | return *this;
|
---|
| 164 | }
|
---|
| 165 |
|
---|
[351] | 166 | bool operator==(const termfreqclass &x, const termfreqclass &y) {
|
---|
[110] | 167 | return ((x.termstr == y.termstr) &&
|
---|
[319] | 168 | (x.termstemstr == y.termstemstr) &&
|
---|
[110] | 169 | (x.termfreq == y.termfreq));
|
---|
| 170 | }
|
---|
| 171 |
|
---|
[351] | 172 | bool operator!=(const termfreqclass &x, const termfreqclass &y) {
|
---|
[110] | 173 | return !(x == y);
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 | // ordered by termfreq and then by termstr
|
---|
[351] | 177 | bool operator<(const termfreqclass &x, const termfreqclass &y) {
|
---|
[110] | 178 | return ((x.termfreq < y.termfreq) ||
|
---|
[319] | 179 | ((x.termfreq == y.termfreq) && (x.termstemstr < y.termstemstr)) ||
|
---|
| 180 | ((x.termfreq == y.termfreq) && (x.termstemstr == y.termstemstr) && (x.termstr < y.termstr)));
|
---|
[110] | 181 | }
|
---|
| 182 |
|
---|
[351] | 183 | bool operator>(const termfreqclass &x, const termfreqclass &y) {
|
---|
[110] | 184 | return ((x.termfreq > y.termfreq) ||
|
---|
[319] | 185 | ((x.termfreq == y.termfreq) && (x.termstemstr > y.termstemstr)) ||
|
---|
| 186 | ((x.termfreq == y.termfreq) && (x.termstemstr == y.termstemstr) && (x.termstr > y.termstr)));
|
---|
[110] | 187 | }
|
---|
| 188 |
|
---|
| 189 | // stream output for debugging purposes
|
---|
[351] | 190 | ostream &operator<< (ostream &outs, termfreqclass &t) {
|
---|
[110] | 191 | outconvertclass text_t2ascii;
|
---|
| 192 |
|
---|
| 193 | outs << text_t2ascii << " t:\"" << t.termstr << "\"";
|
---|
[319] | 194 | outs << text_t2ascii << " s:\"" << t.termstemstr << "\"";
|
---|
[110] | 195 | outs << " f:" << t.termfreq << "\n";
|
---|
| 196 |
|
---|
| 197 | return outs;
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 |
|
---|
| 201 |
|
---|
| 202 | // one query result
|
---|
| 203 |
|
---|
[351] | 204 | docresultclass::docresultclass() {
|
---|
| 205 | clear ();
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | void docresultclass::clear () {
|
---|
[16947] | 209 | docid="";
|
---|
[351] | 210 | docnum=-1;
|
---|
| 211 | docweight=0.0;
|
---|
| 212 | num_query_terms_matched=0;
|
---|
| 213 | num_phrase_match=0;
|
---|
| 214 | }
|
---|
| 215 |
|
---|
| 216 | // merges two result classes relating to a single docnum
|
---|
| 217 | docresultclass &docresultclass::combine(const docresultclass &d) {
|
---|
| 218 | docweight += d.docweight; // budget!
|
---|
| 219 | num_query_terms_matched += d.num_query_terms_matched;
|
---|
| 220 | num_phrase_match += d.num_phrase_match;
|
---|
| 221 |
|
---|
| 222 | return *this;
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | docresultclass &docresultclass::operator=(const docresultclass &d) {
|
---|
[16947] | 226 | docid = d.docid;
|
---|
[351] | 227 | docnum = d.docnum;
|
---|
| 228 | docweight = d.docweight;
|
---|
| 229 | num_query_terms_matched = d.num_query_terms_matched;
|
---|
| 230 | num_phrase_match = d.num_phrase_match;
|
---|
| 231 |
|
---|
| 232 | return *this;
|
---|
| 233 | }
|
---|
| 234 |
|
---|
| 235 |
|
---|
[502] | 236 | bool operator==(const docresultclass &x, const docresultclass &y) {
|
---|
[16947] | 237 | return ((x.docid == y.docid) && (x.docnum == y.docnum) && (x.docweight == y.docweight) &&
|
---|
[502] | 238 | (x.num_query_terms_matched == y.num_query_terms_matched) &&
|
---|
| 239 | (x.num_phrase_match == y.num_phrase_match));
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | bool operator<(const docresultclass &x, const docresultclass &y) {
|
---|
[16947] | 243 | return ((x.docid < y.docid) ||
|
---|
| 244 | ((x.docid == y.docid) &&
|
---|
| 245 | ((x.docnum < y.docnum) ||
|
---|
| 246 | ((x.docnum == y.docnum) &&
|
---|
| 247 | ((x.docweight < y.docweight) ||
|
---|
| 248 | ((x.docweight == y.docweight) &&
|
---|
| 249 | ((x.num_query_terms_matched < y.num_query_terms_matched) ||
|
---|
| 250 | ((x.num_query_terms_matched == y.num_query_terms_matched) &&
|
---|
| 251 | ((x.num_phrase_match < y.num_phrase_match))))))))));
|
---|
[502] | 252 | }
|
---|
| 253 |
|
---|
| 254 |
|
---|
[110] | 255 | // stream output for debugging purposes
|
---|
[351] | 256 | ostream &operator<< (ostream &outs, docresultclass &a) {
|
---|
[110] | 257 | outs << " d:" << a.docnum << " w:" << a.docweight << "\n";
|
---|
| 258 | return outs;
|
---|
| 259 | }
|
---|
| 260 |
|
---|
| 261 |
|
---|
| 262 |
|
---|
[351] | 263 | // many document results
|
---|
| 264 |
|
---|
| 265 | docresultsclass::docresultsclass () {
|
---|
| 266 | clear ();
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | void docresultsclass::clear () {
|
---|
| 270 | docset.erase(docset.begin(), docset.end());
|
---|
| 271 | docorder.erase(docorder.begin(), docorder.end());
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | void docresultsclass::docnum_order() {
|
---|
| 275 | docorder.erase(docorder.begin(), docorder.end());
|
---|
| 276 |
|
---|
| 277 | docresultmap::iterator here = docset.begin();
|
---|
| 278 | docresultmap::iterator end = docset.end();
|
---|
| 279 | while (here != end) {
|
---|
| 280 | docorder.push_back ((*here).first);
|
---|
[9620] | 281 | ++here;
|
---|
[351] | 282 | }
|
---|
| 283 | }
|
---|
| 284 |
|
---|
| 285 | void docresultsclass::combine_and (const docresultsclass &d) {
|
---|
| 286 | docorder.erase(docorder.begin(), docorder.end());
|
---|
| 287 |
|
---|
| 288 | // put the resulting set in tempresults
|
---|
| 289 | docresultmap tempresults;
|
---|
| 290 |
|
---|
| 291 | docresultmap::const_iterator d_here = d.docset.begin();
|
---|
| 292 | docresultmap::const_iterator d_end = d.docset.end();
|
---|
| 293 | docresultmap::iterator found = docset.end();
|
---|
| 294 | while (d_here != d_end) {
|
---|
| 295 | found = docset.find((*d_here).first);
|
---|
| 296 | if (found != docset.end()) {
|
---|
| 297 | (*found).second.combine ((*d_here).second);
|
---|
| 298 | tempresults[(*found).first] = (*found).second;
|
---|
| 299 | }
|
---|
[9620] | 300 | ++d_here;
|
---|
[351] | 301 | }
|
---|
| 302 |
|
---|
| 303 | // then copy it back to docset
|
---|
| 304 | docset = tempresults;
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 | void docresultsclass::combine_or (const docresultsclass &d) {
|
---|
| 308 | docorder.erase(docorder.begin(), docorder.end());
|
---|
| 309 |
|
---|
| 310 | docresultmap::const_iterator d_here = d.docset.begin();
|
---|
| 311 | docresultmap::const_iterator d_end = d.docset.end();
|
---|
| 312 | docresultmap::iterator found = docset.end();
|
---|
| 313 | while (d_here != d_end) {
|
---|
| 314 | found = docset.find((*d_here).first);
|
---|
| 315 | if (found != docset.end()) {
|
---|
| 316 | (*found).second.combine ((*d_here).second);
|
---|
| 317 | } else {
|
---|
| 318 | docset[(*d_here).first] = (*d_here).second;
|
---|
| 319 | }
|
---|
[9620] | 320 | ++d_here;
|
---|
[351] | 321 | }
|
---|
| 322 | }
|
---|
| 323 |
|
---|
| 324 | void docresultsclass::combine_not (const docresultsclass &d) {
|
---|
| 325 | docorder.erase(docorder.begin(), docorder.end());
|
---|
| 326 |
|
---|
| 327 | docresultmap::const_iterator d_here = d.docset.begin();
|
---|
| 328 | docresultmap::const_iterator d_end = d.docset.end();
|
---|
| 329 | docresultmap::iterator found = docset.end();
|
---|
| 330 | while (d_here != d_end) {
|
---|
| 331 | found = docset.find((*d_here).first);
|
---|
| 332 | if (found != docset.end()) docset.erase (found);
|
---|
[9620] | 333 | ++d_here;
|
---|
[351] | 334 | }
|
---|
| 335 | }
|
---|
| 336 |
|
---|
| 337 | docresultsclass &docresultsclass::operator=(const docresultsclass &d) {
|
---|
| 338 | docset = d.docset;
|
---|
| 339 | docorder = d.docorder;
|
---|
| 340 |
|
---|
| 341 | return *this;
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 |
|
---|
| 345 |
|
---|
| 346 |
|
---|
[110] | 347 | // query results
|
---|
| 348 |
|
---|
[319] | 349 | void queryresultsclass::clear () {
|
---|
[12421] | 350 | error_message = g_EmptyText;
|
---|
[311] | 351 | docs_matched = 0;
|
---|
[398] | 352 | is_approx = Exact;
|
---|
[4217] | 353 | syntax_error = false;
|
---|
[326] | 354 | postprocessed = false;
|
---|
| 355 |
|
---|
[351] | 356 | docs.clear();
|
---|
[319] | 357 | orgterms.erase(orgterms.begin(),orgterms.end());
|
---|
[110] | 358 | terms.erase(terms.begin(),terms.end());
|
---|
| 359 | }
|
---|
| 360 |
|
---|
[326] | 361 | queryresultsclass &queryresultsclass::operator=(const queryresultsclass &q) {
|
---|
[12421] | 362 | error_message = q.error_message;
|
---|
[311] | 363 | docs_matched = q.docs_matched;
|
---|
| 364 | is_approx = q.is_approx;
|
---|
[4217] | 365 | syntax_error = q.syntax_error;
|
---|
| 366 | postprocessed = q.postprocessed;
|
---|
[326] | 367 |
|
---|
[110] | 368 | docs = q.docs;
|
---|
| 369 | terms = q.terms;
|
---|
[311] | 370 | termvariants = q.termvariants;
|
---|
[110] | 371 |
|
---|
| 372 | return *this;
|
---|
| 373 | }
|
---|
| 374 |
|
---|
[319] | 375 | void queryresultsclass::sortuniqqueryterms() {
|
---|
[394] | 376 | termfreqclassarray tempterms = orgterms;
|
---|
[351] | 377 | text_tset seenterms;
|
---|
[358] | 378 | terms.erase(terms.begin(), terms.end());
|
---|
[319] | 379 |
|
---|
[351] | 380 | // sort the terms to get the frequencies in ascending order
|
---|
| 381 | sort (tempterms.begin(), tempterms.end());
|
---|
| 382 |
|
---|
| 383 | // insert first occurance of each term (maximum)
|
---|
[394] | 384 | termfreqclassarray::reverse_iterator here = tempterms.rbegin();
|
---|
| 385 | termfreqclassarray::reverse_iterator end = tempterms.rend();
|
---|
[351] | 386 | while (here != end) {
|
---|
| 387 | if (seenterms.find((*here).termstr) == seenterms.end()) {
|
---|
| 388 | // the termstemstr and utf8equivterms might be different for
|
---|
| 389 | // different occurances of the term
|
---|
| 390 | (*here).termstemstr.clear();
|
---|
| 391 | (*here).utf8equivterms.erase((*here).utf8equivterms.begin(),
|
---|
| 392 | (*here).utf8equivterms.end());
|
---|
| 393 | terms.push_back(*here);
|
---|
| 394 | seenterms.insert((*here).termstr);
|
---|
| 395 | }
|
---|
[9620] | 396 | ++here;
|
---|
[351] | 397 | }
|
---|
| 398 |
|
---|
| 399 | // now re-sort in ascending order
|
---|
[110] | 400 | sort (terms.begin(), terms.end());
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 |
|
---|
| 404 | // stream output for debugging purposes
|
---|
[351] | 405 | ostream &operator<< (ostream &outs, queryresultsclass &q) {
|
---|
[110] | 406 | outs << "*** queryresultsclass\n";
|
---|
| 407 | outs << "docs\n";
|
---|
| 408 |
|
---|
[351] | 409 | docresultmap::iterator docshere = q.docs.docset.begin();
|
---|
| 410 | docresultmap::iterator docsend = q.docs.docset.end();
|
---|
[110] | 411 | while (docshere != docsend) {
|
---|
[351] | 412 | outs << (*docshere).second;
|
---|
[9620] | 413 | ++docshere;
|
---|
[110] | 414 | }
|
---|
| 415 |
|
---|
[319] | 416 | outs << "orgterms\n";
|
---|
[394] | 417 | termfreqclassarray::iterator orgtermshere = q.orgterms.begin();
|
---|
| 418 | termfreqclassarray::iterator orgtermsend = q.orgterms.end();
|
---|
[319] | 419 | while (orgtermshere != orgtermsend) {
|
---|
| 420 | outs << (*orgtermshere);
|
---|
[9620] | 421 | ++orgtermshere;
|
---|
[319] | 422 | }
|
---|
| 423 |
|
---|
[110] | 424 | outs << "terms\n";
|
---|
[394] | 425 | termfreqclassarray::iterator termshere = q.terms.begin();
|
---|
| 426 | termfreqclassarray::iterator termsend = q.terms.end();
|
---|
[110] | 427 | while (termshere != termsend) {
|
---|
| 428 | outs << (*termshere);
|
---|
[9620] | 429 | ++termshere;
|
---|
[110] | 430 | }
|
---|
| 431 |
|
---|
| 432 | outs << "\n";
|
---|
| 433 |
|
---|
| 434 | return outs;
|
---|
| 435 | }
|
---|