CShelf
Home ] Up ]

 

//CShelf015.cpp
//Author: AOU
//2006.04.17

/*
data structure:
  booksOnshelf (firstBook, count, widthOccupied)
  book (id, width, bookOnRight)
functions:
init
add(bookID, bookWidth)
remove(bookID)
displayAll
end/displayBrief
search(booID)

*/

#include<iostream>
#include <fstream>
#include <ctime>
using namespace std;

int const UNDEFINED       = -9;
int const MIN_SHELF_WIDTH =  5;
int const MAX_SHELF_WIDTH =  40;//100;
int const MIN_ID          =  1;
int const MAX_ID          =  9;
int const TEST_COUNT      =  10;


/*
book with ID=0 is an empty space
*/
class CBook
  {
  private:
    int id;                                               //  book id, 0 for book removed
    int width;                                            //  book width
    CBook *bookOnRight;                                   //  pointer to book on right side
    CBook *bookOnLeft;                                    //  pointer to book on left side
  public:
    CBook(void);

  friend class CShelf;
  };


class CShelf
  {
  private:
    CBook *firstBook;                                     //  points to the leftmost book     //
    CBook *lastBook;                                      //  points to the rightmost book    //
    int width;                                            //  width of the bookshelf          //
    int count;                                            //  number of books on the shelf    //
    int widthOccupied;                                    //  total width of book on shelf    //
  public:
    CShelf(void);                                         //  test_constructorDefault         //
    bool isValid(void);                                   //  test_isValid
    CShelf(char ch);                                      //  test_constructorRandom
    CShelf(int width);                                    //  test_constructorWidth           //
                                                          //  test_constructorFragmented
    ~CShelf(void);
    bool insertAtLeft(int bookID, int bookWidth);         //  test_insertAtLeft
    void displayDetailed(void) const;                     //  test_displayDetailed
    void displaySimple(void) const;                       //  test_displaySimple
    void displayBrief(void) const;                        //  test_displayBrief
    bool isEmpty(void)const;                              //  test_isEmpty
    int randomBook(void) const;                           //  test_randomBook
    CBook *randomBookPointer(void) const;                 //  test_randomBookPointer
    CBook* search(int bookID) const;                      //  test_search
    bool remove(int bookID);                              //  test_remove_byID
    bool remove(CBook *p);                                //  test_remove_byPointer
    int reclaimSpace(int space);                          //  test_reclaimSpace
    int reclaimableSpace(void) const;                     //  test_reclaimableSpace
  };


void display_test_header(char *test_name);

void test_constructorDefault(void);
void test_isValid(void);
void test_insertAtLeft(void);
void test_constructorRandom(void);
void test_constructorWidth(void);
void test_constructorFragmented(void);
void test_remove_byID(void);
void test_reclaimSpace(void);
void test_randomBook(void);
void test_randomBookPointer(void);
void test_isEmpty(void);
void test_search(void);
void test_remove_byPointer(void);
void test_reclaimableSpace(void);
void test_displayDetailed(void);
void test_displaySimple(void);
void test_displayBrief(void);


bool CShelf::isEmpty(void)const 
  {
  return (firstBook==NULL);
  }

void test_isValid(void)
  {
  }

