/*
   Recursion & Searching

*/ 

#include <iostream> 
#include <cstdio>
#include <stdlib.h>
using namespace std; 


/* Recursion is a method where solution depends on smaller instances of the same problem. 
   Typically this is implemented as a function calling itself with a different set of 
   parameters. 
   
   e.g. write a program that uses recursion to countdown from a given parameter.
*/

/*
void countdown(int i){
  if ( i != 0 ){
    cout << i << endl; 
    countdown( i-1 );
  }
}

int main(int argc, char* argv[]) {  
  if ( argc != 2 ) 
    return 1; 
  
  countdown( atoi(argv[1])); 
  
  return 0;
}
*/

/* To keep track of which function to return to, computer uses a call stack. A stack is a
   first-in last-out (FILO) data structure where a new entry is 'pushed' on top of existing
   entries. Only the member of the top of the stack can be extracted or 'popped'.

   Most recursive functions can be written in an iterative form. In C++ by using 
   looping constructs while and for. 

 */ 

/* ex: write power function using recursion (calculates x to the power y).
   
   power(x, y) = 1                      if y = 0
                 x                      if y = 1
                 x.power(x, y-1)        if y > 1
*/

/*
int power( int x, int y ) {
  if ( y == 0 )
    return 1; 
  else if ( y == 1 )
    return x; 
  else if ( y > 1 ) 
    return x * power( x, y-1 ); 
}


int main(int argc, char* argv[]) {
  if ( argc != 3)
    return 1; 

  cout << "result = " << power(atoi(argv[1]),atoi(argv[2])) << endl;
  return 0; 
}
*/


/* ex: write factorial function using recursion.

   factorial(x) = 1                     if x = 1
                  x.factorial(x-1)      if x > 1

*/

/*
int factorial(int x){
  if ( x == 1 ) 
    return 1; 
  else if( x > 1 )
    return x * factorial( x - 1 ) ; 
}

int main(int argc, char* argv[]) {
  if ( argc != 2 ) 
    return 1; 
  cout << argv[1] << "!= " << factorial(atoi(argv[1])) << endl;
  
  return 0; 
}
*/

/* ex: printing iteratively vs. recursively */

/*
class array{
private: 
  int* data;
  int size; 
public:
  array(int n): size(n) { data = new int[size] ; }
  void set( int x, int v ) { data[x] = v; }
  int getSize() { return size; }
  int getItem(int i) { return data[i]; }
  void printI();
  void printR(int);
  int linearSearch(int);
  int binarySearch(int);
  int binarySearchR(int, int, int);
}; 

void array::printI(){
  for( int i=0; i<getSize(); i++ )
    cout << getItem(i) << " " ;
  cout << endl;
}

void array::printR(int x){
  if ( x < getSize() ) {
    cout << getItem(x) << " " ; 
    printR(x+1); 
  }
  if ( x == 0 )
    cout << endl ;
}
*/

/*
int main(){
  array A(5); 
  for( int i=0; i<5; i++ )
    A.set(i,i*10); 
  cout << "output from iterative print: " << endl; 
  A.printI();     
  cout << "output from recursive print: " << endl; 
  A.printR(0);

  return 0;
}
*/

/* Searching: for a value in a data structure given the key. Depending on how the data is 
   organized efficiency of search strategies may vary drastically. 

   Assume the data structure is an array and not sorted. The below example is an implementation
   of linear search, which compares every value inside the array with the one that it searches
   for. returns the value when it finds it and an error message otherwise. 
   
*/

/*
int array::linearSearch(int key){
  int count = 0; 
  for( int i=0; i<getSize(); i++ ){
    if ( getItem(i) == key ){
      cout << "count= " << count << endl;
      return i; 
    }
    count++ ;
  }
  cout << "count= " << count << endl;
  return -1 ;
}
*/

/*
int main(){
  int x ; 
  array A(5); 
  for( int i=0; i<5; i++ )
    A.set(i, i*10); 
  cout << "here is the array: " ; 
  A.printR(0);

  cout << "looking for item 30..." ;
  x = A.linearSearch(30); 
  if ( x == -1 ) 
    cout << "not found!" << endl; 
  else
    cout << "found at location: " << x << endl; 

  cout << "looking for item 65..." ;
  x = A.linearSearch(65); 
  if ( x == -1 ) 
    cout << "not found!" << endl; 
  else
    cout << "found at location: " << x << endl; 
  
  return 0;
}
*/

/* How would linear search can benefit from underlying data structure being SORTED? */


/* Binary Search: Only works on sorted data. Idea is very similar to how you would search
   for a number in a phone book. Knowing the data is sorted alphabetically, you would start
   looking for a name that starts with a 'K' somewhere from the middle of the book. If the 
   names in the randomly accessed page contains the name that you look for, then the 
   search is over. If the names start with letters preceeding 'K' in the alphabet, you'd 
   ignore the first part of the phone book, otherwise you'd ignore the last part of it. If 
   you repeat this process you would eventually find your search item. 

*/

/*
int array::binarySearch(int key){
  int count = 0;
  int lo = 0, hi = getSize()-1, mid; 
  while( lo <= hi ){
    count++; 
    mid = ( lo + hi )/ 2 ; 
    if ( key == getItem(mid) ){
      cout << "count: " << count << endl; 
      return mid;
    }
    else if ( key < getItem(mid) ) 
      hi = mid - 1;
    else
      lo = mid + 1;
  }
  cout << "count: " << count << endl;
  return -1;
}
*/

/* Recursive Binary Search 
   binarySearchR(key, lo, hi) = -1                                if lo > hi
                                mid ((hi+lo)/2)                   if key = item at mid 
                                binarySearchR(key,mid+1,hi)       if item at mid < key
                                binarySearchR(key,lo,mid-1)       if item at mid > key
*/

/*
int array::binarySearchR(int key, int lo, int hi){
  if ( lo <= hi ){
    int mid = (lo + hi) / 2; 
    if( key == getItem(mid) ) 
      return mid; 
    else if ( getItem(mid) < key ) 
      return binarySearchR(key, mid + 1, hi); 
    else 
      return binarySearchR(key, lo, mid-1);
  }
  else 
    return -1;
}



int main(){
  int x ; 
  array A(5); 
  for( int i= 0; i<5; i++ )
    A.set(i, i*10); 
  cout << "here is the array: " ; 
  A.printR(0);

  cout << "looking for item 30..." ;
  x = A.binarySearchR(30, 0, A.getSize()-1); 
  if ( x == -1 ) 
    cout << "not found!" << endl; 
  else
    cout << "found at location: " << x << endl; 

  cout << "looking for item 15..." ;
  x = A.binarySearchR(15, 0, A.getSize()-1); 
  if ( x == -1 ) 
    cout << "not found!" << endl; 
  else
    cout << "found at location: " << x << endl; 
  
  return 0;
}
*/



/* Compare linear search with binary search, which one is faster? Is this always the case?
   Give an example where linear search would run faster than binary. 
*/
