Debugging

Steven Zeil

Last modified: Jul 15, 2014

Contents:
1. You can’t debug what you don’t understand
1.1 Read the Documentation
1.2 Know What’s Reasonable
1.3 Know the Roadmap
2. Make it fail …
2.1 Make it fail Reliably
2.2 Make it fail Easily
2.3 Make it fail Visibly
3. Example: the Auction Program
3.1 Simplifying the Failing Tests
3.2 Instrument the Code
3.3 Working Backwards
4. Lessons

Testing versus Debugging


Rules of Debugging

  1. You can’t debug what you don’t understand

  2. Make it fail…

  3. Track down the culprit

In this lesson we’ll focus on the first two

1. You can’t debug what you don’t understand

How can you hope to fix (or even recognize) what is wrong if you don’t know what it will look like when it’s right?

1.1 Read the Documentation

1.2 Know What’s Reasonable

Engineers often talk about “sanity checks” and “back-of-the-envelope” estimates. These refer to quick considerations of what is actually a reasonable result in a given situation.

So if you see something that isn’t reasonable, your first responses should be to mistrust the calculations (or, in our case, the code) that produced that “obviously” unreasonable behavior.

Where do we get the knowledge of what is and isn’t reasonable?

1.3 Know the Roadmap

Large programs are made of many components. When you see an output that is incorrect, your debugging will be much more efficient if you have an idea which of those components contributed to that particular output.

Example: if you were debugging this program after seeing an identical mistake in both reports, would you look first at the report generators or would you concentrate on the “shared” input processing and main calculation?

2. Make it fail …

Typically you start debugging because your program failed

What’s the next thing you need to do?


Make It Fail Again

Once You’ve Made it Fail …


Why “Make It Fail Again”?

2.1 Make it fail Reliably

Intermittent Failures


Can Hardware Failures Be Truly Intermittent?


Can Software Failures Be Truly Intermittent?


Tracking Down Intermittent Failures


What Causes Intermittent Software Failures?

Typically,


Uninitialized variables


Out of bounds data access


Pointer errors - dangling pointers

Dangling pointers

We saw earlier that if bc is destroyed, …

we are left with dangling pointers.


Pointer errors - Deleting a pointer twice

Question: What happens if all the collections were destroyed and they each deleted the pointer?

Answer

2.2 Make it fail Easily

Debugging often requires you to run the failing test cases over and over again.

Simplify Your Failing Tests


Simplify, Simplify, Simplify

If you made the program fail on the 100th input or 100th time around a loop, can you find a simpler test that fails after the 10th time?

Shortening that loop gives you less to debug.


KISS

Later we will make a distinction between unit testing and system testing,


If It Takes Too Long to Fail…

Example: When Do Pointer Errors Fail?


Example: When Do Pointer Errors Fail? (cont.)


Tracking Pointer Errors - Redefining Failure

2.3 Make it fail Visibly


Program States

A program state is the combination of


Infected States

A program state is infected if one of its components is affected by a bug :


Propagating an Infection

As we move forward through the code, an infected state can


From Infection to Failure

A failure occurs when an infected state affects (propagates to) the program output.

Instrument the System


Design Instrumentation In

Good rule of thumb for C++ ADT designers:

Always provide an output function for your ADT, showing the value of each data member.


Build Instrumentation In Later

debugOutputDemo.cpp
#include <iostream>

using namespace std;


#define DEBUGOUT(x) cerr << #x << "@" << __FILE__ << ":" << __LINE__ \
   << ": " << x << endl;


int main() 
{
  int k = 0;
  string y = "hello";
  cout << "Real output " << k << endl;
  cerr << "Debugging output " << k+1 << endl;
  cout << "Real output " << y << endl;
  cerr << "I suspect " << y << " is incorrect at "
       << __FILE__ << ":" << __LINE__ << endl;
  cout << "Real output " << k << endl;
  DEBUGOUT (k);
  DEBUGOUT (y);
  DEBUGOUT (k + k);  
  DEBUGOUT (y.size());
  return 0;
}


Never Throw a Debugging Tool Away

When you have finished debugging a problem, deactivate your debug code, don’t delete it!

debugOutputDemo2.cpp
#include <iostream>

using namespace std;


