Tuesday, April 14, 2015

How I passed the Amazon interview!

In this post, I'm going to talk about how I prepared for the Amazon interview and passed it!

I posted on my Facebook profile that I got an offer from the Amazon Instant Video team which was visiting Egypt to hire software development engineers for positions in Seattle, London, and India.

I was offered a Software Development Engineer II position in Seattle, US. Now I'm waiting for the results of the H1B visa lottery which should be coming within days, and see if I'm lucky enough to be selected!

Anyway, I was really glad at the amount of feedback I received after sharing my experience with Amazon especially from Computer Science and Engineering students.
Apparently, many of them didn't know that the big software companies like Amazon and Microsoft regularly hire from Egypt and that there are MANY Egyptians working in the US and Europe for companies like Microsoft, Amazon, Facebook, Google, and even more.
Maybe that's because people no longer share their experience. I'm very glad I did.

Now to the important question: How to prepare for the Amazon interview? Same way you prepare for any other interview at a big software company:
Don't just study for the interview, but learn to become a better developer and continuously learn to improve!

That's what I'm going to talk about her. I will talk about my personal experience, how I improved, and how anyone else can also do the same.
  1. Learn at least C++, Java, or C# very well. It would be better if you learn C++ along with either Java or C#. Read about them and make your choice. For C++, I started by reading "The C++ Programming Language" by Bjarne Stroustrup, then "Professional C++ - Wrox". Some suggest "Object Oriented Programming using C++" too especially for beginners.
  2. Study Mathematics well, especially Discrete Mathematics. This MIT course is a good start http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-spring-2005/index.htm
  3. Read the book "Introduction to Algorithms". You don't have to read about the very advanced topics and know how to implement Red-Black trees. Just read the basics and watch the online video course here http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/video-lectures/. You should at least watch lectures 1-8 and 15-17. You should know about Linked Lists, Stacks, Queues, Heaps and Priority Queues, Binary Search Trees, Hashtable, Graphs. You should know Quick Sort, Merge Sort, Counting Sort which sorts in O(N) time! You should know about order statistics and the median, and how to select the kth smallest element(s) in O(N) using the quicksort partition routine. You should know how to implement hashtables and how they work, and how to do depth-first search and breadth-first search, topological sorting, and how to find the shortest path using at least one algorithm like Dijkstra's.
  4. JOIN ACM! If you're still a student, you have to do this. I didn't, and you can definitely become a good developer and very good at problem solving even if you didn't join ACM, but it helps a lot.
  5. Read the following books and try to do your best to solve the problems "Programming Interviews Exposed", "Cracking the Coding Interview", "Algorithms for Interviews", and "Programming Pearls" which is a bit old but gold.
  6. Become a problem solver! Join TopCoder and CodeForces, LeetCode, and participate in competitions and SRMs!
  7. Watch the very interesting videos here by Mostafa Saad on the topics of your interesthttps://www.facebook.com/ArabicAlgorithmsSeries, and try to solve the exercises at the end of each video. These are some great videos about very interesting problems related to Graph Theory, Dynamic Programming, Number Theory, and more. They definitely can speed things up and help you become better at programming competitions!
  8. Read about operating systems. Understand threads and different types of locks. Read about mutexes, sempahors, critical sections, spinlocks, read-write locks and how paging is done. Read about interrupts and interrupt service routines and understand computer architecture.
  9. Read on Object Oriented Design and understand design patterns. Read this book "Design Patterns: Elements of Reusable Object-Oriented Software"
  10. If you already graduated from college, try to work for a company where you will learn a lot! Amazon, Google and Facebook care a lot about scale! Try to find a company where you will work on scalability problems.
  11. While interviewing, just relax :) The interview is going to be challenging and a lot of fun. Communicate to the interviewer all the time and let him know what's in your mind. Try to solve the problem really hard, but if you're stuck, he'll probably jump in with hints. If he does, really think about them to guide you through the way!
  12. You're going to meet people who work on very interesting projects used by hundreds of millions around the world. It's going to going to be a fun experience that you can learn a lot from. Have fun!
Good Luck :)

Sunday, April 6, 2014

Get That Job at NTP Software!

What is NTP Software?