/*
bool CShelf::isValid(void)
Possibilities:
  Empty shelf
  invalid count
  invalid book id
  invalid book width
  empty space on the righmost side
  widthOccupied more than width of shelf
  virtual width more than width
  duplicate book ids
Algorithm:
  if firstBook==lastBook==null then 
    return true

  if count <> the number of books then
    return false

  if book id of any book is out of range then 
    return false

  if book width of any book is > width of shelf then
    return false

  if empty space on rightmost position then
    return false

  if widthOccupied > width of shelf then
    return false

  if total of books widths and empty spaces > width of shelf then
    return false

  if there is a duplicate book then
    return false

  return true
*/
bool CShelf::isValid(void)
  {
  //if firstBook==lastBook==null then return true
  if ((NULL == this->firstBook) && (NULL == this->lastBook))
    return true;

  //if count <> the number of books then return false
  CBook *p=this->firstBook;
  int tCount = 0;
  while (p != NULL)
    {
    tCount++;
    p = p->bookOnRight;
    }

  if (this->count != tCount)
    return false;

  p=this->lastBook;
  tCount = 0;
  while (p != NULL)
    {
    tCount++;
    p = p->bookOnLeft;
    }

  if (this->count != tCount)
    return false;

  //if book id of any book is out of range then return false
  p=this->firstBook;
  while (p != NULL)
    {
    if ((p->id < MIN_ID) || (p->id > MAX_ID))
      return false;

    p = p->bookOnRight;
    }

  //if book width of any book is > width of shelf then return false
  p=this->firstBook;
  while (p != NULL)
    {
    if (p->id > this->width)
      return false;

    p = p->bookOnRight;
    }

  //if empty space on rightmost position then return false
  if (0 == this->lastBook->id)
    return false;

  //if widthOccupied > width of shelf then return false
  if (this->widthOccupied > this->width)
    return false;

  //if total of books and empty spaces > width of shelf then return false
  p=this->firstBook;
  int tWidth = 0;
  while (p != NULL)
    {
    tWidth = tWidth + p->width;
    p = p->bookOnRight;
    }
  
  if (tWidth > this->width)
    return false;

  //if there is a duplicate book then return false
  p=this->firstBook;
  CBook *q;
  while (p->bookOnRight != NULL)
    {
    //check if there is a book on right with same id
    q = p->bookOnRight;
    while (q != NULL)
      {
      if (p->id == q->id)
        return false;

      q = q->bookOnRight;
      }
    
    p = p->bookOnRight;
    }
  



  return true;
  }


void main(void)
  {
  //srand(133);
  //srand((unsigned int) time(NULL));
  //test_constructorDefault();
  //test_constructorWidth();
  //test_insertAtLeft();
  //test_constructorFragmented();
  //test_constructorRandom();
  //test_isValid();
  //test_remove_byID();
  //test_randomBook();
  //test_randomBookPointer();
  //test_reclaimSpace();

  int a;
  a = 3;
  cout << a << endl;

  int *pa;
  pa = NULL;
  cout << *pa << endl;
  cout << pa << endl;
  cout << &a << endl;
  cout << &pa << endl;

  }


int CShelf::reclaimableSpace(void) const
  {
  if (this->isEmpty())
    return 0;
  
  int space = 0;
  CBook *p = this->firstBook;

  while (p != NULL)
    {
    if (p->id == 0)
      space += p->width;

    p = p->bookOnRight;
    }

  return space;
  }


void test_randomBookPointer(void)
  {
  cout << "======================\n";
  cout << "test_randomBookPointer\n";
  cout << "======================\n";

  for (int t=1; t<=TEST_COUNT/TEST_COUNT; t++)
    {
    cout << "Test Count " << t << endl;

    CShelf myShelf('r');

    cout << "CShelf myShelf('r'); executed\n";

    myShelf.displayBrief();   cout << endl;
    myShelf.displaySimple();  cout << endl;
    myShelf.displayDetailed();cout << endl;

    for (int b=1; b<=TEST_COUNT; b++)
      {
      CBook *p = myShelf.randomBookPointer();
      cout << "random bookPointer " << p     << endl;

      if (myShelf.remove(p))
        cout << "Remove was a success\n";
      else
        cout << "Remove was not a success\n\n";

      myShelf.displayBrief();   cout << endl;
      myShelf.displaySimple();  cout << endl;
      myShelf.displayDetailed();cout << endl;

      if (myShelf.isEmpty())
        break;
      }

    cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
    }
  }


/*
Randomly selects a book pointer from the shelf
*/
CBook * CShelf::randomBookPointer(void) const
  {
  if (0==this->count)
    return NULL;

  int turn = rand()%this->count;
  CBook *p = this->firstBook;
  while (turn > 0)
    {
    p = p->bookOnRight;
    if (p->id > 0)
      turn--;
    }

  return p;
  }