#ifdef DEBUG
#define DEBUGOUT(x) cerr << #x << "@" << __FILE__ << ":" << __LINE__ \
   << ": " << x << endl;
#else
#define DEBUGOUT(x) // ignored if not debugging
#endif

int main() 
{
  int k = 0;
  string y = "hello";
  cout << "Real output " << k << endl;
#ifdef DEBUG
  cerr << "Debugging output " << k+1 << endl;
#endif
  cout << "Real output " << y << endl;
#ifndef NDEBUG
  cerr << "I suspect " << y << " is incorrect at "
       << __FILE__ << ":" << __LINE__ << endl;
#endif
  cout << "Real output " << k << endl;
  DEBUGOUT (k);
  DEBUGOUT (y);
  DEBUGOUT (k + k);  
  DEBUGOUT (y.size());
  return 0;
}


The Heisenberg Uncertainty Principle

Debugging instrumentation may affect the behavior of the bug

3. Example: the Auction Program

As last seen: Auction ADTs


Failure Report

During an early run, the seller of a Chinese Vase complained that a bid for $27 was accepted as a winner even though a reserve price of $50 had been set.

3.1 Simplifying the Failing Tests


Rounding Up the Suspects

We have our test case!

3.2 Instrument the Code

The most obvious place to check for this problem is in the resolveAuction function

3.3 Working Backwards

Problem appears to be in the selection of winningBidderSoFar and highestBidSoFar.

resolveAuction1.cpp
/**
 * Determine the winner of the auction for an item.
 * Announce the winner and remove money from winner's account.
 */
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
                }
            }
            if (bid.getAmount() > item.getReservedPrice())
                reservePriceMet = true;
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
          << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}
\

 These are initialized outside (before) the loop and reset
  inside, so we might want to check that the values are reasonable when
  they are reset.

When a New Best Bid is Found

Adding debugging output:

resolveAuction2.cpp
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
                  cerr << "Best bid so far is "
               << highestBidSoFar << " from "
                         << winningBidderSoFar << endl;
                }
            }
            if (bid.getAmount() > item.getReservedPrice())
                reservePriceMet = true;
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}

gives us the output:

Best bid so far is 26 from jjones
Hummel Figurine won by jjones for 26
Best bid so far is 27 from ssmith
Chinese Vase won by ssmith for 27

which does not really tell us much.


Adding Detail

resolveAuction3.cpp
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
                  cerr << "Best bid so far is ["
               << bid.getBidder() << " "
               << bid.getAmount() << " "
               << bid.getItem() << " ";
          bid.getTimePlacedAt().print(cerr);
          cerr << "] from ["
               << bidder.getName() << " "
               << bidder.getBalance() << "]"
               << endl;
                }
            }
            if (bid.getAmount() > item.getReservedPrice())
                reservePriceMet = true;
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}

gives us the output:

Best bid so far is [jjones 26 Hummel Figurine 00:18:03] from [jjones 75]
Hummel Figurine won by jjones for 26
Best bid so far is [ssmith 27 Chinese Vase 04:03:05] from [ssmith 55]
Chinese Vase won by ssmith for 27

all of which looks OK.


Adding Detail the Easy Way

resolveAuction4.cpp
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
                  cerr << "Best bid so far is ";
          bid.print (cerr);
          cerr << " from ";
          bidder.print (cerr);
          cerr << endl;
                }
            }
            if (bid.getAmount() > item.getReservedPrice())
                reservePriceMet = true;
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}

For this to work, though, we need to add the print functions to bids and bidders (that we should probably have had there in the first place.)


Auction with Output Functions

auction/bids.h
#ifndef BIDS_H
#define BIDS_H

#include <iostream>
#include <string>
#include "time.h"


//
//  Bids Received During Auction
//

class Bid {

  std::string bidderName;
  double amount;
  std::string itemName;
  Time bidPlacedAt;

public:

  Bid (std::string bidder, double amt, 
       std::string item, Time placedAt);

  Bid();

  // Access to attribute
  std::string getBidder() const {return bidderName;}
  double getAmount() const {return amount;}
  std::string getItem() const {return itemName;}
  Time  getTimePlacedAt() const {return bidPlacedAt;}

  // Print a bid
  void print (std::ostream& out) const;
};




#endif

and

auction/bids.cpp
#include <string>
#include <fstream>