NTP Software is a worldwide leader in storage management software. It creates computer software that help enterprises manage their storage servers. We create software to help these enterprises that have data on the order of hundreds of terabytes (or in some cases petabytes) manage their storage, set quotas for their users (NTP Software QFS) , and get detailed reports about their storage composition. We help them find out which servers are going to fill up soon, which users are storing non-business files (NTP Software File Reporter), and allow them to automatically backup unused data to slower (and bigger) servers, then retrieve them when needed (NTP Software Precision Tiering). We allow them to securely access their data from any device consistent with its Active Directory security and other policies (NTP Software Universal File Access).

As you can see, our software works with very huge data, and sometimes thousands or tens of thousands of users (One of our customers has over 200,000 users), so performance and scalability are very important to us.

Why would you want to work for NTP Software?
  1. Experience: You gain more experience working in NTP Software than most of the other software development houses here in Egypt.
  2. Trust: In NTP Software, we choose our own process, and you are trusted to work on any piece of the software and propose any new ideas/features. In fact, you are encouraged and asked to do so!
  3. Team: NTP Software has a great team! It has some of the most talented and bright developers I ever worked with in Egypt. Most of them are Ex-ACMers or active TopCoders. It feels great to work with such a great team. We also hold monthly meetings where we share knowledge about new technologies or product news.
  4. Work/Life Balance: Work/Life balance is encouraged in NTP Software, we set our own deadlines and estimate the time for our tasks. You are expected to work smart on your tasks and do your best to meet the deadline by being a good developer, but rarely if ever you'll ever be asked to work in weekends (I never heard that it happened), you are even discouraged from working in weekends.

What would you need to know to work for NTP Software?

It really depends on which position you're applying for, but let me talk about the C++ Developer position as most of our vacancies are for this position.

  1. C++: You need to know C++ well which is obvious since this is a C++ position.
  2. Multithreading: You need to have good knowledge with multithreading, know about different inter-process communication techniques, understand locks pretty well, know how to avoid deadlocks in your code, etc ..
  3. Data Structures/Algorithms: You need to know about the basic data structures and algorithms and know the time/space tradeoffs.
  4.  Windows Programming: You should have a basic knowledge about Windows API and inter-process communication techniques in Windows.
It would be a good bonus if you know ASP.net, SQL or have good experience with debugging and assembly.

How can you apply for NTP Software?

Send me your resume here :)

ufayez at NTPSoftware dot Com

See you in another post ...

Saturday, February 8, 2014

Get That Job At Valeo

Since many of my friends and friends of my friends usually ask me about Valeo, what it does, what to expect in the interview process and in work, I decided to write a small post to help you pass the interview and to determine if this is what you really need. I will talk about other companies as well. These posts are going to be based on my experience and that of my colleagues and friends.

Valeo is an automotive supplier. It develops components for automotive OEMs like BMW, Ford, VW, etc ... These components are like Park4U (Auto Parking), Temperature Control, Vision, etc ... A modern car can include more than twenty ECUs or computers to control different things inside the car.

Valeo sells the whole component (Mechanical parts, electronics and software) as one package. It has many factories and development and research centers.

Basically, the interview process consists of the following steps (not necessarily in the same order).
  • MCQ Exam (C language, Embedded Systems and Software Development Process)
  • English Exam
  • Technical Interview
  • HR Interview (In English)
  • GM Interview
The MCQ exam is a used to filter candidates who will go through the interview. To pass MCQ, you need to review or search the following

  • C Language basics and know about different keywords and their usage and tricks like "volatile"
  • Know what interrupts and critical sections are
  • Know about V-Cycle model and the different stages of software development using V-Cycle model
  • Research about CAN and LIN protocols that are widely used in Automotive industry. Just know their basics.
  • Know about basic microcontroller architecture, PWM, ISRs, IO Ports, etc ..
The HR interview is pretty typical and mostly like any other HR interview but you should prepare for it well. You have to pass the HR interview to get an offer. I think most rejections are actually because they didn't pass the HR interview.

For the Technical interview, it should be very easy to pass, I was asked some very basic questions like: what is a bootloader? what are the main components inside a microcontroller? write a function to sort N integers ... 
Some of my friends even got easier programming questions like a function to get the average of N integers or calculate the factorial! 

Now to the important part : what to expect from Valeo? 
  1. Stability : Valeo is a well-established corporation that has been there for more than half a century.
  2. Great Environment (Smart Village).
  3. Good Package and Insurance.
  4. Great and Friendly People.
  5. Working on Big Projects that are used by thousands of people.
  6. Personal development opportunities through different training courses (Communication Skills, Team Leadership, Handling work pressure, etc ..)

