]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
use a proper binary search
authorRudolf Polzer <divverent@xonotic.org>
Fri, 11 Oct 2013 04:33:13 +0000 (06:33 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Fri, 11 Oct 2013 04:33:31 +0000 (06:33 +0200)
qcsrc/menu/xonotic/serverlist.c

index 216a98adb7599d789fe8b0fc56352c631eb25844..a69f310cc40dc10f36bcc07655eabd5478755cb7 100644 (file)
@@ -670,124 +670,75 @@ void XonoticServerList_draw(entity me)
                // ^ unfortunately no such optimization can be made-- we must process through the
                // entire list, otherwise there is no way to know which item is first in its category.
 
-               if(itemcount)
-               {
-                       // binary search method suggested by div
-                       /*float cat = 0, x;
-                       float first, middle, last;
-                       float newfirst = 0;
-                       float catf = 0, catl = 0;
-                       for(x = 1; x <= category_ent_count; ++x)
-                       {
-                               first = newfirst;
-                               last = (itemcount - 1);
-                               middle = floor((first + last) / 2);
-
-                               if(
-                                       ((catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first)) < x)
-                                       &&
-                                       ((catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last)) >= x)
-                               )
-                               {
-                                       for(;;)
-                                       {
-                                               cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
-                                               if(cat >= x) { last = middle; }
-                                               else if(cat < x) { first = middle; }
-                                               if((last - middle) == 1)
-                                               {
-                                                       print(sprintf("highhit: x='%d', dc='%d', first='%d', middle='%d', last='%d', cat='%d'.\n", x, category_draw_count, first, middle, last, cat));
-                                                       category_name[category_draw_count] = x;
-                                                       category_item[category_draw_count] = last;
-                                                       ++category_draw_count;
-                                                       ++me.nItems;
-                                                       newfirst = last; // already scanned through these, skip 'em
-                                                       break;
-                                               }
-                                               middle = floor((first + last) / 2);
+               // binary search method suggested by div
+               float x;
+               float begin = 0;
+               for(x = 1; x <= category_ent_count; ++x) {
+                       float first = begin;
+                       float last = (itemcount - 1);
+                       if (first > last) {
+                               // List is empty.
+                               break;
+                       }
+                       float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
+                       float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
+                       if (catf > x) {
+                               // The first one is already > x.
+                               // Therefore, category x does not exist.
+                               // Higher numbered categories do exist though.
+                       } else if (catl < x) {
+                               // The last one is < x.
+                               // Thus this category - and any following -
+                               // don't exist.
+                               break;
+                       } else if (catf == x) {
+                               // Starts at first. This breaks the loop
+                               // invariant in the binary search and thus has
+                               // to be handled separately.
+                               print(sprintf("hit: x='%d', dc='%d', first='%d', last='%d', catf='%d', catl='%d'.\n", x, category_draw_count, first, last, catf, catl));
+                               category_name[category_draw_count] = x;
+                               category_item[category_draw_count] = first;
+                               ++category_draw_count;
+                               ++me.nItems;
+                               begin = first + 1;
+                       } else {
+                               // At this point, catf <= x < catl, thus
+                               // catf < catl, thus first < last.
+                               // INVARIANTS:
+                               // last - first >= 1
+                               // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
+                               // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
+                               // catf < x
+                               // catl >= x
+                               while (last - first > 1) {
+                                       float middle = floor((first + last) / 2);
+                                       // By loop condition, middle != first && middle != last.
+                                       float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
+                                       if (cat >= x) {
+                                               last = middle;
+                                               catl = cat;
+                                       } else {
+                                               first = middle;
+                                               catf = cat;
                                        }
                                }
-                               else if(catf == x)
-                               {
-                                       print(sprintf("lowhit: x='%d', dc='%d', first='%d', middle='%d', last='%d', cat='%d'.\n", x, category_draw_count, first, middle, last, catf));
+                               if (catl == x) {
+                                       print(sprintf("hit: x='%d', dc='%d', first='%d', last='%d', catf='%d', catl='%d'.\n", x, category_draw_count, first, last, catf, catl));
                                        category_name[category_draw_count] = x;
-                                       category_item[category_draw_count] = first;
+                                       category_item[category_draw_count] = last;
                                        ++category_draw_count;
                                        ++me.nItems;
-                                       newfirst = first + 1; // already scanned through these, skip 'em
-                               }
-                       }*/
-
-                       // my binary search method
-                       float cat = 0, x;
-                       float first, middle, last;
-                       float newfirst = 0;
-                       for(x = 1; x <= category_ent_count; ++x)
-                       {
-                               first = newfirst;
-                               last = (itemcount - 1);
-                               middle = floor((first + last) / 2);
-
-                               while(first <= last)
-                               {
-                                       cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
-                                       if(cat > x) { last = middle - 1; }
-                                       else if(cat == x)
-                                       {
-                                               if(middle == 0 || (gethostcachenumber(SLIST_FIELD_CATEGORY, middle - 1) != x)) // check if middle is the first of its category
-                                               {
-                                                       //print(sprintf("hit: x='%d', dc='%d', first='%d', middle='%d', last='%d', cat='%d'.\n", x, category_draw_count, first, middle, last, cat));
-                                                       category_name[category_draw_count] = cat;
-                                                       category_item[category_draw_count] = middle;
-                                                       ++category_draw_count;
-                                                       ++me.nItems;
-                                                       newfirst = middle + 1; // already scanned through these, skip 'em
-                                                       break;
-                                               }
-                                               else { last = middle - 1; } // nope, try again
-                                       }
-                                       else { first = middle + 1; }
-                                       middle = floor((first + last) / 2);
                                }
+                               begin = last + 1; // already scanned through these, skip 'em
                        }
+               }
 
-                       // old linear search method
-                       /*float cat = 0, i = 0, x = 0; 
-                       for(i = 0; i < itemcount; ++i) // FIXME this loop is TOTALLY unacceptable (O(servers)). Make it O(categories * log(servers)). Yes, that is possible.
-                       {
-                               cat = gethostcachenumber(SLIST_FIELD_CATEGORY, i);
-                               if(cat)
-                               {
-                                       if(category_draw_count == 0)
-                                       {
-                                               print(sprintf("hit: i='%d', dc='%d', cat='%d'.\n", i, category_draw_count, cat));
-                                               category_name[category_draw_count] = cat;
-                                               category_item[category_draw_count] = i;
-                                               ++category_draw_count;
-                                               ++me.nItems;
-                                       }
-                                       else
-                                       {
-                                               found = 0;
-                                               for(x = 0; x < category_draw_count; ++x) { if(cat == category_name[x]) { found = 1; } }
-                                               if not(found)
-                                               {
-                                                       print(sprintf("hit: i='%d', dc='%d', cat='%d'.\n", i, category_draw_count, cat));
-                                                       category_name[category_draw_count] = cat;
-                                                       category_item[category_draw_count] = i;
-                                                       ++category_draw_count;
-                                                       ++me.nItems;
-                                               }
-                                       }
-                               }
-                       }*/
-                       if(autocvar_menu_slist_categories_onlyifmultiple && (category_draw_count == 1))
-                       {
-                               category_name[0] = -1;
-                               category_item[0] = -1;
-                               category_draw_count = 0;
-                               me.nItems = itemcount;
-                       }
+               if(autocvar_menu_slist_categories_onlyifmultiple && (category_draw_count == 1))
+               {
+                       category_name[0] = -1;
+                       category_item[0] = -1;
+                       category_draw_count = 0;
+                       me.nItems = itemcount;
                }
        }
        else { me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); }