using namespace std;

//
//  Bids Received During Auction
//

#include "bids.h"


Bid::Bid (std::string bidder, double amt, 
     std::string item, Time placedAt)
  : bidderName(bidder), amount(amt),
    itemName(item), bidPlacedAt(placedAt)
{
}

Bid::Bid ()
  : amount(0.0)
{
}


// Print a bid
void Bid::print (std::ostream& out) const
{
  out << "[" << bidderName << " " << amount
      << " " << itemName << " ";
  bidPlacedAt.print (out);
  out << "]";
}

auction/bidders.h
#ifndef BIDDERS_H
#define BIDDERS_H

#include <iostream>
#include <string>

//
//  Bidders Registered for auction
//


class Bidder {
  // describes someone registered to participate in an auction
  std::string name;
  double balance;

public:
  
  Bidder();
  Bidder (std::string theName, double theBalance);

  // Access to attributes
  std::string getName() const {return name;}

  double getBalance() const {return balance;}
  void setBalance (double newBal) {balance = newBal;}

  // print a bidder
  void print (std::ostream& out) const;
};


#endif

and

auction/bidders.cpp
#include <string>
#include <fstream>
#include <iostream>
//
//  Bidders Registered for auction
//

#include "bidders.h"

using namespace std;



Bidder::Bidder()
{
  name = "";
  balance = 0.0;
}

Bidder::Bidder (std::string theName, double theBalance)
{
  name = theName;
  balance = theBalance;
}


// print a bidder
void Bidder::print (std::ostream& out) const
{
  out << "[" << name << " " << balance << "]";
}

auction/items.h
#ifndef ITEMS_H
#define ITEMS_H

#include <iostream>
#include <string>

#include "time.h"

//
//  Items up for auction
//


class Item {
  std::string name;
  double reservedPrice;
  Time auctionEndsAt;

public:
  Item();
  Item (std::string itemName, double reserve, Time auctionEnd);

  // Access to attribute

  std::string getName() const {return name;}
  
  double getReservedPrice() const {return reservedPrice;}

  Time getAuctionEndTime() const {return auctionEndsAt;}


  /**
   * Read one item from the indicated file
   */
  void read (std::istream& in);

  // Print an item
  void print (std::ostream& out) const;
};


#endif

and

auction/items.cpp
#include <iostream>
#include <fstream>

#include "items.h"

//
//  Items up for auction
//

using namespace std;




Item::Item()
  : reservedPrice(0.0)
{
}

Item::Item (std::string itemName, double reserve, Time auctionEnd)
  : name(itemName), reservedPrice(reserve), auctionEndsAt(auctionEnd)
{
}


/**
 * Read one item from the indicated file
 */
void Item::read (istream& in)
{
  in >> reservedPrice;
  auctionEndsAt.read (in);

  // Reading the item name.
  char c;
  in >> c; // Skips blanks and reads first character of the name
  string line;
  getline (in, line); // Read the rest of the line
  name = string(1,c) + line;
}


// print a bidder
void Item::print (std::ostream& out) const
{
  out << "[" << name << " " << reservedPrice << " ";
  auctionEndsAt.print (out);
  out << "]";
}

auction/time.h
#ifndef TIMES_H
#define TIMES_H

#include <iostream>

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */


struct Time {
  // Create time objects
  Time(); // midnight
  Time (int h, int m, int s);

  // Access to attributes
  int getHours() const;
  int getMinutes() const;
  int getSeconds() const;

  // Calculations with time
  void add (Time delta);
  Time difference (Time fromTime);
  
  /**
   * Read a time (in the format HH:MM:SS) after skipping any
   * prior whitepace.
   */
  void read (std::istream& in);


  /**
   * Print a time in the format HH:MM:SS (two digits each)
   */
  void print (std::ostream& out) const;

  /**
   *  Compare two times. Return true iff time1 is earlier than or equal to
   *  time2
   */
  bool noLaterThan(const Time& time2);


  /**
   *  Compare two times. Return true iff time1 is equal to
   *  time2
   *
   */
  bool equalTo(const Time& time2);

  // From here on is hidden
  int secondsSinceMidnight;
};

#endif // TIMES_H

and

auction/time.cpp
#include "time.h"
#include <iomanip>

using namespace std;

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */



  // Create time objects