I hope this helps you set your expectations, and helps you to get an offer if you wish to apply :-)

See you in another post ..

Friday, September 6, 2013

Josephus Problem!

You're forced to commit suicide, and you will use math to save yourself!

weird, huh? but this actually happened. Quoting from Wikipedia:

"The problem is named after Flavius Josephus, a Jewish historian living in the 1st century. According to Josephus' account of the siege of Yodfat, he and his 40 soldiers were trapped in a cave, the exit of which was blocked by Romans. They chose suicide over capture and decided that they would form a circle and start killing themselves using a step of three. Josephus states that by luck or maybe by the hand of God, he and another man remained the last and gave up to the Romans."

Problem Statement

There are people standing in a circle waiting to be executed. The counting out begins at some point in the circle and proceeds around the circle in a fixed direction. In each step, a certain number of people are skipped and the next person is executed. The elimination proceeds around the circle (which is becoming smaller and smaller as the executed people are removed), until only the last person remains, who is given freedom.
The task is to choose the place in the initial circle so that you are the last one remaining and so survive.


so how can we solve this problem?

We'll consider the case where we repeatedly execute the second person in the circle (k = 2) in a circle of size N. We want to find the position of the person who will be the last to execute himself, but of course he wouldn't, he would like to survive!

let's see the case for N = 1, Solution = 1 as this is the only person in the circle.
for N = 2, Solution = 1, as the second person will execute himself and "1" remains.

for N = 3, we have circle = (1, 2, 3). 2 will execute himself, then we're now standing at "3" as the first element and we have circle = (1, 3). the second person will execute himself "1", then the survivor is "3".

for N = 8, we have circle = (1, 2, 3, 4, 5, 6, 7, 8).

in the first run, "2", "4", "6" and "8" will all be executed and we'll end up with (1, 3, 5, 7).
we repeat again, "3" and "7" will be executed". we now have "1" and "3". "3" will be executed, now we have "1" as the survivor.

here is a list of the results for some N.

N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1

How can we solve this with code?

Trivial solution is to model the group as a circular linked list and repeatedly delete every second element until the size of the list is zero.

Code for this is something like


typedef struct Node
{
   int value;
   Node *pNext;
} Node;

int josephus(int N)
{
   Node *head, *curr;
   // Error case
   if(N <= 0)
      return -1;

   // Create a circular list of people with
   // 1 -> 2 -> 3 -> .... -> N -> 1
   curr = head = new Node();
   head->value = 1;

   for(int i = 2; i <= N; ++i)
   {
         curr->pNext = new Node();
         curr = curr->pNext;
         curr->value = i;
   }

   curr->pNext = head;

   curr = head;
   // While not everyone is dead
   while(N > 1)
   {
      Node *tmp = curr->pNext;
      curr->pNext = tmp->pNext;
      delete tmp;
      curr = curr->pNext;
      --N;
   }

   return curr->value;
}


This is an O(N) time complexity solution and also O(N) space. Can we do better?
Yes!

With a few observations, if we have a circle of size (2 * N), meaning that it has an even number of people, like (1, 2, 3, 4, 5, 6). After the first run, "2", "4", "6" are eliminated and we have "1", 3", and "5". A circle of size "3" where each person id is multiplied by "2" then "1" is subtracted from it.

So, in order to find the solution of a circle with size N where N is even, we simply find the solution of circle (N/2) then multiply it by "2" and subtract "1".

in other means, S(N) = 2 * S(N-1) - 1, where N is even.
What if we have a circle of odd size?

(1, 2, 3, 4, 5, 6, 7) let's say it's of size (2 * N + 1).

After the first run, "2, 4, 6" are deleted which eliminates the "N" part, we then need to eliminate one more item to get to the smaller subproblem, so the next element to be eliminated is "1".

We end up with "3, 5, 7". Comparing it to "1, 2, 3". we see that each element is multiplied by "2", then added to "1"

so the total recurrence is:

S(N) = 2 * S(N/2) - 1, where N is even
S(N) = 2 * S(N/2) + 1, where N is odd

We can simply write a recursive solution for this with the base case of N(1) = 1, the complexity of this solution is O(lg N) as it divides the size of problem by two.

code for this can be something like

