//  Binary Calculator v0.1
// Binary Calculator v0.1
//  Programmer:   Bill McQuain
//  Platform  :   P120, Win95, Visual C++ 4.0
//  Completed :   November 7, 1996
// Completed :   November 7, 1996
//  Purpose:
// Purpose:
//  This program reads simple binary arithmetic expressions from an
//  input file (binary.dat), converts the operands to decimal, performs
//  the operation and writes the expressions and results to a file
//  (binary.out).
// (binary.out).
//  It is assumed the binary operands are valid binary numbers;  but
//  the operator is validated (+, -, *, or /).  All calculations are
//  integer.
// integer.
#include <fstream.h>    // for file streams
#include <iomanip.h>    // for manipulators
#include <ctype.h>      // for isdigit()

//////////////////////////////////////////////////////////////////////
// Function prototypes:
//Function prototypes:
int  BinToDecimal(ifstream& );
char GetOperation(ifstream& );
void SkipWhiteSpace(ifstream& );
int  CalcIt(int , int , char );
void PrintResults(ofstream& ,int , int , char , int );

//////////////////////////////////////////////////////////////////////
int main() {

   int  FirstOperand  = 0,    //  the first operand (in decimal)
        SecondOperand = 0;    //  the second operand (in decimal)
   char Operation   = '!';    //  the operator 
   int  Result;               //  result of performing the operation
                              //         on the two operands
   ifstream inbin;            //  input file stream
   ofstream outbin;           //  output file stream

   ///////////////////////////////////////////////////////////////////
   //  Open files and enable manipulators:
   // Open files and enable manipulators:
   inbin.open("binary.dat");
   outbin.open("binary.out");
   outbin.setf(ios::fixed, ios::floatfield);
   outbin.setf(ios::showpoint);

   ///////////////////////////////////////////////////////////////////
   //  Each pass through the while loop reads the operands and 
   //  operator from a line of the input file, performs the operation
   //  (if valid) and writes the results to the output file.
   // (if valid) and writes the results to the output file.
   //  The loop will terminate when the end of the input file is
   //  reached.  Note:  this assumes the EOF will occur precisely
   //  after the final bit of the second operand on the last line.
   // after the final bit of the second operand on the last line.
   while (!inbin.eof()) {
      FirstOperand  = BinToDecimal(inbin);
      Operation     = GetOperation(inbin);
      SecondOperand = BinToDecimal(inbin);
      Result        = CalcIt(FirstOperand, SecondOperand, Operation);
      PrintResults(outbin, FirstOperand, SecondOperand, Operation, 
                   Result);
   }

   ///////////////////////////////////////////////////////////////////
   //  Close the input and output files:
   inbin.close();
   outbin.close();

   ///////////////////////////////////////////////////////////////////
   //  Return the (final) value of FirstOperand --- useful when the
   //  code is being checked.
   // code is being checked.
   return FirstOperand;
}

//////////////////////////////////////////////////////////////////////
//  BinToDecimal reads a binary string from the input stream, 
//  converts it to decimal and returns the decimal value.  If there's
//  any whitespace preceding the binary string, it's consumed here. 
// any whitespace preceding the binary string, it's consumed here. 
//  Parameters:
//    infile      input file stream
//   infile      input file stream
//  Return value:
//    DecNumber   decimal value of binary string read from input file
//   DecNumber   decimal value of binary string read from input file
int BinToDecimal(ifstream& infile) {

   char ThisBit;           // last character read from stream
   int  DecNumber = 0;     // decimal value of binary string

   SkipWhiteSpace(infile); // skip whitespace (if any) preceding the
                           // binary string
   
   infile.get(ThisBit);    // read (probable) first bit

   ///////////////////////////////////////////////////////////////////
   //  As long as we're still reading bits, update value of DecNumber
   //  and get another character:
   // and get another character:
   while (ThisBit == '0' || ThisBit == '1') {
      switch (ThisBit) {
      case '0':   DecNumber = 2*DecNumber;
                  break;
      case '1':   DecNumber = 2*DecNumber + 1;
                  break;
      }
      infile.get(ThisBit);    // get the next character
   }

   ///////////////////////////////////////////////////////////////////
   //  Whatever the last character read above was, it wasn't part of
   //  this operand, so put it back and let the next function called
   //  deal with it:
   // deal with it:
   infile.putback(ThisBit);

   ///////////////////////////////////////////////////////////////////
   //  Return the decimal value of the binary string to the caller:
   // Return the decimal value of the binary string to the caller:
   return DecNumber;
}