Time::Time() // midnight
{
  secondsSinceMidnight = 0;
}


Time::Time (int h, int m, int s)
{
  secondsSinceMidnight = s + 60 * m + 3600*h;
}

  // Access to attributes
int Time::getHours() const
{
  return secondsSinceMidnight / 3600;
}

int Time::getMinutes() const
{
  return (secondsSinceMidnight % 3600) / 60;
}

int Time::getSeconds() const
{
  return secondsSinceMidnight % 60;
}

// Calculations with time
void Time::add (Time delta)
{
  secondsSinceMidnight += delta.secondsSinceMidnight;
}
      

Time Time::difference (Time fromTime)
{
  Time diff;
  diff.secondsSinceMidnight = 
    secondsSinceMidnight - fromTime.secondsSinceMidnight;
}
  
  
/**
 * Read a time (in the format HH:MM:SS) after skipping any
 * prior whitepace.
 */
void Time::read (std::istream& in)
{
  char c;
  int hours, minutes, seconds;
  in >> hours >> c >> minutes >> c >> seconds;
  Time t (hours, minutes, seconds);
  secondsSinceMidnight = t.secondsSinceMidnight;
}


/**
 * Print a time in the format HH:MM:SS (two digits each)
 */
void Time::print (std::ostream& out) const
{
  out << setfill('0') << setw(2) << getHours() << ':' 
      << setfill('0') << setw(2) << getMinutes() << ':' 
      << setfill('0') << setw(2) << getSeconds();
}



/**
 *  Compare two times. Return true iff time1 is earlier than or equal to
 *  time2
 */
bool Time::noLaterThan(const Time& time2)
{
  return secondsSinceMidnight <= time2.secondsSinceMidnight;
}



/**
 *  Compare two times. Return true iff time1 is equal to
 *  time2
 *
 */
bool Time::equalTo(const Time& time2)
{
  return secondsSinceMidnight == time2.secondsSinceMidnight;
}

auction/bidcollection.h
#ifndef BIDCOLLECTION_H
#define BIDCOLLECTION_H

#include "bids.h"
#include <iostream>

class BidCollection {

  int MaxSize;
  int size;
  Bid* elements; // array of bids 

public:

  /**
   * Create a collection capable of holding the indicated number of bids
   */
  BidCollection (int MaxBids = 1000);

  ~BidCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  const Bid& get(int index) const {return elements[index];}



  // Collection operations


  void addInTimeOrder (const Bid& value);
  //  Adds this bid into a position such that 
  //   all bids are ordered by the time the bid was placed
  //Pre: getSize() < getMaxSize()



  void remove (int index);
  // Remove the bid at the indicated position
  //Pre: 0 <= index < getSize()


  /**
   * Read all bids from the indicated file
   */
  void readBids (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};


#endif

and

auction/bidcollection.cpp
#include "bidcollection.h"
#include "arrayUtils.h"
#include <fstream>

using namespace std;


/**
 * Create a collection capable of holding the indicated number of bids
 */
BidCollection::BidCollection (int MaxBids)
  : MaxSize(MaxBids), size(0)
{
  elements = new Bid [MaxSize];
}

BidCollection::~BidCollection ()
{
  delete [] elements;
}



// Collection operations


void BidCollection::addInTimeOrder (const Bid& value)
//  Adds this bid into a position such that 
//   all bids are ordered by the time the bid was placed
//Pre: getSize() < getMaxSize()
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && 
     value.getTimePlacedAt().noLaterThan(elements[toBeMoved].getTimePlacedAt())) {
    elements[toBeMoved+1] = elements[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  elements[toBeMoved+1] = value;
  ++size;  
}


void BidCollection::remove (int index)
// Remove the bid at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}

/**
 * Read all bids from the indicated file
 */
void BidCollection::readBids (std::string fileName)
{
  size = 0;
  ifstream in (fileName.c_str());
  int nBids;
  in >> nBids;
  for (int i = 0; i < nBids; ++i)
    {
      char c;
      
      string bidderName;
      double amount;
      in >> bidderName >> amount;
      
      Time bidPlacedAt;
      bidPlacedAt.read (in);
      
      string word, line;
      in >> word; // First word of itemName
      getline (in, line); // rest of item name
      
      string itemName = word + line;
      addInTimeOrder (Bid (bidderName, amount, itemName, bidPlacedAt));
    }
}


// Print the collection
void BidCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}