void test_randomBook(void)
  {
  cout << "=================\n";
  cout << "test_reclaimSpace\n";
  cout << "=================\n";

  for (int t=1; t<=TEST_COUNT/TEST_COUNT; t++)
    {
    cout << "Test Count " << t << endl;

    CShelf myShelf('r');

    cout << "CShelf myShelf('r'); executed\n";

    //myShelf.displayBrief();   cout << endl;
    myShelf.displaySimple();  cout << endl;
    //myShelf.displayDetailed();cout << endl;

    for (int b=1; b<=TEST_COUNT; b++)
      {
      int bookID = myShelf.randomBook();
      cout << "random bookID    = " << bookID << endl;

      if (myShelf.remove(bookID))
        cout << "Remove was a success\n";
      else
        cout << "Remove was not a success\n\n";

      //myShelf.displayBrief();   cout << endl;
      myShelf.displaySimple();  cout << endl;
      //myShelf.displayDetailed();cout << endl;
      }

    cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
    }
  }


/*
Randomly selects a book id from the shelf
if the shelf has no book then return 0
skips the empty slots
*/
int CShelf::randomBook(void) const
  {
  if (0==this->count)
    return 0;

  CBook *p;
  p = this->firstBook;
  int n=0;
  while (p != NULL)
    {
    n++;
    p=p->bookOnRight;
    }

  do
    {
    int step = rand()%n;
    p=this->firstBook;
    while (step > 0)
      {
      p = p->bookOnRight;
      step--;
      }
    }
    while (p->id == 0);

  return p->id;
  }


/*
bool CShelf::remove(CBook *p)
Possibilities:
  invalid value in p
  empty list
  only one node in the list
  p is pointing to the first node
  p is pointing to the last node
  p is pointing to node between first and last
Algorithm:
  //invalid value in p
  if p is null then return false

  //empty list
  if list is empty then return false

  //only one node in the list
  set firstBook and lastBook to nuul
  delete p
  set count to 0
  return true

  //p is pointing to the first node
  firstBook = p->bookOnRight
  firstBook->bookOnLeft = null
  delete p
  count--
  return true

  //p is pointing to the last node
  lastBook = p->bookOnLeft
  lastBook->bookOnRight = null
  delete p
  count--
  return true

  //p is pointing to node between first and last
  p->bookOnLeft->bookOnRight = p->bookOnRight
  p->bookOnRight->bookOnLeft = p->bookOnLeft
  delete p
  count--
  return true
*/

bool CShelf::remove(CBook *p)
  {
  //invalid value in p
  if (NULL == p)
    return false;

  //empty list
  if (this->firstBook == NULL)
    return false;

  //only one node in the list
  if ((this->firstBook == p) && (p == this->lastBook))
    {
    this->firstBook = this->lastBook = NULL;
    delete p;
    this->count = 0;
    return true;
    }

  //p is pointing to the first node
  if (this->firstBook == p)
    {
    this->firstBook = p->bookOnRight;
    this->firstBook->bookOnLeft = NULL;
    delete p;
    this->count--;
    return true;
    }

  //p is pointing to the last node
  if (p == this->lastBook)
    {
    this->lastBook = p->bookOnLeft;
    this->lastBook->bookOnRight = NULL;
    delete p;
    this->count--;
    return true;
    }

  //p is pointing to node between first and last
  p->bookOnLeft->bookOnRight = p->bookOnRight;
  p->bookOnRight->bookOnLeft = p->bookOnLeft;
  delete p;
  this->count--;
  return true;
  }


void test_reclaimSpace(void)
  {
  cout << "=================\n";
  cout << "test_reclaimSpace\n";
  cout << "=================\n";

  for (int t=1; t<=TEST_COUNT; t++)
    {
    cout << "Test Count " << t << endl;

    CShelf myShelf('f');

    cout << "CShelf myShelf('f'); executed\n";

    for (int b=1; b<=TEST_COUNT; b++)
      {
      if (myShelf.reclaimableSpace() == 0) 
        break;
      //myShelf.displayBrief();   cout << endl;
      myShelf.displaySimple();  cout << endl;
      //myShelf.displayDetailed();cout << endl;

      int spaceToReclaim = rand()%((MAX_SHELF_WIDTH-MIN_SHELF_WIDTH)/8+1);
      int spaceReclaimed = myShelf.reclaimSpace(spaceToReclaim);

      cout << "       Space to reclaim = " << spaceToReclaim << endl;
      cout << "       Space reclaimed  = " << spaceReclaimed << endl;

      //myShelf.displayBrief();   cout << endl;
      myShelf.displaySimple();  cout << endl;
      //myShelf.displayDetailed();cout << endl;
      cout << "------------------------------------\n";

      if (myShelf.isEmpty())
        break;
      }

    cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
    }
  }


