/*
   This class is a "library" of static methods that either take as
   an input parameter or return as a result a reference to an array.

   int       max                (int[]     inputArray)
   int       min                (int[]     inputArray)
   void      incArrayInPlace    (double[]  inputArray)
   double[]  incArray           (double[]  inputArray)
   int       incArray           (double[]  inputArray, double[] outputArray)
   int       searchFor          (int[]     inputArray, int target)
   int       numberOfOccurances (int[]     inputArray, int target)
   int[]     allOccurances      (int[]     inputArray, int target)
   int[]     randomArray        (int size, int range)

   Notice how, when a method has an input parameter that is a reference
   to an array, the parameter does not specify how big the array is.
   Each of these methods can process input arrays of any size.

   Similarly, when a method returns a reference to an array, the method
   does not specify how big the array is. So a method that returns a
   reference to an array can return a reference to an array of any size.
*/

public class ArrayMethods
{
   /**
      This method returns the maximum value of the contents
      of the array named inputArray.
   */
   public static int max(int[] inputArray)
   {
      int max = inputArray[0];
      for (int i = 1; i < inputArray.length; i++)
      {
         if (inputArray[i] > max)
         {
            max = inputArray[i];
         }
      }
      return max;
   }


   /**
      This method returns the minimum value of the contents
      of the array named inputArray.
   */
   public static int min(int[] inputArray)
   {
      int min = inputArray[0];
      for (int i = 1; i < inputArray.length; i++)
      {
         if (inputArray[i] < min)
         {
            min = inputArray[i];
         }
      }
      return min;
   }


   /**
      This method increments every entry in the input array.
      Notice that this method changes the array that it is
      passed a reference to. Notice also that this method
      does not have a return value. Its "return values" are
      in the array that it is passed a reference to.
   */
   public static void incArrayInPlace(double[] inputArray)
   {
      for(int i = 0; i < inputArray.length; i++)
      {
         ++inputArray[i];
      }
   }


   /**
      This method increments every entry in an input array but
      it puts the incremented values in a new array and then
      returns a reference to the new array. This method does not
      make any changes to the array that it is passed a reference to.
   */
   public static double[] incArray(double[] inputArray)
   {
      double[] outputArray = new double[inputArray.length];

      for(int i = 0; i < inputArray.length; i++)
      {
         outputArray[i] = inputArray[i] + 1;
         // explain why the above line cannot be replaced by the following line
         //outputArray[i] = ++inputArray[i];
      }

      // return a reference to the array named outputArray
      return outputArray;
   }


   /**
      This method increments every entry in an input array but
      it puts the incremented values in an output array using
      what is sometimes referred to as an "output parameter".
      The actual return value of the method is used to indicate
      "error conditions" (such as one of the array objects not
      existing). This is a technique used a lot in the Unix and
      Windows operating systems, i.e., methods "return" values
      through "output" reference parameters while the real return
      value of the method is used to indicate an error status.

      Notice that the previous two methods will cause runtime errors
      if the parameter reference variable inputArray does not point
      to an actual array object. This method detects that problem and
      uses the method's return value to indicate the existence of the
      problem (rather than, say, crashing the whole program).
   */
   public static int incArray(double[] inputArray, double[] outputArray)
   {
      if ( (inputArray != null && outputArray != null)
         &&( inputArray.length == outputArray.length ) )
      {
         for(int i = 0; i < inputArray.length; i++)
         {
            outputArray[i] = inputArray[i] + 1;
         }

         return 0;  // 0 indicate no "error condition"
      }
      else // the arrays either don't exist or are not the same length
      {
         return -1;  // -1 indicate an "error condition"
      }
   }


   /**
      This method returns the index of the first occurrence of the
      value of target in the array called inputArray. If the value
      of target never occurs in inputArray, then the value returned
      is equal to inputArray.length.
   */
   public static int searchFor(int[] inputArray, int target)
   {
      int i;

      for (i = 0; i < inputArray.length && inputArray[i] != target; i++)/*empty*/;

      return i;
   }


   /**
      This method counts and returns the number of times the
      value of target occurs in the input array.
   */
   public static int numberOfOccurances(int[] inputArray, int target)
   {
      int counter = 0;

      for (int i = 0; i < inputArray.length; i++)
      {
         if (inputArray[i] == target)
         {
            counter++;
         }
      }

      return counter;
   }


   /**
      This method returns an array that contains the index values of
      the places in the input array where the value target appears.

      Notice that the size of the output array depends on how many times
      the value of target appears in the input array.

      Notice that for this method, the length of the output array is the
      same as the return value of the previous method, numberOfOccurances().

      (Question: What happens if the value of target never occurs in inputArray?)
   */
   public static int[] allOccurances(int[] inputArray, int target)
   {
      // create a new array the same size as the input array
      int[] tempArray = new int[inputArray.length];

      int counter = 0; // counts number of occurrences of target in inputArray

      // After this for loop, counter will hold the number of occurrences
      // of target in inputArray and tempArray will be a partially filled
      // array containing the indexes of the occurrences of target in inputArray
      for (int i = 0; i < inputArray.length; i++)
      {
         if (inputArray[i] == target)
         {
            // put the index of the occurrence in the current slot of tempArray
            tempArray[counter] = i;
            counter++;
         }
      }

      // create another array the size of which is the number
      // of occurrences of target in inputArray
      int[] outputArray = new int[counter];

      // copy the contents of the partially filled array into outputArray
      for (int i = 0; i < outputArray.length; i++)
      {
         outputArray[i] = tempArray[i];
      }

      // return a reference to the array called outputArray
      return outputArray;
   }


   /**
      This method returns a reference to an array of the
      specified size filled with random integers between
      the values of 0 and range-1.
   */
   public static int[] randomArray(int size, int range)
   {
      int[] outputArray = new int[size];

      for(int i = 0; i < size; i++)
      {
         outputArray[i] = (int)Math.floor( range*Math.random() );
      }

      // return a reference to the array named outputArray
      return outputArray;
   }
}