auction/biddercollection.h
#ifndef BIDDERCOLLECTION_H
#define BIDDERCOLLECTION_H

#include "bidders.h"
#include <iostream>

//
//  Bidders Registered for auction
//


class BidderCollection {

  int MaxSize;
  int size;
  Bidder* elements; // array of items 

public:

  /**
   * Create a collection capable of holding the indicated number of items
   */
  BidderCollection (int MaxBidders = 1000);

  ~BidderCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  Bidder& get(int index) {return elements[index];}



  // Collection operations


  int add (const Bidder& value);
  //  Adds this bidder to the collection at an unspecified position.
  //  Returns the position where added.
  //Pre: getSize() < getMaxSize()



  void remove (int index);
  // Remove the item at the indicated position
  //Pre: 0 <= index < getSize()


  int findBidder (std::string name) const;
  // Returns the position where a bidde mathcing the given
  // name can be found, or getSize() if no bidder with that name exists.



  /**
   * Read all items from the indicated file
   */
  void readBidders (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};


#endif




and

auction/biddercollection.cpp
#include <iostream>
#include "arrayUtils.h"
#include <fstream>

#include "biddercollection.h"

using namespace std;


/**
 * Create a collection capable of holding the indicated number of items
 */
BidderCollection::BidderCollection (int MaxBidders)
  : MaxSize(MaxBidders), size(0)
{
  elements = new Bidder [MaxSize];
}

BidderCollection::~BidderCollection ()
{
  delete [] elements;
}



// Collection operations


int BidderCollection::add (const Bidder& value)
//  Adds this bidder
//Pre: getSize() < getMaxSize()
{
  addToEnd (elements, size, value);
  return size - 1;
}


void BidderCollection::remove (int index)
// Remove the bidder at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}




/**
 * Read all bidders from the indicated file
 */
void BidderCollection::readBidders (std::string fileName)
{
    size = 0;
    ifstream in (fileName.c_str());
    int nBidders;
    in >> nBidders;
    for (int i = 0; i < nBidders && i < MaxSize; ++i)
    {
      string nme;
      double bal;
      in >> nme >> bal;
      Bidder bidder (nme, bal);;
      add (bidder);
    }
}


/**
 * Find the index of the bidder with the given name. If no such bidder exists,
 * return nBidders.
 */
int BidderCollection::findBidder (std::string name) const
{
  int found = size;
  for (int i = 0; i < size && found == size; ++i)
    {
      if (name == elements[i].getName())
    found = i;
    }
  return found;
}


// Print the collection
void BidderCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}

auction/itemcollection.h
#ifndef ITEMCOLLECTION_H
#define ITEMCOLLECTION_H

#include "items.h"
#include <iostream>

class ItemCollection {

  int MaxSize;
  int size;
  Item* elements; // array of items 

public:

  /**
   * Create a collection capable of holding the indicated number of items
   */
  ItemCollection (int MaxItems = 1000);

  ~ItemCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  const Item& get(int index) const {return elements[index];}



  // Collection operations


  void addInTimeOrder (const Item& value);
  //  Adds this item into a position such that 
  //   all items are ordered by the time at which the auction for the
  //   item ends.
  //Pre: getSize() < getMaxSize()



  void remove (int index);
  // Remove the item at the indicated position
  //Pre: 0 <= index < getSize()


  /**
   * Read all items from the indicated file
   */
  void readItems (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};


#endif



and

auction/itemcollection.cpp
#include <iostream>
#include "arrayUtils.h"
#include <fstream>

#include "itemcollection.h"

//
//  Items up for auction
//

using namespace std;


/**
 * Create a collection capable of holding the indicated number of items
 */
ItemCollection::ItemCollection (int MaxItems)
  : MaxSize(MaxItems), size(0)
{
  elements = new Item [MaxSize];
}

ItemCollection::~ItemCollection ()
{
  delete [] elements;
}



// Collection operations


void ItemCollection::addInTimeOrder (const Item& value)
//  Adds this item into a position such that 
//   all items are ordered by the time the item' auction ends
//Pre: getSize() < getMaxSize()
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && 
     value.getAuctionEndTime().noLaterThan(elements[toBeMoved].getAuctionEndTime())) {
    elements[toBeMoved+1] = elements[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  elements[toBeMoved+1] = value;
  ++size;  
}


void ItemCollection::remove (int index)
// Remove the item at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}



/**
 * Read all items from the indicated file
 */
void ItemCollection::readItems (std::string fileName)
{
  size = 0;
  ifstream in (fileName.c_str());
  int nItems;
  in >> nItems;
  for (int i = 0; i < nItems; ++i)
    {
      Item item;
      item.read (in);
      addInTimeOrder (item);
    }
}

// Print the collection
void ItemCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}