//////////////////////////////////////////////////////////////////////
//  GetOperation reads the operator from the input stream and returns
//  it to the caller (as a character).  Note that I assume the next
//  nonwhitespace character after the first operand is the operator,
//  which is the only way the input line could be syntactically
//  correct.
// correct.
//  If there's any whitespace preceding the operator it's consumed 
//  here.
// here.
//  Parameters:
//    infile      input file stream
//   infile      input file stream
//  Return value:
//    Operator   decimal value of binary string read from input file
//   Operator   decimal value of binary string read from input file
char GetOperation(ifstream& infile) {

   char Operator;          // the operator
   SkipWhiteSpace(infile); // skip any preceding whitespace
   infile.get(Operator);   // read the operator
   return Operator;        // return it to the caller
}

//////////////////////////////////////////////////////////////////////
//  CalcIt computes and returns the result of performing the operation
//  on the two operands, if a valid operator was found.  If the 
//  operator was not valid, a value of -1 is returned (something must
//  be returned, might as well be -1).
// be returned, might as well be -1).
//  Parameters:
//    First       the first operand
//    Second      the second operand
//    Op          the operator (as a character)
//   Op          the operator (as a character)
//  Return value:
//    Result      the result of the integer operation
//   Result      the result of the integer operation
int CalcIt(int First, int Second, char Op) {

   int Result;       // the result

   ///////////////////////////////////////////////////////////////////
   //  Determine which operator was found and compute the correct
   //  result for that case.  The guard against a division by zero
   //  was not specifically required but should be done.
   // was not specifically required but should be done.
   switch (Op) {
      case '+':   Result = First + Second;
                  break;
      case '-':   Result = First - Second;
                  break;
      case '*':   Result = First * Second;
                  break;
      case '/':   if (Second != 0)
                     Result = First / Second;
                  else
                     Result = -1;
                  break;
      default :   Result = -1;
  }
  return Result;        // return the result to the caller
}

//////////////////////////////////////////////////////////////////////
//  SkipWhiteSpace reads characters from the input file until it finds
//  a nonwhitespace character and leaves that character at the front
//  of the input stream.
// of the input stream.
//  Parameters:
//    infile      input file stream
//   infile      input file stream
void SkipWhiteSpace(ifstream& infile) {

   char ch;             // last character read from stream

   infile.get(ch);      // read a character

   ///////////////////////////////////////////////////////////////////
   //  As long as we're reading whitespace, continue reading:
   // As long as we're reading whitespace, continue reading:
   while (ch == ' ' || ch == '\t' || ch == '\n')
      infile.get(ch);

   ///////////////////////////////////////////////////////////////////
   //  The last character we read was not whitespace, so put it back:
   // The last character we read was not whitespace, so put it back:
   infile.putback(ch);
}

//////////////////////////////////////////////////////////////////////
//  PrintResults prints out the expression just read (in decimal), and
//  either the value of that expression or an error message, as
//  appropriate.
// appropriate.
//  Parameters:
//    outfile     output file stream
//    First       first operand
//    Second      second operand
//    Op          operator
//    Result      result of performing operation on operands
//   Result      result of performing operation on operands
void PrintResults(ofstream& outfile, int First, int Second, char Op, 
                      int Result) {

   outfile << setw(5) << First;        // print first operand
   outfile << setw(3) << Op;           //     and operator
   outfile << setw(5) << Second;       //     and second operand
   outfile <<  " = "                  //     and the equals sign
   
   ///////////////////////////////////////////////////////////////////
   //  If the operator isn't valid, print an error message.  Otherwise
   //  if there's a divide-by-zero, print an error message.  Otherwise
   //  just print the result:
   // just print the result:
   if (Op != '+' && Op != '-' && Op != '*' && Op != '/') 
      outfile << "invalid operation" << endl;
   else
      if (Op == '/' && Second == 0)
         outfile << "attempt to divide by zero" << endl;
      else
         outfile << setw(5) << Result << endl;
}