int josephus(int N)
{
   if(N <= 0)
      return -1;
   else if (N == 1)
      return 1;
   else
   {
      // testing the least significant bit to know
      // if this is an odd number
      if(N&1)
         return 2 * josephus(N/2) + 1;
      else
         return 2 * josephus(N/2) - 1;
   }
}

Can we do better than O(lg n)? Yes we can!
Let's see some other simple observations

N = 1, S = 1
N= 2, S = 1
N = 3, S = 3
N = 4, S = 1
N = 5, S = 3
N = 6, S = 5
N = 7, S = 7
N = 8, S = 1
N = 9, S = 3
N = 10, S = 5
N = 11, S = 7
N = 12, S = 9
N = 13, S = 11
N = 14, S = 13
N = 15, S = 15.

You can see that at every power of "2", solution resets to "1" then, "2" is added repeatedly till we reach the next power of 2. This can be proved by mathematical induction. So one way to solve this is the following:

if M is the biggest power of "2" that is less than or equal to N, let L = N - M;
Now multiply L by 2 as we're adding "2" at every step, then finally add "1". The result will be S = 2 * L + 1.

let's see some example
for N = 14, the last power of "2" is "8", set L = N - 8 = 6;
S = 2 * L + 1 = 13!

how can we find the last power of "2" greater than or equal to N? this is simply the leftmost set "1" bit in N if we represent it in binary.

Now, how can we write code for this in O(1)? it should be something like this:

This code will work if int is 32-bit size. It's called bit smearing and is used to find the left-most "1" bit, we're then going to reset it and that will simply subtract the power of two from the number.
we'll then shift it left by "1" to multiply it by two and add "1".

int josephus(int n)
{
   int m = n;
   if(n <= 0)
      return -1;
   m |= (m >> 16);
   m |= (m >> 8);
   m |= (m >> 4);
   m |= (m >> 2);
   m |= (m >> 1);
   m ^= (m >> 1);
   n &= ~m;
   return (n << 1)|1;
}

You can search or think of solutions for the general case when k != 2.


See you soon...

Wednesday, September 4, 2013

TopCoder BiggestRectangleEasy

This is an easy problem, but can be solved with short code using bit-wise binary operators, and as I'm always a big fan of binary tricks, I decided to post it here.

Problem Statement

     Little Josh has found several sticks that are each 1 inch long. He wants to form a rectangle with the biggest possible area, using these sticks as the perimeter. He is allowed to glue sticks together, but he is not allowed to break a single stick into multiple shorter sticks.
For example, if Josh has 11 sticks, he can create a 2 x 3 rectangle using 10 sticks. This rectangle has an area of 6 square inches, which is the biggest area that can be achieved in this case.
You will be given an int N, and you must return the maximal area (in square inches) of a rectangle that can be created using N or less sticks.


so how can we solve this problem?

Let's see what we can do. First if the number of sticks is odd, we have to remove one stick as there can't be a rectangle with an odd perimeter. We have a rectangle with a perimeter N, and we all know that the perimeter of a rectangle is 2 * (rectangle's width + rectangle's height).

suppose width is "w" and height is "h", we divide by "2" to get sum = "w + h".

Now, we want to maximize "w * h" and we already have sum = "w + h".
one way to find "w" and "h" that maximize the product, is to try with "w" = 1 and h = "sum - 1" then store their product, then keep increasing "w" and decreasing "h" to find the values that will maximizes their product.

So this will give us a linear-time solution in terms of "w + h". A simple observation however, is that in order to maximize "w * h", "w" and "h' have to be as close as possible (I don't know a mathematical proof for that, if you know one please post it here". So, we simply set "w" = sum/2 and "h" = sum - w, and we have the maximum area that we can have with N sticks!.

Now let's see code for this.

int findArea(int N)
{
   // If the number of sticks is odd, remove one.
   // Divide by 2 to get "w+h"
   N >>= 1;
   // set "w" to "N/2"
   int w = N >> 1;
   // return "w * h"
   return (w * (N - w));
}

Tuesday, August 27, 2013

UVa 10570 - Meeting with Aliens

UVa Problem

This problem has to do with an important concept, permutation cycles.

Code:


#include <iostream>
#include <list>
#include <vector>

using namespace std;

typedef vector<int> perm;