Looking at All Bids

Since the determination of the best bid is not obviously wrong, we might wonder if all the non-best bids are being handled correctly. In particular, we recall that there were more than two bids for the two items.

resolveAuction5.cpp
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  cerr << "resolving item ";
  item.print(cerr);
  cerr << endl;
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      cerr << "Looking at bid ";
      bid.print (cerr);
      cerr << endl;
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
          cerr << "Bidder is ";
          bidder.print (cerr);
          cerr << endl;
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
          cerr << "Best bid so far is ";
          bid.print (cerr);
          cerr << " from ";
          bidder.print (cerr);
          cerr << endl;
                }
            }
            if (bid.getAmount() > item.getReservedPrice())
                reservePriceMet = true;
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}

gives output

resolving item [Hummel Figurine 25 15:30:11]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Bidder is [jjones 75]
Best bid so far is [jjones 26 Hummel Figurine 00:18:03] from [jjones 75]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Hummel Figurine won by jjones for 26
resolving item [Chinese Vase 50 20:06:27]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
Bidder is [jjones 49]
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
Bidder is [ssmith 55]
Best bid so far is [ssmith 27 Chinese Vase 04:03:05] from [ssmith 55]
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Chinese Vase won by ssmith for 27

It’s still not clear why the low bid by smith is being accepted, but we can see that all bids are in fact being processed in some fashion.

Since we are not getting enough info about the reserve, let’s also monitor when the reservePriceMet flag changes.


When Does reservePriceMet change?

resolveAuction6.cpp
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
  cerr << "resolving item ";
  item.print(cerr);
  cerr << endl;
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
      cerr << "Looking at bid ";   
      bid.print (cerr);
      cerr << endl;
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
          cerr << "Bidder is ";
          bidder.print (cerr);
          cerr << endl;
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
          cerr << "Best bid so far is ";
          bid.print (cerr);
          cerr << " from ";
          bidder.print (cerr);
          cerr << endl;
                }
            }
            if (bid.getAmount() > item.getReservedPrice()) 
          {
                reservePriceMet = true;
                cerr << "reserve price met by bid ";
        bid.print (cerr);
        cerr << endl;
          }
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}

which has output

resolving item [Hummel Figurine 25 15:30:11]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Bidder is [jjones 75]
Best bid so far is [jjones 26 Hummel Figurine 00:18:03] from [jjones 75]
reserve price met by bid [jjones 26 Hummel Figurine 00:18:03]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
reserve price met by bid [jjones 60 Chinese Vase 04:03:01]
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
reserve price met by bid [ssmith 27 Chinese Vase 04:03:05]
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Hummel Figurine won by jjones for 26
resolving item [Chinese Vase 50 20:06:27]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
Bidder is [jjones 49]
reserve price met by bid [jjones 60 Chinese Vase 04:03:01]
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
Bidder is [ssmith 55]
Best bid so far is [ssmith 27 Chinese Vase 04:03:05] from [ssmith 55]
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Chinese Vase won by ssmith for 27

Diagnosis

Looking at that output:

debugOutput.listing
resolving item [Hummel Figurine 25 15:30:11]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Bidder is [jjones 75]
Best bid so far is [jjones 26 Hummel Figurine 00:18:03] from [jjones 75]
reserve price met by bid [jjones 26 Hummel Figurine 00:18:03]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
reserve price met by bid [jjones 60 Chinese Vase 04:03:01]
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
reserve price met by bid [ssmith 27 Chinese Vase 04:03:05] ➊
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Hummel Figurine won by jjones for 26
resolving item [Chinese Vase 50 20:06:27]
Looking at bid [jjones 26 Hummel Figurine 00:18:03]
Looking at bid [jjones 60 Chinese Vase 04:03:01]
Bidder is [jjones 49]                                      ➌
reserve price met by bid [jjones 60 Chinese Vase 04:03:01] ➋
Looking at bid [ssmith 27 Chinese Vase 04:03:05]
Bidder is [ssmith 55]
Best bid so far is [ssmith 27 Chinese Vase 04:03:05] from [ssmith 55]
Looking at bid [jjones 25 Chinese Vase 05:00:00]
Chinese Vase won by ssmith for 27