/*int CShelf::reclaimSpace(int spaceToClaim)
Description:
  reclaim empty space on the shelf due removal of books
  return the amount of space reclaimed
Example:
  000111000222, 2 => 0111000222, 2
  111222, 2       => 111222, 0
  000111000222, 6 => 111222, 6
  000111000222, 7 => 111222, 6
  000111000222, 0 => 000111000222, 0
  111222223333, 5 => 111222223333, 0
Algorithm:
  if space <=0 then
    return 0
  if widthOccupied >= widthShelf then
    return 0
  spaceClaimed = 0
  from first entry to the last entry do the following
    if entry is empty space then
        if entry width < (spaceToClaim-spaceClaimed) then 
            spaceClaimed += entry width
            delete this entry
          elseif entry width == (spaceToClaim-spaceClaimed) then 
            spaceClaimed += entry width
            delete this entry
            return spaceClaimed
          elseif entry width > (spaceToClaim-spaceClaimed) then 
            decrease the entry width by (spaceToClaim-spaceClaimed)
            spaceClaimed += (spaceToClaim-spaceClaimed)
            return spaceClaimed
          end if
      end if
    go to the next entry
    end loop

*/
int CShelf::reclaimSpace(int spaceToClaim)
  {
  //cout << "????   space to reclaim " << spaceToClaim << endl;
  if (spaceToClaim <=0)
    return 0;

  if (this->widthOccupied >= this->width)
    return 0;

  int spaceClaimed = 0;
  CBook *p=this->firstBook; 
  while (p!=NULL)
    {
    //cout << "????   p= " << p << endl;
    if (p->id == 0)
        {
        //cout << "************** p->id = " << p->id << "  p->width = " << p->width << endl;
        if (p->width < (spaceToClaim-spaceClaimed))
            {
            //cout << "????   if (p->width < (spaceToClaim-spaceClaimed))\n";
            spaceClaimed += p->width;
            CBook *q = p;
            p = p->bookOnRight;
            this->remove(q);
            }
          else if (p->width == (spaceToClaim-spaceClaimed))
            {
            //cout << "????   else if (p->width == (spaceToClaim-spaceClaimed))\n";
            spaceClaimed += p->width;
            this->remove(p);
            return spaceClaimed;
            }
          else if (p->width > (spaceToClaim-spaceClaimed))
            {
            //cout << "????   else if (p->width > (spaceToClaim-spaceClaimed))\n";
            p->width -= (spaceToClaim-spaceClaimed);
            spaceClaimed += (spaceToClaim-spaceClaimed);
            return spaceClaimed;
            }
        }
      else
        {
        //cout << "????   p=p->bookOnRight;\n";
        p=p->bookOnRight;
        }
    }
  
  //cout << "????   Getting out of reclaimSpace\n";
  return spaceClaimed;
  }


//destructor for CSheld
CShelf::~CShelf(void)
  {
  while (this->firstBook != NULL)
    {
    this->lastBook = this->firstBook->bookOnRight;
    //cout << "Deleting " << this->firstBook->id << " " << this->firstBook->width << endl;
    delete this->firstBook;
    this->firstBook = this->lastBook;
    }

  //cout << "....................................\n";
  }


void test_constructorFragmented(void)
  {
  cout << "--------------------------\n";
  cout << "test_constructorFragmented\n";
  cout << "--------------------------\n";
  for (int t=1; t<=TEST_COUNT; t++)
    {
    CShelf myShelf('f');

    //myShelf.displayBrief(); cout << endl;
    myShelf.displaySimple();cout << endl;
    //myShelf.displayDetailed();
    cout << "------------------------------------\n";
    }
  }


