/*

 * list.c

 * A package for managing doubly-linked lists.

 */

#include "list.h"

#include <stdlib.h>

 

/* Prototypes of static functions: */

static void list_delete_element (List *p_list, Element *p_elem);

 

/* ------------------------------------------------------------------------

 * Function: list_init

 */

List *list_init ()

{

  List    *p_list;

 

  /* Allocate a new List object. */

  p_list = (List *) malloc (sizeof(List));

 

  /* Set its fields to indicate and empty list. */

  p_list->p_head = NULL;

  p_list->p_tail = NULL;

  p_list->size = 0;

 

  return (p_list);

}

 

/* ------------------------------------------------------------------------

 * Function: list_prepend

 */

void list_prepend (List *p_list, double x)

{

  Element *p_elem;

 

  /* Sanity check: */

  if (p_list == NULL)

    exit(-1);

 

  /* Allocate a new Element object and set its fields. */

  p_elem = (Element *) malloc (sizeof(Element));

 

  p_elem->value = x;

  p_elem->p_prev = NULL;

  p_elem->p_next = NULL;

 

  /* Set the element as the new list head. */

  if (p_list->p_head != NULL)

  {

    /* The new element should be placed before the current list head. */

    p_list->p_head->p_prev = p_elem;

    p_elem->p_next = p_list->p_head;

 

    /* Set the new element as the new list head. */

    p_list->p_head = p_elem;

  }

  else

  {

    /* The list is empty - the new element should be both its head and

       its tail. */

    p_list->p_head = p_elem;

    p_list->p_tail = p_elem;

  }

 

  /* Increment the list size. */

  p_list->size++;

 

  return;

}

 

/* ------------------------------------------------------------------------

 * Function: list_append

 */

void list_append (List *p_list, double x)

{

  Element *p_elem;

 

  /* Sanity check: */

  if (p_list == NULL)

    exit(-1);

 

  /* Allocate a new Element object and set its fields. */

  p_elem = (Element *) malloc (sizeof(Element));

 

  p_elem->value = x;

  p_elem->p_prev = NULL;

  p_elem->p_next = NULL;

 

  /* Set the element as the new list tail. */

  if (p_list->p_tail != NULL)

  {

    /* The new element should be placed after the current list tail. */

    p_list->p_tail->p_next = p_elem;

    p_elem->p_prev = p_list->p_tail;

 

    /* Set the new element as the new list tail. */

    p_list->p_tail = p_elem;

  }

  else

  {

    /* The list is empty - the new element should be both its head and

       its tail. */

    p_list->p_head = p_elem;

    p_list->p_tail = p_elem;

  }

 

  /* Increment the list size. */

  p_list->size++;

 

  return;

}

 

/* ------------------------------------------------------------------------

 * Function: list_find

 */

const Element *list_find (const List *p_list, double x)

{

  Element *p_elem;

 

  /* Sanity check: */

  if (p_list == NULL)

    exit(-1);

 

  /* Go over the list. */

  p_elem = p_list->p_head;

  while (p_elem != NULL)

  {

    /* Check if the current element contains the requested value. */

    if (p_elem->value == x)

      return (p_elem);

 

    /* Proceed to the next element. */

    p_elem = p_elem->p_next;

  }

 

  /* If we reached here, the value is not found in the list. */

  return (NULL);

}

 

/* ------------------------------------------------------------------------

 * Function: list_delete

 */

int list_delete (List *p_list, double x)

{

  Element *p_elem;

 

  /* Sanity check: */

  if (p_list == NULL)

    exit(-1);

 

  /* Find the element containing x in the list. */

  p_elem = (Element *) list_find (p_list, x);

 

  if (p_elem == NULL)

    /* The value x is not found in the list: */

    return (0);

 

  /* Delete p_elem from the list. */

  list_delete_element (p_list, p_elem);

 

  return (1);

}

 

/* ------------------------------------------------------------------------

 * Function: list_pop_first

 */

double list_pop_first (List *p_list)

{

  double  x;

 

  /* Sanity checks: */

  if (p_list == NULL || p_list->size == 0)

    exit(-1);

 

  /* Keep the value of the list head. */

  x = p_list->p_head->value;

 

  /* Delete the list head, and return the value it stored. */

  list_delete_element (p_list, p_list->p_head);

  return (x);

}

 

/* ------------------------------------------------------------------------

 * Function: list_pop_last

 */

double list_pop_last (List *p_list)

{

  double  x;

 

  /* Sanity checks: */

  if (p_list == NULL || p_list->size == 0)

    exit(-1);

 

  /* Keep the value of the list tail. */

  x = p_list->p_tail->value;

 

  /* Delete the list tail, and return the value it stored. */

  list_delete_element (p_list, p_list->p_tail);

  return (x);

}

 

/* ------------------------------------------------------------------------

 * Function: list_reset

 */

void list_reset (List *p_list)

{

  Element *p_elem;

 

  /* Sanity check: */

  if (p_list == NULL)

    exit(-1);

 

  while (p_list->p_head != NULL)

  {

    /* Keep track of the current list head. */

    p_elem = p_list->p_head;

 

    /* Make the second element the new list head. */

    p_list->p_head = p_list->p_head->p_next;

 

    /* Free memory. */

    free (p_elem);

  }

 

  /* Reset the rest of the fields. */

  p_list->p_tail = NULL;

  p_list->size = 0;

 

  return;

}

 

/* ------------------------------------------------------------------------

 * Function: list_delete_element

 * Purpose:  Delete the given element from the list.

 * Input:    p_list - The list.

 *           p_elem - The element to be deleted.

 * Returns:  Nothing.

 */

static void list_delete_element (List *p_list, Element *p_elem)

{

  /* Link the previous element and the next element of p_elem so none of

     them will keep pointing to p_elem. */

  if (p_elem->p_prev != NULL)

  {

    /* Make the previous element point to the next element (skip p_elem). */

    p_elem->p_prev->p_next = p_elem->p_next;

  }

  else

  {

    /* p_elem is the current list head - assign the next element as the

       new head. */

    p_list->p_head = p_elem->p_next;

  }

 

  if (p_elem->p_next != NULL)

  {

    /* Make the previous element point to the next element (skip p_elem). */

    p_elem->p_next->p_prev = p_elem->p_prev;

  }

  else

  {

    /* p_elem is the current list tail - assign the previous element as the

       new tail. */

    p_list->p_tail = p_elem->p_prev;

  }

 

  /* Free p_elem itself. */

  free (p_elem);

 

  /* Update the list size. */

  p_list->size--;

 

  return;

}