Now we can see, not just one, but two problems.

So the condition

  if (bid.getAmount() > item.getReservedPrice()) 
    {
      reservePriceMet = true;

is far too lax.


Diagnosis Precedes the Cure

At this point, we have diagnosed the problem.

auction/auctionMain.cpp
#include <iostream>
#include <string>

using namespace std;

#include "items.h"
#include "itemcollection.h"
#include "bidders.h"
#include "biddercollection.h"
#include "bids.h"
#include "bidcollection.h"
#include "time.h"

const int MaxBidders = 100;
const int MaxBids = 5000;
const int MaxItems = 100;


/**
 * Determine the winner of the auction for item #i.
 * Announce the winner and remove money from winner's account.
 */
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids);


int main (int argc, char** argv)
{
  if (argc != 4)
    {
      cerr << "Usage: " << argv[0] << " itemsFile biddersFile bidsFile" << endl;
      return -1;
    }

  ItemCollection items (MaxItems);
  items.readItems (argv[1]);

  BidderCollection bidders (MaxBidders);
  bidders.readBidders (argv[2]);

  BidCollection bids (MaxBids);
  bids.readBids (argv[3]);

  for (int i = 0; i < items.getSize(); ++i)
    {
      resolveAuction(items.get(i), bidders, bids);
    }
  return 0;
}


/**
 * Determine the winner of the auction for an item.
 * Announce the winner and remove money from winner's account.
 */
void resolveAuction (const Item& item, 
                     BidderCollection& bidders, 
                     BidCollection& bids)
{
#ifdef DEBUG
  cerr << "resolving item ";
  item.print(cerr);
  cerr << endl;
#endif
  double highestBidSoFar = 0.0;
  string winningBidderSoFar;
  bool reservePriceMet = false;
  for (int bidNum = 0; bidNum < bids.getSize(); ++bidNum)
    {
      Bid bid = bids.get(bidNum);
#ifdef DEBUG
      cerr << "Looking at bid ";   
      bid.print (cerr);
      cerr << endl;
#endif
      if (bid.getTimePlacedAt().noLaterThan(item.getAuctionEndTime()))
        {
          if (bid.getItem() == item.getName()
              && bid.getAmount() > highestBidSoFar
              )
            {
              int bidderNum = bidders.findBidder(bid.getBidder());
              Bidder bidder = bidders.get(bidderNum);
#ifdef DEBUG
          cerr << "Bidder is ";
          bidder.print (cerr);
          cerr << endl;
#endif
              // Can this bidder afford it?
              if (bid.getAmount() <= bidder.getBalance())
                {
                  highestBidSoFar = bid.getAmount();
                  winningBidderSoFar = bid.getBidder();
#ifdef DEBUG
          cerr << "Best bid so far is ";
          bid.print (cerr);
          cerr << " from ";
          bidder.print (cerr);
          cerr << endl;
#endif
          if (bid.getAmount() > item.getReservedPrice()) 
            {
              reservePriceMet = true;
#ifdef DEBUG
              cerr << "reserve price met by bid ";
              bid.print (cerr);
              cerr << endl;
#endif
            }
                }
            }
        }
    }

    // If highestBidSoFar is non-zero, we have a winner
    if (reservePriceMet && highestBidSoFar > 0.0)
      {
        int bidderNum = bidders.findBidder(winningBidderSoFar);
        cout << item.getName()
             << " won by " << winningBidderSoFar
             << " for " << highestBidSoFar << endl;
        Bidder& bidder = bidders.get(bidderNum);
        bidder.setBalance (bidder.getBalance() - highestBidSoFar);
      }
    else
      {
        cout << item.getName()
             << " reserve not met"
             << endl;
      }
}




4. Lessons