Add list_min(), list_max().
[pintos-anon] / src / lib / kernel / list.c
index 3631b2e68da0ddc5292c588bf0acbe34fb1ca996..ee47669fd1213098f352f210538e4592f5261d3f 100644 (file)
@@ -78,7 +78,7 @@ list_begin (struct list *list)
 list_elem *
 list_next (list_elem *elem)
 {
-  ASSERT (is_interior (elem));
+  ASSERT (is_head (elem) || is_interior (elem));
   return elem->next;
 }
 
@@ -216,15 +216,15 @@ list_push_back (struct list *list, list_elem *elem)
   list_insert (list_end (list), elem);
 }
 
-/* Removes ELEM from its list.  Undefined behavior if ELEM is not
-   in a list. */
+/* Removes ELEM from its list and returns the element that
+   followed it.  Undefined behavior if ELEM is not in a list.  */
 list_elem *
 list_remove (list_elem *elem)
 {
   ASSERT (is_interior (elem));
   elem->prev->next = elem->next;
   elem->next->prev = elem->prev;
-  return elem;
+  return elem->next;
 }
 
 /* Removes the front element from LIST and returns it.
@@ -232,8 +232,9 @@ list_remove (list_elem *elem)
 list_elem *
 list_pop_front (struct list *list)
 {
-  ASSERT (list != NULL);
-  return list_remove (list_front (list));
+  list_elem *front = list_front (list);
+  list_remove (front);
+  return front;
 }
 
 /* Removes the back element from LIST and returns it.
@@ -241,8 +242,9 @@ list_pop_front (struct list *list)
 list_elem *
 list_pop_back (struct list *list)
 {
-  ASSERT (list != NULL);
-  return list_remove (list_back (list));
+  list_elem *back = list_back (list);
+  list_remove (back);
+  return back;
 }
 
 /* Returns the front element in LIST.
@@ -283,24 +285,28 @@ list_empty (struct list *list)
   return list_begin (list) == list_end (list);
 }
 
+/* Swaps the `list_elem *'s that A and B point to. */
+static void
+swap (list_elem **a, list_elem **b) 
+{
+  list_elem *t = *a;
+  *a = *b;
+  *b = t;
+}
+
 /* Reverses the order of LIST. */
 void
 list_reverse (struct list *list)
 {
-  list_elem te, *e;
-
-  /* Swap the prev and next pointers in each element of LIST. */
-  for (e = &list->head; e != NULL; e = e->prev)
+  if (!list_empty (list)) 
     {
-      list_elem *tep = e->prev;
-      e->prev = e->next;
-      e->next = tep;
-    }
+      list_elem *e;
 
-  /* Swap the head and tail. */
-  te = list->head;
-  list->head = list->tail;
-  list->tail = te;
+      for (e = list_begin (list); e != list_end (list); e = e->prev)
+        swap (&e->prev, &e->next);
+      swap (&list->head.next, &list->tail.prev);
+      swap (&list->head.next->prev, &list->tail.prev->next);
+    }
 }
 
 /* Merges lists AL and BL, which must each be sorted according to
@@ -333,40 +339,50 @@ list_merge (struct list *al, struct list *bl,
   list_splice (list_end (al), list_begin (bl), list_end (bl));
 }
 
-/* Sorts LIST according to LESS given auxiliary data AUX.
-   Runs in O(n lg n) in the number of elements in LIST. */
-void
-list_sort (struct list *list,
-           list_less_func *less, void *aux)
+/* Returns the middle element in LIST, that is, the N/2'th
+   element (rounding down) in a N-element list.
+   Given an empty list, returns the list tail. */
+static list_elem *
+middle_of_list (struct list *list) 
 {
-  struct list tmp;
   list_elem *middle, *last;
 
-  ASSERT (list != NULL);
-  ASSERT (less != NULL);
-
-  /* Empty and 1-element lists are already sorted. */
-  if (list_empty (list) || list_next (list_begin (list)) == list_end (list))
-    return;
-
-  /* Find middle of LIST.  (We're not interested in the end of
-     the list but it's incidentally needed.) */
-  middle = list_begin (list);
-  last = list_next (middle);
+  middle = last = list_begin (list);
   while (last != list_end (list) && list_next (last) != list_end (list))
     {
       middle = list_next (middle);
       last = list_next (list_next (last));
     }
+  return middle;
+}
 
-  /* Extract first half of LIST into a temporary list. */
-  list_init (&tmp);
-  list_splice (list_begin (&tmp), list_begin (list), middle);
-
-  /* Sort each half-list and merge the result. */
-  list_sort (&tmp, less, aux);
-  list_sort (list, less, aux);
-  list_merge (list, &tmp, less, aux);
+/* Sorts LIST according to LESS given auxiliary data AUX.
+   Runs in O(n lg n) time in the number of elements in LIST. */
+void
+list_sort (struct list *list,
+           list_less_func *less, void *aux)
+{
+  /* Find the middle of the list. */
+  list_elem *middle = middle_of_list (list);
+  if (middle != list_begin (list))
+    {
+      /* Extract first half of LIST into a temporary list. */
+      struct list tmp;
+      list_init (&tmp);
+      list_splice (list_begin (&tmp), list_begin (list), middle);
+
+      /* Sort each half-list and merge the result. */
+      list_sort (&tmp, less, aux);
+      list_sort (list, less, aux);
+      list_merge (list, &tmp, less, aux);
+    }
+  else 
+    {
+      /* The middle is at the beginning of the list.
+         This only happens in empty lists and 1-element lists.
+         Because such lists are already sorted, we have nothing
+         to do. */
+    }
 }
 
 /* Inserts ELEM in the proper position in LIST, which must be
@@ -414,3 +430,41 @@ list_unique (struct list *list, struct list *duplicates,
     else
       elem = next;
 }
+
+/* Returns the element in LIST with the largest value according
+   to LESS given auxiliary data AUX.  If there is more than one
+   maximum, returns the one that appears earlier in the list.  If
+   the list is empty, returns its tail. */
+list_elem *
+list_max (struct list *list, list_less_func *less, void *aux)
+{
+  list_elem *max = list_begin (list);
+  if (max != list_end (list)) 
+    {
+      list_elem *e;
+      
+      for (e = list_next (max); e != list_end (list); e = list_next (e))
+        if (less (max, e, aux))
+          max = e; 
+    }
+  return max;
+}
+
+/* Returns the element in LIST with the smallest value according
+   to LESS given auxiliary data AUX.  If there is more than one
+   minimum, returns the one that appears earlier in the list.  If
+   the list is empty, returns its tail. */
+list_elem *
+list_min (struct list *list, list_less_func *less, void *aux)
+{
+  list_elem *min = list_begin (list);
+  if (min != list_end (list)) 
+    {
+      list_elem *e;
+      
+      for (e = list_next (min); e != list_end (list); e = list_next (e))
+        if (less (e, min, aux))
+          min = e; 
+    }
+  return min;
+}