void test_constructorRandom(void)
  {
  cout << "---------------\n";
  cout << "test_constructorRandom\n";
  cout << "---------------\n";
  for (int t=1; t<=TEST_COUNT; t++)
    {
    CShelf myShelf('r');

    myShelf.displayBrief(); cout << endl;
    myShelf.displaySimple();cout << endl;
    myShelf.displayDetailed();
    cout << "------------------------------------\n";
    }
  }

  
/*
A constructor to build a populated shelf
*/
CShelf::CShelf(char ch)
  {
  if ('r' == ch)
    {
    this->count = 0;
    this->width = rand()%(MAX_SHELF_WIDTH-MIN_SHELF_WIDTH+1) + MIN_SHELF_WIDTH;
    this->widthOccupied = 0;
    this->firstBook = NULL;
    this->lastBook = NULL;

    for (int b=1; b<=TEST_COUNT; b++)
      {
      int bookID = rand()% (MAX_ID-MIN_ID+1) + MIN_ID;
      int bookWidth = rand()%((MAX_SHELF_WIDTH-MIN_SHELF_WIDTH)/8+1) + 1;
      //cout << "bookID=" << bookID << ", booWidth=" << bookWidth << endl;
      if (this->width >= (this->widthOccupied + bookWidth)) 
        this->insertAtLeft(bookID, bookWidth);
      else
        ;//cout << "    No room on the shelf\n";
      }
    }
  else if ('f' == ch) //fragmented (with empty bookshelf
    {
    this->count = 0;
    this->width = rand()%(MAX_SHELF_WIDTH-MIN_SHELF_WIDTH+1) + MIN_SHELF_WIDTH;
    this->widthOccupied = 0;
    this->firstBook = NULL;
    this->lastBook = NULL;

    for (int b=1; b<=TEST_COUNT; b++)
      {
      int bookID = rand()% (MAX_ID-MIN_ID+1) + MIN_ID;
      int bookWidth = rand()%((MAX_SHELF_WIDTH-MIN_SHELF_WIDTH)/8+1) + 1;
      //cout << "bookID=" << bookID << ", booWidth=" << bookWidth << endl;
      if (this->width >= (this->widthOccupied + bookWidth)) 
        this->insertAtLeft(bookID, bookWidth);
      else
        ;//cout << "    No room on the shelf\n";
      }

    //fragment this bookshelf
    CBook *p = this->lastBook;

    if (p != NULL)
      {
      while (p->bookOnLeft != NULL)
        {
        p = p->bookOnLeft;
        if (rand()%2 == 1)
          {
          p->id = 0;
          this->widthOccupied -=p->width;
          }
        }
      }
    }
   else
    {
    this->count = 0;
    this->width = rand()%(MAX_SHELF_WIDTH-MIN_SHELF_WIDTH+1) + MIN_SHELF_WIDTH;
    this->widthOccupied = 0;
    this->firstBook = NULL;
    this->lastBook = NULL;
    }
  }


void test_remove_byID(void)
  {
  cout << "==========\n";
  cout << "test_remove_byID\n";
  cout << "==========\n";
  for (int t=1; t<=TEST_COUNT/TEST_COUNT; t++)
    {
    CShelf myShelf('r');

    for (int b=1; b<=TEST_COUNT*3; b++)
      {
      myShelf.displayBrief(); cout << endl;
      myShelf.displaySimple();cout << endl;
      myShelf.displayDetailed();cout << endl;

      int bookID = rand()% (MAX_ID-MIN_ID+1) + MIN_ID;
      
      cout << "About to delete\n";
      cout << "bookID    = " << bookID << endl << endl;

      if (myShelf.remove(bookID))
        cout << "Remove was a success\n";
      else
        cout << "Remove was not a success\n\n";

      myShelf.displayBrief(); cout << endl;
      myShelf.displaySimple();cout << endl;
      myShelf.displayDetailed();cout << endl;

      cout << "------------------------------------\n";

      if (myShelf.isEmpty())
        break;
      }

    cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
    }
  }