list<perm> getPermCycles(perm p)
{
   const int N = p.size();
   list<perm> permCycles;
   vector<bool> used(N, false);

   for(int i = 0; i < N; ++i)
   {
      if(used[i] == false)
      {
         used[i] = true;
         perm newPerm;
         newPerm.push_back(i);

         int nextIndex = p[i];

         while(nextIndex != i)
         {
            newPerm.push_back(nextIndex);
            used[nextIndex] = true;
            nextIndex = p[nextIndex];
         }
         permCycles.push_back(newPerm);
      }
   }
   return permCycles;
}

int numSwaps(const list<perm>& perms)
{
   int result = 0;

   for(list<perm>::const_iterator i = perms.begin();
         i != perms.end(); ++i)
   {
      result += i->size();
   }

   result -= perms.size();

   return result;
}

perm mulPerm(perm perm1, perm perm2)
{
   perm result(perm1.size());
   perm inverted(perm1.size());

   for(int i = 0; i < inverted.size(); ++i)
      inverted[perm1[i]] = i;

   for(int i = 0; i < perm1.size(); ++i)
   {
      result[inverted[i]] = perm2[i];
   }

   return result;
}

int main(int argc, char *argv[])
{
   int N;

   while( (cin >> N) && N)
   {
      perm initPerm(N);
      perm rInitPerm(N);
      for(int i = 0; i < N; ++i)
      {
         cin >> initPerm[i];
         --initPerm[i];
         rInitPerm[N - i - 1] = initPerm[i];
      }

      int nSwaps = N - 1;

      perm identity(N);
      for(int i = 0; i < N; ++i)
         identity[i] = i;

      for(int i = 0; i < N; ++i)
      {
         perm res = mulPerm(identity, initPerm);
         nSwaps = min(nSwaps,
               numSwaps(getPermCycles(res)));

         res = mulPerm(identity, rInitPerm);
         nSwaps = min(nSwaps,
               numSwaps(getPermCycles(res)));

         for(int j = 0; j < N; ++j)
            identity[j] = (identity[j] + 1) % N;
      }
      cout << nSwaps << endl;
   }
   return 0;
}


Monday, August 12, 2013

UVa 533 - Equation Solver

UVa Problem

This problem is about how to solve a linear equation. Since we're sure that it will always be a linear equation, the solution is simple.

we have a complex equation like this "(42-6*7)*x=2*5-10"

my solution divides the equation into two expressions around the "=" operator.
then we reduce each expression into one of the form "a * x + b" where "a" and "b" are integers.

so we reduces the whole equation to "a1 * x + b1 = a2 * x + b2"

there are three possibilities:

  1. "a1 = a2" and "b1 = b2", in this case we have an infinite number of solutions as any value of x will satisfy the equation.
  2. "a1 = a2" and "b1 != b2", then we have no solutions as no value of x will ever satisfy the equation.
  3. "a1 != a2", then we can calculate "x" simply as "x" = (b2 - b1) / (a1 - a2).
I wrote a simple recursive-decent parser for the problem and kept reducing the expression as I go up in the parse tree.

Code:


#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <sstream>

using namespace std;

enum TokenType
{
   TOKEN_INT,
   TOKEN_EQ,
   TOKEN_X,
   TOKEN_LP,
   TOKEN_RP,
   TOKEN_PLUS,
   TOKEN_MINUS,
   TOKEN_MUL,
   TOKEN_END
};

class Token
{
public:
   Token(TokenType tokType) : mType(tokType)
   {

   }
   TokenType getType()
   {
      return mType;
   }
private:
   TokenType mType;
};

class IntToken : public Token
{
public:
   IntToken(int value) : mValue(value),
   Token(TOKEN_INT)
   {

   }
   int getValue()
   {
      return mValue;
   }
private:
   int mValue;
};

class Lexer
{
public:
   Lexer(istream& iStream) : mStream(iStream)
   {

   }
   Token* getNextToken()
   {
      int ch = mStream.get();
      char ccc = (char)ch;
      while(ch == ' ')
         ch = mStream.get();

      switch(ch)
      {
         case '=':
            return new Token(TOKEN_EQ);
            break;
         case '+':
            return new Token(TOKEN_PLUS);
            break;
         case '-':
            return new Token(TOKEN_MINUS);
            break;
         case '*':
            return new Token(TOKEN_MUL);
            break;
         case 'x':
            return new Token(TOKEN_X);
            break;
         case '(':
            return new Token(TOKEN_LP);
            break;
         case ')':
            return new Token(TOKEN_RP);
            break;
         case EOF:
            return new Token(TOKEN_END);
            break;
         default:
            if(ch >= '0' && ch <= '9')
            {
               int number = 0;

               while(ch >= '0' && ch <= '9')
               {
                  number *= 10;
                  number += ch - '0';
                  ch = mStream.get();
               }

               mStream.putback(ch);

               return new IntToken(number);
            }
            else
            {
               return new Token(TOKEN_END);
            }
            break;

      }
   }
private:
   istream& mStream;
};

