 
 // These are really only global to match/simplify the pseudocode in the slides.
 
-uchar *old_image;  // original image, CPU-side
+uchar *old_image;  // original image, CPU-side.  we don't actually use this.
 uchar *new_image;  // processed image, CPU-side
 int image_height;  // number of rows in image
 int image_width;   // number of columns in image

  // TODO: handle borders of image, i.e. when px_x==0 and so on
}

////////////////////////////////// CPU code: ///////////////////////////////////

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

    exit(EXIT_FAILURE);
  }

  if (mode==0) { ///////////////// Single-threaded CPU version /////////////////
    for (int row=0; row<image_height; row++) {
      for (int col=0; col<image_width; col++) {
	  new_image[row*image_width + col] = average_3x3_CPU(image.data, col, row);
      }
    }    
  }

  ///////////////////////////////// GPU code: //////////////////////////////////

  int devID;  // which GPU to use

  if (mode > 1) {
    // Choose the fastest GPU (optional):
    devID = gpuGetMaxGflopsDeviceId();
    checkCudaErrors( cudaSetDevice(devID) );
    // checkCudaErrors( cudaSetDevice(0) );  // or just use the first GPU
    
    // Prepare input and output arrays on GPU:
    checkCudaErrors( cudaMalloc((void**)&old_image_G,image_size*sizeof(uchar)) );
    checkCudaErrors( cudaMalloc((void**)&new_image_G,image_size*sizeof(uchar)) );
    
    // Set GPU output to zero (optional):
    //checkCudaErrors( cudaMemset(new_image_G, 0, image_size) );
    
    // Copy CPU variables to GPU:
    checkCudaErrors( cudaMemcpyToSymbol(image_height_G, &image_height, sizeof(int)) );
    checkCudaErrors( cudaMemcpyToSymbol(image_width_G,  &image_width,  sizeof(int)) );
  }

  if ((mode == 2) || 
      (mode == 3) || 