/*
Possibilities:
  bookID is <= 0
  shelf is empty
  sought book is not on the shelf
  book is the rightmost
  book is not the rightmost (change th id to 0)
  only book on the shelf

Algorithm:
  if shelf is empty then return false
  sought book is not on the shelf then return false
  set the id to 0
  delete the rightmost empty space
*/
bool CShelf::remove(int bookID)
  {
  //if bookID is 0 or negative return false
  if (bookID <= 0)
    return false;

  //if shelf is empty then return false
  if (this->firstBook == NULL)
    return false;

  //sought book is not on the shelf then return false
  CBook *p;
  p = this->search(bookID);
  if (NULL == p) 
    return false;

  //set the id to 0
  p->id = 0;
  this->count--;
  this->widthOccupied -= p->width;

  //delete the righmost empty books
  while (this->lastBook->id == 0)
    {
    if (this->lastBook == this->firstBook)
      {
      delete this->lastBook;
      this->lastBook = this->firstBook = NULL;
      break;
      }
    else
      {
      p = this->lastBook;
      this->lastBook = this->lastBook->bookOnLeft;
      this->lastBook->bookOnRight = NULL;
      delete p;
      }
    }

  return true;
  }


CBook* CShelf::search(int bookID) const
  {
  CBook *p;

  p = this->firstBook;

  while (p != NULL)
    {
    if (p->id == bookID)
      return p;
    p = p->bookOnRight;
    }

  return NULL;
  }


void CShelf::displayDetailed(void) const
  {
  cout << "Shelf\n";
  cout << "  count         = " << this->count << endl;
  cout << "  width         = " << this->width << endl;
  cout << "  widthOccupied = " << this->widthOccupied << endl;
  cout << "  firstBook @   = " << this->firstBook << endl;
  cout << "  lastBook @    = " << this->lastBook << endl;

  cout << "First to last order:\n";
  CBook *p;
  p = this->firstBook;
  while (p != NULL)
    {
    cout << "@" << p 
         <<" [" << p->bookOnLeft << ", "
         << p->id << ", " 
         << p->width << ", "
         << p->bookOnRight << "]\n";
    p = p->bookOnRight;
    }
 
  cout << "\nLast to first order:\n";
  p = this->lastBook;
  while (p != NULL)
    {
    cout << "@" << p 
         <<" [" << p->bookOnLeft << ", "
         << p->id << ", " 
         << p->width << ", "
         << p->bookOnRight << "]\n";
    p = p->bookOnLeft;
    }
 
  }


void CShelf::displaySimple(void) const
  {
  CBook *p;

  p = this->firstBook;
  cout << "[c:" << this->count << " w:"
    << this->width <<   " o:"
    << this->widthOccupied <<   " e:"
    << this->reclaimableSpace() << "]=";

  while (p != NULL)
    {
    for (int i=1; i<=p->width; i++)
      cout << p->id;

    p = p->bookOnRight;
    }
  }


void CShelf::displayBrief(void) const
  {
  CBook *p;
  p = this->firstBook;
  while (p != NULL)
    {
    cout << "[" << p->id << ", " << p->width << "] ";
    p = p->bookOnRight;
    }
 
  }


void test_insertAtLeft(void)
  {
  display_test_header("test_insertAtLeft");

  for (int t=1; t<=TEST_COUNT/TEST_COUNT; t++)
    {
    int shelfWidth = 
      rand()% (MAX_SHELF_WIDTH-MIN_SHELF_WIDTH+1) + MIN_SHELF_WIDTH;
    CShelf myShelf(shelfWidth);

    cout << "Shelf width = " << shelfWidth << "\n";

    for (int b=1; b<=TEST_COUNT; b++)
      {
      int bookID = rand()% (MAX_ID-MIN_ID+1) + MIN_ID;
      int bookWidth = rand()%(shelfWidth/2+1) + 1;
      
      cout << "About to insert\n";
      cout << "bookID    = " << bookID << endl;
      cout << "bookWidth = " << bookWidth << endl;

      if (myShelf.insertAtLeft(bookID, bookWidth))
        cout << "Insert was a success\n";
      else
        cout << "Insert was not a success\n";

      myShelf.displayBrief(); cout << endl;
      myShelf.displaySimple();cout << endl;
      myShelf.displayDetailed();cout << endl;
      cout << "------------------------------------\n";
      }
    cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";
    }
  }

  