class Solver
{
public:
   Solver(istringstream& exprStream):
      mLexer(exprStream), mCurrTok(0)
   {
      mCurrTok = mLexer.getNextToken();
      pair<int, int> result = expr();
      m_x = result.first;
      m_num = result.second;
   }


   int get_xFactor()
   {
      return m_x;
   }

   int get_numFactor()
   {
      return m_num;
   }

private:

   pair<int, int> expr()
   {
      pair<int, int> result = term();
      pair<int, int> tmp = rExpr();
      result.first += tmp.first;
      result.second += tmp.second;
      return result;
   }

   pair<int, int> rExpr()
   {
      pair<int, int> result = make_pair(0, 0);
      if(mCurrTok->getType() == TOKEN_PLUS)
      {
         match(TOKEN_PLUS);
         pair<int, int> tmp = term();
         result.first += tmp.first;
         result.second += tmp.second;
         tmp = rExpr();
         result.first += tmp.first;
         result.second += tmp.second;
      }
      else if (mCurrTok->getType() == TOKEN_MINUS)
      {
         match(TOKEN_MINUS);
         pair<int, int> tmp = term();
         result.first -= tmp.first;
         result.second -= tmp.second;
         tmp = rExpr();
         result.first += tmp.first;
         result.second += tmp.second;
      }
      return result;
   }

   pair<int, int> term()
   {
      pair<int, int> tmp1 = factor();
      pair<int, int> tmp2 = rTerm();

      return make_pair(tmp1.first * tmp2.second +
            tmp1.second * tmp2.first,
            tmp1.second * tmp2.second);

   }

   pair<int, int> rTerm()
   {
      if(mCurrTok->getType() == TOKEN_MUL)
      {
         match(TOKEN_MUL);
         return term();

      }
      return make_pair(0, 1);;
   }

   pair<int, int> factor()
   {
      if(mCurrTok->getType() == TOKEN_X)
      {
         match(TOKEN_X);
         return make_pair(1, 0);
      }
      else if(mCurrTok->getType() == TOKEN_INT)
      {
         IntToken* intTok = (IntToken*)mCurrTok;
         int val = intTok->getValue();
         match(TOKEN_INT);
         return make_pair(0, val);
      }
      else
      {
         match(TOKEN_LP);
         pair<int, int> exprVal = expr();
         match(TOKEN_RP);
         return exprVal;
      }
   }

   bool match(TokenType tokType)
   {
      if(mCurrTok->getType() == tokType)
      {
         delete mCurrTok;
         mCurrTok = mLexer.getNextToken();
         return true;
      }
      else
      {
         // Handle errors
      }

      return false;
   }
private:
   Lexer mLexer;
   Token *mCurrTok;
   int m_num;
   int m_x;
};

int main(int argc, char* argv[])
{

   string strLine;
   int iEqu = 1;
   bool firstTime = true;

   while(cin >> strLine)
   {
      istringstream inputStream(strLine);
      int iPos = strLine.find_first_of('=');
      string expr1 = strLine.substr(0, iPos);
      string expr2 = strLine.substr(iPos+1);

      istringstream strStream1(expr1);
      istringstream strStream2(expr2);

      Solver s1(strStream1);
      Solver s2(strStream2);

      if(!firstTime)
         cout << '\n';
      else
         firstTime = false;

      cout << "Equation #" << iEqu << endl;

      if(s1.get_xFactor() == s2.get_xFactor())
      {
         if(s1.get_numFactor() == s2.get_numFactor())
         {
            cout << "Infinitely many solutions.\n";
         }
         else
         {
            cout << "No solution.\n";
         }
      }
      else
      {
         double x = (double)(s2.get_numFactor() -
               s1.get_numFactor())/
               (double)(s1.get_xFactor() -
               s2.get_xFactor());
         
         printf("x = %.6f\n", x);
      }
      ++iEqu;
   }
   return 0;
}