/*
insertAtLeft(booID, bookWidth)
Description:
  A new book is pushed on the left end of the shelf, 
  pushing other books to the right as needed. 
  No book moves to the right unless it is pushed by 
  an adjacent (touching) book on its left. Any book 
  that is not entirely on the shelf falls off the 
  right edge. No single book will ever be wider than 
  the given shelf. No book that is currently on the 
  shelf will be added again. 
  
Algorithm:
  if bookWidth > shelfWidth then return false
  if search(bookID) is true then return false
  insert book(bookID, bookWidth) at the front
  count++
  if count >1 then
    occupy empty space (ID=0) on right if necessary
    space occupied should be equal to bookWidth
    if widthOccupied > shelfWidth then
      drop the last book(s), 
      update widthOccupied, 
      update count
*/
bool CShelf::insertAtLeft(int bookID, int bookWidth)
  {
  if (bookWidth > this->width)
    return false;

  if (this->search(bookID) != NULL)
    return false;

  CBook *p;
  p = new CBook;
  p->id = bookID;
  p->width = bookWidth;
  p->bookOnLeft = NULL;

  if (0 == this->count)
    {
    p->bookOnRight = NULL;
    this->firstBook = p;
    this->lastBook = p;
    }
  else
    {
    p->bookOnRight = this->firstBook;
    this->firstBook->bookOnLeft = p;
    this->firstBook = p;
    }

  this->widthOccupied +=bookWidth;
  this->count++;

  //drop the last book(s) if necessary
  while (widthOccupied > this->width)
    //drop the last book(s), 
    {
    CBook *p;
    p = this->lastBook;
    this->lastBook = this->lastBook->bookOnLeft;
    this->lastBook->bookOnRight = NULL;
    this->widthOccupied -=p->width;
    //cout << "Book id=" << p->id << " of width=" << p->width << " was dropped\n";
    delete p;
    this->count--;
    }

  return true;
  }


void test_constructorWidth(void)
  {
  char *test_name = "test_constructorWidth";
  display_test_header(test_name);

  for (int t=1; t<=TEST_COUNT; t++)
    {
    int width = rand()%(MAX_SHELF_WIDTH-MIN_SHELF_WIDTH+1) + MIN_SHELF_WIDTH;
    CShelf myShelf(width);

    myShelf.displayBrief(); cout << endl;
    myShelf.displaySimple();cout << endl;
    myShelf.displayDetailed();
    cout << "------------------------------------\n";
    }
  }


CShelf::CShelf(int width)
  {
  this->count = 0;
  this->width = width;
  this->widthOccupied = 0;
  this->firstBook = NULL;
  this->lastBook = NULL;
  }


void test_constructorDefault(void)
  {
  char *test_name = "test_constructorDefault";
  display_test_header(test_name);

  for (int t=1; t<=TEST_COUNT; t++)
    {
    CShelf myShelf;

    myShelf.displayBrief(); cout << endl;
    myShelf.displaySimple();cout << endl;
    myShelf.displayDetailed();
    cout << "------------------------------------\n";
    }
  }


void display_test_header(char *test_name)
  {
  cout << '+';
  for (unsigned int i=1; i<=strlen(test_name)+2; i++)
    cout << '-';

  cout << '+' << endl;
  
  cout << "| " << test_name << " |" << endl;

  cout << '+';
  for (unsigned int i=1; i<=strlen(test_name)+2; i++)
    cout << '-';

  cout << '+' << endl << endl;
  }


CShelf::CShelf(void)
  {
  this->count = 0;
  this->width = MIN_SHELF_WIDTH;
  this->widthOccupied = 0;
  this->firstBook = NULL;
  this->lastBook = NULL;
  }


CBook::CBook(void)
  {
  this->id = this->width = UNDEFINED;
  this->bookOnLeft = this->bookOnRight = NULL;
  }