/***************************************************************************
    FPGA Brook Source Code
    Copyright (C) by Frank Franjo Plavec 2006-2012
    Please note that some sections of this file were adopted from GPU Brook
    and are Copyright (c) 2003, Stanford University.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The full text of the GNU General Public License in the file named
    LICENSE.cTool.
*****************************************************************************/


/*
 * main.cpp
 *
 *      Bulk of source-to-source compilation from FPGA Brook to C2H happens here.
 *      Recommended to start reading the code from the main() function at the very bottom.
*/


// Uncomment the following line to output lots of debug information into the output files.
// Please note that the ouptut files will be unusable as a result of this, but this option
// comes handy for debugging when making changes to the compiler source code.
//#define DEBUG


#ifdef _WIN32
#pragma warning(disable:4786)
//the above warning disables visual studio's annoying habit of warning when using the standard set lib
#endif


#include <algorithm>
#include <fstream>
#ifdef DEBUG
#include <typeinfo>
#endif

extern "C" {
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <direct.h>
#include <windows.h>
#include <math.h>
#include "getopt.h"
}

#include "ctool.h"
#include "brtscatter.h"
#include "brtvout.h"
#include "codegen.h"
#include <sstream>
#include "streaminfo.h"
#include "main.h"


struct globals_struct globals;
using namespace std;

/*
 * usage --
 *
 *      Dumps the legitimate commandline options and exits.
 */

static void
usage (void) {
  fprintf (stderr, "FPGA Brook Compiler\n");
  fprintf (stderr, "Version: 1.1\n");
  fprintf (stderr,
	  "brcc [-h] <filename.br>\n\n"
        "   -h            help (print this message)\n");

  exit(1);
}


/*
 * parse_args --
 *
 *      Parses argv and sets the global options as a result.
 */

static void
parse_args (int argc, char *argv[]) {
  int opt, n;
  char *outputprefix = NULL;

  /*
   * zero initialization from the bss will take care of the rest of the
   * defaults.
   */
  globals.printLineDirectives = false;
  globals.workspace    = 1024;
  globals.compilername = argv[0];
  while ((opt = getopt(argc, argv, "d:hkntyANSla:f:o:p:vw")) != EOF) {
     switch(opt) {
     case 'a':
       if (strcasecmp (optarg, "default") == 0)
         globals.arch = GPU_ARCH_DEFAULT;
       else if (strcasecmp (optarg, "x800") == 0)
         globals.arch = GPU_ARCH_X800;
       else if (strcasecmp (optarg, "6800") == 0)
         globals.arch = GPU_ARCH_6800;
       else
         usage();
       break;
     case 'h':
        usage();
        break;
     case 'k':
        globals.keepFiles = true;
        break;
     case 'n':
        globals.parseOnly = true;
        break;
     case 't':
        globals.noTypeChecks = true;
        break;
    // TIM: totally hacked
     case 'y':
        globals.allowDX9MultiOut = true;
        break;
     case 'A':
        globals.enableGPUAddressTranslation = true;
        break;
     case 'N':
        globals.allowKernelToKernel = false;
        break;
     case 'S':
        globals.enableKernelSplitting = true;
        break;
     case 'o':
	if (outputprefix) usage();
	outputprefix = strdup(optarg);
	break;
     case 'f':
       if (strcasecmp (optarg, "cgc") == 0)
         globals.favorcompiler = COMPILER_CGC;
       else if (strcasecmp (optarg, "fxc") == 0)
         globals.favorcompiler = COMPILER_FXC;
       else if (strcasecmp (optarg, "default") == 0)
         globals.favorcompiler = COMPILER_DEFAULT;
       else
         usage();
       break;
     case 'p':
	if (strcasecmp (optarg, "cpu") == 0)
	  globals.target |= TARGET_CPU;
	else if (strcasecmp (optarg, "ps20") == 0)
	  globals.target |= TARGET_PS20;
	else if (strcasecmp (optarg, "ps2b") == 0)
	  globals.target |= TARGET_PS2B;
	else if (strcasecmp (optarg, "ps2a") == 0)
	  globals.target |= TARGET_PS2A;
	else if (strcasecmp (optarg, "ps30") == 0)
	  globals.target |= TARGET_PS30;
	else if (strcasecmp (optarg, "fp30") == 0)
	  globals.target |= TARGET_FP30;
	else if (strcasecmp (optarg, "fp40") == 0)
	  globals.target |= TARGET_FP40;
	else if (strcasecmp (optarg, "arb") == 0)
	  globals.target |= TARGET_ARB;
	else if (strcasecmp (optarg, "cpumt") == 0)
	  globals.target |= TARGET_MULTITHREADED_CPU;
	else
	  usage();
	break;
     case 'v':
	globals.verbose = 1;
	break;
     case 'l':
        globals.printLineDirectives = true;
        break;
     case 'd':
        Project::gDebug = true;
        break;
     case 'w':
	globals.workspace = strtol(optarg, NULL, 0);
	if (globals.workspace < 16 ||
	    globals.workspace > 2048)
	  usage();
	break;
     default:
	usage();
     }
  }

  // The default build targets
  if (globals.target == 0)
     globals.target = TARGET_PS20 | TARGET_CPU | TARGET_MULTITHREADED_CPU |
                      TARGET_FP30 | TARGET_ARB | TARGET_FP40 | TARGET_PS30 |
                      TARGET_PS2B | TARGET_PS2A;

  argv += optind;
  argc -= optind;
  if (argc < 1) usage();
  globals.sourcename = (char *) argv[0];

  n = strlen(globals.sourcename);
  int suffixLength;
  bool isHeader = false;
  if (n >= 3 && !strcmp (globals.sourcename + n - 3, ".br")) {
    suffixLength = 3;
  }
  else if (n >= 4 && !strcmp(globals.sourcename + n - 4, ".brh")) {
    suffixLength = 4;
    isHeader = true;
  }
  else {
    usage();
  }

  if (!outputprefix) {
    outputprefix = strdup(globals.sourcename);
    outputprefix[n-suffixLength] = (char)  '\0';
  }

  globals.shaderoutputname = strdup(outputprefix);

  globals.coutputname = (char *) malloc (strlen(outputprefix) +
					 suffixLength + 2);
  if( isHeader ) {
    sprintf (globals.coutputname, "%s.hpp",outputprefix);
  } else {
    sprintf (globals.coutputname, "%s.c",outputprefix);
  }

	globals.targetfolder = (char *) malloc (strlen(outputprefix) + 12 + 1);
	sprintf (globals.targetfolder, "%s_sopc_system",outputprefix);
	globals.elfoutputname = (char *) malloc (strlen(outputprefix) + 4 + 1);
	sprintf (globals.elfoutputname, "%s.elf",outputprefix);
	globals.dataflowdotfilename = (char *) malloc (strlen(outputprefix) + 4 + 1);
	sprintf (globals.dataflowdotfilename, "%s.dot",outputprefix);
	globals.dataflowreplicateddotfilename = (char *) malloc (strlen(outputprefix) + 15 + 1);
	sprintf (globals.dataflowreplicateddotfilename, "%s_replicated.dot",outputprefix);

  // Initialize the codegen unit.
  CodeGen_Init();

  free(outputprefix);
}


/*
 * ConvertToBrtStreamParams --
 *
 *      Converts stream arguments to non-kernel functions into their actual
 *      BRT types (i.e. makes it possible to pass streams as arguments to
 *      functions).
 */

static void
ConvertToBrtStreamParams(FunctionType *fType)
{
   unsigned int i;

   for (i = 0; i < fType->nArgs; i++) {
      Type ** paramTyp=&fType->args[i]->form;
      while(paramTyp) {
         if ((*paramTyp)->isStream()) {
            Type *newForm;
            
            newForm = new BrtStreamParamType((ArrayType *) *paramTyp);

            /*
             * Types are all on the global type list, so we can't just nuke it.
             delete fType->args[i]->form;
            */
            (*paramTyp) = newForm;
         } else if (fType->isKernel() && (*paramTyp)->isArray()) {
            Type* newForm = new BrtStreamParamType((ArrayType *) *paramTyp);
            (*paramTyp) = newForm;
         }
         paramTyp = (*paramTyp)->getSubType();
      }
   }
   return;
}


/*
 * ConvertToBrtStreams --
 *
 *      Converts stream declaration statement objects into BrtStreams.
 */

static void
ConvertToBrtStreams(Statement *s)
{
   DeclStemnt *declStemnt;

   if (!s->isDeclaration()) { return; }
   declStemnt = (DeclStemnt *) s;

   for (unsigned int i=0; i<declStemnt->decls.size(); i++) {
      Decl *decl = declStemnt->decls[i];
      ArrayType *stream;
      Type *brtType;

      if (!decl->form) continue;

      if (decl->form->isFunction()) {
         assert(decl->form->type == TT_Function);
         ConvertToBrtStreamParams((FunctionType *)decl->form);
      }

      if (!decl->isStream()) continue;
      stream = (ArrayType *) decl->form;

      if (decl->initializer == NULL) {
         assert((stream->getQualifiers() & TQ_Iter) == 0);
         brtType = new BrtInitializedStreamType (stream);
         // TIM: we don't want any initializer for stream types...
         // they are initialized by their constructor...
         //
         // decl->initializer = new BrtStreamInitializer(brtStream,
         //                                              declStemnt->location);
      } else {
         assert(decl->initializer->etype == ET_FunctionCall);
         assert((stream->getQualifiers() & TQ_Iter) != 0);
         brtType = new BrtIterType(stream, (FunctionCall *) decl->initializer);
         delete decl->initializer;
         decl->initializer = NULL;
      }
      assert (decl->initializer == NULL);
      decl->form = brtType;
   }
}


/*
 * ConvertToBrtFunctions --
 *
 *      This is the callback for the portion of the transformation phase
 *      when we iterate over all the function definitions and convert Brook
 *      specific ones to our types.
 */

static FunctionDef *
ConvertToBrtFunctions(FunctionDef *fDef)
{
   /*
    * The 'isReduce()' check _must_ come before the 'isKernel()' check
    * because, for better or for worse, reduction kernels are also
    * considered kernels.
    */

   if (fDef->decl->isReduce()) {
      return new BRTReduceKernelDef(*fDef);
   } else if (fDef->decl->isKernel()) {
      return new BRTMapKernelDef(*fDef);
   } else {
      assert(fDef->decl->form->type == TT_Function);
      ConvertToBrtStreamParams((FunctionType *) fDef->decl->form);
      return NULL;
   }

}

static void
ConvertToBrtDecls(Statement *stmt)
{
   DeclStemnt* declStemnt;

   if (!stmt->isDeclaration()) { return; }
   declStemnt = (DeclStemnt *) stmt;

   for (unsigned int i=0; i<declStemnt->decls.size(); i++) {
      Decl *decl = declStemnt->decls[i];

      if (!decl->form) continue;

      if (decl->form->isKernel()) {

         Type *brtType = new BrtKernelType((FunctionType* ) decl->form);
         decl->form = brtType;
      }
   }
}

/*
 * TypeCheckFunctionCallsExprFinder --
 *
 *     This is the callback that actually checks if the type of an expr
 *     is a function call ... and if so then it calls checkKernelCall() member
 *     on that function call
 */

static Expression * TypeCheckFunctionCallsExprFinder (Expression * e) {
   bool ret=true;
   if (e->etype==ET_FunctionCall) {
      FunctionCall * fc = static_cast<FunctionCall*>(e);
      ret=fc->checkKernelCall()&&ret;//print out type errors
   }
   /* 
    * Only assert after all functions are checked
    */
   assert(ret);
   return e;
}

static void TypeCheckFunctionCallsStatementFinder(Statement * ste) {
   ste->findExpr(&TypeCheckFunctionCallsExprFinder);
}

static void TypeCheckFunctionCalls(TransUnit * tu) {
   tu->findStemnt (&TypeCheckFunctionCallsStatementFinder);
}


/*
* readConfigFile --
* FP
*	Reads the configuration file CONFIGURATION.cfg and imports predefined parameters into the globals structure
*
*/
int readConfigFile () {

	ifstream config_file;
	char line[MAX_LINE_SIZE+1];
	std::string line_string, parameter, value;
	int sequence_start, sequence_end;
	int value_integer;

	// Open the configuration file to extract parameters
	config_file.open ("CONFIGURATION.cfg");

	if (config_file.fail()) {
		cerr << "***Unable to open CONFIGURATION.cfg\n";
		exit(1);
	}



	while (!config_file.getline(line, MAX_LINE_SIZE).eof()) {
		if (config_file.fail ()) {
			cerr << "***Error reading file CONFIGURATION.cfg\n";
			if (config_file.failbit)
				cerr << "Reason: The file contains a line longer than " << MAX_LINE_SIZE << " characters\n";
			exit(1);
		}
		line_string = std::string(line);
		// First, skip any spaces or tabs from the beginning of the line
		sequence_start = line_string.find_first_not_of(" \t");
		// If there are no non-whitespace characters, OR
		// if the first non-whitespace character is #, skip this line because it is empty or a comment
		if ((sequence_start == string::npos) || (line_string[sequence_start] == '#')) {
			continue;
		}
		// Otherwise find the end of the character sequence
		sequence_end = line_string.find_first_of(" \t", sequence_start);
		if (sequence_end == string::npos) {
			cerr << "ERROR in the CONFIGURATION.cfg file. Line\n" << line << "\nhas unexpected format. No space or tab found after the parameter name";
			exit(1);
		}
		// Capture the parameter being defined
		parameter = line_string.substr (sequence_start, sequence_end-sequence_start);

		// Find the value being defined
		sequence_start = line_string.find_first_not_of(" \t", sequence_end);
		if (sequence_start == string::npos) {
			cerr << "ERROR in the CONFIGURATION.cfg file. Line\n" << line << "\nhas unexpected format. No parameter value found!";
			exit(1);
		}
		// The value for the parameter should contain everything to the end of the line, excluding any space or tab characters at the end
		sequence_end = line_string.find_last_not_of(" \t");
		value = line_string.substr(sequence_start, sequence_end-sequence_start+1);
		// If the value is enclosed in quotes, remove the quote characters
		if (value[0] == '\"') {
			if (value[value.length()-1] == '\"') {
				value = value.substr(1, value.length()-2);
			} else {
				cerr << "ERROR in the CONFIGURATION.cfg file. Line\n" << line << "\nhas unexpected format. Quote character is missing from the end of parameter value!";
				exit(1);
			}
		}

		// If the paramater is one of the integer parameters, let's convert it to integer value
		if ((parameter.compare("INITIAL_SOPC_ELEMENT_COUNT") == 0) || (parameter.compare("FIFO_BASE_ADDRESS") == 0)) {
			stringstream temp_int;
			temp_int << value;
			if (!(temp_int >> value_integer)) {
				cerr << "ERROR in the CONFIGURATION.cfg file. Line\n" << line << "\nhas unexpected format. This parameter must have integer value, but conversion to integer failed!";
				exit (1);
			}
		}
		if ((parameter.compare("SOPC_TEMPLATE_FILE") == 0)) {
			globals.SOPC_TEMPLATE_FILE = value;
		} else if ((parameter.compare("SOPC_TARGET_FILE") == 0)) {
			globals.SOPC_TARGET_FILE = value;
		} else if ((parameter.compare("FIFO_TEMPLATE_FILE") == 0)) {
			globals.FIFO_TEMPLATE_FILE = value;
		} else if ((parameter.compare("HARDWARE_FIFO_FOLDER") == 0)) {
			globals.HARDWARE_FIFO_FOLDER = value;
		} else if ((parameter.compare("BSP_TEMPLATE_FILE") == 0)) {
			globals.BSP_TEMPLATE_FILE = value;
		} else if ((parameter.compare("APP_TEMPLATE_FILE") == 0)) {
			globals.APP_TEMPLATE_FILE = value;
		} else if ((parameter.compare("COPY_FOLDER") == 0)) {
			globals.COPY_FOLDER = value;
		} else if ((parameter.compare("SOPC_COMPONENT_PATH") == 0)) {
			globals.SOPC_COMPONENT_PATH = value;
		} else if ((parameter.compare("INITIAL_SOPC_ELEMENT_COUNT") == 0)) {
			globals.INITIAL_SOPC_ELEMENT_COUNT = value_integer;
		} else if ((parameter.compare("FIFO_BASE_ADDRESS") == 0)) {
			globals.FIFO_BASE_ADDRESS = value_integer;
		} else {
			cerr << "ERROR in the CONFIGURATION.cfg file. Line\n" << line << "\ncontains unknown parameter!";
			exit (1);
		}
	}
	config_file.close();

	return 0;
}


/*
* getDataWidth --
* FP
*	Given base type specifier returns the width of data (in bytes)
*	For example, for char returns 1, for short returns 2, for int 4,...
*
*/
int getDataWidth (BaseTypeSpec &typemask) {

	if (typemask & BT_Char) return 1;
	if (typemask & BT_Short) return 2;
	if (typemask & BT_Int) return 4;
	if (typemask & BT_Double) return 8;
	if (typemask & BT_Long) return 4;
	if (typemask & BT_Float) return 4;
	if (typemask & BT_LongLong) return 8;

	// If we reach this point, we haven't encountered any of the familiar types -> For now produce an error
	fatal ("Inside of getDataWidth: Encountered unhandled data type!");
	return -1;
}

/*
* generateReductionReplicas --
* FP
*	Generates C2H code for all nodes in the replication tree of a reduction kernel
*	
*
*/
void generateReductionReplicas (stringstream &out) {

	std::list<GraphNode>::iterator n_iter;
	string original_name;
	string original_name_2d2d;

	string temp_storage = out.str();
	size_t f_start, f_end, f_function;
	size_t f_start_2d2d, f_end_2d2d, f_function_2d2d;
	string str_begin = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432";
	string str_end = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432";


	// Iterate over all nodes
	for (n_iter=globals.dataflow.nodes.begin(); n_iter != globals.dataflow.nodes.end(); n_iter++) {
		// We are only interested in replicated reduction nodes
		if (n_iter->isReduce() && (n_iter->replication_factor > 1)) {
			// Record the name of the original node
			original_name = n_iter->name;
			original_name_2d2d = original_name;
			if ((n_iter->reducenodehandling == Reduce_2d_1d) || (n_iter->reducenodehandling == Reduce_2d_2Phase) || (n_iter->reducenodehandling == Reduce_2d_2Phase_Both_Replicated)) {
				// If 2-D reduction was converted into 1-D reduction, we have to change the name of the kernel that is called to perform reduction
				// Change the name of the kernel from 2d2d into 1d1d
				string temp_string("_2d2d_replica_");
				size_t loc = original_name.find(temp_string, 0);
				original_name.replace (loc, temp_string.length(), "_1d1d_replica_");
			}
			// Find the beginning and end tags of the reduction kernel
			f_start = 0;
			while ((f_start = temp_storage.find (str_begin, f_start)) != string::npos) {
				f_end = temp_storage.find (str_end, f_start+1);
				f_function = temp_storage.find (original_name, f_start+1);
				if (f_function < f_end) {
					break;
				} else {
					f_start = f_end;
				}
			}
			if (f_end == string::npos || f_start == string::npos) {
				cerr << "\nIn generateReductionReplicas(1): Could not find a kernel start or end tag for the kernel " << original_name << endl;
				exit (1);
			}
			string original_kernel_code = temp_storage.substr (f_start, f_end-f_start+str_end.length());

			string working_copy;
			string replacement;
			// Go into the replicated graph to extract information we need to generate correct code
			GraphNode *cur_node = n_iter->copy_ptr;
			// Make sure to skip the double round-robin distributor node used in replicated kernels with multiple trees
			if ((NULL != cur_node) && (n_iter->trees > 1)) {
				cur_node = cur_node->copy_ptr;
			}
			// Make sure we process all nodes
			while ((NULL != cur_node) && ((cur_node->copy_ptr != NULL) || (n_iter->reducenodehandling != Reduce_2d_2Phase)) && ((cur_node != n_iter->phase_2_ptr) || (n_iter->reducenodehandling != Reduce_2d_2Phase_Both_Replicated))) {
				working_copy = original_kernel_code;
				// First, find the name of the kernel and replace it
				size_t name_start;
				name_start = working_copy.find (original_name, 0);
				if (name_start == string::npos) {
					cerr << "\nIn generateReductionReplicas(2): Could not find the kernel name " << original_name << " in the kernel code" << endl;
					exit (1);
				}
				working_copy.replace (name_start, original_name.length(), cur_node->name);

				// Next, find the tag for the reduction modulo counter declaration
				string tag = BRCC_REDUCE_MODULO_ITERATOR_TAG_0 + original_name;
				size_t tag_start;
				tag_start = working_copy.find (tag, 0);
				if (tag_start == string::npos) {
					cerr << "\nIn generateReductionReplicas(3): Could not find the tag " << tag << " for the kernel " << original_name << endl;
					cout << out.str();
					exit (1);
				}
				// The reduce modulo counter is only necessary if the number of elements combined to produce one output
				// does not divide the number of leaves which means that each leaf node has to
				// periodically change the number of elements it processes
				// This can be determined by looking at the elements_to_process numbers in the node
				if (cur_node->elements_to_process_1 != cur_node->elements_to_process_2) {
					stringstream temp_ss;
					temp_ss << cur_node->my_index;
					stringstream counter_threshold;
					counter_threshold << cur_node->counter_threshold;
					string replacement_expression = string("int ") + BRCC_REDUCE_MODULO_ITERATOR_0 + " = 0;\n";
					replacement_expression += string("int ") + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + " = " + counter_threshold.str() + ";";
					working_copy.replace (tag_start, tag.length(), replacement_expression);
				} else {
					working_copy.replace (tag_start, tag.length(), "");
				}

				// Next, find the tag for the total number of elements
				tag = BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 + original_name;
				tag_start = working_copy.find (tag, 0);
				if (tag_start == string::npos) {
					cerr << "\nIn generateReductionReplicas(4): Could not find the tag " << tag << " for the kernel " << original_name << endl;
					exit (1);
				}
				stringstream temp_ss;
				temp_ss << cur_node->total_elements_to_process;
				string replacement_expression = temp_ss.str();
				working_copy.replace (tag_start, tag.length(), replacement_expression);

				// Next, find the tag for the reduction modulo counter usage
				tag = BRCC_REDUCE_MODULO_USAGE_TAG_0 + original_name;
				tag_start = working_copy.find (tag, 0);
				if (tag_start == string::npos) {
					cerr << "\nIn generateReductionReplicas(5): Could not find the tag " << tag << " for the kernel " << original_name << endl;
					exit (1);
				}
				// The reduce modulo counter is only necessary if the number of elements combined to produce one output
				// does not divide the number of leaves which means that each leaf node has to
				// periodically change the number of elements it processes
				// This can be determined by looking at the elements_to_process numbers in the node
				stringstream elements1;
				elements1 << cur_node->elements_to_process_1;
				if (cur_node->elements_to_process_1 != cur_node->elements_to_process_2) {
					stringstream elements2;
					elements2 << cur_node->elements_to_process_2;
					stringstream my_index;
					my_index << cur_node->my_index;
					string replacement_expression = string("((") + BRCC_REDUCE_MODULO_ITERATOR_0 + " < " + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + ") ? ((" + my_index.str() + ">=" + BRCC_REDUCE_MODULO_ITERATOR_0 + ")&&(" + my_index.str() + "<" + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + ")) : (("  + my_index.str() + ">=" + BRCC_REDUCE_MODULO_ITERATOR_0 + ")||(" + my_index.str() + "<" + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + "))) ? " + elements1.str() + " : " + elements2.str();
					working_copy.replace (tag_start, tag.length(), replacement_expression);
				} else {
					working_copy.replace (tag_start, tag.length(), elements1.str());
				}

				// Now find the tag for the reduction modulo counter update
				tag = BRCC_REDUCE_MODULO_UPDATE_TAG_0 + original_name;
				tag_start = working_copy.find (tag, 0);
				if (tag_start == string::npos) {
					cerr << "\nIn generateReductionReplicas(6): Could not find the tag " << tag << " for the kernel " << original_name << endl;
					exit (1);
				}
				// The reduce modulo counter is only necessary if the number of elements combined to produce one output
				// does not divide the number of leaves which means that each leaf node has to
				// periodically change the number of elements it processes
				// This can be determined by looking at the elements_to_process numbers in the node
				if (cur_node->elements_to_process_1 != cur_node->elements_to_process_2) {
					stringstream leaves;
					stringstream counter_threshold;
					stringstream counter_threshold_minus_leaves;
					stringstream leaves_minus_counter_threshold;
					counter_threshold << cur_node->counter_threshold;
					leaves << (n_iter->replicated_leaves);
					counter_threshold_minus_leaves << (cur_node->counter_threshold - n_iter->replicated_leaves);
					leaves_minus_counter_threshold << (n_iter->replicated_leaves - cur_node->counter_threshold);
					string replacement_expression = string(BRCC_REDUCE_MODULO_ITERATOR_0) + " = " + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + ";\n";
					replacement_expression += string(BRCC_NEXT_REDUCE_MODULO_ITERATOR_0) + " = (" + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + " >= " + leaves_minus_counter_threshold.str() + ") ? (" + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + "+" + counter_threshold_minus_leaves.str() + ") : (" + BRCC_NEXT_REDUCE_MODULO_ITERATOR_0 + "+" + counter_threshold.str() + ");";
					working_copy.replace (tag_start, tag.length(), replacement_expression);
				} else {
					working_copy.replace (tag_start, tag.length(), "");
				}
				// Finally, add the working copy into the replacement string
				replacement += working_copy;
				// All nodes in a tree are linked through copy_ptr
				cur_node = cur_node->copy_ptr;
			}
			// Once this is all done, the replacement string contains the code for all replicas
			// that needs to be inserted into the program in the place of the original kernel code
			temp_storage.replace (f_start, f_end-f_start+str_end.length(), replacement);

			string original_kernel_code_2d2d;
			string replacement_2d2d;
			if ((NULL != cur_node) && ((n_iter->reducenodehandling == Reduce_2d_2Phase) || (n_iter->reducenodehandling == Reduce_2d_2Phase_Both_Replicated))) {
				// This means that there is the second phase to the 2-D reduction
				// Find the beginning and end tags of the reduction kernel (2-D version)
				f_start_2d2d = 0;
				while ((f_start_2d2d = temp_storage.find (str_begin, f_start_2d2d)) != string::npos) {
					f_end_2d2d = temp_storage.find (str_end, f_start_2d2d+1);
					f_function_2d2d = temp_storage.find (original_name_2d2d, f_start_2d2d+1);
					if (f_function_2d2d < f_end_2d2d) {
						break;
					} else {
						f_start_2d2d = f_end_2d2d;
					}
				}
				if (f_end_2d2d == string::npos || f_start_2d2d == string::npos) {
					cerr << "\nIn generateReductionReplicas(11): Could not find a kernel start or end tag for the kernel " << original_name_2d2d << endl;
					exit (1);
				}
				original_kernel_code_2d2d = temp_storage.substr (f_start_2d2d, f_end_2d2d-f_start_2d2d+str_end.length());
				while (NULL != cur_node) {

					working_copy = original_kernel_code_2d2d;
					// First, find the name of the kernel and replace it
					size_t name_start;
					name_start = working_copy.find (original_name_2d2d, 0);
					if (name_start == string::npos) {
						cerr << "\nIn generateReductionReplicas(12): Could not find the kernel name " << original_name << " in the kernel code" << endl;
						exit (1);
					}
					working_copy.replace (name_start, original_name.length(), cur_node->name);

					// Next, find the tags for the sizes of input and reduce streams
					// The tags we are looking for are:
					// BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0, BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1, BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_0, BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1

					string tag[4] = {BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0, BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1, BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_0, BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1};
					int tag_replacement[4] = {cur_node->insize_dim0, cur_node->insize_dim1, cur_node->reducesize_dim0, cur_node->reducesize_dim1};
					for (int i=0; i<4; i++) {
						size_t tag_start;
						tag_start = working_copy.find (tag[i], 0);
						if (tag_start == string::npos) {
							cerr << "\nIn generateReductionReplicas(13): Could not find even one tag of kind " << tag[i] << " for the kernel " << original_name << endl;
							cout << out.str();
							exit (1);
						}
						stringstream temp_ss;
						temp_ss << tag_replacement[i];
						string replacement_expression = temp_ss.str();
						while (tag_start != string::npos) {
							working_copy.replace (tag_start, tag[i].length(), replacement_expression);
							tag_start = working_copy.find (tag[i], tag_start+tag[i].length());
						}
					}
					// Add the current working copy to the replacement string
					replacement_2d2d += working_copy;
					cur_node = cur_node->copy_ptr;
				}
				// Finally, make the replacement
				temp_storage.replace (f_start_2d2d, f_end_2d2d-f_start_2d2d+str_end.length(), replacement_2d2d);
			}

			// All that remains is to locate the tag where the reduction kernel is called,
			// and replace it with calls to all replicas.
			// The complicated subexpression is there to remove the _replica_0 suffix from the name, because this particualr tag does not contain that suffix
			string tag = n_iter->name.substr(0, original_name.length() - (string(BRCC_REPLICA_SUFFIX)).length()-1) + BRCC_REDUCE_REPLICA_REPLACEMENT_TAG;
			size_t tag_start, semicolon;
			// Find the beginning of the tag
			tag_start = temp_storage.find (tag, 0);
			// Find the semicolon that marks the end of the call
			semicolon = temp_storage.find (";", tag_start);
			if (tag_start == string::npos || semicolon == string::npos) {
				cerr << "\nIn generateReductionReplicas(7): Could not find the tag " << tag << "for the kernel " << original_name << endl;
				cout << out.str();
				exit (1);
			}
			string call, new_call;
			call = temp_storage.substr (tag_start, semicolon-tag_start+1);
			cur_node = n_iter->copy_ptr;
			// Make sure to skip the double round-robin distributor node used in replicated kernels with multiple trees
			if ((NULL != cur_node) && (n_iter->trees > 1)) {
				cur_node = cur_node->copy_ptr;
			}
			// Make sure we process all nodes
			while (NULL != cur_node) {
				string temp_call = call;
				temp_call.replace (0, tag.length(), cur_node->name);
				new_call += temp_call + "\n";
				// All nodes in a tree are linked through copy_ptr
				cur_node = cur_node->copy_ptr;
			}
			// Insert the new_call string in the place of the old call site
			temp_storage.replace (tag_start, semicolon-tag_start+1, new_call);
		}
	}

	// Finally, write the result back into the out stream
	out.str(temp_storage);
	// Put write pointer for out stream to the end
	// (the above command puts that pointer to the beginning of the file),
	out.seekp(0, ios_base::end);
}


/*
* generatePragmas --
* FP
*	Prints out all C2H pragmas based on the replicated dataflow graph
*	pragma_stream is a reference to ostringstream where the pragmas are to be printed into
*	This method is also a convenient place to print out iniatalization of pointers to FIFOs
*	holding streams of arrays. It is convenient to do it here because we are defining FIFO names in this method
*
*/
void generatePragmas (ostringstream &pragma_stream, stringstream &out) {

	std::list<GraphNode>::reverse_iterator n_iter;
	std::list<GraphEdge>::iterator e_iter;
	bool first_real_node_already_encountered = false;

	// Iterate over all nodes
	for (n_iter=globals.replicated.nodes.rbegin(); n_iter != globals.replicated.nodes.rend(); ++n_iter) {
		if ((n_iter->name.find(BRCC_COLLECTOR) == string::npos) && (n_iter->name.find(BRCC_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_COLLECTOR_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos)) {
			// If we are here, the node name is not one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
			// i.e. it is an ordinary node (i.e. some kind of kernel)
			if (first_real_node_already_encountered) {
				// For all nodes except the very last one (which is equal to the first one encountered in the reverse traversal)
				// we enable interrupts
				// This means that we will only wait for the very last kernel to finish; this will be the signal
				// that the program has finished processing.
// __FIX_ME - This is a simplification; there might be applications with complex dataflow where this condition would not be sufficient
				pragma_stream << "\n#pragma altera_accelerate enable_interrupt_for_function " << n_iter->name;
			} else {
				first_real_node_already_encountered = true;
			}
		} else {	// i.e. the node is one of the COLLECTOR, DISTRIBUTOR, COLLECTOR/DISTRIBUTOR, DOUBLE DISTRIBUTOR or COLLECTOR/DOUBLE DISTRIBUTOR
			struct collector_distributor_info info;
			info.name = n_iter->name;
			// Number of its inputs and outputs can be determined from the number of its input and output edges
			info.inputs = n_iter->inEdges();
			info.outputs = n_iter->out_edges.size();
			info.array_fifo = n_iter->out_edges.front().stream_of_arrays;
			info.array_size = n_iter->out_edges.front().array_size;
			info.word_size = n_iter->out_edges.front().word_size;
			info.trees = -1;
			info.leaves = -1;
			info.x_over_y = -1;
			if (n_iter->name.find(BRCC_COLLECTOR) != string::npos) {
				// If the collector is followed by a double distributor, add it to the list of collector/double distributor
				if (n_iter->out_edges.front().destination->preceded_by_a_collector) {
					info.name = string ("COLLECTOR_") + n_iter->out_edges.front().destination->name;
					info.trees = n_iter->out_edges.front().destination->trees;
					info.leaves = n_iter->out_edges.front().destination->replicated_leaves;
					info.x_over_y = n_iter->out_edges.front().destination->insize_dim0/n_iter->out_edges.front().destination->reducesize_dim0;
					globals.collectors_double_distributors.push_back (info);
				} else {
					globals.collectors.push_back (info);
				}
			} else if (n_iter->name.find(BRCC_DISTRIBUTOR) != string::npos) {
				globals.distributors.push_back (info);
			} else if (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) != string::npos) {
				// Only those double distributors not preceded by a collector should be handled separately
				if (!n_iter->preceded_by_a_collector) {
					info.trees = n_iter->trees;
					info.leaves = n_iter->replicated_leaves;
					info.x_over_y = n_iter->insize_dim0/n_iter->reducesize_dim0;
					globals.double_distributors.push_back (info);
				}
			} else {
				globals.collectors_distributors.push_back (info);
			}
		}
		// Iterate through all edges
		for (e_iter=n_iter->out_edges.begin(); e_iter != n_iter->out_edges.end(); e_iter++) {
			if ((n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_COLLECTOR) == string::npos) && (n_iter->name.find(BRCC_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_COLLECTOR_DISTRIBUTOR) == string::npos)) {
			// If we are here, the source node name is not one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
			// i.e. it is an ordinary node (i.e. kernel)
				if ((e_iter->destination->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos) && (e_iter->destination->name.find(BRCC_COLLECTOR) == string::npos) && (e_iter->destination->name.find(BRCC_DISTRIBUTOR) == string::npos) && (e_iter->destination->name.find(BRCC_COLLECTOR_DISTRIBUTOR) == string::npos)) {
					// If we are here, both source and destination node name is not one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
					// i.e. it is an ordinary node (i.e. some kind of kernel)
					// This means this is an edge between two ordinary kernels
					// For each out edge add a connect pragma
					pragma_stream << "\n#pragma altera_accelerate connect_variable ";
					pragma_stream << n_iter->name << "/" << e_iter->source_var << " to ";
					// If the edge carries a stream of arrays, we also have to add a control input
					if (e_iter->stream_of_arrays) {
						pragma_stream << "array_fifo_" << e_iter->name << "/in";
						pragma_stream << "_array";
						pragma_stream << "\n#pragma altera_accelerate connect_variable ";
						pragma_stream << n_iter->name << "/" << e_iter->source_var << "_br2c_ctrl to array_fifo_" << e_iter->name << "/in_control";
					} else {
						pragma_stream << "hardware_fifo_" << e_iter->name << "/in";
					}

#ifdef DEBUG
					cerr << "\nEdge name= " << e_iter->name << ", source_var= " << e_iter->source_var << ", dest_var= " << e_iter->dest_var << ", destination_node_name= " << e_iter->destination->name;
					cerr << "\nCalling RKP(1) with parameters " << n_iter->name << ", " << e_iter->source_var;
					cerr << "\nEdge name= " << e_iter->name << ", source_var= " << e_iter->source_var << ", dest_var= " << e_iter->dest_var << ", destination_node_name= " << e_iter->destination->name;
#endif
					// Since this variable is used, replace the pointer initialization
					string init_string;
					init_string = string("ARRAY_FIFO_") + e_iter->name + string("_IN_ARRAY_BASE");
					transform(init_string.begin(), init_string.end(), init_string.begin(), ::toupper);
					init_string = e_iter->source_var + " = " + init_string + ";\n";
					replaceKernelPointerInit (n_iter->name, e_iter->source_var, e_iter->stream_of_arrays, init_string, out);
					pragma_stream << "\n#pragma altera_accelerate connect_variable ";
					pragma_stream << e_iter->destination->name << "/" << e_iter->dest_var << " to ";
					if (e_iter->stream_of_arrays) {
						pragma_stream << "array_fifo_" << e_iter->name << "/out";
						pragma_stream << "_array";
						pragma_stream << "\n#pragma altera_accelerate connect_variable ";
						pragma_stream << e_iter->destination->name << "/" << e_iter->dest_var << "_br2c_ctrl to array_fifo_" << e_iter->name << "/out_control";
					} else {
						pragma_stream << "hardware_fifo_" << e_iter->name << "/out";
					}
#ifdef DEBUG
					cerr << "\nCalling RKP(2) with parameters " << e_iter->destination->name << ", " << e_iter->dest_var;
#endif
					// Since this variable is used, replace the pointer initialization
					init_string = string("ARRAY_FIFO_") + e_iter->name + string("_OUT_ARRAY_BASE");
					transform(init_string.begin(), init_string.end(), init_string.begin(), ::toupper);
					init_string = e_iter->dest_var + " = " + init_string + ";\n";
					replaceKernelPointerInit (e_iter->destination->name, e_iter->dest_var, e_iter->stream_of_arrays, init_string, out);
					struct fifo_info temp_fifo_info;
					if (e_iter->stream_of_arrays) {
						temp_fifo_info.fifo_name = "array_fifo_";
						temp_fifo_info.array_fifo = true;
						temp_fifo_info.array_size = e_iter->array_size;
						temp_fifo_info.word_size = e_iter->word_size;
					} else {
						temp_fifo_info.fifo_name = "hardware_fifo_";
					}
					temp_fifo_info.fifo_name += e_iter->name;
					globals.fifos.push_back (temp_fifo_info);
				} else { // If we are here, source is ordinary kernel and destination is one of the COLLECTOR, DISTRIBUTOR, COLLECTOR/DISTRIBUTOR or DOUBLE DISTRIBUTOR
					pragma_stream << "\n#pragma altera_accelerate connect_variable ";
					pragma_stream << n_iter->name << "/" << e_iter->source_var << " to ";
					// If destination is a collector followed by a distributor, need to handle the name correctly
					if ((e_iter->destination->name.find(BRCC_COLLECTOR) != string::npos) && e_iter->destination->out_edges.front().destination->preceded_by_a_collector) {
						// Change the name
						pragma_stream << "COLLECTOR_" << e_iter->destination->out_edges.front().destination->name;
					} else {
						// Otherwise just use the original name
						pragma_stream << e_iter->destination->name;
					}
#ifdef DEBUG
					cerr << "\nCalling RKP(3) with parameters " << n_iter->name << ", " << e_iter->source_var;
#endif
					// Since this variable is used, replace the pointer initialization
					string init_string;
					if ((e_iter->destination->name.find(BRCC_DISTRIBUTOR) != string::npos) || (e_iter->destination->name.find(BRCC_DOUBLE_DISTRIBUTOR) != string::npos)) {
						// Destination is a distributor, so just connect to its only "in" port
						pragma_stream << "/in";
						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add suffix to the current pragma and add another pragma for the control port
							pragma_stream << "_array";
							pragma_stream << "\n#pragma altera_accelerate connect_variable ";
							pragma_stream << n_iter->name << "/" << e_iter->source_var << "_br2c_ctrl to ";
							// If destination is a collector followed by a distributor, need to handle the name correctly
							if ((e_iter->destination->name.find(BRCC_COLLECTOR) != string::npos) && e_iter->destination->out_edges.front().destination->preceded_by_a_collector) {
								// Change the name
								pragma_stream << "COLLECTOR_" << e_iter->destination->out_edges.front().destination->name;
							} else {
								// Otherwise just use the original name
								pragma_stream << e_iter->destination->name;
							}
							pragma_stream << "/in_control";
						}
						init_string = e_iter->destination->name + string("_IN_ARRAY_BASE");
						transform(init_string.begin(), init_string.end(), init_string.begin(), ::toupper);
						init_string = e_iter->source_var + " = " + init_string + ";\n";
						replaceKernelPointerInit (n_iter->name, e_iter->source_var, e_iter->stream_of_arrays, init_string, out);
					} else {
						// Destination is a COLLECTOR, or COLLECTOR/DISTRIBUTOR so add an instance number to connect to one of its "in" ports
						pragma_stream << "/in_";
						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add suffix to the current pragma
							pragma_stream << "array_";
						}
						if (!n_iter->isReduce()) {
							// For non-reduce streams, replication_factor (which in reality contains replica ID at this stage)
							// is a perfect number for this purpose
							pragma_stream << n_iter->replication_factor;
							std::ostringstream temp_strm;
							temp_strm << n_iter->replication_factor;
							init_string = e_iter->destination->name + string("_IN_ARRAY_") + temp_strm.str() + string("_BASE");
						} else {
							// When reduction streams are replicated, trees are built and only parts of trees
							// are used to enter one collector node. Therefore, their IDs may not be consecutive,
							// or may not start at 0, so we have to use a different ID for this purpose
							pragma_stream << n_iter->node_order;
							std::ostringstream temp_strm;
							temp_strm << n_iter->node_order;
							init_string = e_iter->destination->name + string("_IN_ARRAY_") + temp_strm.str() + string("_BASE");
						}
						
						transform(init_string.begin(), init_string.end(), init_string.begin(), ::toupper);
						init_string = e_iter->source_var + " = " + init_string + ";\n";
						replaceKernelPointerInit (n_iter->name, e_iter->source_var, e_iter->stream_of_arrays, init_string, out);

						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add another pragma for the control port
							pragma_stream << "\n#pragma altera_accelerate connect_variable ";
							pragma_stream << n_iter->name << "/" << e_iter->source_var << "_br2c_ctrl to ";
							// If destination is a collector followed by a distributor, need to handle the name correctly
							if ((e_iter->destination->name.find(BRCC_COLLECTOR) != string::npos) && e_iter->destination->out_edges.front().destination->preceded_by_a_collector) {
								// Change the name
								pragma_stream << "COLLECTOR_" << e_iter->destination->out_edges.front().destination->name;
							} else {
								// Otherwise just use the original name
								pragma_stream << e_iter->destination->name;
							}
							pragma_stream << "/in_control_";
							if (!n_iter->isReduce()) {
								// For non-reduce streams, replication_factor (which in reality contains replica ID at this stage)
								// is a perfect number for this purpose
								pragma_stream << n_iter->replication_factor;
							} else {
								// When reduction streams are replicated, trees are built and only parts of trees
								// are used to enter one collector node. Therefore, their IDs may not be consecutive,
								// or may not start at 0, so we have to use a different ID for this purpose
								pragma_stream << n_iter->node_order;
							}
						}
					}
				}
			} else { // Source node name is one of the COLLECTOR, DISTRIBUTOR, COLLECTOR/DISTRIBUTOR, or DOUBLE DISTRIBUTOR
				// Destination in this case cannot be one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
				// However, if the source is collector, destination could be double-distributor
				// Since these two nodes will be merged into one, no pragma needs to be generated in such a case
				if ((n_iter->name.find(BRCC_COLLECTOR) == string::npos) || (e_iter->destination->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos)) {
					// Therefore, the destination is an ordinary kernel, just need to distinguish among different possible sources
					pragma_stream << "\n#pragma altera_accelerate connect_variable ";
					pragma_stream << e_iter->destination->name << "/" << e_iter->dest_var << " to ";
					if (n_iter->preceded_by_a_collector) {
						// If the double distributor is preceded by a collector, we have to modify the name appropriately
						pragma_stream << "COLLECTOR_" << n_iter->name;
					} else {
						// Otherwise just use the original name
						pragma_stream << n_iter->name;
					}
#ifdef DEBUG
					cerr << "\nCalling RKP(4) with parameters " << e_iter->destination->name << ", " << e_iter->dest_var;
#endif
					// Since this variable is used, replace the pointer initialization
					string init_string;
					if (n_iter->name.find(BRCC_COLLECTOR) != string::npos) {
						// Source is a collector, so just connect it to its only "out" port
						pragma_stream << "/out";
						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add suffix to the current pragma and add another pragma for the control port
							pragma_stream << "_array";
							pragma_stream << "\n#pragma altera_accelerate connect_variable ";
							pragma_stream << e_iter->destination->name << "/" << e_iter->dest_var << "_br2c_ctrl to ";
							if (n_iter->preceded_by_a_collector) {
								// If the double distributor is preceded by a collector, we have to modify the name appropriately
								pragma_stream << "COLLECTOR_" << n_iter->name;
							} else {
								// Otherwise just use the original name
								pragma_stream << n_iter->name;
							}
							pragma_stream << "/out_control";
						}
						init_string = n_iter->name + string("_OUT_ARRAY_BASE");
					} else {
						pragma_stream << "/out_";
						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add suffix to the current pragma
							pragma_stream << "array_";
						}
						if (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos) {
							// Source is a DISTRIBUTOR, or COLLECTOR/DISTRIBUTOR so add an instance number to connect to one of its "out" ports
							pragma_stream << e_iter->destination->replication_factor;
							std::ostringstream temp_strm;
							temp_strm << e_iter->destination->replication_factor;
							init_string = n_iter->name + string("_OUT_ARRAY_") + temp_strm.str() + string("_BASE");
						} else {
							// Source is double-distributor, so we cannot use the replication factor of destination to connect out ports
							// because the numbers are not consecutive
							pragma_stream << e_iter->destination->node_order_distributor;
							std::ostringstream temp_strm;
							temp_strm << e_iter->destination->node_order_distributor;
							init_string = n_iter->name + string("_OUT_ARRAY_") + temp_strm.str() + string("_BASE");
						}
						if (e_iter->stream_of_arrays) {
							// If the edge carries stream of arrays, need to add another pragma for the control port
							pragma_stream << "\n#pragma altera_accelerate connect_variable ";
							pragma_stream << e_iter->destination->name << "/" << e_iter->dest_var << "_br2c_ctrl to ";
							if (n_iter->preceded_by_a_collector) {
								// If the double distributor is preceded by a collector, we have to modify the name appropriately
								pragma_stream << "COLLECTOR_" << n_iter->name;
							} else {
								// Otherwise just use the original name
								pragma_stream << n_iter->name;
							}
							pragma_stream << "/out_control_";
							if (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos) {
								// Source is a DISTRIBUTOR, or COLLECTOR/DISTRIBUTOR so add an instance number to connect to one of its "out" ports
								pragma_stream << e_iter->destination->replication_factor;
							} else {
								// Source is double-distributor, so we cannot use the replication factor of destination to connect out ports
								// because the numbers are not consecutive
								pragma_stream << e_iter->destination->node_order_distributor;
							}
						}
					}
					transform(init_string.begin(), init_string.end(), init_string.begin(), ::toupper);
					init_string = e_iter->dest_var + " = " + init_string + ";\n";
					replaceKernelPointerInit (e_iter->destination->name, e_iter->dest_var, e_iter->stream_of_arrays, init_string, out);
				}
			}
		}
		pragma_stream << "\n";
	}
}


/*
* splitStringInThree --
* FP
*	Splits the input string using whitespace as a delimiter
*	After the first two whitespaces are detected, the rest of the string
*	is put into the third string
*	The function is used to split the #define directive into #define VAR_NAME and EXPRESSION
*
*/
void splitStringInThree (const string& str, string &define, string &varname, string &definedexpression) {

    string buf; // Have a buffer string
    stringstream ss(str); // Insert the string into a stream

    ss >> define;
	ss >> varname;
	definedexpression = ss.str().substr(ss.tellg());
	// Drop the last \n character if it exists
	if ((definedexpression.substr(definedexpression.length()-1, 1)[0] == '\n') || (definedexpression.substr(definedexpression.length()-1, 1)[0] == '\r')) {
		definedexpression = definedexpression.substr(0,definedexpression.length()-1);
	}
}

/*
* addModDivCounter --
* FP
*	This function adds a variable used to compute / or % operation involving indexof operator
*	The expression to be replaced is of the form indexof%(expr), or indexof/(expr)
*	The function performs the following steps
*		1. Find the BRCC_MOD_DIV_DECL_BEGIN and BRCC_MOD_DIV_DECL_END tags
*		2. Find the last variable that was declared between these two tags (if any)
*		3. Add a new variable which sequentially follows
*		4. Find the BRCC_MOD_DIV_BLOCK_0/1_BEGIN and BRCC_MOD_DIV_BLOCK_0/1_END tags
*		5. Add code that properly updates the newly created variable to implement division or modulo operation
*	Arguments:
*		function_name - The name of the function inside of which the expression is (used to find the correct tag)
*		expr          - The expression on the right side of the / or % operation
*		columns_variable - A string containing the variable that contains the number of columns (usually <stream_name>_size_1 or something like that)
*		mod           - Boolean that tells us whether the operation is % (when mod is true), or / (when mod is false)
*		block_number  - Tells us whether we are in the outer for loop (=0) or inner for loop (=0)
*		                (=0) also covers the 1-D case
*		replicated_ordinary_2d - True only if we are handling a replicated 2-D ordinary kernel that has to be replicated
*		replication_factor - Desired number of replicas
*		replica_number- Ordinal number of the current replica in a replicated kernel (this argument is valid for both 1-D and 2-D cases)
*		out           - The output stream where the code should be written
*	Return value:
*		Returns the name of the new variable introduced
*
// __FIX_ME - Limitation: For replicated kernels % operation is handled correctly, but division operation is not yet implemented
//            => it is a straghtforward extension of the same principle
*/

string addModDivCounter (string function_name, string expr, string columns_variable, bool mod, int block_number, bool replicated_ordinary_2d, int replication_factor, int replica_number, stringstream &out) {

	string temp_storage = out.str();
	string return_value;
	size_t decl_begin=0, decl_end, after_decl_begin, after_decl_end, block_begin, block_end;
	string decl_begin_str = BRCC_MOD_DIV_DECL_BEGIN;
	string decl_end_str = BRCC_MOD_DIV_DECL_END;
	string after_decl_begin_str = BRCC_MOD_DIV_AFTER_DECL_BEGIN;
	string after_decl_end_str = BRCC_MOD_DIV_AFTER_DECL_END;
	string block_begin_str = (block_number) ? BRCC_MOD_DIV_BLOCK_1_BEGIN : BRCC_MOD_DIV_BLOCK_0_BEGIN;
	string block_end_str = (block_number) ? BRCC_MOD_DIV_BLOCK_1_END : BRCC_MOD_DIV_BLOCK_0_END;
	stringstream number;
	ostringstream number_os;
	bool expr_is_const_int;
	int expr_const_int;

	decl_begin_str += function_name;
	decl_end_str += function_name;
	block_begin_str += function_name;
	block_end_str += function_name;
	after_decl_begin_str += function_name;
	after_decl_end_str += function_name;

	// Determine whether the expression is a constant integer, and if so, extract it into an integer variable
	expr_is_const_int = true;
	for (unsigned int i=0; i<expr.length(); i++) {
		// 48 and 57 are ascii for 0 and 9
		if ((expr[i] != ' ') && (expr[i] != '\t') && ((expr[i] < 48) || (expr[i] > 57))) {
			expr_is_const_int = false;
		}
	}
	if (expr_is_const_int) {
		stringstream temp_int;
		temp_int << expr;
		if (!(temp_int >> expr_const_int)) {
			cerr << "\nError 432: Could not convert the string that was supposed to hold an ordinary number of a div/mod variable into an integer in a function " << function_name << endl;
			cerr << "The string on which the conversion was attempted was " << temp_int.str(); 
			exit (1);
		}
	}
	// Locate the BRCC_MOD_DIV_DECL_BEGIN and other tags
	decl_begin = temp_storage.find (decl_begin_str, 0);
	decl_end = temp_storage.find (decl_end_str, decl_begin);
	if (decl_begin == string::npos) {
		cerr << "\nInternal Error 001: Could not locate " << decl_begin_str << " in the code generated so far.\n";
		cout << temp_storage;
		exit (1);
	}
	if (decl_end == string::npos) {
		cerr << "\nInternal Error 002: Could not locate " << decl_end_str << " in the code generated so far.\n";
		exit (1);
	}
	// Find out if there was another variable declared between BRCC_MOD_DIV_DECL_BEGIN and BRCC_MOD_DIV_DECL_END before
	if (temp_storage[decl_end-1] == ';') {
		// If the character is a semicolon, we know that it is a declaration, so we look it up
		// The variable declaration looks like this: int brcc_mod_div_iterator_NUMBER=0;
		// Therefore, the ordinal number of the declaration is between the characters _ and =
		size_t underscore, equals;
		equals = decl_end;
		while (temp_storage[equals] != '=') {
			equals--;
		}
		underscore = equals-1;
		while (temp_storage[underscore] != '_') {
			underscore--;
		}
		number.str(temp_storage.substr(underscore+1, equals-underscore-1));
		int temp_number=23;
		if (!(number >> temp_number)) {
			cerr << "\nError: Could not convert the string that was supposed to hold an ordinary number of a div/mod variable into an integer in a function " << function_name << endl;
			cerr << "The string on which the conversion was attempted was " << number.str(); 
			exit (1);
		}
		// Increment the variable counter
		temp_number++;
		number_os.str("");
		number_os << temp_number;
		number.str(number_os.str());
		if (mod) {
			return_value = "brcc_mod_div_iterator_";
		} else {
			return_value = "brcc_div_result_";
		}
		return_value += number.str();
	} else {
		// Otherwise, there has not been a previous declaration
		return_value = "brcc_mod_div_iterator_0";
	}
	// Add new initialization
	string replacement_string = "\nint ";
	replacement_string += return_value;
	// Initialize the variable to -1, because it will be incremented upon entering the loop
	if (replication_factor > 1) {
		// If there is replication, the counters have to behave differently
		stringstream ss_temp;
		if (replicated_ordinary_2d) {
			if (block_number == 0)  {
				ss_temp << replica_number << BRCC_DIV_OPERATOR_TO_REPLACE << columns_variable;
			} else {
				ss_temp << replica_number << BRCC_MOD_OPERATOR_TO_REPLACE << columns_variable;
			}
		} else {
			// Constant case can be optimized, so that it does not require further adjustment
			if (expr_is_const_int) {
				if (replication_factor%expr_const_int == 0) {
					// In this case, the modulo iterator will not be modified later, so it's a special case
					ss_temp << (replica_number%expr_const_int);
				} else {
					ss_temp << ((replica_number%expr_const_int)-(replication_factor%expr_const_int));
				}
			} else {
				ss_temp << replica_number;
			}
		}
		replacement_string += string(" = ") + ss_temp.str() + " ;";
	} else {
		replacement_string += "=-1;";
	}
	if (!mod) {
		// If it is a division operator, we actually require two different variables
		replacement_string += "\nint brcc_mod_div_iterator_";
		replacement_string += number.str();
		replacement_string += "=-1;";
	}
	replacement_string += decl_end_str;
	temp_storage.replace (decl_end, decl_end_str.length(), replacement_string);

	// If the kernel is replicated, place the proper adjustment to the value into the block after initialization
	// This is not needed for 1-D case if the expression is a constant
	if ((replication_factor  > 1) && (!expr_is_const_int || replicated_ordinary_2d)) {
		after_decl_begin = temp_storage.find (after_decl_begin_str, 0);
		after_decl_end = temp_storage.find (after_decl_end_str, after_decl_begin);
		if (after_decl_begin == string::npos) {
			cerr << "\nInternal Error 003: Could not locate " << after_decl_begin_str << " in the code generated so far.\n";
			cout << temp_storage;
			exit (1);
		}
		if (after_decl_end == string::npos) {
			cerr << "\nInternal Error 004: Could not locate " << after_decl_end_str << " in the code generated so far.\n";
			exit (1);
		}
		replacement_string = "while (" + return_value + ">=" + expr + ") {\n";
		replacement_string += "\t" + return_value + " -= " + expr + ";\n}\n";
		stringstream ss_temp;
		ss_temp << replication_factor;
		// This last statement is to compensate for the initial increase in the value inside the for loop
		// there may be other ways to handle this, but this was the easiest from the perspective of code generation
		if (!replicated_ordinary_2d || block_number == 1) {
			replacement_string += return_value + " -= " + ss_temp.str() + ";\n";
		}
		replacement_string += after_decl_end_str;
		temp_storage.replace (after_decl_end, after_decl_end_str.length(), replacement_string);
	}

	block_begin = temp_storage.find (block_begin_str, decl_end);
	block_end = temp_storage.find (block_end_str, block_begin);
	if (block_begin == string::npos) {
		cerr << "\nInternal Error (123): Could not locate " << block_begin_str << " in the code generated so far.\n";
		cerr << temp_storage;
		exit (1);
	}
	if (block_end == string::npos) {
		cerr << "\nInternal Error (124): Could not locate " << block_end_str << " in the code generated so far.\n";
		cerr << temp_storage;
		exit (1);
	}

	// Now produce the calculation that correctly updates the variable
	stringstream calculation;
	if (mod) {
		if (replication_factor <= 1) {
			// For non-replicated kernels:
			// Mod operator can be replaced by the following expression
			// indexof%(expr) <=> (brcc_mod_iterator = ((brcc_mod_iterator < (expr-1)) ? (brcc_mod_iterator+1):0))
			// brcc_mod_iterator has to be initialized to -1 at the beginning of execution
			// We cast the expression to int just to make sure that it is not unsigned, in which case we would run into trouble because
			// of a signed and unsigned comparison
			calculation << return_value << "++;\n";
			calculation << "(" << return_value << " = ((" << return_value << " == (" << expr;
			calculation << ")) ? 0:(" << return_value << ")));\n";
		} else {
			// For replicated kernels, modulo operator has to be increased by the replication factor to account for other replicas
			// After that, it has to be adjusted to make sure it did not go over the maximum value
			if (replicated_ordinary_2d) {
				if (block_number == 1) {
					stringstream ss_temp;
					ss_temp << replication_factor;
					calculation << return_value << " += " << ss_temp.str() << ";\n";
					calculation << "while (" << return_value << ">=" << expr << ") {\n";
					calculation << "\t" << return_value << " -= " << expr << ";\n}\n";
				}
			} else {
				// We handle constant and non-consant expressions differently, because the case with constant expression can be optimized
				if (expr_is_const_int) {
					if (replication_factor%expr_const_int == 0) {
						// Do nothing - if replication factor divides the expression, the modulo counter never changes
					} else {
						stringstream ss_temp1, ss_temp2, ss_temp3;
						int temp1, temp2, temp3;
						temp1 = expr_const_int - (replication_factor%expr_const_int);
						temp2 = (replication_factor%expr_const_int);
						temp3 = (replication_factor%expr_const_int) - expr_const_int;
						ss_temp1 << temp1;
						ss_temp2 << temp2;
						ss_temp3 << temp3;
						calculation << return_value << " = (" << return_value << "<" << ss_temp1.str() << ") ? (" << return_value << " + " << ss_temp2.str() << ") : (" << return_value << " + " << ss_temp3.str() << ");\n";
					}
				} else {
					stringstream ss_temp;
					ss_temp << replication_factor;
					calculation << return_value << " += " << ss_temp.str() << ";\n";
					calculation << "while (" << return_value << ">=" << expr << ") {\n";
					calculation << "\t" << return_value << " -= " << expr << ";\n}\n";
				}
			}
		}
	} else {
		// Div operator can be replaced by the following expression
		// indexof/(expr) <=> (brcc_div_result = (((brcc_div_iterator=((brcc_div_iterator<(expr))?(brcc_div_iterator+1):0))==0)?(brcc_div_result+1):brcc_div_result))
		// brcc_div_result and brcc_div_iterator have to be initialized to -1 at the beginning of the execution
		// We cast the expression to int just to make sure that it is not unsigned, in which case we would run into trouble because
		// of a signed and unsigned comparison
		calculation << "brcc_mod_div_iterator_" << number.str() << "++;\n";
		calculation << "(" << return_value << " = (((brcc_mod_div_iterator_" << number.str() << "=((brcc_mod_div_iterator_" << number.str() << " == (" << expr;
		calculation << "))? 0:(brcc_mod_div_iterator_" << number.str() << ")))==0)?(" << return_value << "+1):" << return_value << "));\n";
	}
	// Finally, place the new calculation where it is required
	replacement_string = calculation.str() + block_end_str;
	temp_storage.replace (block_end, block_end_str.length(), replacement_string);

	// If the block_number is 1, then we are introducing counters into the inner for loop
	// In such a case we have to initialize these counters to -1 outside that loop, which is inside the outer loop
	if ((block_number == 1) && !replicated_ordinary_2d) {
		block_begin_str = BRCC_MOD_DIV_BLOCK_0_BEGIN;
		block_begin_str += function_name;
		block_end_str = BRCC_MOD_DIV_BLOCK_0_END;
		block_end_str += function_name;

		// Find the start and the end of the first block
		block_begin = temp_storage.find (block_begin_str, decl_end);
		block_end = temp_storage.find (block_end_str, block_begin);
		if (block_begin == string::npos) {
			cerr << "\nInternal Error (125): Could not locate " << block_begin_str << " in the code generated so far.\n";
			cerr << temp_storage;
			exit (1);
		}
		if (block_end == string::npos) {
			cerr << "\nInternal Error (126): Could not locate " << block_end_str << " in the code generated so far.\n";
			cerr << temp_storage;
			exit (1);
		}
		// Add new initialization
		string replacement_string = "\n";
		replacement_string += return_value;
		// Initialize the variable to -1, because it will be incremented upon entering the loop
		replacement_string += "=-1;";
		if (!mod) {
			// If it is a division operator, we actually require two different variables
			replacement_string += "\nbrcc_mod_div_iterator_";
			replacement_string += number.str();
			replacement_string += "=-1;";
		}
		replacement_string += block_end_str;
		temp_storage.replace (block_end, block_end_str.length(), replacement_string);
	}
	if (replicated_ordinary_2d) {
		// If we are dealing with replicated 2-D kernel, we need to make additional adjustments to both variables
		// inside the block where the column iterator is updated 
		// This block is marked by special flags, so that it can be found
		block_begin_str = BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_BEGIN;
		block_begin_str += function_name;
		block_end_str = BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_END;
		block_end_str += function_name;

		// Find the start and the end of the first block
		block_begin = temp_storage.find (block_begin_str, decl_end);
		block_end = temp_storage.find (block_end_str, block_begin);
		if (block_begin == string::npos) {
			cerr << "\nInternal Error (127): Could not locate " << block_begin_str << " in the code generated so far.\n";
			cerr << temp_storage;
			exit (1);
		}
		if (block_end == string::npos) {
			cerr << "\nInternal Error (128): Could not locate " << block_end_str << " in the code generated so far.\n";
			cerr << temp_storage;
			exit (1);
		}
		string replacement_string = "\n";
		if (mod) {
			if (block_number == 0) {
				replacement_string += return_value + " = (" + return_value + " == ((" + expr + ")-1)) ? 0 : (" + return_value + "+1);";
			} else {
				replacement_string += "if (( " + columns_variable + BRCC_MOD_OPERATOR_TO_REPLACE + expr + " ) != 0) {\n";
				replacement_string += return_value + " += " + expr + " - ( " + columns_variable + BRCC_MOD_OPERATOR_TO_REPLACE + expr + " );\n";
				replacement_string += "while (" + return_value + " >= " + expr + ") { " + return_value + " -= " + expr + ";}\n}";
			}
		} else {
			cerr << "\nIn addModDivCounter: indexof division in 2-D replicated kernels not supported yet\n";
			exit (1);
		}
		replacement_string += block_end_str;
		temp_storage.replace (block_end, block_end_str.length(), replacement_string);
	}

	out.str(temp_storage);
	// Put write pointer for out stream to the end
	// Apparently, the above command puts that pointer to the beginning of the file,
	out.seekp(0, ios_base::end);

	return return_value;

}
/*
* replacePPDirectives --
* FP
*	Checks the expression for any variables that are defined using a preprocessing directive
*	If such a variable is found, it is replaced by the actual defined value
*   The procedure is iterative, and it tries to replace variables until no more variables are left
*	to replace. This is necessary to handle recursive preprocessing directives, such as
*	#define MAX 50
*	#define double MAX*2
*	Unfortunately, regardless of this effort, we do not really support complex #define directives
*	like this because the original BRCC represents directives as simple strings, so it is very hard
*	to work with them in such a manner, and it's too much work to fix it up so that it works
*	__FIX_ME - This should eventually be fixed
*	The function also calls itself recursively if the expression is complicated
*	Return value
*		True - if at least one variable has been replaced
*		False - if no replacement occured
*
*/
bool replacePPDirectives (Expression *expr, std::vector<PPDirective *> &ppdirectives) {

	bool replaced_something=true;

	while (replaced_something) {
		replaced_something = false;
		switch (expr->etype) {
			case ET_Variable:
			{
				Variable *var = (Variable *)(expr);
				std::vector<PPDirective *>::iterator iter;
				for (iter=ppdirectives.begin(); iter != ppdirectives.end(); iter++) {
					// If the directive is a define, find out what it defines
					if ((*iter)->directive.find("#define") != string::npos) {
						vector<string> ppelements;
						// Split the directive into three parts
						string define, varname, definedexpression;
						splitStringInThree ((*iter)->directive, define, varname, definedexpression);
						// Check again that the first element is #define
						if (define.compare("#define") == 0) {
							// Compare the variable name to the name in the #define directive
							if (var->name->name.compare(varname) == 0) {
								var->name->name = definedexpression;
								replaced_something = true;
								// Get out of the for loop
								break;
							}
						}
					}
				}
				break;
			}
			case ET_BinaryExpr:
				replaced_something = replacePPDirectives (((BinaryExpr *)(expr))->_leftExpr, ppdirectives);
				replaced_something = replacePPDirectives (((BinaryExpr *)(expr))->_rightExpr, ppdirectives);
				break;
			case ET_TrinaryExpr:
				replaced_something = replacePPDirectives (((TrinaryExpr *)(expr))->_condExpr, ppdirectives);
				replaced_something = replacePPDirectives (((TrinaryExpr *)(expr))->_trueExpr, ppdirectives);
				replaced_something = replacePPDirectives (((TrinaryExpr *)(expr))->_falseExpr, ppdirectives);
				break;
			case ET_ConstructorExpr:
			{
				ConstructorExpr * constructor = (ConstructorExpr *) expr;
				if (constructor->_nExprs > 0) {
					for (unsigned int i=0; i<constructor->_nExprs; i++) {
						replaced_something = replacePPDirectives (constructor->exprN(i), ppdirectives);
					}
				}
				break;
			}
			case ET_FunctionCall:
			{
				FunctionCall *fcall = (FunctionCall *) expr;
				for (int i=0; i<(fcall->nArgs()); i++) {
					replaced_something = replacePPDirectives (fcall->args[i], ppdirectives);
				}
				break;
			}
			case ET_IndexExpr:
			{
				IndexExpr *index = (IndexExpr *) expr;
				replaced_something = replacePPDirectives (index->array, ppdirectives);
				replaced_something = replacePPDirectives (index->_subscript, ppdirectives);
				break;
			}
			case ET_CastExpr:
			{
				CastExpr *cast = (CastExpr *) expr;
				replaced_something = replacePPDirectives (cast->expr, ppdirectives);
				break;
			}
			case ET_UnaryExpr:
			{
				UnaryExpr *unary = (UnaryExpr *)expr;
				replaced_something = replacePPDirectives (unary->_operand, ppdirectives);
				break;
			}
			default:
				// Do nothing for other expressions
				break;
		}
	}
	return replaced_something;
}

/*
* removeTags --
* FP
*	This function searches for occurrences of BRCC_MOD_DIV_DECL and BRCC_MOD_DIV_BLOCK tags
*	and removes them from the stringstream passed as a parameter
*
*/
void removeTags (stringstream &out) {

	string temp_storage = out.str();

	string decl[11] = {BRCC_MOD_DIV_AFTER_DECL_BEGIN, BRCC_MOD_DIV_AFTER_DECL_END, BRCC_MOD_DIV_DECL_BEGIN, BRCC_MOD_DIV_DECL_END, BRCC_MOD_DIV_BLOCK_0_BEGIN, BRCC_MOD_DIV_BLOCK_0_END, BRCC_MOD_DIV_BLOCK_1_BEGIN, BRCC_MOD_DIV_BLOCK_1_END, BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_BEGIN, BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_END, BRCC_STREAM_ARRAY_POINTER_INIT};

	for (int i=0; i<11; i++) {
		size_t pos = 0;
		while ((pos = temp_storage.find (decl[i], pos)) != string::npos) {
			// Found the tag, now find the \n character
			size_t pos_new_line = pos+decl[i].length();
			while (temp_storage[pos_new_line] != '\n') {
				pos_new_line++;
			}
			// Now we have the length of the string that needs to be removed
			temp_storage.replace (pos, pos_new_line-pos, "");
		}
	}

	out.str(temp_storage);
	// Put write pointer for out stream to the end
	// Apparently, the above command puts that pointer to the beginning of the file,
	out.seekp(0, ios_base::end);
}

/*
* replaceBRCCDivMod --
* FP
*	This function searches for occurrences of BRCC_DIV_OPERATOR_TO_REPLACE and BRCC_MOD_OPERATOR_TO_REPLACE in the code.
*	Once found, the function attempts to locate operands of this operation, and if they are
*	constant integers, it divides or performs % operation on them and replaces the whole expression with the result.
*	BRCC_DIV_OPERATOR_TO_REPLACE is usually generated by the reduce kernel generator instead
*	of the regular division (i.e. "/") operator when generating reduction kernel code.
*	The two numbers being divided are usually stream sizes.
*	BRCC_MOD_OPERATOR_TO_REPLACE is usually generated by replicated 2-D kernels
*	Using / and % operators produces suboptimal results by C2H. Therefore, we try to avoid them
*	A simple way to avoid these operators is for the compiler to precompute results when the operands are constant
*	Stream sizes are not known when producing the kernel code, so it is not possible to do it then
*	Instead this is done after the call site for a kernel is processed, so the stream sizes are known.
*	
*	Currently the code only works if the operands are constant integers, otherwise it fails with an error message
*   __FIX_ME - This should be fixed eventually by parsing the expression and computing it
*
*/
void replaceBRCCDivMod (stringstream &out) {

	string temp_storage = out.str();
	size_t f_start=0, var1_begin, var1_end, var2_begin, var2_end;
	string div_operator = BRCC_DIV_OPERATOR_TO_REPLACE;
	string mod_operator = BRCC_MOD_OPERATOR_TO_REPLACE;
	int first_operand, second_operand, result;
	// Locate the division operator
	string div_or_mod_operator;
	for (int i=0; i<2; i++) {
		if (i==0) {
			div_or_mod_operator = div_operator;
		} else {
			div_or_mod_operator = mod_operator;
		}
		f_start = 0;
		while ((f_start = temp_storage.find (div_or_mod_operator, f_start)) != string::npos) {
			// Now locate the two variables before and after the operator
			// Find the first non-white-space character before the division operator
			var1_end = f_start-1;
			while ((var1_end != 0) && ((temp_storage[var1_end] == ' ') || (temp_storage[var1_end] == '\t') || (temp_storage[var1_end] == '\n'))) {
				var1_end--;
			}
			// Find the next white-space character going backwards
			var1_begin = var1_end;
			while ((var1_begin != 0) && ((temp_storage[var1_begin] != ' ') && (temp_storage[var1_begin] != '\t') && (temp_storage[var1_end] != '\n'))) {
				var1_begin--;
			}
			if ((temp_storage[var1_begin] == ' ') || (temp_storage[var1_begin] == '\t') || (temp_storage[var1_end] == '\n')) {
				var1_begin++;
			}
			stringstream ss_first(temp_storage.substr(var1_begin, var1_end-var1_begin+1));
			if (!(ss_first >> first_operand)) {
				cerr << "\nError STR_TO_INT1: Inside replaceBRCCDivMod: Cannot convert the following string into integer: " << ss_first.str() << endl;
				cerr << "Did you use non-constant stream sizes? Only constant stream sizes are currently supported!\n";
				cerr << "Another possibility is that you used a complicated #define directive? The compiler currently supports only straightforward #define directives\n";
				cerr << "This means that you can only use #define directive of the form #define VAR VALUE, where VALUE has to be a constant (integer), if VAR is used to determine size of a stream\n";
				cout << out.str();
				exit (1);
			}
			// Find the first non-white space character after the division operator
			var2_begin = f_start + div_or_mod_operator.length();
			while ((var2_begin < (temp_storage.length()-1)) && ((temp_storage[var2_begin] == ' ') || (temp_storage[var2_begin] == '\t') || (temp_storage[var1_end] == '\n'))) {
				var2_begin++;
			}
			// Find the next white-space character going forward
			var2_end = var2_begin;
			while ((var2_end < (temp_storage.length()-1)) && ((temp_storage[var2_end] != ' ') && (temp_storage[var2_end] != '\t') && (temp_storage[var1_end] != '\n'))) {
				var2_end++;
			}
			if ((temp_storage[var2_end] == ' ') || (temp_storage[var2_end] == '\t') || (temp_storage[var1_end] == '\n')) {
				var2_end--;
			}
			stringstream ss_second(temp_storage.substr(var2_begin, var2_end-var2_begin+1));
			if (!(ss_second >> second_operand)) {
				cerr << "\nError STR_TO_INT2: Inside replaceBRCCDivMod: Cannot convert the following string into integer:" << ss_second.str() << endl;
				cerr << "Did you use non-constant stream sizes? Only constant stream sizes are currently supported!\n";
				cerr << "Another possibility is that you used a complicated #define directive? The compiler currently supports only straightforward #define directives\n";
				cerr << "This means that you can only use #define directive of the form #define VAR VALUE, where VALUE has to be a constant (integer), if VAR is used to determine size of a stream\n";
				cout << out.str();
				exit (1);
			}
			// Now we have both numbers, so we divide them or mod them ,depending on the operator currently being handled
			if (div_or_mod_operator == div_operator) {
				result = first_operand / second_operand;
			} else {
				result = first_operand % second_operand;
			}
			// Finally, replace the expression with thew result
			stringstream replacement;
			replacement << result;
			temp_storage.replace (var1_begin, var2_end-var1_begin+1, replacement.str());
			f_start = var1_begin;
		}
	}

	out.str(temp_storage);
	// Put write pointer for out stream to the end
	// Apparently, the above command puts that pointer to the beginning of the file,
	out.seekp(0, ios_base::end);

}

/*
* replace_stream_size_arguments --
* FP
*	Finds the function that is being passed to it in the output stream and within that function locates
*	all occurences of the variable_to_replace string. Verifies that it is not simply a substring of another
*   variable, and if it is not, replaces it with the replacement_expression. 
*
*/
void replace_stream_size_arguments (string function_called, string variable_to_replace, string replacement_expression, stringstream &out) {

	string temp_storage = out.str();
	size_t f_start=0, f_end, f_var, f_function;
	string str_begin = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432";
	string str_end = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432";
	while ((f_start = temp_storage.find (str_begin, f_start)) != string::npos) {
		f_end = temp_storage.find (str_end, f_start+1);
		f_function = temp_storage.find (function_called, f_start+1);
		if (f_function < f_end) {
			break;
		} else {
			f_start = f_end;
		}
	}
	// Once the function has been found, find the opening "{" to skip the function declaration
	// The reason for this is that we do not want to change variable names in the declaration
	f_start = temp_storage.find ("{", f_start+1);
	if (f_end == string::npos || f_start == string::npos) {
		fatal ("In replace_stream_size_arguments: Could not find a kernel start or end tag!");
	}
#ifdef DEBUG
	if (temp_storage.find (variable_to_replace, f_start+1) == string::npos)
		cerr << "\nCould not find " << variable_to_replace << " in function " <<  function_called;
#endif
	while ((f_var = temp_storage.find (variable_to_replace, f_start+1)) != string::npos) {
		// In case the variable is between start and end, we have to replace the variable
		if (f_var < f_end) {
//__FIX_ME - Make sure that the variable to replace is not a part of a longer variable name
//			 Check not implemented yet.
			temp_storage.replace (f_var, variable_to_replace.length(), replacement_expression);
		} else {
			// Otherwise we are done with this function
			break;
		}
	}
	out.str(temp_storage);
	// Put write pointer for out stream to the end
	// (the above command puts that pointer to the beginning of the file),
	out.seekp(0, ios_base::end);
}


/*
* replaceKernelPointerInit --
* FP
*	Finds the initialization of the FIFO pointer inside the kernel and initializes it
*	to the base address of that FIFO
*	
*
*/
void replaceKernelPointerInit (string function_name, string var_name, bool stream_of_arrays, string init_string, stringstream &out) {
		string base_function_name;

		// Eliminate the version of the kernel and replica number from the function name
		// This has to be done because the variable names inside kernels only have basic kernel name in them,
		// not the actual version
		// First eliminate the replica number
		size_t replica_loc = function_name.find ("_replica_");
		if (replica_loc == string::npos) {
			base_function_name = function_name;
		} else {
			// Make sure that we find the last occurence of "_replica_"
			size_t temp = replica_loc+1;
			while ((temp = function_name.find ("_replica_", temp)) != string::npos) {
				replica_loc = temp;
				temp++;
			}
			base_function_name = function_name.substr(0, replica_loc);
		}

#ifdef DEBUG
		cerr << endl << function_name << ", " << replica_loc << ", " << base_function_name << endl;
#endif

		// Now eliminate the version suffix (_1d or _2d, or whatever)
		size_t version_loc = base_function_name.find_last_of ("_");
		if (version_loc == string::npos) {
			cerr << "\nInside replaceKernelPointerInit: Could not locate character \"_\" inside base function name " << base_function_name;
			fatal ("");
		}
		base_function_name = base_function_name.substr(0, version_loc);
		string search_string = var_name + " = 876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_" + base_function_name + ";";


		// For now, we will make the replace string be nothing for all initializations
		// Apparently the FIFOs we are using do not contain address port, because
		// there is only one location they can be accessing
		// Therefore, to save some area, we will not initialize it at all
		string replace_string = var_name + ";";
		// Finally, we go on with replacement in the original string
		// First we locate the actual function (because there are multiple versions of the function
		string temp_storage = out.str();
		size_t func_location = temp_storage.find (function_name);
		if (func_location == string::npos) {
			cerr << "\nError RKP001: Could not locate kernel function with a pointer variable to initialize\n";
			cerr << "Was looking for \"" << function_name << "\"";
			cout << out.str();
			fatal ("Terminating program!!!");
		}
		// Then we locate the variable inside that function
		size_t var_location = temp_storage.find(search_string, func_location);
		if (var_location != string::npos) {
			// Now replace the initialization with a real variable
			temp_storage.replace (var_location, search_string.length(), replace_string);
		} else {
			cerr << "\nCould not locate FIFO pointer variable to initialize (1)\n";
			cerr << "Was looking for \"" << search_string << "\" in " << temp_storage.substr (func_location, 512);
			cout << out.str();
			fatal ("Terminating compilation!!!");
		}
		// If the pointer points to stream of arrays, also add initialization inside the for loops
		if (stream_of_arrays) {
			string tag_stream_array_pointer = BRCC_STREAM_ARRAY_POINTER_INIT;
			tag_stream_array_pointer += function_name;
			size_t tag_location = temp_storage.find(tag_stream_array_pointer, func_location);
			if (tag_location != string::npos) {
				replace_string = init_string + tag_stream_array_pointer;
				temp_storage.replace (tag_location, tag_stream_array_pointer.length(), replace_string);
			}
			// If not found, the kernel might be streamRead or streamWrite, which do not require this
		}
		out.str(temp_storage);
		// Put write pointer for out stream to the end
		// (the above command puts that pointer to the beginning of the file),
		out.seekp(0, ios_base::end);
}



/*
* findVarInArray --
* FP
*	Looks for a variable name in an array
*		returns the index of the variable if it is found, else -1
*
*/
int findVarInArray (Decl **var_array, int array_size, Symbol *variable) {

	int i;

	for (i=0; i<array_size; i++) {
		if (var_array[i]->name->name == variable->name)
			return i;
	}
	return -1;
}


/*
* findVarInVector --
* FP
*	Looks for a variable name in a given vector
*		returns the index of the variable if it is found, else -1
*
*/
int findVarInVector (DeclVector &dec_vector, Symbol *variable) {
	unsigned int i;

	for (i=0; i<dec_vector.size(); i++) {
		if (dec_vector[i]->name->name == variable->name)
			return i;
	}
	return -1;
}
/* overloaded to find a variable in vector of StreamInfo */
int findVarInVector (StreamInfoVector &dec_vector, Symbol *variable) {
	unsigned int i;

	for (i=0; i<dec_vector.size(); i++) {
		if (dec_vector[i].stream->name->name == variable->name)
			return i;
	}
	return -1;
}



static
void
printAssignOp(ostream& out, AssignOp op)
{
    switch (op)
    {
      case AO_Equal:          //  =
        out << "=";
        break;

      case AO_PlusEql:        // +=
        out << "+=";
        break;

      case AO_MinusEql:       // -=
        out << "-=";
        break;

      case AO_MultEql:        // *=
        out << "*=";
        break;

      case AO_DivEql:         // /=
        out << "/=";
        break;

      case AO_ModEql:         // %=
        out << "%=";
        break;

      case AO_ShlEql:         // <<=
        out << "<<=";
        break;

      case AO_ShrEql:         // >>=
        out << ">>=";
        break;

      case AO_BitAndEql:      // &=
        out << "&=";
        break;

      case AO_BitXorEql:      // ^=
        out << "^=";
        break;

      case AO_BitOrEql:       // |=
        out << "|=";
        break;
    }
}

// o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o
static
void
printRelOp(ostream& out, RelOp op)
{
    switch (op)
    {
      case RO_Equal:          // ==
        out << "==";
        break;

      case RO_NotEqual:       // !=
        out << "!=";
        break;

      case RO_Less:           // < 
        out << "<";
        break;

      case RO_LessEql:        // <=
        out << "<=";
        break;

      case RO_Grtr:           // > 
        out << ">";
        break;

      case RO_GrtrEql:        // >=
        out << ">=";
        break;
    }
}

/*
* printBinOp --
* FP
*	Prints a binary operand (code copied from express.cpp)
*
*/
void printBinOp (ostream& out, BinaryOp op) {
    switch (op)
    {
      case BO_Plus:        // +
        out << "+";
        break;

      case BO_Minus:       // -
        out << "-";
        break;

      case BO_Mult:        // *
        out << "*";
        break;

      case BO_Div:         // /
        out << "/";
        break;

      case BO_Mod:         // %
        out << "%";
        break;

      case BO_Shl:         // <<
        out << "<<";
        break;

      case BO_Shr:         // >>
        out << ">>";
        break;

      case BO_BitAnd:      // &
        out << "&";
        break;

      case BO_BitXor:      // ^
        out << "^";
        break;

      case BO_BitOr:       // |
        out << "|";
        break;

      case BO_And:         // &&
        out << "&&";
        break;

      case BO_Or:          // ||
        out << "||";
        break;

      case BO_Comma:       // x,y
        out << ",";
        break;

      case BO_Member:      // x.y
        out << ".";
        break;

      case BO_PtrMember:   // x->y
        out << "->";
        break;

      //  case BO_Index        // x[y]
      case BO_Assign:      // An AssignExpr
        break;
      case BO_Rel:         // A RelExpr
        break;
      default:
		  out << "  unkown op:" << op << "    ";
		  break;
    }
}

// o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o+o
static
void
printUnaryOp(ostream& out, UnaryOp op)
{
    switch (op)
    {
      case UO_Plus:           // +
        out << "+";
        break;

      case UO_Minus:          // -
        out << "-";
        break;

      case UO_BitNot:         // ~
        out << "~";
        break;

      case UO_Not:            // !
        out << "!";
        break;

      case UO_PreInc:         // ++x
      case UO_PostInc:        // x++
        out << "++";
        break;

      case UO_PreDec:         // --x
      case UO_PostDec:        // x--
        out << "--";
        break;

      case UO_AddrOf:         // &
        out << "&";
        break;

      case UO_Deref:          // *
        out << "*";
        break;
    }
}


/*
* findVarsUsed --
* FP
*	Analyzes an expression and finds any references to any of the variables in the input array.
*	Returns an array of booleans (one for each variable). The array should be allocated outside the function,
*   and pointer passed to this function.
*	If a variable is used inside the expression, the corresponding entry in the array will be true, otherwise false
*		Arguments are: the expression, a list of variable declarations, the number of variable declarations
*		the list of known function declarations and the pointer to the output boolean array
*
*/

void findVarsUsed (Expression *expr, Statement *ste, DeclVector &funcdecs, Decl **variables, unsigned int vars, bool *usage_array) {

// __FIX_ME - Not all expression types have been handled


	if (expr == NULL)
		return;
	switch (expr->etype) {
		case ET_Variable:
		{
			int var_pos = findVarInArray (variables, vars, ((Variable *)(expr))->name);
			// If the variable is not a stream, we should simply print it out
			if ((var_pos == -1)) {
				break;
			}
			// Otherwise the variable was found in the list of variables to check
			usage_array[var_pos] = true;
			break;
		}
		case ET_BinaryExpr:
			// Check the left side of the binary expression, unless it is an assignment
			// This is because we are looking for variables being used, not being defined.
			if (((BinaryExpr *)(expr))->bOp != BO_Assign) {
				findVarsUsed (((BinaryExpr *)(expr))->_leftExpr, ste, funcdecs, variables, vars, usage_array);
			}
			// There is one caveat with the binary expressions. Access to a structure member is also considered a binary expression
			// Therefore we have to be careful how we analyze that. For instance let the structure be declared as struct point {float x, float y};
			// and let there be an instance struct point a, and another variable called x. a.x refers to the x memeber of the structure a
			// and has nothing to do with the other variable x. Therefore, we should not analyze the right side (after the dot) of the struct reference
			// That is why we distinguish between these two cases here
			if (((BinaryExpr *)(expr))->bOp != BO_Member) {
				// This means this is NOT a struct reference, so we analyze the right side
				findVarsUsed (((BinaryExpr *)(expr))->_rightExpr, ste, funcdecs, variables, vars, usage_array);
			}
			break;
		case ET_TrinaryExpr:
			findVarsUsed (((TrinaryExpr *)(expr))->_condExpr, ste, funcdecs, variables, vars, usage_array);
			findVarsUsed (((TrinaryExpr *)(expr))->_trueExpr, ste, funcdecs, variables, vars, usage_array);
			findVarsUsed (((TrinaryExpr *)(expr))->_falseExpr, ste, funcdecs, variables, vars, usage_array);
			break;
		case ET_ConstructorExpr:
		{
			ConstructorExpr * constructor = (ConstructorExpr *) expr;
			if (constructor->_nExprs > 0) {
				for (unsigned int i=0; i<constructor->_nExprs; i++) {
					findVarsUsed (constructor->exprN(i), ste, funcdecs, variables, vars, usage_array);
				}
			}
			break;
		}
		case ET_FunctionCall:
		{
			FunctionCall *fcall = (FunctionCall *) expr;
			for (int i=0; i<(fcall->nArgs()); i++) {
				findVarsUsed (fcall->args[i], ste, funcdecs, variables, vars, usage_array);
			}
			break;
		}
		case ET_IndexExpr:
		{
			IndexExpr *index = (IndexExpr *) expr;
			findVarsUsed (index->array, ste, funcdecs, variables, vars, usage_array);
			findVarsUsed (index->_subscript, ste, funcdecs, variables, vars, usage_array);
			break;
		}
		case ET_CastExpr:
		{
			CastExpr *cast = (CastExpr *) expr;
			findVarsUsed (cast->expr, ste, funcdecs, variables, vars, usage_array);
			break;
		}
		case ET_UnaryExpr:
		{
			UnaryExpr *unary = (UnaryExpr *)expr;
			findVarsUsed (unary->_operand, ste, funcdecs, variables, vars, usage_array);
			break;
		}
		default:
			// For some expression types, such as constants, we don't do anything.
			break;
	}
}


/*
* generateCollectorsDistributors --
* FP
*	Creates Verilog code for all collectors, distributors and collectors/distributors required by the current application
*	For each Verilog file created, a TCL file is also generated, which is necessary to integrate the Verilog module into SOPC system
*	Custom modules have to be generated because replication factors vary for different applications
*	so the number of inputs and outputs to/from collectors and distributors varies as well
*	Therefore custom code has to be generated
*		Arguments are: NONE
*/
void generateCollectorsDistributors () {
	ofstream verilog_file, tcl_file;
	std::list<GraphNode>::reverse_iterator n_iter;
	std::list<GraphEdge>::iterator e_iter;
	int inputs, outputs, counter, bit_counter;
	ostringstream verilog_file_name, tcl_file_name;

	for (n_iter=globals.replicated.nodes.rbegin(); n_iter != globals.replicated.nodes.rend(); ++n_iter) {
		if ((n_iter->name.find(BRCC_COLLECTOR) != string::npos)) {
			// Generate custom collector code
			// First find out the number of inputs and outputs
			inputs = n_iter->inEdges();
			outputs = n_iter->out_edges.size();
			if (outputs != 1) {
				cerr << "\nNumber of outputs of the collector node " << n_iter->name << " is " << outputs;
				fatal ("Number of outputs of any collector node has to be 1");
			}
			if (inputs < 2) {
				cerr << "\nNumber of inputs to the collector node " << n_iter->name << " is " << inputs;
				fatal ("Number of inputs to any collector node has to be >= 2");
			}
			// Check if the collector is followed by a double distributor
			if (n_iter->out_edges.front().destination->name.find(BRCC_DOUBLE_DISTRIBUTOR) != string::npos) {
				// Need to generate a collector/double distributor node
				// Generate custom Verilog code
				// First find out the number of inputs and outputs
				inputs = n_iter->inEdges();
				outputs = n_iter->out_edges.front().destination->out_edges.size();
				if (n_iter->out_edges.front().destination->inEdges() != 1) {
					cerr << "\nNumber of inputs of the double distributor node " << n_iter->name << " is " << inputs;
					fatal ("Number of inputs of any distributor node has to be 1");
				}
				if (outputs < 2) {
					cerr << "\nNumber of outputs of the distributor node " << n_iter->name << " is " << outputs;
					fatal ("Number of outputs to any distributor node has to be >= 2");
				}
				int trees = n_iter->out_edges.front().destination->trees;
				int leaves = n_iter->out_edges.front().destination->replicated_leaves;
				if (trees*leaves != outputs) {
					cerr << "\nNumber of outputs of the double distributor node " << n_iter->name << " is " << outputs;
					cerr << "\nNumber of trees is " << trees << " and the number of leaves in each tree is " << leaves;
					fatal ("Errorr: Number of outputs to any double distributor node should be == trees*leaves");
				}
				// Find out the reduction ratio
				int x_over_y = n_iter->out_edges.front().destination->insize_dim0/n_iter->out_edges.front().destination->reducesize_dim0;
				bool generate_cdd_with_preserved_register = false;
				if (x_over_y%leaves != 0) {
					generate_cdd_with_preserved_register = true;
				}
				// Generate a name for the file
				verilog_file_name.str("");
				tcl_file_name.str("");
				if (generate_cdd_with_preserved_register) {
					verilog_file_name << globals.SOPC_COMPONENT_PATH << "collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << ".v";
					tcl_file_name << globals.SOPC_COMPONENT_PATH << "collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << "_hw.tcl";
				} else {
					verilog_file_name << globals.SOPC_COMPONENT_PATH << "collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << ".v";
					tcl_file_name << globals.SOPC_COMPONENT_PATH << "collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << "_hw.tcl";
				}
				verilog_file.open (verilog_file_name.str().c_str());
				tcl_file.open (tcl_file_name.str().c_str());
				if (verilog_file.fail()) {
					cerr << "***Unable to open file (1) " << verilog_file_name.str() << "\n";
					exit(1);
				}
				if (tcl_file.fail()) {
					cerr << "***Unable to open file (2) " << tcl_file_name.str() << "\n";
					exit(1);
				}
				// Emit Verilog code
				// Add many spaces after the module declaration, so that we can easily replace the module name
				if (generate_cdd_with_preserved_register) {
					verilog_file << "module collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << " (                                                                                              \n";
				} else {
					verilog_file << "module collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << " (                                                                                              \n";
				}
				verilog_file << "\tinput\t\t\t\t\tclk,\n";
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tinput\t\t\t\t\tread_" << counter << ",\n";
					verilog_file << "\toutput [DATA_WIDTH-1:0] readdata_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_" << counter << ",\n";
				}
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\tinput\t\t\t\t\twrite_" << counter << ",\n";
					verilog_file << "\tinput [DATA_WIDTH-1:0]\twritedata_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_" << counter << ",\n";
				}
				verilog_file << "\tinput\t\t\t\t\treset\n";
				verilog_file << ");\n";
				verilog_file << "parameter FIFO_DEPTH = 4, DATA_WIDTH = 32, NO_INPUTS = " << inputs << ", NO_TREES = " << trees << ", NO_LEAVES = " << leaves << ", X_OVER_Y = " << n_iter->insize_dim0/n_iter->reducesize_dim0 << ";\n\n";
				verilog_file << "reg [NO_INPUTS-1:0]\t\tinput_selector;\n";
				verilog_file << "reg [NO_TREES-1:0] tree_selector;\n";
				verilog_file << "reg [NO_LEAVES-1:0] leaf_selector;\n";
				if (generate_cdd_with_preserved_register) {
					verilog_file << "reg [NO_LEAVES-1:0] leaf_selector_preserved;\n";
				}
				verilog_file << "reg [X_OVER_Y-1:0] element_counter;  // This register has too many elements, which will be optimized by the synthesis tool\n";
				verilog_file << "reg [DATA_WIDTH-1:0]	writedata;\n\n";

				verilog_file << "wire\t\t\t\t\twait_write;\n";
				verilog_file << "wire\t\t\t\t\twrite;\n\n";
				verilog_file << "\nwire [(NO_LEAVES*NO_TREES)-1:0] output_selector;\n";
				verilog_file << "wire read;\n";
				verilog_file << "wire wait_read;\n";
				verilog_file << "wire [DATA_WIDTH-1:0] readdata;\n";

				verilog_file << "assign read = ";
				for (counter = 0; counter < outputs; counter++) {
					if (0 != counter) {
						verilog_file << " | ";
					}
					verilog_file << "(read_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "assign wait_read_" << counter << " = ~output_selector[" << counter << "] | wait_read;\n";
					verilog_file << "assign readdata_" << counter << " = readdata;\n";
				}
				verilog_file << "\n";

				for (int i=0; i<trees; i++) {
					for (int j=0; j<leaves; j++) {
						verilog_file << "assign output_selector[" << i*leaves+j << "] = tree_selector[" << i << "] & leaf_selector[" << j << "];\n";
					}
				}

				verilog_file << "\n";

				if (leaves > 1) {
					verilog_file << "always @(posedge clk)\n";
					verilog_file << "begin\n";
					verilog_file << "if (reset)\n";
					verilog_file << "begin\n";
					verilog_file << "leaf_selector <= {{NO_LEAVES-1{1'b0}}, 1'b1};\n";
					verilog_file << "end\n";
					verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
					verilog_file << "begin\n";
					if (generate_cdd_with_preserved_register) {
						verilog_file << "leaf_selector <= leaf_selector_preserved;\n";
					} else {
						verilog_file << "leaf_selector <= {{NO_LEAVES-1{1'b0}}, 1'b1};\n";
					}
					verilog_file << "end\n";
					verilog_file << "else if (read & ~wait_read)\n";
					verilog_file << "begin\n";
					verilog_file << "leaf_selector <= {leaf_selector[NO_LEAVES-2:0], leaf_selector[NO_LEAVES-1]};\n";
					verilog_file << "end\n";
					verilog_file << "end\n\n";
				} else {
					verilog_file << "\nalways @(*)\nbegin\n leaf_selector = 1'b1;\nend\n\n";
				}
				if ((leaves > 1) && generate_cdd_with_preserved_register) {
					verilog_file << "always @(posedge clk)\n";
					verilog_file << "begin\n";
					verilog_file << "if (reset)\n";
					verilog_file << "begin\n";
					verilog_file << "leaf_selector_preserved <= {{NO_LEAVES-1{1'b0}}, 1'b1};\n";
					verilog_file << "end\n";
					if (2 == trees) {
						verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)) & (tree_selector == 2'b01))\n";
					} else {
						verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)) & (tree_selector == {2'b01, {(NO_TREES-2){1'b0}}}))\n";
					}
					verilog_file << "begin\n";
					verilog_file << "leaf_selector_preserved <=  {leaf_selector[NO_LEAVES-2:0], leaf_selector[NO_LEAVES-1]};\n";
					verilog_file << "end\n";
					verilog_file << "end\n\n";
				}
				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "if (reset)\n";
				verilog_file << "begin\n";
				verilog_file << "element_counter <= {X_OVER_Y{1'b0}};\n";
				verilog_file << "end\n";
				verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
				verilog_file << "begin\n";
				verilog_file << "element_counter <= {X_OVER_Y{1'b0}};\n";
				verilog_file << "end\n";
				verilog_file << "else if (read & ~wait_read)\n";
				verilog_file << "begin\n";
				verilog_file << "element_counter <= element_counter + 1'b1;\n";
				verilog_file << "end\n";
				verilog_file << "end\n\n";

				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "if (reset)\n";
				verilog_file << "begin\n";
				verilog_file << "tree_selector <= {{(NO_TREES-1){1'b0}}, 1'b1};\n";
				verilog_file << "end\n";
				verilog_file << "else\n";
				verilog_file << "begin\n";
				verilog_file << "if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
				verilog_file << "tree_selector <= {tree_selector[NO_TREES-2:0], tree_selector[NO_TREES-1]};\n";
				verilog_file << "end\n";
				verilog_file << "end\n\n";

				verilog_file << "assign write = ";
				for (counter = 0; counter < inputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(write_" << counter << " & input_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";

				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "assign wait_write_" << counter << " = ~input_selector[" << counter << "] | wait_write;\n";
				}
				verilog_file << "\n";
				verilog_file << "always @(*)\n";
				verilog_file << "begin\n";
				verilog_file << "\tcase (input_selector) // synthesis full_case\n";
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\t\t" << inputs << "'b";
					for (bit_counter = inputs-1; bit_counter >=0; bit_counter--) {
						if (counter == bit_counter) {
							verilog_file << "1";
						} else {
							verilog_file << "0";
						}
					}
					verilog_file << ": writedata = writedata_" << counter << ";\n";
				}
				verilog_file << "\tendcase\n";
				verilog_file << "end\n\n";
				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "\tif (reset)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tinput_selector = {{NO_INPUTS-1{1'b0}}, 1'b1};\n";
				verilog_file << "\tend\n";
				verilog_file << "\telse\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (write & ~wait_write)\n";
				verilog_file << "\t\t\tinput_selector = {input_selector[NO_INPUTS-2:0], input_selector[NO_INPUTS-1]};\n";
				verilog_file << "\tend\n";
				verilog_file << "end\n\n";


				// Now generate TCL file
				tcl_file << "# TCL File Generated by BR2C Compiler\n";
				tcl_file << "# DO NOT MODIFY\n\n";

				if (generate_cdd_with_preserved_register) {
					tcl_file << "set_source_file \"collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << ".v\"\n";
					tcl_file << "set_module \"collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << "\"\n";
				} else {
					tcl_file << "set_source_file \"collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << ".v\"\n";
					tcl_file << "set_module \"collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << "\"\n";
				}
				tcl_file << "set_module_description \"\"\n";
				if (generate_cdd_with_preserved_register) {
					tcl_file << "set_module_property \"className\" \"collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << "\"\n";
				} else {
					tcl_file << "set_module_property \"className\" \"collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << "\"\n";
				}
				tcl_file << "set_module_property \"group\" \"\"\n";
				tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
				if (generate_cdd_with_preserved_register) {
					tcl_file << "set_module_property \"synthesisFiles\" \"collector_double_distributor_fifo_with_preserved_register_" << inputs << "_to_" << trees << "_" << leaves << ".v\"\n\n";
				} else {
					tcl_file << "set_module_property \"synthesisFiles\" \"collector_double_distributor_fifo_" << inputs << "_to_" << trees << "_" << leaves << ".v\"\n\n";
				}
				tcl_file << "# Module parameters\n";
				tcl_file << "add_parameter \"FIFO_DEPTH\" \"integer\" \"4\" \"\"\n";
				tcl_file << "set_parameter_property \"FIFO_DEPTH\" \"enabled\" \"false\"\n";
				tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
				tcl_file << "set_parameter_property \"DATA_WIDTH\" \"enabled\" \"false\"\n";
				tcl_file << "add_parameter \"NO_INPUTS\" \"integer\" \"" << inputs << "\" \"\"\n\n";
				tcl_file << "set_parameter_property \"NO_INPUTS\" \"enabled\" \"false\"\n";
				tcl_file << "add_parameter \"NO_TREES\" \"integer\" \"" << trees << "\" \"\"\n";
				tcl_file << "set_parameter_property \"NO_TREES\" \"enabled\" \"false\"\n";
				tcl_file << "add_parameter \"NO_LEAVES\" \"integer\" \"" << leaves << "\" \"\"\n";
				tcl_file << "set_parameter_property \"NO_LEAVES\" \"enabled\" \"false\"\n";
				tcl_file << "add_parameter \"X_OVER_Y\" \"integer\" \"" << n_iter->insize_dim0/n_iter->reducesize_dim0 << "\" \"\"\n";
				tcl_file << "set_parameter_property \"X_OVER_Y\" \"enabled\" \"false\"\n\n";

				tcl_file << "# Interface clock\n";
				tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
				tcl_file << "# Ports in interface clock\n";
				tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
				tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

				for (counter = 0; counter < inputs; counter++) {
					tcl_file << "# Interface in_" << counter << "\n";
					tcl_file << "add_interface \"in_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface in_" << counter << "\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"write_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"writedata_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"wait_write_" << counter << "\" \"waitrequest\"\n\n";
				}

				for (counter = 0; counter < outputs; counter++) {
					tcl_file << "# Interface out_" << counter << "\n";
					tcl_file << "add_interface \"out_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitTime\" \"1\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"read_" << counter << "\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"readdata_" << counter << "\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"wait_read_" << counter << "\" \"waitrequest\"\n\n";
				}
				tcl_file.close();
			} else {
				// Otherwise it's an ordinary collector node
				// Generate a name for the file
				verilog_file_name.str("");
				tcl_file_name.str("");
				// Check whether the edge carries a stream of arrays
				// We could check any edge, because they should all be labeled. Check the first one for simplicity.
				if (n_iter->out_edges.front().stream_of_arrays) {
					verilog_file_name << globals.SOPC_COMPONENT_PATH << "array_collector_fifo_" << inputs << "_to_" << outputs << ".v";
					tcl_file_name << globals.SOPC_COMPONENT_PATH << "array_collector_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
					verilog_file.open (verilog_file_name.str().c_str());
					tcl_file.open (tcl_file_name.str().c_str());
					if (verilog_file.fail()) {
						cerr << "***Unable to open file (3) " << verilog_file_name.str() << "\n";
						exit(1);
					}
					if (tcl_file.fail()) {
						cerr << "***Unable to open file (4) " << tcl_file_name.str() << "\n";
						exit(1);
					}
					// Emit Verilog code
					// Add many spaces after the module declaration, so that we can easily replace the module name
					verilog_file << "module " << "array_collector_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
					verilog_file << "\tinput\t\t\t\t\tclk,\n";
					verilog_file << "\tinput\t\t\t\t\treset,\n";
					//START Write ports (need as many as there are inputs to the collector)
					for (counter = 0; counter < inputs; counter++) {
						verilog_file << "\n\tinput\t\t\t\t\twrite_in_control_" << counter << ",\n";
						verilog_file << "\tinput\t\t\t\t\twrite_array_" << counter << ",\n";
						verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_write_array_" << counter << ",\n";
						verilog_file << "\tinput\t[DATA_WIDTH-1:0]\twritedata_array_" << counter << ",\n";
						verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_in_control_" << counter << ",\n";
						verilog_file << "\toutput\t\t\t\t\twait_write_in_control_" << counter << ",\n";
						verilog_file << "\toutput\t\t\t\t\twait_write_array_" << counter << ",\n";
					}
					// Read ports (only need one instance of these ports)
					verilog_file << "\tinput\t\t\t\t\twrite_out_control,\n";
					verilog_file << "\tinput\t\t\t\t\tread_array,\n";
					verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_read_array,\n";
					verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_out_control,\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_out_control,\n";
					verilog_file << "\toutput\t[DATA_WIDTH-1:0]\treaddata_array,\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_array);\n";
					verilog_file << "parameter	MEMORY_SIZE="  << n_iter->out_edges.front().array_size << ",\n"; 	// The number of words in each FIFO buffer slot
					verilog_file << "\t\t\tFIFO_DEPTH_LOG=2,\n";	// LOG_2{Number of buffer slots in the FIFO (i.e. FIFO depth)}
					verilog_file << "\t\t\tDATA_WIDTH=32,\n";		// Width of words stored in the buffer
					verilog_file << "\t\t\tADDR_WIDTH=" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << ",\n"; // Width of the address bus (has to be == log_2 (MEMORY_SIZE))
					verilog_file << "\t\t\tNO_INPUTS = " << inputs << ",\n";
					verilog_file << "\t\t\tNO_INPUTS_LOG = " << (int)(ceil(log(1.0*inputs)/log(2.0))) << ";\n\n";		// Number of inputs to the collector
			
					verilog_file << "reg\t[NO_INPUTS-1:0]\t\tinput_selector;\n";
					verilog_file << "reg\t[NO_INPUTS_LOG-1:0]\tbinary_input_selector;\n";
					verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\tread_ptr;\n";				// Will contain index of the memory module currently used for reading
					verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\twrite_ptr;\n\n";				// Will contain index of the memory module currently used for writing
					// Both pointers declared above this line have one extra bit, which is used to distinguish between the buffer being full or empty
					verilog_file << "wire\tbuffer_full;\n";
					verilog_file << "wire\tbuffer_empty, write_array;\n";
					
					verilog_file << "wire\t[(2**FIFO_DEPTH_LOG)-1:0]\twrite_array_selected;\n";
					verilog_file << "wire\twrite_in_control, wait_write_in_control;\n";
					verilog_file << "reg\t[ADDR_WIDTH-1:0]\taddress_write_array;\n";
					verilog_file << "reg\t[DATA_WIDTH-1:0]\twritedata_array;\n";
					verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\tempty_slots;\n";
					verilog_file << "reg\t[NO_INPUTS_LOG-1:0]\tinput_distance [NO_INPUTS-1:0];\n";
					verilog_file << "reg\t[FIFO_DEPTH_LOG-1:0]\tfifo_distance [(2**FIFO_DEPTH_LOG)-1:0];\n\n";

					verilog_file << "wire\t[DATA_WIDTH*(2**FIFO_DEPTH_LOG)-1:0]\tdata_read;\n\n";
					// Address selector has as many bits as needed to address all FIFO slots
					// This is necessary so that all FIFO slots can receive a valid write signal
					verilog_file << "wire\t[FIFO_DEPTH_LOG-1:0]address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n";
					verilog_file << "wire\t[FIFO_DEPTH_LOG:0]temp_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n"; // This one has one bit more to allow detection of overflow
					verilog_file << "wire\t[FIFO_DEPTH_LOG-1:0]write_selector[(2**FIFO_DEPTH_LOG)-1:0];\n\n";

					verilog_file << "wire\t[ADDR_WIDTH-1:0]\tselected_address_write_array [(2**FIFO_DEPTH_LOG)-1:0];\n";
					verilog_file << "wire\t[DATA_WIDTH-1:0]\tselected_writedata_array [(2**FIFO_DEPTH_LOG)-1:0];\n\n";

					verilog_file << "genvar\ti;\n";
					verilog_file << "integer\tj;\n\n";

					verilog_file << "\tassign write_array = ";
					for (counter = 0; counter < inputs; counter++) {
						if (counter != 0) {
							verilog_file << " | ";
						}
						verilog_file << "(write_array_" << counter << " & input_selector[" << counter << "])";
					}
					verilog_file <<	";\n";

					verilog_file << "\tassign write_in_control = ";
					for (counter = 0; counter < inputs; counter++) {
						if (counter != 0) {
							verilog_file << " | ";
						}
						verilog_file << "(write_in_control_" << counter << " & input_selector[" << counter << "])";
					}
					verilog_file << ";\n\n";
					for (counter = 0; counter < inputs; counter++) {
						verilog_file << "\tassign wait_write_array_" << counter << " = write_array_" << counter << " & (input_distance[" << counter << "] >= empty_slots);\n";
						verilog_file << "\tassign wait_write_in_control_" << counter << " = ~input_selector[" << counter << "] | wait_write_in_control;\n";
					}

					verilog_file << "\talways @(posedge clk)\n\tbegin\n";
					verilog_file << "\t\tif (reset)\n\t\tbegin\n";
					verilog_file << "\t\t\tinput_selector = {{NO_INPUTS-1{1'b0}}, 1'b1};\n\t\tend\n\t\telse\n\t\tbegin\n";
					verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
					verilog_file << "\t\t\t\tinput_selector = {input_selector[NO_INPUTS-2:0], input_selector[NO_INPUTS-1]};\n";
					verilog_file << "\t\tend\n\tend\n\n";

					verilog_file << "\talways @(posedge clk)\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tif (reset)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tbinary_input_selector <= {NO_INPUTS_LOG{1'b0}};\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
					verilog_file << "\t\t\tbegin\n";
					verilog_file << "\t\t\t\tif (binary_input_selector == NO_INPUTS-1)\n";
					verilog_file << "\t\t\t\t\tbinary_input_selector <= {NO_INPUTS_LOG{1'b0}};\n";
					verilog_file << "\t\t\t\telse\n";
					verilog_file << "\t\t\t\t\tbinary_input_selector <= binary_input_selector + 1'b1;\n";
					verilog_file << "\t\t\tend\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\tend\n";


					verilog_file << "\tassign wait_read_array = read_array & buffer_empty;\n";

					verilog_file << "\tparameterized_mux the_mux (.data (data_read),\n";
					verilog_file << "\t\t\t.sel (read_ptr[FIFO_DEPTH_LOG-1:0]),\n";
					verilog_file << "\t\t\t.result(readdata_array));\n";
					verilog_file << "\tdefparam\tthe_mux.LPM_WIDTH = DATA_WIDTH,\n";
					verilog_file << "\t\t\t\tthe_mux.LPM_SIZE = (2**FIFO_DEPTH_LOG),\n";
					verilog_file << "\t\t\t\tthe_mux.LPM_WIDTHS = FIFO_DEPTH_LOG;\n\n";


					verilog_file << "\talways @(posedge clk or posedge reset)\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tif (reset == 1'b1)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tempty_slots <= (2**FIFO_DEPTH_LOG);\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control & write_out_control & ~wait_write_out_control)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\t// Both pointers changing at the same time, the number of full slots does not change\n";
					verilog_file << "\t\t\tempty_slots <= empty_slots;\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\t// New element being written; number of full slots should increase\n";
					verilog_file << "\t\t\tempty_slots <= empty_slots - 1'b1;\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse if (write_out_control & ~wait_write_out_control)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\t// Element has been read; decrease the number of full slots\n";
					verilog_file << "\t\t\tempty_slots <= empty_slots + 1'b1;\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\tend\n\n";

					verilog_file << "\talways @(posedge clk)\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tif (reset)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tfor (j=0; j<NO_INPUTS; j=j+1)\n";
					verilog_file << "\t\t\tbegin\n";
					verilog_file << "\t\t\t\tinput_distance[j] <= j;\n";
					verilog_file << "\t\t\tend\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
					verilog_file << "\t\t\tbegin\n";
					verilog_file << "\t\t\t\tfor (j=0; j<NO_INPUTS; j=j+1)\n";
					verilog_file << "\t\t\t\tbegin\n";
					verilog_file << "\t\t\t\t\tif (input_distance[j] == {NO_INPUTS{1'b0}})\n";
					verilog_file << "\t\t\t\t\t\tinput_distance[j] <= NO_INPUTS-1;\n";
					verilog_file << "\t\t\t\t\telse\n";
					verilog_file << "\t\t\t\t\t\tinput_distance[j] <= input_distance[j]-1'b1;\n";
					verilog_file << "\t\t\t\tend\n";
					verilog_file << "\t\t\tend\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\tend\n\n";

					verilog_file << "\talways @(posedge clk)\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tif (reset)\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
					verilog_file << "\t\t\tbegin\n";
					verilog_file << "\t\t\t\tfifo_distance[j] <= j;\n";
					verilog_file << "\t\t\tend\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\t\telse\n";
					verilog_file << "\t\tbegin\n";
					verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
					verilog_file << "\t\t\tbegin\n";
					verilog_file << "\t\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
					verilog_file << "\t\t\t\tbegin\n";
					verilog_file << "\t\t\t\t\tfifo_distance[j] <= fifo_distance[j]-1'b1;\n";
					verilog_file << "\t\t\t\tend\n";
					verilog_file << "\t\t\tend\n";
					verilog_file << "\t\tend\n";
					verilog_file << "\tend\n\n";

					verilog_file << "\tgenerate\n";
					verilog_file << "\tfor(i = 0; i<(2**FIFO_DEPTH_LOG); i = i+1)\n";
					verilog_file << "\tbegin : memory_block\n";
					verilog_file << "\t\taltsyncram	memory (\n";
					verilog_file << "\t\t\t.wren_a (write_array_selected[i]),\n";
					verilog_file << "\t\t\t.clock0 (clk),\n";
					verilog_file << "\t\t\t.address_a (selected_address_write_array[i]),\n";
					verilog_file << "\t\t\t.address_b (address_read_array),\n";
					verilog_file << "\t\t\t.data_a (selected_writedata_array[i]),\n";
					verilog_file << "\t\t\t.q_b (data_read[DATA_WIDTH*(i+1)-1:DATA_WIDTH*i]),\n";
					verilog_file << "\t\t\t.aclr0 (1'b0),\n";
					verilog_file << "\t\t\t.aclr1 (1'b0),\n";
					verilog_file << "\t\t\t.addressstall_a (1'b0),\n";
					verilog_file << "\t\t\t.addressstall_b (1'b0),\n";
					verilog_file << "\t\t\t.byteena_a (1'b1),\n";
					verilog_file << "\t\t\t.byteena_b (1'b1),\n";
					verilog_file << "\t\t\t.clock1 (1'b1),\n";
					verilog_file << "\t\t\t.clocken0 (1'b1),\n";
					verilog_file << "\t\t\t.clocken1 (1'b1),\n";
					verilog_file << "\t\t\t.clocken2 (1'b1),\n";
					verilog_file << "\t\t\t.clocken3 (1'b1),\n";
					verilog_file << "\t\t\t.data_b ({32{1'b1}}),\n";
					verilog_file << "\t\t\t.eccstatus (),\n";
					verilog_file << "\t\t\t.q_a (),\n";
					verilog_file << "\t\t\t.rden_a (1'b1),\n";
					verilog_file << "\t\t\t.rden_b (1'b1),\n";
					verilog_file << "\t\t\t.wren_b (1'b0));\n";
					verilog_file << "\t\tdefparam\n";
					verilog_file << "\t\t\tmemory.address_reg_b = \"CLOCK0\",\n";
					verilog_file << "\t\t\tmemory.clock_enable_input_a = \"BYPASS\",\n";
					verilog_file << "\t\t\tmemory.clock_enable_input_b = \"BYPASS\",\n";
					verilog_file << "\t\t\tmemory.clock_enable_output_a = \"BYPASS\",\n";
					verilog_file << "\t\t\tmemory.clock_enable_output_b = \"BYPASS\",\n";
					verilog_file << "\t\t\tmemory.lpm_type = \"altsyncram\",\n";
					verilog_file << "\t\t\tmemory.operation_mode = \"DUAL_PORT\",\n";
					verilog_file << "\t\t\tmemory.outdata_aclr_b = \"NONE\",\n";
					verilog_file << "\t\t\tmemory.outdata_reg_b = \"UNREGISTERED\",\n";
					verilog_file << "\t\t\tmemory.power_up_uninitialized = \"FALSE\",\n";
					verilog_file << "\t\t\tmemory.read_during_write_mode_mixed_ports = \"DONT_CARE\",\n";
					verilog_file << "\t\t\tmemory.widthad_a = ADDR_WIDTH,\n";
					verilog_file << "\t\t\tmemory.widthad_b = ADDR_WIDTH,\n";
					verilog_file << "\t\t\tmemory.width_a = DATA_WIDTH,\n";
					verilog_file << "\t\t\tmemory.width_b = DATA_WIDTH,\n";
					verilog_file << "\t\t\tmemory.width_byteena_a = 1;\n";

					verilog_file << "\t\tassign temp_address_selector[i] = binary_input_selector + fifo_distance[i];\n";
					verilog_file << "\t\tassign address_selector[i] = (temp_address_selector[i] >= NO_INPUTS) ? (temp_address_selector[i] - NO_INPUTS) : temp_address_selector[i];\n";
					verilog_file << "\t\t// If the current FIFO slot is more than NO_INPUTS away from the currently selected slot, we do not write to it\n";
					verilog_file << "\t\t// Otherwise, we can use address selector to select the appropriate write signal\n";
					verilog_file << "\t\tassign write_selector[i] = (fifo_distance[i] >= NO_INPUTS) ? {NO_INPUTS{1'b1}} : address_selector[i];\n\n";

					verilog_file << "\t\tparameterized_mux address_mux (.data ({";
					for (counter = inputs-1; counter >= 0; counter--) {
						verilog_file << "address_write_array_" << counter;
						if (counter != 0) {
							verilog_file << ", ";
						}
					}
					verilog_file << "}),\n";
					verilog_file << "\t\t\t\t.sel (address_selector[i]),\n";
					verilog_file << "\t\t\t\t.result(selected_address_write_array[i]));\n";
					verilog_file << "\t\tdefparam\taddress_mux.LPM_WIDTH = ADDR_WIDTH,\n";
					verilog_file << "\t\t\t\t\taddress_mux.LPM_SIZE = NO_INPUTS,\n";
					verilog_file << "\t\t\t\t\taddress_mux.LPM_WIDTHS = NO_INPUTS_LOG;\n\n";
					
					verilog_file << "\t\tparameterized_mux writedata_mux (.data ({";
					for (counter = inputs-1; counter >= 0; counter--) {
						verilog_file << "writedata_array_" << counter;
						if (counter != 0) {
							verilog_file << ", ";
						}
					}
					verilog_file << "}),\n";
					verilog_file << "\t\t\t\t.sel (address_selector[i]),\n";
					verilog_file << "\t\t\t\t.result(selected_writedata_array[i]));\n";
					verilog_file << "\t\tdefparam\twritedata_mux.LPM_WIDTH = DATA_WIDTH,\n";
					verilog_file << "\t\t\t\t\twritedata_mux.LPM_SIZE = NO_INPUTS,\n";
					verilog_file << "\t\t\t\t\twritedata_mux.LPM_WIDTHS = NO_INPUTS_LOG;\n\n";
					
					verilog_file << "\t\t// Need to pad the data, so that there are as many bits as there are FIFO slots (i.e. FIFO_DEPTH)\n";
					verilog_file << "\t\t// This only has to be done if the number of inputs is less than the FIFO depth\n";
					verilog_file << "\t\t// Pad it with 0's, meaning that FIFO slots above the selected ones are not written\n";
					verilog_file << "\t\t// We did not have to do this for address and data, because we do not care what address and data go to the FIFO slots\n";
					verilog_file << "\t\t// that are not being written\n";
					verilog_file << "\t\tparameterized_mux write_signal_mux (.data ({";
// __FIX_ME_ - Need to change the expression below from (4-inputs) to (FIFO_DEPTH-inputs)
					// This next line has to change if FIFO_DEPTH_LOG is changed from 2
					for (counter = 0; counter < (4-inputs); counter++) {
						verilog_file << "1'b0, ";
					}
					for (counter = inputs-1; counter >= 0; counter--) {
						verilog_file << "(write_array_" << counter << " & ~wait_write_array_" << counter << ")";
						if (counter != 0) {
							verilog_file << ", ";
						}
					}
					verilog_file << "}),\n";
					verilog_file << "\t\t\t\t.sel (write_selector[i]),\n";
					verilog_file << "\t\t\t\t.result(write_array_selected[i]));\n";
					verilog_file << "\t\tdefparam\twrite_signal_mux.LPM_WIDTH = 1,\n";
					verilog_file << "\t\t\t\t\twrite_signal_mux.LPM_SIZE = (2**FIFO_DEPTH_LOG),\n";
					verilog_file << "\t\t\t\t\twrite_signal_mux.LPM_WIDTHS = FIFO_DEPTH_LOG;\n";

					verilog_file << "\tend\n";
					verilog_file << "\tendgenerate\n";

					// Now generate TCL file
					tcl_file << "# TCL File Generated by BR2C Compiler\n";
					tcl_file << "# DO NOT MODIFY\n\n";
					tcl_file << "set_source_file \"" << "array_collector_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
					tcl_file << "set_module \"" << "array_collector_fifo_" << inputs << "_to_" << outputs << "\"\n";
					tcl_file << "set_module_description \"\"\n";
					tcl_file << "set_module_property \"className\" " << "\"array_collector_fifo_" << inputs << "_to_" << outputs << "\"\n";
					tcl_file << "set_module_property \"group\" \"Custom\"\n";
					tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
					tcl_file << "set_module_property \"synthesisFiles\" \"" << "array_collector_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";
					
					tcl_file << "# Module parameters\n";
					tcl_file << "add_parameter \"MEMORY_SIZE\" \"integer\" \"" << n_iter->out_edges.front().array_size << "\" \"\"\n";
					tcl_file << "add_parameter \"FIFO_DEPTH_LOG\" \"integer\" \"2\" \"\"\n";
					tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
					tcl_file << "add_parameter \"ADDR_WIDTH\" \"integer\" \"" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << "\" \"\"\n";
					tcl_file << "add_parameter \"NO_INPUTS\" \"integer\" \"" << inputs << "\" \"\"\n";
					tcl_file << "add_parameter \"NO_INPUTS_LOG\" \"integer\" \"" << (int)(ceil(log(1.0*inputs)/log(2.0))) << "\" \"\"\n";

					tcl_file << "# Interface clock\n";
					tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
					tcl_file << "# Ports in interface clock\n";
					tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
					tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

					tcl_file << "# Interface out_control\n";
					tcl_file << "add_interface \"out_control\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_control\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_control\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_control\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_control\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_control\n";
					tcl_file << "add_port_to_interface \"out_control\" \"write_out_control\" \"write\"\n";
					tcl_file << "add_port_to_interface \"out_control\" \"writedata_out_control\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"out_control\" \"wait_write_out_control\" \"waitrequest\"\n\n";

					tcl_file << "# Interface out_array\n";
					tcl_file << "add_interface \"out_array\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_array\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array\" \"readLatency\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_array\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_array\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_array\n";
					tcl_file << "add_port_to_interface \"out_array\" \"read_array\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_array\" \"address_read_array\" \"address\"\n";
					tcl_file << "add_port_to_interface \"out_array\" \"readdata_array\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_array\" \"wait_read_array\" \"waitrequest\"\n\n";


					for (counter = 0; counter < inputs; counter++) {
						tcl_file << "# Interface in_control_" << counter << "\n";
						tcl_file << "add_interface \"in_control_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readLatency\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readWaitStates\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"holdTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"printableDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"setupTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"writeWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"linewrapBursts\" \"false\"\n";
						tcl_file << "set_interface_property \"in_control_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
						tcl_file << "# Ports in interface in_control_" << counter << "\n";
						tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"write_in_control_" << counter << "\" \"write\"\n";
						tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"writedata_in_control_" << counter << "\" \"writedata\"\n";
						tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"wait_write_in_control_" << counter << "\" \"waitrequest\"\n\n";

						tcl_file << "# Interface in_array_" << counter << "\n";
						tcl_file << "add_interface \"in_array_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readLatency\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readWaitStates\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"holdTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"printableDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"setupTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"writeWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"linewrapBursts\" \"false\"\n";
						tcl_file << "set_interface_property \"in_array_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
						tcl_file << "# Ports in interface in_array_" << counter << "\n";
						tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"write_array_" << counter << "\" \"write\"\n";
						tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"address_write_array_" << counter << "\" \"address\"\n";
						tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"writedata_array_" << counter << "\" \"writedata\"\n";
						tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"wait_write_array_" << counter << "\" \"waitrequest\"\n\n";
					}
					tcl_file.close();
				} else {
					verilog_file_name << globals.SOPC_COMPONENT_PATH << "collector_fifo_" << inputs << "_to_" << outputs << ".v";
					tcl_file_name << globals.SOPC_COMPONENT_PATH << "collector_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
					verilog_file.open (verilog_file_name.str().c_str());
					tcl_file.open (tcl_file_name.str().c_str());
					if (verilog_file.fail()) {
						cerr << "***Unable to open file (3) " << verilog_file_name.str() << "\n";
						exit(1);
					}
					if (tcl_file.fail()) {
						cerr << "***Unable to open file (4) " << tcl_file_name.str() << "\n";
						exit(1);
					}
					// Emit Verilog code
					// Add many spaces after the module declaration, so that we can easily replace the module name
					verilog_file << "module " << "collector_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
					verilog_file << "\tinput\t\t\t\t\tclk,\n";
					verilog_file << "\tinput\t\t\t\t\treset,\n";
					verilog_file << "\tinput\t\t\t\t\tread,\n";
					for (counter = 0; counter < inputs; counter++) {
						verilog_file << "\tinput\t\t\t\t\twrite_" << counter << ",\n";
						verilog_file << "\tinput [DATA_WIDTH-1:0]\twritedata_" << counter << ",\n";
						verilog_file << "\toutput\t\t\t\t\twait_write_" << counter << ",\n";
					}
					verilog_file << "\toutput [DATA_WIDTH-1:0]\treaddata,\n";
					verilog_file << "\toutput\t\t\t\t\twait_read);\n";
					verilog_file << "parameter	FIFO_DEPTH = 4,	DATA_WIDTH = 32, NO_INPUTS = " << inputs << ";\n\n";
					verilog_file << "reg [NO_INPUTS-1:0]\t\tinput_selector;\n";
					verilog_file << "reg [DATA_WIDTH-1:0]	writedata;\n\n";

					verilog_file << "wire\t\t\t\t\twait_write;\n";
					verilog_file << "wire\t\t\t\t\twrite;\n\n";
					
					verilog_file << "assign write = ";
					for (counter = 0; counter < inputs; counter++) {
						if (counter != 0) {
							verilog_file << " | ";
						}
						verilog_file << "(write_" << counter << " & input_selector[" << counter << "])";
					}
					verilog_file << ";\n\n";

					for (counter = 0; counter < inputs; counter++) {
						verilog_file << "assign wait_write_" << counter << " = ~input_selector[" << counter << "] | wait_write;\n";
					}
					verilog_file << "\n";
					verilog_file << "always @(*)\n";
					verilog_file << "begin\n";
					verilog_file << "\tcase (input_selector) // synthesis full_case\n";
					for (counter = 0; counter < inputs; counter++) {
						verilog_file << "\t\t" << inputs << "'b";
						for (bit_counter = inputs-1; bit_counter >=0; bit_counter--) {
							if (counter == bit_counter) {
								verilog_file << "1";
							} else {
								verilog_file << "0";
							}
						}
						verilog_file << ": writedata = writedata_" << counter << ";\n";
					}
					verilog_file << "\tendcase\n";
					verilog_file << "end\n\n";
					verilog_file << "always @(posedge clk)\n";
					verilog_file << "begin\n";
					verilog_file << "\tif (reset)\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tinput_selector = {{NO_INPUTS-1{1'b0}}, 1'b1};\n";
					verilog_file << "\tend\n";
					verilog_file << "\telse\n";
					verilog_file << "\tbegin\n";
					verilog_file << "\t\tif (write & ~wait_write)\n";
					verilog_file << "\t\t\tinput_selector = {input_selector[NO_INPUTS-2:0], input_selector[NO_INPUTS-1]};\n";
					verilog_file << "\tend\n";
					verilog_file << "end\n\n";
					
					// Now generate TCL file
					tcl_file << "# TCL File Generated by BR2C Compiler\n";
					tcl_file << "# DO NOT MODIFY\n\n";
					tcl_file << "set_source_file \"" << "collector_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
					tcl_file << "set_module \""  << "collector_fifo_" << inputs << "_to_" << outputs << "\"\n";
					tcl_file << "set_module_description \"\"\n";
					tcl_file << "set_module_property \"className\" " << "\"collector_fifo_" << inputs << "_to_" << outputs << "\"\n";
					tcl_file << "set_module_property \"group\" \"Custom\"\n";
					tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
					tcl_file << "set_module_property \"synthesisFiles\" \"" << "collector_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";
					
					tcl_file << "# Module parameters\n";
					tcl_file << "add_parameter \"FIFO_DEPTH\" \"integer\" \"4\" \"\"\n";
					tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
					tcl_file << "add_parameter \"NO_INPUTS\" \"integer\" \"" << inputs << "\" \"\"\n\n";

					tcl_file << "# Interface clock\n";
					tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
					tcl_file << "# Ports in interface clock\n";
					tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
					tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

					tcl_file << "# Interface out\n";
					tcl_file << "add_interface \"out\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out\n";
					tcl_file << "add_port_to_interface \"out\" \"read\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out\" \"readdata\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out\" \"wait_read\" \"waitrequest\"\n\n";

					for (counter = 0; counter < inputs; counter++) {
						tcl_file << "# Interface in_" << counter << "\n";
						tcl_file << "add_interface \"in_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"readLatency\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitStates\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"holdTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"printableDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"setupTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"writeWaitTime\" \"0\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"linewrapBursts\" \"false\"\n";
						tcl_file << "set_interface_property \"in_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
						tcl_file << "# Ports in interface in_" << counter << "\n";
						tcl_file << "add_port_to_interface \"in_" << counter << "\" \"write_" << counter << "\" \"write\"\n";
						tcl_file << "add_port_to_interface \"in_" << counter << "\" \"writedata_" << counter << "\" \"writedata\"\n";
						tcl_file << "add_port_to_interface \"in_" << counter << "\" \"wait_write_" << counter << "\" \"waitrequest\"\n\n";
					}
					tcl_file.close();
				}
			}
		} else if (n_iter->name.find(BRCC_DISTRIBUTOR) != string::npos) {	// i.e. the node is one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
			// Generate custom distributor code
			// First find out the number of inputs and outputs
			inputs = n_iter->inEdges();
			outputs = n_iter->out_edges.size();
			if (inputs != 1) {
				cerr << "\nNumber of inputs of the distributor node " << n_iter->name << " is " << inputs;
				fatal ("Number of inputs of any distributor node has to be 1");
			}
			if (outputs < 2) {
				cerr << "\nNumber of outputs of the distributor node " << n_iter->name << " is " << outputs;
				fatal ("Number of outputs to any distributor node has to be >= 2");
			}
			// Generate a name for the file
			verilog_file_name.str("");
			tcl_file_name.str("");
			// Check whether the edge carries a stream of arrays
			// We could check any edge, because they should all be labeled. Check the first one for simplicity.
			if (n_iter->out_edges.front().stream_of_arrays) {
				// If the distributor stores arrays, its name should be different, and the generated code is different too
				verilog_file_name << globals.SOPC_COMPONENT_PATH << "array_distributor_fifo_" << inputs << "_to_" << outputs << ".v";
				tcl_file_name << globals.SOPC_COMPONENT_PATH << "array_distributor_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
				verilog_file.open (verilog_file_name.str().c_str());
				tcl_file.open (tcl_file_name.str().c_str());
				if (verilog_file.fail()) {
					cerr << "***Unable to open file (5) " << verilog_file_name.str() << "\n";
					exit(1);
				}
				if (tcl_file.fail()) {
					cerr << "***Unable to open file (6) " << tcl_file_name.str() << "\n";
					exit(1);
				}
				// Emit Verilog code
				// Add many spaces after the module declaration, so that we can easily replace the module name
				verilog_file << "module array_distributor_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
				verilog_file << "\tinput\t\t\t\t\tclk,\n";
				verilog_file << "\tinput\t\t\t\t\treset,\n";

				//START Read ports (need as many as there are outputs from the distributor)
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\n\tinput\t\t\t\t\twrite_out_control_" << counter << ",\n";
					verilog_file << "\tinput\t\t\t\t\tread_array_" << counter << ",\n";
					verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_read_array_" << counter << ",\n";
					verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_out_control_" << counter << ",\n";

					verilog_file << "\toutput\t[DATA_WIDTH-1:0]\treaddata_array_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_out_control_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_array_" << counter << ",\n";
				}
				// Write ports (only need one instance of these ports)
				verilog_file << "\tinput\t\t\t\t\twrite_in_control,\n";
				verilog_file << "\tinput\t\t\t\t\twrite_array,\n";
				verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_write_array,\n";
				verilog_file << "\tinput\t[DATA_WIDTH-1:0]\twritedata_array,\n";
				verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_in_control,\n";

				verilog_file << "\toutput\t\t\t\t\twait_write_in_control,\n";
				verilog_file << "\toutput\t\t\t\t\twait_write_array);\n";
				verilog_file << "parameter	MEMORY_SIZE="  << n_iter->out_edges.front().array_size << ",\n"; 	// The number of words in each FIFO buffer slot
				verilog_file << "\t\t\tFIFO_DEPTH_LOG=2,\n";	// LOG_2{Number of buffer slots in the FIFO (i.e. FIFO depth)}
				verilog_file << "\t\t\tDATA_WIDTH=32,\n";		// Width of words stored in the buffer
				verilog_file << "\t\t\tADDR_WIDTH=" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << ",\n"; // Width of the address bus (has to be == log_2 (MEMORY_SIZE))
				verilog_file << "\t\t\tNO_OUTPUTS = " << outputs << ",\n";		// Number of outputs of the distributor
				verilog_file << "\t\t\tNO_OUTPUTS_LOG = " << (int)(ceil(log(1.0*outputs)/log(2.0)))	<< ";\n\n";		// log2(Number of outputs of the distributor)

				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\tread_ptr;\n";				// Will contain index of the memory module currently used for reading
				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\twrite_ptr;\n\n";				// Will contain index of the memory module currently used for reading
				// Both pointers declared above this line have one extra bit, which is used to distinguish between the buffer being full or empty
				verilog_file << "wire\tbuffer_full;\n";
				verilog_file << "wire\tbuffer_empty, read_array, write_out_control;\n";
				verilog_file << "wire\t[(2**FIFO_DEPTH_LOG)-1:0]\twrite;\n";
				verilog_file << "wire\t[DATA_WIDTH*(2**FIFO_DEPTH_LOG)-1:0]\tdata_read;\n";
				verilog_file << "wire\t[NO_OUTPUTS_LOG-1:0]address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "wire\t[NO_OUTPUTS_LOG:0]temp_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n\n"; // This one has one bit more to allow detection of overflow

				verilog_file << "genvar\ti;\n";
				verilog_file << "integer\tj;\n\n";
				verilog_file << "reg\t[NO_OUTPUTS-1:0]\t\toutput_selector;\n";
				verilog_file << "reg\t[NO_OUTPUTS_LOG-1:0]\tbinary_output_selector;\n";
				verilog_file << "wire\t[ADDR_WIDTH-1:0]\tselected_address_read_array [(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\t\tfull_slots;\n";
				verilog_file << "reg\t[NO_OUTPUTS_LOG-1:0]\toutput_distance [NO_OUTPUTS-1:0];\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG-1:0]\tfifo_distance [(2**FIFO_DEPTH_LOG)-1:0];\n";

				verilog_file << "\tassign read_array = ";
				for (counter = 0; counter < outputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(read_array_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file <<	";\n";

				verilog_file << "\tassign write_out_control = ";
				for (counter = 0; counter < outputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(write_out_control_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file <<	";\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tassign wait_read_array_" << counter << " = read_array_" << counter << " & (output_distance[" << counter << "] >= full_slots);\n";
					verilog_file << "\tassign wait_write_out_control_" << counter << " = ~output_selector[" << counter << "] | wait_write_out_control;\n";
				}
				verilog_file << "\tassign wait_write_array = write_array & buffer_full;\n";

				verilog_file << "\talways @(posedge clk)\n\tbegin\n";
				verilog_file << "\t\tif (reset)\n\t\tbegin\n";
				verilog_file << "\t\t\toutput_selector = {{NO_OUTPUTS-1{1'b0}}, 1'b1};\n\t\tend\n\t\telse\n\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\t\toutput_selector = {output_selector[NO_OUTPUTS-2:0], output_selector[NO_OUTPUTS-1]};\n";
				verilog_file << "\t\tend\n\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tbinary_output_selector <= {NO_OUTPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tif (binary_output_selector == NO_OUTPUTS-1)\n";
				verilog_file << "\t\t\t\t\tbinary_output_selector <= {NO_OUTPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\tbinary_output_selector <= binary_output_selector + 1'b1;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tparameterized_mux the_mux_" << counter << " (.data (data_read),\n";
					verilog_file << "\t\t\t.sel (read_ptr[FIFO_DEPTH_LOG-1:0]+output_distance[" << counter << "]),\n";
					verilog_file << "\t\t\t.result(readdata_array_" << counter << "));\n";
					verilog_file << "\tdefparam\tthe_mux_" << counter << ".LPM_WIDTH = DATA_WIDTH,\n";
					verilog_file << "\t\t\t\tthe_mux_" << counter << ".LPM_SIZE = (2**FIFO_DEPTH_LOG),\n";
					verilog_file << "\t\t\t\tthe_mux_" << counter << ".LPM_WIDTHS = FIFO_DEPTH_LOG;\n\n";
				}

				verilog_file << "\talways @(posedge clk or posedge reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\tif (reset == 1'b1)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfull_slots <= {FIFO_DEPTH_LOG{1'b0}};\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control & write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Both pointers changing at the same time, the number of full slots does not change\n";
				verilog_file << "\t\t\tfull_slots <= full_slots;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// New element being written; number of full slots should increase\n";
				verilog_file << "\t\t\tfull_slots <= full_slots + 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Element has been read; decrease the number of full slots\n";
				verilog_file << "\t\t\tfull_slots <= full_slots - 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<NO_OUTPUTS; j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\toutput_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<NO_OUTPUTS; j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\tif (output_distance[j] == {NO_OUTPUTS{1'b0}})\n";
				verilog_file << "\t\t\t\t\t\toutput_distance[j] <= NO_OUTPUTS-1;\n";
				verilog_file << "\t\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\t\toutput_distance[j] <= output_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfifo_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\tfifo_distance[j] <= fifo_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n";

				verilog_file << "\tgenerate\n";
				verilog_file << "\tfor(i = 0; i<(2**FIFO_DEPTH_LOG); i = i+1)\n";
				verilog_file << "\tbegin : memory_block\n";
				verilog_file << "\t\taltsyncram	memory (\n";
				verilog_file << "\t\t\t.wren_a (write[i]),\n";
				verilog_file << "\t\t\t.clock0 (clk),\n";
				verilog_file << "\t\t\t.address_a (address_write_array),\n";
				verilog_file << "\t\t\t.address_b (selected_address_read_array[i]),\n";
				verilog_file << "\t\t\t.data_a (writedata_array),\n";
				verilog_file << "\t\t\t.q_b (data_read[DATA_WIDTH*(i+1)-1:DATA_WIDTH*i]),\n";
				verilog_file << "\t\t\t.aclr0 (1'b0),\n";
				verilog_file << "\t\t\t.aclr1 (1'b0),\n";
				verilog_file << "\t\t\t.addressstall_a (1'b0),\n";
				verilog_file << "\t\t\t.addressstall_b (1'b0),\n";
				verilog_file << "\t\t\t.byteena_a (1'b1),\n";
				verilog_file << "\t\t\t.byteena_b (1'b1),\n";
				verilog_file << "\t\t\t.clock1 (1'b1),\n";
				verilog_file << "\t\t\t.clocken0 (1'b1),\n";
				verilog_file << "\t\t\t.clocken1 (1'b1),\n";
				verilog_file << "\t\t\t.clocken2 (1'b1),\n";
				verilog_file << "\t\t\t.clocken3 (1'b1),\n";
				verilog_file << "\t\t\t.data_b ({32{1'b1}}),\n";
				verilog_file << "\t\t\t.eccstatus (),\n";
				verilog_file << "\t\t\t.q_a (),\n";
				verilog_file << "\t\t\t.rden_a (1'b1),\n";
				verilog_file << "\t\t\t.rden_b (1'b1),\n";
				verilog_file << "\t\t\t.wren_b (1'b0));\n";
				verilog_file << "\t\tdefparam\n";
				verilog_file << "\t\t\tmemory.address_reg_b = \"CLOCK0\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_input_a = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_input_b = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_output_a = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_output_b = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.lpm_type = \"altsyncram\",\n";
				verilog_file << "\t\t\tmemory.operation_mode = \"DUAL_PORT\",\n";
				verilog_file << "\t\t\tmemory.outdata_aclr_b = \"NONE\",\n";
				verilog_file << "\t\t\tmemory.outdata_reg_b = \"UNREGISTERED\",\n";
				verilog_file << "\t\t\tmemory.power_up_uninitialized = \"FALSE\",\n";
				verilog_file << "\t\t\tmemory.read_during_write_mode_mixed_ports = \"DONT_CARE\",\n";
				verilog_file << "\t\t\tmemory.widthad_a = ADDR_WIDTH,\n";
				verilog_file << "\t\t\tmemory.widthad_b = ADDR_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_a = DATA_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_b = DATA_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_byteena_a = 1;\n";
				
				verilog_file << "\t\tassign write[i] = write_array & (write_ptr[FIFO_DEPTH_LOG-1:0] == i) & ~wait_write_array;\n";
				verilog_file << "\t\tassign temp_address_selector[i] = binary_output_selector + fifo_distance[i];\n";
				verilog_file << "\t\tassign address_selector[i] = (temp_address_selector[i] >= NO_OUTPUTS) ? (temp_address_selector[i] - NO_OUTPUTS) : temp_address_selector[i];\n\n";

				verilog_file << "\t\tparameterized_mux address_mux (.data ({";
				for (counter = outputs-1; counter >= 0; counter--) {
					verilog_file << "address_read_array_" << counter;
					if (counter != 0) {
						verilog_file << ", ";
					}
				}
				verilog_file << "}),\n";
				verilog_file << "\t\t\t\t.sel (address_selector[i]),\n";
				verilog_file << "\t\t\t\t.result(selected_address_read_array[i]));\n";
				verilog_file << "\t\tdefparam\taddress_mux.LPM_WIDTH = ADDR_WIDTH,\n";
				verilog_file << "\t\t\t\t\taddress_mux.LPM_SIZE = NO_OUTPUTS,\n";
				verilog_file << "\t\t\t\t\taddress_mux.LPM_WIDTHS = NO_OUTPUTS_LOG;\n";
				verilog_file << "\tend\n";
				verilog_file << "\tendgenerate\n";


				// Now generate TCL file
				tcl_file << "# TCL File Generated by BR2C Compiler\n";
				tcl_file << "# DO NOT MODIFY\n\n";

				tcl_file << "set_source_file \"array_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
				tcl_file << "set_module \"array_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_description \"\"\n";
				tcl_file << "set_module_property \"className\" \"array_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_property \"group\" \"Custom\"\n";
				tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
				tcl_file << "set_module_property \"synthesisFiles\" \"array_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";

				tcl_file << "# Module parameters\n";
				tcl_file << "add_parameter \"MEMORY_SIZE\" \"integer\" \"" << n_iter->out_edges.front().array_size << "\" \"\"\n";
				tcl_file << "add_parameter \"FIFO_DEPTH_LOG\" \"integer\" \"2\" \"\"\n";
				tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
				tcl_file << "add_parameter \"ADDR_WIDTH\" \"integer\" \"" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS\" \"integer\" \"" << outputs << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS_LOG\" \"integer\" \"" << (int)(ceil(log(1.0*outputs)/log(2.0))) << "\" \"\"\n\n";

				tcl_file << "# Interface clock\n";
				tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
				tcl_file << "# Ports in interface clock\n";
				tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
				tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

				for (counter = 0; counter < outputs; counter++) {
					tcl_file << "# Interface out_control_" << counter << "\n";
					tcl_file << "add_interface \"out_control_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_control_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"write_out_control_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"writedata_out_control_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"wait_write_out_control_" << counter << "\" \"waitrequest\"\n\n";

					tcl_file << "# Interface out_array_" << counter << "\n";
					tcl_file << "add_interface \"out_array_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readLatency\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_array_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"read_array_" << counter << "\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"address_read_array_" << counter << "\" \"address\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"readdata_array_" << counter << "\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"wait_read_array_" << counter << "\" \"waitrequest\"\n\n";
				}
				tcl_file << "# Interface in_control\n";
				tcl_file << "add_interface \"in_control\" \"avalon\" \"slave\" \"clock\"\n";
				tcl_file << "set_interface_property \"in_control\" \"isNonVolatileStorage\" \"false\"\n";
				tcl_file << "set_interface_property \"in_control\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
				tcl_file << "set_interface_property \"in_control\" \"readLatency\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"readWaitStates\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"holdTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"printableDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in_control\" \"readWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"setupTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"addressAlignment\" \"DYNAMIC\"\n";
				tcl_file << "set_interface_property \"in_control\" \"writeWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_control\" \"timingUnits\" \"Cycles\"\n";
				tcl_file << "set_interface_property \"in_control\" \"minimumUninterruptedRunLength\" \"1\"\n";
				tcl_file << "set_interface_property \"in_control\" \"isMemoryDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in_control\" \"linewrapBursts\" \"false\"\n";
				tcl_file << "set_interface_property \"in_control\" \"maximumPendingReadTransactions\" \"0\"\n";
				tcl_file << "# Ports in interface in_control\n";
				tcl_file << "add_port_to_interface \"in_control\" \"write_in_control\" \"write\"\n";
				tcl_file << "add_port_to_interface \"in_control\" \"writedata_in_control\" \"writedata\"\n";
				tcl_file << "add_port_to_interface \"in_control\" \"wait_write_in_control\" \"waitrequest\"\n\n";

				tcl_file << "# Interface in_array\n";
				tcl_file << "add_interface \"in_array\" \"avalon\" \"slave\" \"clock\"\n";
				tcl_file << "set_interface_property \"in_array\" \"isNonVolatileStorage\" \"false\"\n";
				tcl_file << "set_interface_property \"in_array\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
				tcl_file << "set_interface_property \"in_array\" \"readLatency\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"readWaitStates\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"holdTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"printableDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in_array\" \"readWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"setupTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"addressAlignment\" \"DYNAMIC\"\n";
				tcl_file << "set_interface_property \"in_array\" \"writeWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in_array\" \"timingUnits\" \"Cycles\"\n";
				tcl_file << "set_interface_property \"in_array\" \"minimumUninterruptedRunLength\" \"1\"\n";
				tcl_file << "set_interface_property \"in_array\" \"isMemoryDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in_array\" \"linewrapBursts\" \"false\"\n";
				tcl_file << "set_interface_property \"in_array\" \"maximumPendingReadTransactions\" \"0\"\n";
				tcl_file << "# Ports in interface in_array\n";
				tcl_file << "add_port_to_interface \"in_array\" \"write_array\" \"write\"\n";
				tcl_file << "add_port_to_interface \"in_array\" \"address_write_array\" \"address\"\n";
				tcl_file << "add_port_to_interface \"in_array\" \"writedata_array\" \"writedata\"\n";
				tcl_file << "add_port_to_interface \"in_array\" \"wait_write_array\" \"waitrequest\"\n\n";
				
				tcl_file.close();
			} else {
				// It's an ordinary distributor FIFO (i.e. it does NOT store arrays)
				verilog_file_name << globals.SOPC_COMPONENT_PATH << "distributor_fifo_" << inputs << "_to_" << outputs << ".v";
				tcl_file_name << globals.SOPC_COMPONENT_PATH << "distributor_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
				verilog_file.open (verilog_file_name.str().c_str());
				tcl_file.open (tcl_file_name.str().c_str());
				if (verilog_file.fail()) {
					cerr << "***Unable to open file (5) " << verilog_file_name.str() << "\n";
					exit(1);
				}
				if (tcl_file.fail()) {
					cerr << "***Unable to open file (6) " << tcl_file_name.str() << "\n";
					exit(1);
				}
				// Emit Verilog code
				// Add many spaces after the module declaration, so that we can easily replace the module name
				verilog_file << "module distributor_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
				verilog_file << "\tinput\t\t\t\t\tclk,\n";
				verilog_file << "\tinput\t\t\t\t\treset,\n";
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tinput\t\t\t\t\tread_" << counter << ",\n";
					verilog_file << "\toutput [DATA_WIDTH-1:0] readdata_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_" << counter << ",\n";
				}
				verilog_file << "\tinput\t\t\t\t\twrite,\n";
				verilog_file << "\tinput [DATA_WIDTH-1:0] writedata,\n";
				verilog_file << "\toutput\t\t\t\t\twait_write);\n";
				verilog_file << "parameter FIFO_DEPTH = 4, DATA_WIDTH = 32, NO_OUTPUTS = " << outputs << ";\n\n";

				verilog_file << "reg [NO_OUTPUTS-1:0] output_selector;\n\n";

				verilog_file << "wire\t\t\t\t\tread;\n";
				verilog_file << "wire\t\t\t\t\twait_read;\n";
				verilog_file << "wire [DATA_WIDTH-1:0] readdata;\n\n";

				verilog_file << "assign read = ";
				for (counter = 0; counter < outputs; counter++) {
					if (0 != counter) {
						verilog_file << " | ";
					}
					verilog_file << "(read_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "assign wait_read_" << counter << " = ~output_selector[" << counter << "] | wait_read;\n";
					verilog_file << "assign readdata_" << counter << " = readdata;\n";
				}
				verilog_file << "\n";

				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "\tif (reset)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\toutput_selector = {{NO_OUTPUTS-1{1'b0}}, 1'b1};\n";
				verilog_file << "\tend\n";
				verilog_file << "\telse\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (read & ~wait_read)\n";
				verilog_file << "\t\t\toutput_selector = {output_selector[NO_OUTPUTS-2:0], output_selector[NO_OUTPUTS-1]};\n";
				verilog_file << "\tend\n";
				verilog_file << "end\n\n";

				// Now generate TCL file
				tcl_file << "# TCL File Generated by BR2C Compiler\n";
				tcl_file << "# DO NOT MODIFY\n\n";

				tcl_file << "set_source_file \"distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
				tcl_file << "set_module \"distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_description \"\"\n";
				tcl_file << "set_module_property \"className\" \"distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_property \"group\" \"Custom\"\n";
				tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
				tcl_file << "set_module_property \"synthesisFiles\" \"distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";

				tcl_file << "# Module parameters\n";
				tcl_file << "add_parameter \"FIFO_DEPTH\" \"integer\" \"4\" \"\"\n";
				tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS\" \"integer\" \"" << outputs << "\" \"\"\n\n";

				tcl_file << "# Interface clock\n";
				tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
				tcl_file << "# Ports in interface clock\n";
				tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
				tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

				tcl_file << "# Interface in\n";
				tcl_file << "add_interface \"in\" \"avalon\" \"slave\" \"clock\"\n";
				tcl_file << "set_interface_property \"in\" \"isNonVolatileStorage\" \"false\"\n";
				tcl_file << "set_interface_property \"in\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
				tcl_file << "set_interface_property \"in\" \"readLatency\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"readWaitStates\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"holdTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"printableDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in\" \"readWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"setupTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"addressAlignment\" \"DYNAMIC\"\n";
				tcl_file << "set_interface_property \"in\" \"writeWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"in\" \"timingUnits\" \"Cycles\"\n";
				tcl_file << "set_interface_property \"in\" \"minimumUninterruptedRunLength\" \"1\"\n";
				tcl_file << "set_interface_property \"in\" \"isMemoryDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"in\" \"linewrapBursts\" \"false\"\n";
				tcl_file << "set_interface_property \"in\" \"maximumPendingReadTransactions\" \"0\"\n";
				tcl_file << "# Ports in interface in\n";
				tcl_file << "add_port_to_interface \"in\" \"write\" \"write\"\n";
				tcl_file << "add_port_to_interface \"in\" \"writedata\" \"writedata\"\n";
				tcl_file << "add_port_to_interface \"in\" \"wait_write\" \"waitrequest\"\n\n";

				for (counter = 0; counter < outputs; counter++) {
					tcl_file << "# Interface out_" << counter << "\n";
					tcl_file << "add_interface \"out_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"read_" << counter << "\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"readdata_" << counter << "\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"wait_read_" << counter << "\" \"waitrequest\"\n\n";
				}
				tcl_file.close();
			}
		} else if (n_iter->name.find(BRCC_COLLECTOR_DISTRIBUTOR) != string::npos) {
			// Generate custom collector/distributor code
			// First find out the number of inputs and outputs
			inputs = n_iter->inEdges();
			outputs = n_iter->out_edges.size();
			if (inputs < 2) {
				cerr << "\nNumber of inputs of the collector/distributor node " << n_iter->name << " is " << inputs;
				fatal ("Number of inputs of any collector/distributor node has to be >= 2");
			}
			if (outputs < 2) {
				cerr << "\nNumber of outputs of the collector/distributor node " << n_iter->name << " is " << outputs;
				fatal ("Number of outputs to any collector/distributor node has to be >= 2");
			}
			// Generate a name for the file
			verilog_file_name.str("");
			tcl_file_name.str("");
			if (n_iter->out_edges.front().stream_of_arrays) {
				verilog_file_name << globals.SOPC_COMPONENT_PATH << "array_collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v";
				tcl_file_name << globals.SOPC_COMPONENT_PATH << "array_collector_distributor_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
				verilog_file.open (verilog_file_name.str().c_str());
				tcl_file.open (tcl_file_name.str().c_str());
				if (verilog_file.fail()) {
					cerr << "***Unable to open file (7) " << verilog_file_name.str() << "\n";
					exit(1);
				}
				if (tcl_file.fail()) {
					cerr << "***Unable to open file (8) " << tcl_file_name.str() << "\n";
					exit(1);
				}
				// Emit Verilog code
				// Add many spaces after the module declaration, so that we can easily replace the module name
				verilog_file << "module array_collector_distributor_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
				verilog_file << "\tinput\t\t\t\t\tclk,\n";
				//START Write ports (need as many as there are inputs to the collector)
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\n\tinput\t\t\t\t\twrite_in_control_" << counter << ",\n";
					verilog_file << "\tinput\t\t\t\t\twrite_array_" << counter << ",\n";
					verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_write_array_" << counter << ",\n";
					verilog_file << "\tinput\t[DATA_WIDTH-1:0]\twritedata_array_" << counter << ",\n";
					verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_in_control_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_in_control_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_array_" << counter << ",\n";
				}
				//START Read ports (need as many as there are outputs from the distributor)
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\n\tinput\t\t\t\t\twrite_out_control_" << counter << ",\n";
					verilog_file << "\tinput\t\t\t\t\tread_array_" << counter << ",\n";
					verilog_file << "\tinput\t[ADDR_WIDTH-1:0]\taddress_read_array_" << counter << ",\n";
					verilog_file << "\tinput\t[7:0]\t\t\t\twritedata_out_control_" << counter << ",\n";

					verilog_file << "\toutput\t[DATA_WIDTH-1:0]\treaddata_array_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_out_control_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_array_" << counter << ",\n";
				}
				verilog_file << "\tinput\t\t\t\t\treset\n";
				verilog_file << ");\n";
				verilog_file << "parameter	MEMORY_SIZE="  << n_iter->out_edges.front().array_size << ",\n"; 	// The number of words in each FIFO buffer slot
				verilog_file << "\t\t\tFIFO_DEPTH_LOG=2,\n";	// LOG_2{Number of buffer slots in the FIFO (i.e. FIFO depth)}
				verilog_file << "\t\t\tDATA_WIDTH=32,\n";		// Width of words stored in the buffer
				verilog_file << "\t\t\tADDR_WIDTH=" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << ",\n"; // Width of the address bus (has to be == log_2 (MEMORY_SIZE))
				verilog_file << "\t\t\tNO_INPUTS = " << inputs << ",\n";
				verilog_file << "\t\t\tNO_INPUTS_LOG = " << (int)(ceil(log(1.0*inputs)/log(2.0))) << ",\n";		// Number of inputs to the collector
				verilog_file << "\t\t\tNO_OUTPUTS = " << outputs << ",\n";		// Number of outputs of the distributor
				verilog_file << "\t\t\tNO_OUTPUTS_LOG = " << (int)(ceil(log(1.0*outputs)/log(2.0)))	<< ";\n\n";		// log2(Number of outputs of the distributor)

				verilog_file << "reg\t[NO_INPUTS-1:0]\t\tinput_selector;\n";
				verilog_file << "reg\t[NO_INPUTS_LOG-1:0]\tbinary_input_selector;\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\tread_ptr;\n";				// Will contain index of the memory module currently used for reading
				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\twrite_ptr;\n\n";				// Will contain index of the memory module currently used for reading
				// Both pointers declared above this line have one extra bit, which is used to distinguish between the buffer being full or empty
				verilog_file << "wire\tbuffer_full;\n";
				verilog_file << "wire\tbuffer_empty, write_out_control;\n";
				verilog_file << "wire\t[(2**FIFO_DEPTH_LOG)-1:0]\twrite_array_selected;\n";
				
				verilog_file << "wire\twrite_in_control, wait_write_in_control;\n";
				verilog_file << "wire\t[DATA_WIDTH*(2**FIFO_DEPTH_LOG)-1:0]\tdata_read;\n";
				verilog_file << "wire\t[NO_OUTPUTS_LOG-1:0]read_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "wire\t[NO_OUTPUTS_LOG:0]temp_read_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n\n"; // This one has one bit more to allow detection of overflow
				verilog_file << "wire\t[NO_INPUTS_LOG-1:0]write_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "wire\t[NO_INPUTS_LOG:0]temp_write_address_selector[(2**FIFO_DEPTH_LOG)-1:0];\n"; // This one has one bit more to allow detection of overflow
				verilog_file << "wire\t[FIFO_DEPTH_LOG-1:0]write_selector[(2**FIFO_DEPTH_LOG)-1:0];\n\n";

				verilog_file << "genvar\ti;\n";
				verilog_file << "integer\tj;\n\n";

				verilog_file << "reg\t[NO_OUTPUTS-1:0]\t\toutput_selector;\n";
				verilog_file << "reg\t[NO_OUTPUTS_LOG-1:0]\tbinary_output_selector;\n";
				verilog_file << "wire\t[ADDR_WIDTH-1:0]\tselected_address_read_array [(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "wire\t[ADDR_WIDTH-1:0]\tselected_address_write_array [(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "wire\t[DATA_WIDTH-1:0]\tselected_writedata_array [(2**FIFO_DEPTH_LOG)-1:0];\n\n";

				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\tempty_slots;\n";
				verilog_file << "reg\t[NO_INPUTS_LOG-1:0]\tinput_distance [NO_INPUTS-1:0];\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG:0]\t\tfull_slots;\n";
				verilog_file << "reg\t[NO_OUTPUTS_LOG-1:0]\toutput_distance [NO_OUTPUTS-1:0];\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG-1:0]\tread_fifo_distance [(2**FIFO_DEPTH_LOG)-1:0];\n";
				verilog_file << "reg\t[FIFO_DEPTH_LOG-1:0]\twrite_fifo_distance [(2**FIFO_DEPTH_LOG)-1:0];\n\n";

				verilog_file << "\tassign write_in_control = ";
				for (counter = 0; counter < inputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(write_in_control_" << counter << " & input_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\tassign wait_write_array_" << counter << " = write_array_" << counter << " & (input_distance[" << counter << "] >= empty_slots);\n";
					verilog_file << "\tassign wait_write_in_control_" << counter << " = ~input_selector[" << counter << "] | wait_write_in_control;\n";
				}

				verilog_file << "\tassign write_out_control = ";
				for (counter = 0; counter < outputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(write_out_control_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file <<	";\n";
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tassign wait_read_array_" << counter << " = read_array_" << counter << " & (output_distance[" << counter << "] >= full_slots);\n";
					verilog_file << "\tassign wait_write_out_control_" << counter << " = ~output_selector[" << counter << "] | wait_write_out_control;\n";
				}

				verilog_file << "\talways @(posedge clk)\n\tbegin\n";
				verilog_file << "\t\tif (reset)\n\t\tbegin\n";
				verilog_file << "\t\t\tinput_selector = {{NO_INPUTS-1{1'b0}}, 1'b1};\n\t\tend\n\t\telse\n\t\tbegin\n";
				verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\t\t\tinput_selector = {input_selector[NO_INPUTS-2:0], input_selector[NO_INPUTS-1]};\n";
				verilog_file << "\t\tend\n\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tbinary_input_selector <= {NO_INPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tif (binary_input_selector == NO_INPUTS-1)\n";
				verilog_file << "\t\t\t\t\tbinary_input_selector <= {NO_INPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\tbinary_input_selector <= binary_input_selector + 1'b1;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n";

				verilog_file << "\talways @(posedge clk)\n\tbegin\n";
				verilog_file << "\t\tif (reset)\n\t\tbegin\n";
				verilog_file << "\t\t\toutput_selector = {{NO_OUTPUTS-1{1'b0}}, 1'b1};\n\t\tend\n\t\telse\n\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\t\toutput_selector = {output_selector[NO_OUTPUTS-2:0], output_selector[NO_OUTPUTS-1]};\n";
				verilog_file << "\t\tend\n\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tbinary_output_selector <= {NO_OUTPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tif (binary_output_selector == NO_OUTPUTS-1)\n";
				verilog_file << "\t\t\t\t\tbinary_output_selector <= {NO_OUTPUTS_LOG{1'b0}};\n";
				verilog_file << "\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\tbinary_output_selector <= binary_output_selector + 1'b1;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tparameterized_mux the_mux_" << counter << " (.data (data_read),\n";
					verilog_file << "\t\t\t.sel (read_ptr[FIFO_DEPTH_LOG-1:0]+output_distance[" << counter << "]),\n";
					verilog_file << "\t\t\t.result(readdata_array_" << counter << "));\n";
					verilog_file << "\tdefparam\tthe_mux_" << counter << ".LPM_WIDTH = DATA_WIDTH,\n";
					verilog_file << "\t\t\t\tthe_mux_" << counter << ".LPM_SIZE = (2**FIFO_DEPTH_LOG),\n";
					verilog_file << "\t\t\t\tthe_mux_" << counter << ".LPM_WIDTHS = FIFO_DEPTH_LOG;\n\n";
				}

				verilog_file << "\talways @(posedge clk or posedge reset)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset == 1'b1)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tempty_slots <= (2**FIFO_DEPTH_LOG);\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control & write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Both pointers changing at the same time, the number of full slots does not change\n";
				verilog_file << "\t\t\tempty_slots <= empty_slots;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// New element being written; number of full slots should increase\n";
				verilog_file << "\t\t\tempty_slots <= empty_slots - 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Element has been read; decrease the number of full slots\n";
				verilog_file << "\t\t\tempty_slots <= empty_slots + 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk or posedge reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\tif (reset == 1'b1)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfull_slots <= {FIFO_DEPTH_LOG{1'b0}};\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control & write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Both pointers changing at the same time, the number of full slots does not change\n";
				verilog_file << "\t\t\tfull_slots <= full_slots;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// New element being written; number of full slots should increase\n";
				verilog_file << "\t\t\tfull_slots <= full_slots + 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse if (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\t// Element has been read; decrease the number of full slots\n";
				verilog_file << "\t\t\tfull_slots <= full_slots - 1'b1;\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<NO_INPUTS; j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tinput_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<NO_INPUTS; j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\tif (input_distance[j] == {NO_INPUTS{1'b0}})\n";
				verilog_file << "\t\t\t\t\t\tinput_distance[j] <= NO_INPUTS-1;\n";
				verilog_file << "\t\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\t\tinput_distance[j] <= input_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<NO_OUTPUTS; j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\toutput_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<NO_OUTPUTS; j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\tif (output_distance[j] == {NO_OUTPUTS{1'b0}})\n";
				verilog_file << "\t\t\t\t\t\toutput_distance[j] <= NO_OUTPUTS-1;\n";
				verilog_file << "\t\t\t\t\telse\n";
				verilog_file << "\t\t\t\t\t\toutput_distance[j] <= output_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tread_fifo_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_out_control & ~wait_write_out_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\tread_fifo_distance[j] <= read_fifo_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n";

				verilog_file << "\talways @(posedge clk)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (reset)\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\twrite_fifo_distance[j] <= j;\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\t\telse\n";
				verilog_file << "\t\tbegin\n";
				verilog_file << "\t\t\tif (write_in_control & ~wait_write_in_control)\n";
				verilog_file << "\t\t\tbegin\n";
				verilog_file << "\t\t\t\tfor (j=0; j<(2**FIFO_DEPTH_LOG); j=j+1)\n";
				verilog_file << "\t\t\t\tbegin\n";
				verilog_file << "\t\t\t\t\twrite_fifo_distance[j] <= write_fifo_distance[j]-1'b1;\n";
				verilog_file << "\t\t\t\tend\n";
				verilog_file << "\t\t\tend\n";
				verilog_file << "\t\tend\n";
				verilog_file << "\tend\n\n";

				verilog_file << "\tgenerate\n";
				verilog_file << "\tfor(i = 0; i<(2**FIFO_DEPTH_LOG); i = i+1)\n";
				verilog_file << "\tbegin : memory_block\n";
				verilog_file << "\t\taltsyncram	memory (\n";
				verilog_file << "\t\t\t.wren_a (write_array_selected[i]),\n";
				verilog_file << "\t\t\t.clock0 (clk),\n";
				verilog_file << "\t\t\t.address_a (selected_address_write_array[i]),\n";
				verilog_file << "\t\t\t.address_b (selected_address_read_array[i]),\n";
				verilog_file << "\t\t\t.data_a (selected_writedata_array[i]),\n";
				verilog_file << "\t\t\t.q_b (data_read[DATA_WIDTH*(i+1)-1:DATA_WIDTH*i]),\n";
				verilog_file << "\t\t\t.aclr0 (1'b0),\n";
				verilog_file << "\t\t\t.aclr1 (1'b0),\n";
				verilog_file << "\t\t\t.addressstall_a (1'b0),\n";
				verilog_file << "\t\t\t.addressstall_b (1'b0),\n";
				verilog_file << "\t\t\t.byteena_a (1'b1),\n";
				verilog_file << "\t\t\t.byteena_b (1'b1),\n";
				verilog_file << "\t\t\t.clock1 (1'b1),\n";
				verilog_file << "\t\t\t.clocken0 (1'b1),\n";
				verilog_file << "\t\t\t.clocken1 (1'b1),\n";
				verilog_file << "\t\t\t.clocken2 (1'b1),\n";
				verilog_file << "\t\t\t.clocken3 (1'b1),\n";
				verilog_file << "\t\t\t.data_b ({32{1'b1}}),\n";
				verilog_file << "\t\t\t.eccstatus (),\n";
				verilog_file << "\t\t\t.q_a (),\n";
				verilog_file << "\t\t\t.rden_a (1'b1),\n";
				verilog_file << "\t\t\t.rden_b (1'b1),\n";
				verilog_file << "\t\t\t.wren_b (1'b0));\n";
				verilog_file << "\t\tdefparam\n";
				verilog_file << "\t\t\tmemory.address_reg_b = \"CLOCK0\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_input_a = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_input_b = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_output_a = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.clock_enable_output_b = \"BYPASS\",\n";
				verilog_file << "\t\t\tmemory.lpm_type = \"altsyncram\",\n";
				verilog_file << "\t\t\tmemory.operation_mode = \"DUAL_PORT\",\n";
				verilog_file << "\t\t\tmemory.outdata_aclr_b = \"NONE\",\n";
				verilog_file << "\t\t\tmemory.outdata_reg_b = \"UNREGISTERED\",\n";
				verilog_file << "\t\t\tmemory.power_up_uninitialized = \"FALSE\",\n";
				verilog_file << "\t\t\tmemory.read_during_write_mode_mixed_ports = \"DONT_CARE\",\n";
				verilog_file << "\t\t\tmemory.widthad_a = ADDR_WIDTH,\n";
				verilog_file << "\t\t\tmemory.widthad_b = ADDR_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_a = DATA_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_b = DATA_WIDTH,\n";
				verilog_file << "\t\t\tmemory.width_byteena_a = 1;\n";

				verilog_file << "\t\tassign temp_read_address_selector[i] = binary_output_selector + read_fifo_distance[i];\n";
				verilog_file << "\t\tassign read_address_selector[i] = (temp_read_address_selector[i] >= NO_OUTPUTS) ? (temp_read_address_selector[i] - NO_OUTPUTS) : temp_read_address_selector[i];\n\n";

				verilog_file << "\t\tassign temp_write_address_selector[i] = binary_input_selector + write_fifo_distance[i];\n";
				verilog_file << "\t\tassign write_address_selector[i] = (temp_write_address_selector[i] >= NO_INPUTS) ? (temp_write_address_selector[i] - NO_INPUTS) : temp_write_address_selector[i];\n";
				verilog_file << "\t\t// If the current FIFO slot is more than NO_INPUTS away from the currently selected slot, we do not write to it\n";
				verilog_file << "\t\t// Otherwise, we can use address selector to select the appropriate write signal\n";
				verilog_file << "\t\tassign write_selector[i] = (write_fifo_distance[i] >= NO_INPUTS) ? {NO_INPUTS{1'b1}} : write_address_selector[i];\n\n";

				verilog_file << "\t\tparameterized_mux write_address_mux (.data ({";
				for (counter = inputs-1; counter >= 0; counter--) {
					verilog_file << "address_write_array_" << counter;
					if (counter != 0) {
						verilog_file << ", ";
					}
				}
				verilog_file << "}),\n";
				verilog_file << "\t\t\t\t.sel (write_address_selector[i]),\n";
				verilog_file << "\t\t\t\t.result(selected_address_write_array[i]));\n";
				verilog_file << "\t\tdefparam\twrite_address_mux.LPM_WIDTH = ADDR_WIDTH,\n";
				verilog_file << "\t\t\t\t\twrite_address_mux.LPM_SIZE = NO_INPUTS,\n";
				verilog_file << "\t\t\t\t\twrite_address_mux.LPM_WIDTHS = NO_INPUTS_LOG;\n\n";
				
				verilog_file << "\t\tparameterized_mux writedata_mux (.data ({";
				for (counter = inputs-1; counter >= 0; counter--) {
					verilog_file << "writedata_array_" << counter;
					if (counter != 0) {
						verilog_file << ", ";
					}
				}
				verilog_file << "}),\n";
				verilog_file << "\t\t\t\t.sel (write_address_selector[i]),\n";
				verilog_file << "\t\t\t\t.result(selected_writedata_array[i]));\n";
				verilog_file << "\t\tdefparam\twritedata_mux.LPM_WIDTH = DATA_WIDTH,\n";
				verilog_file << "\t\t\t\t\twritedata_mux.LPM_SIZE = NO_INPUTS,\n";
				verilog_file << "\t\t\t\t\twritedata_mux.LPM_WIDTHS = NO_INPUTS_LOG;\n\n";
				
				verilog_file << "\t\t// Need to pad the data, so that there are as many bits as there are FIFO slots (i.e. FIFO_DEPTH)\n";
				verilog_file << "\t\t// This only has to be done if the number of inputs is less than the FIFO depth\n";
				verilog_file << "\t\t// Pad it with 0's, meaning that FIFO slots above the selected ones are not written\n";
				verilog_file << "\t\t// We did not have to do this for address and data, because we do not care what address and data go to the FIFO slots\n";
				verilog_file << "\t\t// that are not being written\n";
				verilog_file << "\t\tparameterized_mux write_signal_mux (.data ({";
// __FIX_ME_ - Need to change the expression below from (4-inputs) to (FIFO_DEPTH-inputs)
				// This next line has to change if FIFO_DEPTH_LOG is changed from 2
				for (counter = 0; counter < (4-inputs); counter++) {
					verilog_file << "1'b0, ";
				}
				for (counter = inputs-1; counter >= 0; counter--) {
					verilog_file << "(write_array_" << counter << " & ~wait_write_array_" << counter << ")";
					if (counter != 0) {
						verilog_file << ", ";
					}
				}
				verilog_file << "}),\n";
				verilog_file << "\t\t\t\t.sel (write_selector[i]),\n";
				verilog_file << "\t\t\t\t.result(write_array_selected[i]));\n";
				verilog_file << "\t\tdefparam\twrite_signal_mux.LPM_WIDTH = 1,\n";
				verilog_file << "\t\t\t\t\twrite_signal_mux.LPM_SIZE = (2**FIFO_DEPTH_LOG),\n";
				verilog_file << "\t\t\t\t\twrite_signal_mux.LPM_WIDTHS = FIFO_DEPTH_LOG;\n";

				verilog_file << "\t\tparameterized_mux read_address_mux (.data ({";
				for (counter = outputs-1; counter >= 0; counter--) {
					verilog_file << "address_read_array_" << counter;
					if (counter != 0) {
						verilog_file << ", ";
					}
				}
				verilog_file << "}),\n";
				verilog_file << "\t\t\t\t.sel (read_address_selector[i]),\n";
				verilog_file << "\t\t\t\t.result(selected_address_read_array[i]));\n";
				verilog_file << "\t\tdefparam\tread_address_mux.LPM_WIDTH = ADDR_WIDTH,\n";
				verilog_file << "\t\t\t\t\tread_address_mux.LPM_SIZE = NO_OUTPUTS,\n";
				verilog_file << "\t\t\t\t\tread_address_mux.LPM_WIDTHS = NO_OUTPUTS_LOG;\n";

				verilog_file << "\tend\n";
				verilog_file << "\tendgenerate\n";

				// Now generate TCL file
				tcl_file << "# TCL File Generated by BR2C Compiler\n";
				tcl_file << "# DO NOT MODIFY\n\n";
				tcl_file << "set_source_file \"" << "array_collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
				tcl_file << "set_module \"" << "array_collector_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_description \"\"\n";
				tcl_file << "set_module_property \"className\" " << "\"array_collector_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_property \"group\" \"Custom\"\n";
				tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
				tcl_file << "set_module_property \"synthesisFiles\" \"" << "array_collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";
				
				tcl_file << "# Module parameters\n";
				tcl_file << "add_parameter \"MEMORY_SIZE\" \"integer\" \"" << n_iter->out_edges.front().array_size << "\" \"\"\n";
				tcl_file << "add_parameter \"FIFO_DEPTH_LOG\" \"integer\" \"2\" \"\"\n";
				tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
				tcl_file << "add_parameter \"ADDR_WIDTH\" \"integer\" \"" << (int)(ceil(log(1.0*n_iter->out_edges.front().array_size)/log(2.0))) << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_INPUTS\" \"integer\" \"" << inputs << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_INPUTS_LOG\" \"integer\" \"" << (int)(ceil(log(1.0*inputs)/log(2.0))) << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS\" \"integer\" \"" << outputs << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS_LOG\" \"integer\" \"" << (int)(ceil(log(1.0*outputs)/log(2.0))) << "\" \"\"\n\n";

				tcl_file << "# Interface clock\n";
				tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
				tcl_file << "# Ports in interface clock\n";
				tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
				tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

				for (counter = 0; counter < inputs; counter++) {
					tcl_file << "# Interface in_control_" << counter << "\n";
					tcl_file << "add_interface \"in_control_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"in_control_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface in_control_" << counter << "\n";
					tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"write_in_control_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"writedata_in_control_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"in_control_" << counter << "\" \"wait_write_in_control_" << counter << "\" \"waitrequest\"\n\n";

					tcl_file << "# Interface in_array_" << counter << "\n";
					tcl_file << "add_interface \"in_array_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"in_array_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface in_array_" << counter << "\n";
					tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"write_array_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"address_write_array_" << counter << "\" \"address\"\n";
					tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"writedata_array_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"in_array_" << counter << "\" \"wait_write_array_" << counter << "\" \"waitrequest\"\n\n";
				}

				for (counter = 0; counter < outputs; counter++) {
					tcl_file << "# Interface out_control_" << counter << "\n";
					tcl_file << "add_interface \"out_control_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_control_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_control_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"write_out_control_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"writedata_out_control_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"out_control_" << counter << "\" \"wait_write_out_control_" << counter << "\" \"waitrequest\"\n\n";

					tcl_file << "# Interface out_array_" << counter << "\n";
					tcl_file << "add_interface \"out_array_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readLatency\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_array_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_array_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"read_array_" << counter << "\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"address_read_array_" << counter << "\" \"address\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"readdata_array_" << counter << "\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_array_" << counter << "\" \"wait_read_array_" << counter << "\" \"waitrequest\"\n\n";
				}
				
				tcl_file.close();
			} else {
				// It's an ordinary collector/distributor FIFO (i.e. it does NOT store arrays)
				verilog_file_name << globals.SOPC_COMPONENT_PATH << "collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v";
				tcl_file_name << globals.SOPC_COMPONENT_PATH << "collector_distributor_fifo_" << inputs << "_to_" << outputs << "_hw.tcl";
				verilog_file.open (verilog_file_name.str().c_str());
				tcl_file.open (tcl_file_name.str().c_str());
				if (verilog_file.fail()) {
					cerr << "***Unable to open file (7) " << verilog_file_name.str() << "\n";
					exit(1);
				}
				if (tcl_file.fail()) {
					cerr << "***Unable to open file (8) " << tcl_file_name.str() << "\n";
					exit(1);
				}
				// Emit Verilog code
				// Add many spaces after the module declaration, so that we can easily replace the module name
				verilog_file << "module collector_distributor_fifo_" << inputs << "_to_" << outputs << " (                                                                                              \n";
				verilog_file << "\tinput\t\t\t\t\tclk,\n";
				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "\tinput\t\t\t\t\tread_" << counter << ",\n";
					verilog_file << "\toutput [DATA_WIDTH-1:0] readdata_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_read_" << counter << ",\n";
				}
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\tinput\t\t\t\t\twrite_" << counter << ",\n";
					verilog_file << "\tinput [DATA_WIDTH-1:0]\twritedata_" << counter << ",\n";
					verilog_file << "\toutput\t\t\t\t\twait_write_" << counter << ",\n";
				}
				verilog_file << "\tinput\t\t\t\t\treset\n";
				verilog_file << ");\n";
				verilog_file << "parameter FIFO_DEPTH = 4, DATA_WIDTH = 32, NO_OUTPUTS = " << outputs << ", NO_INPUTS = " << inputs << ";\n\n";

				verilog_file << "reg [NO_OUTPUTS-1:0] output_selector;\n";
				verilog_file << "reg [NO_INPUTS-1:0]\t\tinput_selector;\n";
				verilog_file << "reg [DATA_WIDTH-1:0]	writedata;\n\n";

				verilog_file << "wire\t\t\t\t\tread;\n";
				verilog_file << "wire\t\t\t\t\twait_read;\n";
				verilog_file << "wire [DATA_WIDTH-1:0] readdata;\n\n";
				verilog_file << "wire\t\t\t\t\twait_write;\n";
				verilog_file << "wire\t\t\t\t\twrite;\n\n";
				

				verilog_file << "assign read = ";
				for (counter = 0; counter < outputs; counter++) {
					if (0 != counter) {
						verilog_file << " | ";
					}
					verilog_file << "(read_" << counter << " & output_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";

				for (counter = 0; counter < outputs; counter++) {
					verilog_file << "assign wait_read_" << counter << " = ~output_selector[" << counter << "] | wait_read;\n";
					verilog_file << "assign readdata_" << counter << " = readdata;\n";
				}
				verilog_file << "\n";

				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "\tif (reset)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\toutput_selector = {{NO_OUTPUTS-1{1'b0}}, 1'b1};\n";
				verilog_file << "\tend\n";
				verilog_file << "\telse\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (read & ~wait_read)\n";
				verilog_file << "\t\t\toutput_selector = {output_selector[NO_OUTPUTS-2:0], output_selector[NO_OUTPUTS-1]};\n";
				verilog_file << "\tend\n";
				verilog_file << "end\n\n";


				verilog_file << "assign write = ";
				for (counter = 0; counter < inputs; counter++) {
					if (counter != 0) {
						verilog_file << " | ";
					}
					verilog_file << "(write_" << counter << " & input_selector[" << counter << "])";
				}
				verilog_file << ";\n\n";

				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "assign wait_write_" << counter << " = ~input_selector[" << counter << "] | wait_write;\n";
				}
				verilog_file << "\n";
				verilog_file << "always @(*)\n";
				verilog_file << "begin\n";
				verilog_file << "\tcase (input_selector) // synthesis full_case\n";
				for (counter = 0; counter < inputs; counter++) {
					verilog_file << "\t\t" << inputs << "'b";
					for (bit_counter = inputs-1; bit_counter >=0; bit_counter--) {
						if (counter == bit_counter) {
							verilog_file << "1";
						} else {
							verilog_file << "0";
						}
					}
					verilog_file << ": writedata = writedata_" << counter << ";\n";
				}
				verilog_file << "\tendcase\n";
				verilog_file << "end\n\n";
				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "\tif (reset)\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tinput_selector = {{NO_INPUTS-1{1'b0}}, 1'b1};\n";
				verilog_file << "\tend\n";
				verilog_file << "\telse\n";
				verilog_file << "\tbegin\n";
				verilog_file << "\t\tif (write & ~wait_write)\n";
				verilog_file << "\t\t\tinput_selector = {input_selector[NO_INPUTS-2:0], input_selector[NO_INPUTS-1]};\n";
				verilog_file << "\tend\n";
				verilog_file << "end\n\n";

				// Now generate TCL file
				tcl_file << "# TCL File Generated by BR2C Compiler\n";
				tcl_file << "# DO NOT MODIFY\n\n";

				tcl_file << "set_source_file \"collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n";
				tcl_file << "set_module \"collector_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_description \"\"\n";
				tcl_file << "set_module_property \"className\" \"collector_distributor_fifo_" << inputs << "_to_" << outputs << "\"\n";
				tcl_file << "set_module_property \"group\" \"Custom\"\n";
				tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
				tcl_file << "set_module_property \"synthesisFiles\" \"collector_distributor_fifo_" << inputs << "_to_" << outputs << ".v\"\n\n";

				tcl_file << "# Module parameters\n";
				tcl_file << "add_parameter \"FIFO_DEPTH\" \"integer\" \"4\" \"\"\n";
				tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
				tcl_file << "add_parameter \"NO_INPUTS\" \"integer\" \"" << inputs << "\" \"\"\n";
				tcl_file << "add_parameter \"NO_OUTPUTS\" \"integer\" \"" << outputs << "\" \"\"\n\n";

				tcl_file << "# Interface clock\n";
				tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
				tcl_file << "# Ports in interface clock\n";
				tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
				tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

				for (counter = 0; counter < inputs; counter++) {
					tcl_file << "# Interface in_" << counter << "\n";
					tcl_file << "add_interface \"in_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"in_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface in_" << counter << "\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"write_" << counter << "\" \"write\"\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"writedata_" << counter << "\" \"writedata\"\n";
					tcl_file << "add_port_to_interface \"in_" << counter << "\" \"wait_write_" << counter << "\" \"waitrequest\"\n\n";
				}

				for (counter = 0; counter < outputs; counter++) {
					tcl_file << "# Interface out_" << counter << "\n";
					tcl_file << "add_interface \"out_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readLatency\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitStates\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"holdTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"printableDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"setupTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"writeWaitTime\" \"0\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"linewrapBursts\" \"false\"\n";
					tcl_file << "set_interface_property \"out_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
					tcl_file << "# Ports in interface out_" << counter << "\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"read_" << counter << "\" \"read\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"readdata_" << counter << "\" \"readdata\"\n";
					tcl_file << "add_port_to_interface \"out_" << counter << "\" \"wait_read_" << counter << "\" \"waitrequest\"\n\n";
				}
				tcl_file.close();
			}
		} else if (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) != string::npos) {
			if (n_iter->preceded_by_a_collector) {
				// If the double distributor  is preceded by a collector node,
				// a collector/double distributor should be generated, so no need to process it here
				continue;
			}
			// Otherwise generate custom Verilog code and TCL for double distributor
			// Generate custom distributor code
			// First find out the number of inputs and outputs
			inputs = n_iter->inEdges();
			outputs = n_iter->out_edges.size();
			if (inputs != 1) {
				cerr << "\nNumber of inputs of the distributor node " << n_iter->name << " is " << inputs;
				fatal ("Number of inputs of any distributor node has to be 1");
			}
			if (outputs < 2) {
				cerr << "\nNumber of outputs of the distributor node " << n_iter->name << " is " << outputs;
				fatal ("Number of outputs to any distributor node has to be >= 2");
			}
			int trees = n_iter->trees;
			int leaves = n_iter->replicated_leaves;
			if (trees*leaves != outputs) {
				cerr << "\nNumber of outputs of the double distributor node " << n_iter->name << " is " << outputs;
				cerr << "\nNumber of trees is " << trees << " and the number of leaves in each tree is " << leaves;
				fatal ("Errorr: Number of outputs to any double distributor node should be == trees*leaves");
			}
			// Generate a name for the file
			verilog_file_name.str("");
			tcl_file_name.str("");
			verilog_file_name << globals.SOPC_COMPONENT_PATH << "double_distributor_fifo_" << trees << "_" << leaves << ".v";
			tcl_file_name << globals.SOPC_COMPONENT_PATH << "double_distributor_fifo_" << trees << "_" << leaves << "_hw.tcl";
			verilog_file.open (verilog_file_name.str().c_str());
			tcl_file.open (tcl_file_name.str().c_str());
			if (verilog_file.fail()) {
		        cerr << "***Unable to open file (9) " << verilog_file_name.str() << "\n";
				exit(1);
			}
			if (tcl_file.fail()) {
		        cerr << "***Unable to open file (10) " << tcl_file_name.str() << "\n";
				exit(1);
			}
			// Emit Verilog code
			// Add many spaces after the module declaration, so that we can easily replace the module name
			verilog_file << "module double_distributor_fifo_" << trees << "_" << leaves << " (                                                                                              \n";
			verilog_file << "\tinput\t\t\t\t\tclk,\n";
			verilog_file << "\tinput\t\t\t\t\treset,\n";
			for (counter = 0; counter < outputs; counter++) {
				verilog_file << "\tinput\t\t\t\t\tread_" << counter << ",\n";
				verilog_file << "\toutput [DATA_WIDTH-1:0] readdata_" << counter << ",\n";
				verilog_file << "\toutput\t\t\t\t\twait_read_" << counter << ",\n";
			}
			verilog_file << "\tinput\t\t\t\t\twrite,\n";
			verilog_file << "\tinput [DATA_WIDTH-1:0] writedata,\n";
			verilog_file << "\toutput\t\t\t\t\twait_write);\n";
			verilog_file << "parameter FIFO_DEPTH = 4, DATA_WIDTH = 32, NO_TREES = " << trees << ", NO_LEAVES = " << leaves << ", X_OVER_Y = " << n_iter->insize_dim0/n_iter->reducesize_dim0 << ";\n\n";
			verilog_file << "reg [NO_TREES-1:0] tree_selector;\n";
			verilog_file << "reg [NO_LEAVES-1:0] leaf_selector;\n";
			verilog_file << "reg [X_OVER_Y-1:0] element_counter;  // This register has too many elements, which will be optimized by the synthesis tool\n";
			verilog_file << "\nwire [(NO_LEAVES*NO_TREES)-1:0] output_selector;\n";
			verilog_file << "wire read;\n";
			verilog_file << "wire wait_read;\n";
			verilog_file << "wire [DATA_WIDTH-1:0] readdata;\n";

			verilog_file << "assign read = ";
			for (counter = 0; counter < outputs; counter++) {
				if (0 != counter) {
					verilog_file << " | ";
				}
				verilog_file << "(read_" << counter << " & output_selector[" << counter << "])";
			}
			verilog_file << ";\n\n";

			for (counter = 0; counter < outputs; counter++) {
				verilog_file << "assign wait_read_" << counter << " = ~output_selector[" << counter << "] | wait_read;\n";
				verilog_file << "assign readdata_" << counter << " = readdata;\n";
			}
			verilog_file << "\n";

			for (int i=0; i<trees; i++) {
				for (int j=0; j<leaves; j++) {
					verilog_file << "assign output_selector[" << i*leaves+j << "] = tree_selector[" << i << "] & leaf_selector[" << j << "];\n";
				}
			}

			verilog_file << "\n";

			if (leaves > 1) {
				verilog_file << "always @(posedge clk)\n";
				verilog_file << "begin\n";
				verilog_file << "if (reset)\n";
				verilog_file << "begin\n";
				verilog_file << "leaf_selector <= {{NO_LEAVES-1{1'b0}}, 1'b1};\n";
				verilog_file << "end\n";
				verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
				verilog_file << "begin\n";
				verilog_file << "leaf_selector <= {{NO_LEAVES-1{1'b0}}, 1'b1};\n";
				verilog_file << "end\n";
				verilog_file << "else if (read & ~wait_read)\n";
				verilog_file << "begin\n";
				verilog_file << "leaf_selector <= {leaf_selector[NO_LEAVES-2:0], leaf_selector[NO_LEAVES-1]};\n";
				verilog_file << "end\n";
				verilog_file << "end\n\n";
			} else {
				verilog_file << "\nalways @(*)\nbegin\n leaf_selector = 1'b1;\nend\n\n";
			}

			verilog_file << "always @(posedge clk)\n";
			verilog_file << "begin\n";
			verilog_file << "if (reset)\n";
			verilog_file << "begin\n";
			verilog_file << "element_counter <= {X_OVER_Y{1'b0}};\n";
			verilog_file << "end\n";
			verilog_file << "else if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
			verilog_file << "begin\n";
			verilog_file << "element_counter <= {X_OVER_Y{1'b0}};\n";
			verilog_file << "end\n";
			verilog_file << "else if (read & ~wait_read)\n";
			verilog_file << "begin\n";
			verilog_file << "element_counter <= element_counter + 1'b1;\n";
			verilog_file << "end\n";
			verilog_file << "end\n\n";

			verilog_file << "always @(posedge clk)\n";
			verilog_file << "begin\n";
			verilog_file << "if (reset)\n";
			verilog_file << "begin\n";
			verilog_file << "tree_selector <= {{(NO_TREES-1){1'b0}}, 1'b1};\n";
			verilog_file << "end\n";
			verilog_file << "else\n";
			verilog_file << "begin\n";
			verilog_file << "if (read & ~wait_read & (element_counter == (X_OVER_Y-1)))\n";
			verilog_file << "tree_selector <= {tree_selector[NO_TREES-2:0], tree_selector[NO_TREES-1]};\n";
			verilog_file << "end\n";
			verilog_file << "end\n\n";

			// Now generate TCL file
			tcl_file << "# TCL File Generated by BR2C Compiler\n";
			tcl_file << "# DO NOT MODIFY\n\n";

			tcl_file << "set_source_file \"double_distributor_fifo_" << trees << "_" << leaves << ".v\"\n";
			tcl_file << "set_module \"double_distributor_fifo_" << trees << "_" << leaves << "\"\n";
			tcl_file << "set_module_description \"\"\n";
			tcl_file << "set_module_property \"className\" \"double_distributor_fifo_" << trees << "_" << leaves << "\"\n";
			tcl_file << "set_module_property \"group\" \"\"\n";
			tcl_file << "set_module_property \"instantiateInSystemModule\" \"true\"\n";
			tcl_file << "set_module_property \"synthesisFiles\" \"double_distributor_fifo_" << trees << "_" << leaves << ".v\"\n\n";

			tcl_file << "# Module parameters\n";
			tcl_file << "add_parameter \"FIFO_DEPTH\" \"integer\" \"4\" \"\"\n";
			tcl_file << "set_parameter_property \"FIFO_DEPTH\" \"enabled\" \"false\"\n";
			tcl_file << "add_parameter \"DATA_WIDTH\" \"integer\" \"32\" \"\"\n";
			tcl_file << "set_parameter_property \"DATA_WIDTH\" \"enabled\" \"false\"\n";
			tcl_file << "add_parameter \"NO_TREES\" \"integer\" \"" << trees << "\" \"\"\n";
			tcl_file << "set_parameter_property \"NO_TREES\" \"enabled\" \"false\"\n";
			tcl_file << "add_parameter \"NO_LEAVES\" \"integer\" \"" << leaves << "\" \"\"\n";
			tcl_file << "set_parameter_property \"NO_LEAVES\" \"enabled\" \"false\"\n";
			tcl_file << "add_parameter \"X_OVER_Y\" \"integer\" \"" << n_iter->insize_dim0/n_iter->reducesize_dim0 << "\" \"\"\n";
			tcl_file << "set_parameter_property \"X_OVER_Y\" \"enabled\" \"false\"\n\n";

			tcl_file << "# Interface clock\n";
			tcl_file << "add_interface \"clock\" \"clock\" \"sink\" \"asynchronous\"\n";
			tcl_file << "# Ports in interface clock\n";
			tcl_file << "add_port_to_interface \"clock\" \"clk\" \"clk\"\n";
			tcl_file << "add_port_to_interface \"clock\" \"reset\" \"reset\"\n\n";

			tcl_file << "# Interface in\n";
			tcl_file << "add_interface \"in\" \"avalon\" \"slave\" \"clock\"\n";
			tcl_file << "set_interface_property \"in\" \"isNonVolatileStorage\" \"false\"\n";
			tcl_file << "set_interface_property \"in\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
			tcl_file << "set_interface_property \"in\" \"readLatency\" \"0\"\n";
			tcl_file << "set_interface_property \"in\" \"holdTime\" \"0\"\n";
			tcl_file << "set_interface_property \"in\" \"printableDevice\" \"false\"\n";
			tcl_file << "set_interface_property \"in\" \"readWaitTime\" \"1\"\n";
			tcl_file << "set_interface_property \"in\" \"setupTime\" \"0\"\n";
			tcl_file << "set_interface_property \"in\" \"addressAlignment\" \"DYNAMIC\"\n";
			tcl_file << "set_interface_property \"in\" \"writeWaitTime\" \"0\"\n";
			tcl_file << "set_interface_property \"in\" \"timingUnits\" \"Cycles\"\n";
			tcl_file << "set_interface_property \"in\" \"minimumUninterruptedRunLength\" \"1\"\n";
			tcl_file << "set_interface_property \"in\" \"isMemoryDevice\" \"false\"\n";
			tcl_file << "set_interface_property \"in\" \"linewrapBursts\" \"false\"\n";
			tcl_file << "set_interface_property \"in\" \"maximumPendingReadTransactions\" \"0\"\n";
			tcl_file << "# Ports in interface in\n";
			tcl_file << "add_port_to_interface \"in\" \"write\" \"write\"\n";
			tcl_file << "add_port_to_interface \"in\" \"writedata\" \"writedata\"\n";
			tcl_file << "add_port_to_interface \"in\" \"wait_write\" \"waitrequest\"\n\n";

			for (counter = 0; counter < outputs; counter++) {
				tcl_file << "# Interface out_" << counter << "\n";
				tcl_file << "add_interface \"out_" << counter << "\" \"avalon\" \"slave\" \"clock\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"isNonVolatileStorage\" \"false\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"burstOnBurstBoundariesOnly\" \"false\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"readLatency\" \"0\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"holdTime\" \"0\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"printableDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"readWaitTime\" \"1\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"setupTime\" \"0\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"addressAlignment\" \"DYNAMIC\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"writeWaitTime\" \"0\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"timingUnits\" \"Cycles\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"minimumUninterruptedRunLength\" \"1\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"isMemoryDevice\" \"false\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"linewrapBursts\" \"false\"\n";
				tcl_file << "set_interface_property \"out_" << counter << "\" \"maximumPendingReadTransactions\" \"0\"\n";
				tcl_file << "# Ports in interface out_" << counter << "\n";
				tcl_file << "add_port_to_interface \"out_" << counter << "\" \"read_" << counter << "\" \"read\"\n";
				tcl_file << "add_port_to_interface \"out_" << counter << "\" \"readdata_" << counter << "\" \"readdata\"\n";
				tcl_file << "add_port_to_interface \"out_" << counter << "\" \"wait_read_" << counter << "\" \"waitrequest\"\n\n";
			}
			tcl_file.close();
		} else {
			// Move on to the next node if this one is not collector, distributor, or collector/distributor
			continue;
		}
		// This part is common to all three options (although different for elements storing streams of arrays -> the if statement below)
		if (!n_iter->out_edges.front().stream_of_arrays) {
			// If the stream does not contain arrays, an ordinary FIFO is good enough
			verilog_file << "scfifo	the_fifo (\n";
			verilog_file << "\t.rdreq (read & ~wait_read),\n";
			verilog_file << "\t.aclr (reset),\n";
			verilog_file << "\t.clock (clk),\n";
			verilog_file << "\t.wrreq (write & ~wait_write),\n";
			verilog_file << "\t.data (writedata),\n\n";
			verilog_file << "\t.empty (wait_read),\n";
			verilog_file << "\t.q (readdata),\n";
			verilog_file << "\t.full (wait_write),\n\n";
			verilog_file << "\t.almost_empty (),\n";
			verilog_file << "\t.almost_full (),\n";
			verilog_file << "\t.sclr (),\n";
			verilog_file << "\t.usedw ());\n";
			verilog_file << "defparam\n";
			verilog_file << "\tthe_fifo.add_ram_output_register = \"OFF\",\n";
			verilog_file << "\tthe_fifo.lpm_numwords = FIFO_DEPTH,\n";
			verilog_file << "\tthe_fifo.lpm_showahead = \"ON\",\n";
			verilog_file << "\tthe_fifo.lpm_type = \"scfifo\",\n";
			verilog_file << "\tthe_fifo.lpm_width = DATA_WIDTH,\n";
			verilog_file << "\tthe_fifo.overflow_checking = \"OFF\",\n";
			verilog_file << "\tthe_fifo.underflow_checking = \"OFF\",\n";
			verilog_file << "\tthe_fifo.use_eab = \"ON\";\n\n";
			verilog_file << "endmodule\n";
			verilog_file.close();
		} else {
			// Otherwise, stream stores arrays, so a custom FIFO is needed
			verilog_file << "\tassign buffer_empty = (read_ptr == write_ptr);\n";
			verilog_file << "\tassign buffer_full = (read_ptr[FIFO_DEPTH_LOG-1:0] == write_ptr[FIFO_DEPTH_LOG-1:0]) && (read_ptr[FIFO_DEPTH_LOG] != write_ptr[FIFO_DEPTH_LOG]);\n";
			verilog_file << "\tassign wait_write_in_control = write_in_control & buffer_full;\n";
			verilog_file << "\tassign wait_write_out_control = write_out_control & buffer_empty;\n\n";

			verilog_file << "\talways @(posedge clk or posedge reset)\n";
			verilog_file << "\tbegin\n";
			verilog_file << "\t\tif (reset == 1'b1)\n";
			verilog_file << "\t\tbegin\n";
			verilog_file << "\t\t\tread_ptr = {(FIFO_DEPTH_LOG+1){1'b0}};\n";		// Initially the memory with index 0 will be used for reading
			verilog_file << "\t\tend\n";
			verilog_file << "\t\telse if (write_out_control & ~wait_write_out_control)\n";
			verilog_file << "\t\tbegin\n";
			verilog_file << "\t\t\tread_ptr = read_ptr + 1'b1;\n";
			verilog_file << "\t\tend\n";
			verilog_file << "\tend\n\n";

			verilog_file << "\talways @(posedge clk or posedge reset)\n";
			verilog_file << "\tbegin\n";
			verilog_file << "\t\tif (reset == 1'b1)\n";
			verilog_file << "\t\tbegin\n";
			verilog_file << "\t\t\twrite_ptr = {(FIFO_DEPTH_LOG+1){1'b0}};\n";		// Initially the memory with index 0 will be used for writing
			verilog_file << "\t\tend\n";
			verilog_file << "\t\telse if (write_in_control & ~wait_write_in_control)\n";
			verilog_file << "\t\tbegin\n";
			verilog_file << "\t\t\twrite_ptr = write_ptr + 1'b1;\n";
			verilog_file << "\t\tend\n";
			verilog_file << "\tend\n";

			verilog_file << "endmodule\n";
			verilog_file.close();
		}
	}
}

/*
* copyCollectorDistributorFiles --
* FP
*	Copies generated collectors, distributors and collector/distributor Verilog files into files with instance names
*	This is necessary because SOPC builder wants to have these files to be able to verify the system before
*	adding accelerators to the system
*		Arguments are: 
*/
void copyCollectorDistributorFiles (const string &source, const string &dest) {

	ostringstream source_file_name;
	ostringstream old_module_name;
	ostringstream new_module_name;
	fstream fifo_out;
	streampos last_read=0;
	char line[2*MAX_LINE_SIZE];

	source_file_name.str("");
	old_module_name.str("");
	new_module_name.str("");
	source_file_name << globals.SOPC_COMPONENT_PATH << source;
	// The module name is the same as the file name, but without last two characters .v
	old_module_name << source.substr(0,source.size()-2);
	// The same for the new module name
	new_module_name << dest.substr(0,dest.size()-2);

	if (CopyFile (source_file_name.str().c_str(), dest.c_str(), false) == 0) {
		DWORD dw = GetLastError();
		LPTSTR lpMsgBuf;
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
		cout << lpMsgBuf;
		string temp_str1 = "Cannot copy Collector/distributor template file from ";
		string temp_str2 = source_file_name.str();
		string temp_str3 = " to ";
		string temp_str4 = " Keep in mind that the paths are relative to the output folder for SOPC system!";
		fatal (temp_str1+temp_str2+temp_str3+dest+temp_str4);
	}
	fifo_out.open(dest.c_str(), ios_base::out|ios_base::in);
	if (fifo_out.fail ()) {
        cerr << "***Error opening file " << dest.c_str() << "\n";
	    exit(1);
	}
	while (!fifo_out.getline(line, MAX_LINE_SIZE).eof()) {
		if (fifo_out.fail ()) {
			cerr << "***Error reading file " << dest.c_str() << "\n";
			if (fifo_out.failbit)
				cerr << "Reason: The file conatins a line longer than " << MAX_LINE_SIZE << " characters\n";
			exit(1);
		}
		string temp_string = "module ";
		temp_string += old_module_name.str();
		temp_string += " (";
		if (strstr(line, temp_string.c_str()) != NULL) {
			// Once we find the module declaration we insert the module declaration with the new name.
			// We do it this way to avoid having to copy the file line by line.
			fifo_out.seekp(last_read);
			unsigned int old_length = temp_string.size();
			temp_string.clear();
			temp_string += "module ";
			temp_string += new_module_name.str();
			temp_string += " (";
			while (temp_string.size() < old_length) {
				temp_string += "    ";
			}
			fifo_out.write (temp_string.c_str(), temp_string.length());
			break;
		}
		last_read = fifo_out.tellg();
	}
	fifo_out.close();
}


/*
* createSOPCFiles --
* FP
*	Creates SOPC file with all necessary FIFO buffers
*	Also creates make-this-app and make-this-bsp scripts
*	Finally, it creates a folder structure and copies all the files necessary to build 
*	a complete SOPC system
*		The function assumes existence of a template SOPC file, which already contains definition for all system elements
*		(e.g. Nios, timer,...), and just requires FIFOs and appropriate connections to be added
*		It also assumes that there are other files in the templates folder: make-this-app, make-this-bsp, and hardware_fifo.v
*		The function copies any files found in the globals.COPY_FOLDER into the destination folder
*		The names of these files and folders are defined in main.h
*		Arguments are: NONE
*/

void createSOPCFiles () {

	ifstream sopc_template;
	ofstream sopc_out;
	fstream fifo_out;
	streampos last_read=0;
	bool element = false, module = false, connection = false;
	// We allocate the line to be 4*MAX_LINE_SIZE because we will be adding something to the line we read,
	// so we want to leave some extra space
	char line[4*MAX_LINE_SIZE];
	// This counter is initialized to globals.INITIAL_SOPC_ELEMENT_COUNT, to place the FIFOs below all other elements in the SOPC builder GUI
	unsigned int sort_counter = globals.INITIAL_SOPC_ELEMENT_COUNT;
	int i;
	vector<struct fifo_info>::iterator iter;
	vector <struct collector_distributor_info>::iterator cd_iter;
	
	sopc_template.open (globals.SOPC_TEMPLATE_FILE);
	if (sopc_template.fail()) {
        cerr << "***Unable to open " << globals.SOPC_TEMPLATE_FILE << "\n";
        exit(1);
    }
	_mkdir (globals.targetfolder);
	_chdir (globals.targetfolder);
	// Copy all the files from the copy folder
	string copy_command = "copy ";
	copy_command += globals.COPY_FOLDER;
	// Redirect stdout of the copy command into null
	copy_command += " .>NUL";
	system (copy_command.c_str());
	copy_command.clear();
	// Now copy the actual hardware_fifo
	copy_command = "copy ";
	copy_command += globals.HARDWARE_FIFO_FOLDER;
	copy_command += " ";
	copy_command += globals.SOPC_COMPONENT_PATH;
	// Redirect stdout of the copy command into null
	copy_command += ".>NUL";
	system (copy_command.c_str());

	// Create the .sopc file from the template
	sopc_out.open(globals.SOPC_TARGET_FILE, ios_base::out);
	if (sopc_out.fail()) {
        cerr << "***Unable to open " << globals.SOPC_TARGET_FILE << "\n";
        exit(1);
    }
	while (!sopc_template.getline(line, MAX_LINE_SIZE).eof()) {
		if (sopc_template.fail ()) {
	        cerr << "***Error reading file " << globals.SOPC_TEMPLATE_FILE << "\n";
			if (sopc_template.failbit)
				cerr << "Reason: The file contains a line longer than " << MAX_LINE_SIZE << " characters\n";
		    exit(1);
		}
		sopc_out << line << "\n";
		if (!element && (line[0] == '{')) {
			element = true;
			int address = globals.FIFO_BASE_ADDRESS;
			// Once we find the first curly bracket, we print out FIFO declarations
			for (iter = globals.fifos.begin(), i=0; iter != globals.fifos.end(); iter++, sort_counter++) {
				sopc_out << "\telement " << iter->fifo_name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << iter->fifo_name;
				if (iter->array_fifo) {
					sopc_out << ".in_control\n";
				} else {
					sopc_out << ".in\n";
				}
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				if (iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << iter->fifo_name;
				if (iter->array_fifo) {
					sopc_out << ".out_control\n";
				} else {
					sopc_out << ".out\n";
				}
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				if (iter->array_fifo) {
					address += 3;	// The reason this is 3 instead of 1 is to align it to 32-bit boundary
				} else {
					address += 4;
				}
				sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				if (iter->array_fifo) {
					sopc_out << "\telement " << iter->fifo_name;
					sopc_out << ".in_array\n";
					// Apparently, SOPC builder wants each memory's starting address to be aligned to the size of that memory
					// If the size is not a power of 2, align it to the nearest higher power of 2, because I think SOPC builder
					// determines the size based on the address-bus size, not actual memory size
					int alignment = (int) pow(2.0,ceil(log(1.0*(iter->array_size)*(iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += alignment; 

					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
					sopc_out << "\telement " << iter->fifo_name;
					sopc_out << ".out_array\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += alignment;
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
			}
			// Also print out collectors
			for (cd_iter = globals.collectors.begin(); cd_iter != globals.collectors.end(); cd_iter++, sort_counter++) {
				sopc_out << "\telement " << cd_iter->name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << cd_iter->name << ".out";
				if (cd_iter->array_fifo) {
					sopc_out << "_control";
				}
				sopc_out << "\n";
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				if (cd_iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".in_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
				// Now add addresses for array ports
				if (cd_iter->array_fifo) {
					sopc_out << "\telement " << cd_iter->name << ".out_array\n";
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += alignment;
					for (i = 0; i < cd_iter->inputs; i++) {
						sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
						sopc_out << "\telement " << cd_iter->name << ".in_array_";
						sopc_out << i << "\n";
						sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
						address += alignment;
						sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
					}
				}
			}
			// Also print out distributors
			for (cd_iter = globals.distributors.begin(); cd_iter != globals.distributors.end(); cd_iter++, sort_counter++) {
				sopc_out << "\telement " << cd_iter->name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << cd_iter->name << ".in";
				if (cd_iter->array_fifo)  {
					sopc_out << "_control";
				}
				sopc_out << "\n";
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				if (cd_iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".out_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
				// Now add addresses for array ports
				if (cd_iter->array_fifo) {
					sopc_out << "\telement " << cd_iter->name << ".in_array\n";
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += alignment;
					for (i = 0; i < cd_iter->outputs; i++) {
						sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
						sopc_out << "\telement " << cd_iter->name << ".out_array_";
						sopc_out << i << "\n";
						sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
						address += alignment;
						sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
					}
				}
			}
			// Also print out collectors/distributors
			for (cd_iter = globals.collectors_distributors.begin(); cd_iter != globals.collectors_distributors.end(); cd_iter++, sort_counter++) {
				sopc_out << "\telement " << cd_iter->name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".in_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".out_";
					if (cd_iter->array_fifo)  {
						sopc_out << "control_";
					}
					sopc_out << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
				// Now add addresses for array ports
				if (cd_iter->array_fifo) {
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					for (i = 0; i < cd_iter->inputs; i++) {
						sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
						sopc_out << "\telement " << cd_iter->name << ".in_array_";
						sopc_out << i << "\n";
						sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
						address += alignment;
						sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
					}
					for (i = 0; i < cd_iter->outputs; i++) {
						sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
						sopc_out << "\telement " << cd_iter->name << ".out_array_";
						sopc_out << i << "\n";
						sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
						address += alignment;
						sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
					}
				}
			}
			// Also print out double distributors
			for (cd_iter = globals.double_distributors.begin(); cd_iter != globals.double_distributors.end(); cd_iter++, sort_counter++) {
				sopc_out << "\telement " << cd_iter->name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << cd_iter->name << ".in\n";
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				address += 4;
				sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".out_" << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += 4;
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
			}
			// Also print out collectors/double distributors
			for (cd_iter = globals.collectors_double_distributors.begin(); cd_iter != globals.collectors_double_distributors.end(); cd_iter++, sort_counter++) {
				sopc_out << "\telement " << cd_iter->name << "\n";
				sopc_out << "\t{\n\t\tdatum _sortIndex\n\t\t{\n\t\t\tvalue = \"" << sort_counter << "\";\n";
				sopc_out << "\t\t\ttype = \"int\";\n\t\t}\n\t}\n";
				sopc_out << "\telement " << cd_iter->name << ".in\n";
				sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
				address += 4;
				sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".in_" << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += 4;
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
				for (i = 0; i < (cd_iter->trees*cd_iter->leaves); i++) {
					sopc_out << "\t\t\ttype = \"long\";\n\t\t{\n\t{\n";
					sopc_out << "\telement " << cd_iter->name << ".out_" << i << "\n";
					sopc_out << "\t{\n\t\tdatum baseAddress\n\t\t{\n\t\t\tvalue = \"" << address << "\";\n";
					address += 4;
					sopc_out << "\t\t\ttype = \"long\";\n\t\t}\n\t}\n";
				}
			}
		} else if (!module && (strstr(line, "</module>") != NULL)) {
			module = true;
			// Once we find </module>, which denotes end of definition of a module, we print all FIFO module instantiations
			for (iter = globals.fifos.begin(); iter != globals.fifos.end(); iter++) {
				sopc_out << "\t<module version=\"1.0\" name=\"" << iter->fifo_name;
				if (iter->array_fifo) {
					sopc_out << "\" kind=\"array_fifo_64\">\n";
				} else {
					sopc_out << "\" kind=\"hardware_fifo\">\n";
				}
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				if (iter->array_fifo) {
					sopc_out << "\t\t<parameter valueString=\""<< iter->array_size << "\" name=\"MEMORY_SIZE\"/>\n";	// The number of words in each FIFO buffer slot
					sopc_out << "\t\t<parameter valueString=\"2\" name=\"FIFO_DEPTH_LOG\"/>\n";							// log (Number of buffer slots in the FIFO) (i.e. FIFO depth)
					sopc_out << "\t\t<parameter valueString=\"" << 8*iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"" << (int)(ceil(log(1.0*iter->array_size)/log(2.0)));		// Width of the address bus (has to be == log2 (MEMORY_SIZE))
					sopc_out << "\" name=\"ADDR_WIDTH\"/>\n";
				} else {
					sopc_out << "\t\t<parameter valueString=\"" << 8*iter->word_size << "\" name=\"data_width\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"4\" name=\"fifo_depth\"/>\n";
				}
				sopc_out << "\t</module>\n"; 
			}
			// Also print out collectors
			for (cd_iter = globals.collectors.begin(); cd_iter != globals.collectors.end(); cd_iter++) {
				sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"";
				if (cd_iter->array_fifo) {
					sopc_out << "array_";
				}
				sopc_out << "collector_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				if (cd_iter->array_fifo) {
					sopc_out << "\t\t<parameter valueString=\"" << (int)(ceil(log(1.0*cd_iter->array_size)/log(2.0)));		// Width of the address bus (has to be == log2 (MEMORY_SIZE))
					sopc_out << "\" name=\"ADDR_WIDTH\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"";
					// If the number of inputs or outputs is larger than 4, then the array FIFO should have enough elements to store at least that many arrays
					int max_inputs_outputs = (cd_iter->inputs > cd_iter->outputs) ? cd_iter->inputs : cd_iter->outputs;
					if (max_inputs_outputs > 4) {
						sopc_out << (int)(ceil(log(1.0*max_inputs_outputs)/log(2.0)));
					} else {
						sopc_out << "2";
					}
					sopc_out << "\" name=\"FIFO_DEPTH_LOG\"/>\n";
					sopc_out << "\t\t<parameter valueString=\""<< cd_iter->array_size << "\" name=\"MEMORY_SIZE\"/>\n";	// The number of words in each FIFO buffer slot
				} else {
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"4\" name=\"FIFO_DEPTH\"/>\n";
				}
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->inputs << "\" name=\"NO_INPUTS\"/>\n";
				sopc_out << "\t</module>\n";
			}
			// Also print out distributors
			for (cd_iter = globals.distributors.begin(); cd_iter != globals.distributors.end(); cd_iter++) {
				sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"";
				if (cd_iter->array_fifo) {
					sopc_out << "array_";
				}
				sopc_out << "distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				if (cd_iter->array_fifo) {
					sopc_out << "\t\t<parameter valueString=\"" << (int)(ceil(log(1.0*cd_iter->array_size)/log(2.0)));		// Width of the address bus (has to be == log2 (MEMORY_SIZE))
					sopc_out << "\" name=\"ADDR_WIDTH\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"";
					// If the number of inputs or outputs is larger than 4, then the array FIFO should have enough elements to store at least that many arrays
					int max_inputs_outputs = (cd_iter->inputs > cd_iter->outputs) ? cd_iter->inputs : cd_iter->outputs;
					if (max_inputs_outputs > 4) {
						sopc_out << (int)(ceil(log(1.0*max_inputs_outputs)/log(2.0)));
					} else {
						sopc_out << "2";
					}
					sopc_out << "\" name=\"FIFO_DEPTH_LOG\"/>\n";
					sopc_out << "\t\t<parameter valueString=\""<< cd_iter->array_size << "\" name=\"MEMORY_SIZE\"/>\n";	// The number of words in each FIFO buffer slot
				} else {
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"4\" name=\"FIFO_DEPTH\"/>\n";
				}
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->outputs << "\" name=\"NO_OUTPUTS\"/>\n";
				sopc_out << "\t</module>\n";
			}
			// Also print out collectors/distributors
			for (cd_iter = globals.collectors_distributors.begin(); cd_iter != globals.collectors_distributors.end(); cd_iter++) {
				sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"";
				if (cd_iter->array_fifo) {
					sopc_out << "array_";
				}
				sopc_out << "collector_distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				if (cd_iter->array_fifo) {
					sopc_out << "\t\t<parameter valueString=\"" << (int)(ceil(log(1.0*cd_iter->array_size)/log(2.0)));		// Width of the address bus (has to be == log2 (MEMORY_SIZE))
					sopc_out << "\" name=\"ADDR_WIDTH\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"";
					// If the number of inputs or outputs is larger than 4, then the array FIFO should have enough elements to store at least that many arrays
					int max_inputs_outputs = (cd_iter->inputs > cd_iter->outputs) ? cd_iter->inputs : cd_iter->outputs;
					if (max_inputs_outputs > 4) {
						sopc_out << (int)(ceil(log(1.0*max_inputs_outputs)/log(2.0)));
					} else {
						sopc_out << "2";
					}
					sopc_out << "\" name=\"FIFO_DEPTH_LOG\"/>\n";
					sopc_out << "\t\t<parameter valueString=\""<< cd_iter->array_size << "\" name=\"MEMORY_SIZE\"/>\n";	// The number of words in each FIFO buffer slot
				} else {
					sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
					sopc_out << "\t\t<parameter valueString=\"4\" name=\"FIFO_DEPTH\"/>\n";
				}
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->inputs << "\" name=\"NO_INPUTS\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->outputs << "\" name=\"NO_OUTPUTS\"/>\n";
				sopc_out << "\t</module>\n";
			}
			// Also print out double distributors
			for (cd_iter = globals.double_distributors.begin(); cd_iter != globals.double_distributors.end(); cd_iter++) {
				sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"double_distributor_fifo_" << cd_iter->trees << "_" << cd_iter->leaves << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
				sopc_out << "\t\t<parameter valueString=\"4\" name=\"FIFO_DEPTH\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->trees << "\" name=\"NO_TREES\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->leaves << "\" name=\"NO_LEAVES\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->x_over_y << "\" name=\"X_OVER_Y\"/>\n";
				sopc_out << "\t</module>\n";
			}
			// Also print out collectors/double distributors
			for (cd_iter = globals.collectors_double_distributors.begin(); cd_iter != globals.collectors_double_distributors.end(); cd_iter++) {
				if (cd_iter->x_over_y%cd_iter->leaves != 0) {
					sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"collector_double_distributor_fifo_with_preserved_register_" << cd_iter->inputs << "_to_" << cd_iter->trees << "_" << cd_iter->leaves << "\">\n";
				} else {
					sopc_out << "\t<module version=\"1.0\" name=\"" << cd_iter->name << "\" kind=\"collector_double_distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->trees << "_" << cd_iter->leaves << "\">\n";
				}
				sopc_out << "\t\t<parameter valueString=\"\" name=\"sharedPorts\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << 8*cd_iter->word_size << "\" name=\"DATA_WIDTH\"/>\n";	// Width of words stored in the buffer (8* to convert it to # of bits from bytes)
				sopc_out << "\t\t<parameter valueString=\"4\" name=\"FIFO_DEPTH\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->inputs << "\" name=\"NO_INPUTS\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->trees << "\" name=\"NO_TREES\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->leaves << "\" name=\"NO_LEAVES\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << cd_iter->x_over_y << "\" name=\"X_OVER_Y\"/>\n";
				sopc_out << "\t</module>\n";
			}
		} else if (!connection && (strstr(line, "</connection>") != NULL)) {
			connection = true;
			int address = globals.FIFO_BASE_ADDRESS;
			// Once we find </connection>, which denotes end of definition of a connection, we print all FIFO module connections
			for (iter = globals.fifos.begin(), i=0; iter != globals.fifos.end(); iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << iter->fifo_name << ".clock\"/>\n";
				sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << iter->fifo_name;
				if (iter->array_fifo) {
					sopc_out << ".in_control\">\n";
				} else {
					sopc_out << ".in\">\n";
				}
				sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
				if (iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				sopc_out << "\t</connection>\n";
				sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << iter->fifo_name;
				if (iter->array_fifo) {
					sopc_out << ".out_control\">\n";
				} else {
					sopc_out << ".out\">\n";
				}
				sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
				if (iter->array_fifo) {
					address += 3;	// The reason this is 3 instead of 1 is to align it to 32-bit address
				} else {
					address += 4;
				}
				sopc_out << "\t</connection>\n";
				if (iter->array_fifo) {
					sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << iter->fifo_name;
					sopc_out << ".in_array\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					// Apparently, SOPC builder wants each memory's starting address to be aligned to the size of that memory
					// If the size is not a power of 2, align it to the nearest higher power of 2, because I think SOPC builder
					// determines the size based on the address-bus size, not actual memory size
					int alignment = (int) pow(2.0,ceil(log(1.0*(iter->array_size)*(iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += alignment; 

					sopc_out << "\t</connection>\n";
					sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << iter->fifo_name;
					sopc_out << ".out_array\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += alignment;
					sopc_out << "\t</connection>\n";
				}
			}
			// Also print out collectors
			for (cd_iter = globals.collectors.begin(); cd_iter != globals.collectors.end(); cd_iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << cd_iter->name << ".clock\"/>\n";
				sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out";
				if (cd_iter->array_fifo) {
					sopc_out << "_control";
				}
				sopc_out << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
				if (cd_iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				sopc_out << "\t</connection>\n";
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t</connection>\n";
				}
				// Now add connections for array ports
				if (cd_iter->array_fifo) {
					sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_array\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += alignment;
					sopc_out << "\t</connection>\n";
					for (i = 0; i < cd_iter->inputs; i++) {
						sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_array_";
						sopc_out << i << "\">\n";
						sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
						sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
						address += alignment;
						sopc_out << "\t</connection>\n";
					}
				}
			}
			// Also print out distributors
			for (cd_iter = globals.distributors.begin(); cd_iter != globals.distributors.end(); cd_iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << cd_iter->name << ".clock\"/>\n";
				sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in";
				if (cd_iter->array_fifo)  {
					sopc_out << "_control";
				}
				sopc_out << "\">\n";
				sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
				if (cd_iter->array_fifo) {
					address += 1;
				} else {
					address += 4;
				}
				sopc_out << "\t</connection>\n";
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t</connection>\n";
				}
				// Now add connections for array ports
				if (cd_iter->array_fifo) {
					sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_array\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += alignment;
					sopc_out << "\t</connection>\n";
					for (i = 0; i < cd_iter->outputs; i++) {
						sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_array_";
						sopc_out << i << "\">\n";
						sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
						sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
						address += alignment;
						sopc_out << "\t</connection>\n";
					}
				}
			}
			// Also print out collectors/distributors
			for (cd_iter = globals.collectors_distributors.begin(); cd_iter != globals.collectors_distributors.end(); cd_iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << cd_iter->name << ".clock\"/>\n";
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_";
					if (cd_iter->array_fifo) {
						sopc_out << "control_";
					}
					sopc_out << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t</connection>\n";
				}
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_";
					if (cd_iter->array_fifo)  {
						sopc_out << "control_";
					}
					sopc_out << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					if (cd_iter->array_fifo) {
						address += 1;
					} else {
						address += 4;
					}
					sopc_out << "\t</connection>\n";
				}
				// Now add connections for array ports
				if (cd_iter->array_fifo) {
					int alignment = (int) pow(2.0,ceil(log(1.0*(cd_iter->array_size)*(cd_iter->word_size))/log(2.0)));
					if ((address%alignment) != 0) {
						address += alignment-(address%alignment);
					}
					for (i = 0; i < cd_iter->inputs; i++) {
						sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_array_";
						sopc_out << i << "\">\n";
						sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
						sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
						address += alignment;
						sopc_out << "\t</connection>\n";
					}
					for (i = 0; i < cd_iter->outputs; i++) {
						sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_array_";
						sopc_out << i << "\">\n";
						sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
						sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
						address += alignment;
						sopc_out << "\t</connection>\n";
					}
				}
			}
			// Also print out double distributors
			for (cd_iter = globals.double_distributors.begin(); cd_iter != globals.double_distributors.end(); cd_iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << cd_iter->name << ".clock\"/>\n";
				sopc_out << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in\">\n";
				sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
				sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
				address += 4;
				sopc_out << "\t</connection>\n";
				for (i = 0; i < cd_iter->outputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_" << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += 4;
					sopc_out << "\t</connection>\n";
				}
			}
			// Also print out collectors/double distributors
			for (cd_iter = globals.collectors_double_distributors.begin(); cd_iter != globals.collectors_double_distributors.end(); cd_iter++) {
				sopc_out << "\t<connection version=\"7.2\" start=\"clk.clk\" kind=\"clock\" end=\"" << cd_iter->name << ".clock\"/>\n";
				for (i = 0; i < cd_iter->inputs; i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".in_" << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += 4;
					sopc_out << "\t</connection>\n";
				}
				for (i = 0; i < (cd_iter->trees*cd_iter->leaves); i++) {
					sopc_out << noshowbase << dec << "\t<connection version=\"7.2\" start=\"cpu.data_master\" kind=\"avalon\" end=\"" << cd_iter->name << ".out_" << i << "\">\n";
					sopc_out << "\t\t<parameter valueString=\"1\" name=\"arbitrationPriority\"/>\n";
					sopc_out << "\t\t<parameter valueString=\"" << showbase << hex << address << "\" name=\"baseAddress\"/>";
					address += 4;
					sopc_out << "\t</connection>\n";
				}
			}
		}
	}
	sopc_template.close();
	sopc_out.close();

	generateCollectorsDistributors ();


	for (iter = globals.fifos.begin(); iter != globals.fifos.end(); iter++) {
		string temp_string = iter->fifo_name;
		temp_string += ".v";
		if (CopyFile (globals.FIFO_TEMPLATE_FILE.c_str(), temp_string.c_str(), false) == 0) {
			DWORD dw = GetLastError();
			LPTSTR lpMsgBuf;
			FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
			cout << lpMsgBuf;
			string temp_str1 = "Cannot copy FIFO template file from ";
			string temp_str2 = globals.FIFO_TEMPLATE_FILE;
			string temp_str3 = " to ";
			string temp_str4 = " Keep in mind that the paths are relative to the output folder for SOPC system!";
			fatal (temp_str1+temp_str2+temp_str3+temp_string+temp_str4);
		}
		fifo_out.open(temp_string.c_str(), ios_base::out|ios_base::in);
		if (fifo_out.fail ()) {
	        cerr << "***Error opening file " << temp_string << "\n";
		    exit(1);
		}
		while (!fifo_out.getline(line, MAX_LINE_SIZE).eof()) {
			if (fifo_out.fail ()) {
				cerr << "***Error reading file " << temp_string << "\n";
				if (fifo_out.failbit)
					cerr << "Reason: The file conatins a line longer than " << MAX_LINE_SIZE << " characters\n";
				exit(1);
			}
			if (strstr(line, "<REPLACE_THIS>") != NULL) {
				// Once we find the "<REPLACE_THIS>" tag, we insert the module name.
				// We do it this way to avoid having to copy the file line by line.
				fifo_out.seekp(last_read);
				temp_string.clear();
				temp_string += "module ";
				temp_string += iter->fifo_name;
				fifo_out.write (temp_string.c_str(), temp_string.length());
				if (temp_string.length() < 14) {
					fifo_out.write ("              ", 14);
				}
				break;
			}
			last_read = fifo_out.tellg();
		}
		fifo_out.close();
	}
	ostringstream source, dest;
	// Also copy files for all collectors
	for (cd_iter = globals.collectors.begin(); cd_iter != globals.collectors.end(); cd_iter++) {
		source.str("");
		dest.str("");
		if (cd_iter->array_fifo) {
			source << "array_";
		}
		source << "collector_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << ".v";
		dest << cd_iter->name << ".v";
		copyCollectorDistributorFiles (source.str(), dest.str());
	}
	// Also copy files for all distributors
	for (cd_iter = globals.distributors.begin(); cd_iter != globals.distributors.end(); cd_iter++) {
		source.str("");
		dest.str("");
		if (cd_iter->array_fifo) {
			source << "array_";
		}
		source << "distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << ".v";
		dest << cd_iter->name << ".v";
		copyCollectorDistributorFiles (source.str(), dest.str());
	}
	// Also copy files for all collectors/distributors
	for (cd_iter = globals.collectors_distributors.begin(); cd_iter != globals.collectors_distributors.end(); cd_iter++) {
		source.str("");
		dest.str("");
		if (cd_iter->array_fifo) {
			source << "array_";
		}
		source << "collector_distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->outputs << ".v";
		dest << cd_iter->name << ".v";
		copyCollectorDistributorFiles (source.str(), dest.str());
	}
	// Also copy files for all double distributors
	for (cd_iter = globals.double_distributors.begin(); cd_iter != globals.double_distributors.end(); cd_iter++) {
		source.str("");
		dest.str("");
		source << "double_distributor_fifo_" << cd_iter->trees << "_" << cd_iter->leaves << ".v";
		dest << cd_iter->name << ".v";
		copyCollectorDistributorFiles (source.str(), dest.str());
	}
	// Also copy files for all COLLECTORS/double distributors
	for (cd_iter = globals.collectors_double_distributors.begin(); cd_iter != globals.collectors_double_distributors.end(); cd_iter++) {
		source.str("");
		dest.str("");
		if (cd_iter->x_over_y%cd_iter->leaves != 0) {
			source << "collector_double_distributor_fifo_with_preserved_register_" << cd_iter->inputs << "_to_" << cd_iter->trees << "_" << cd_iter->leaves << ".v";
		} else {
			source << "collector_double_distributor_fifo_" << cd_iter->inputs << "_to_" << cd_iter->trees << "_" << cd_iter->leaves << ".v";
		}
		dest << cd_iter->name << ".v";
		copyCollectorDistributorFiles (source.str(), dest.str());
	}

	_mkdir (".\\software");
	_chdir (".\\software");
	_mkdir (".\\bsp");
	_mkdir (".\\app");
	// Copy create-this-bsp file, which does not require any modifications
	_chdir (".\\bsp");
	if (CopyFile (globals.BSP_TEMPLATE_FILE.c_str(), "create-this-bsp", false) == 0) {
		DWORD dw = GetLastError();
		LPTSTR lpMsgBuf;
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
		cout << lpMsgBuf;
		string temp_str1 = "Cannot copy BSP template file from ";
		string temp_str2 = globals.BSP_TEMPLATE_FILE;
		string temp_str3 = " to ";
		string temp_str4 = "create-this-bsp";
		string temp_str5 = " Keep in mind that the paths are relative to the output folder for BSP file!";
		fatal (temp_str1+temp_str2+temp_str3+temp_str4+temp_str5);
	}
	// Copy the compiled C file (result of our BRCC)
	_chdir ("..\\app");
	string temp_string = "..\\..\\..\\";
	temp_string += globals.coutputname;
	if (CopyFile (temp_string.c_str(), globals.coutputname, false) == 0) {
		DWORD dw = GetLastError();
		LPTSTR lpMsgBuf;
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
		cout << lpMsgBuf;
		string temp_str1 = "Cannot copy C file ";
		string temp_str2 = temp_string;
		string temp_str3 = " to ";
		string temp_str4 = globals.coutputname;
		string temp_str5 = " Keep in mind that the paths are relative to the output folder for APP file!";
		fatal (temp_str1+temp_str2+temp_str3+temp_str4+temp_str5);
	}
	// Create the create-this-app file, based on the template file
	ifstream createthisapp_template;
	ofstream createthisapp_out;
	createthisapp_template.open (globals.APP_TEMPLATE_FILE);
	if (createthisapp_template.fail()) {
        cerr << "***Unable to open " << globals.APP_TEMPLATE_FILE << "\n";
        exit(1);
    }
	// Open the file as binary to make sure that line endings are in Unix format for Cygwin not to complain
	createthisapp_out.open("create-this-app", ios_base::binary | ios_base::out);
	if (createthisapp_out.fail()) {
        cerr << "***Unable to open " << "create-this-app" << "\n";
        exit(1);
    }
	while (!createthisapp_template.getline(line, MAX_LINE_SIZE).eof()) {
		if (createthisapp_template.fail ()) {
	        cerr << "***Error reading file " << globals.APP_TEMPLATE_FILE << "\n";
			if (createthisapp_template.failbit)
				cerr << "Reason: The file conatins a line longer than " << MAX_LINE_SIZE << " characters\n";
		    exit(1);
		}
		if (strstr(line, "cmd=\"nios2-app-generate-makefile") != NULL) {
			strcat (line, globals.elfoutputname);
			strcat (line, " --src-files ");
			strcat (line, globals.coutputname);
			strcat (line, "\"");
		} else if (strstr(line, "cmd=\"nios2-c2h-generate-makefile") != NULL) {
			//  Go through the replicated dataflow graph and specify that all kernels should become accelerators
			std::list<GraphNode>::iterator n_iter;

			// Iterate over all nodes
			for (n_iter=globals.replicated.nodes.begin(); n_iter != globals.replicated.nodes.end(); n_iter++) {
				if ((n_iter->name.find(BRCC_COLLECTOR) == string::npos) && (n_iter->name.find(BRCC_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_COLLECTOR_DISTRIBUTOR) == string::npos) && (n_iter->name.find(BRCC_DOUBLE_DISTRIBUTOR) == string::npos)) {
					// If we are here, the node name is not one of the COLLECTOR, DISTRIBUTOR or COLLECTOR/DISTRIBUTOR
					// i.e. it is an ordinary node (i.e. kernel)
					strcat (line, "--accelerator=");
					strcat (line, n_iter->name.c_str());
					strcat (line, ",");
					strcat (line, globals.coutputname);
					strcat (line, ",1,");
					// If the node has successors, we will not flush data cache before calling it
					// i.e. data cache is only flushed for the "last" node in the dataflow graph
					if (n_iter->out_edges.size() > 0) {
						strcat (line, "0 ");
					} else {
						strcat (line, "1 ");
					}
				}
			}
			strcat (line, "--enable_quartus=1\"");
		}
		createthisapp_out << line << "\n";
	}
	createthisapp_out.close();
	createthisapp_template.close();
	// Change folder back
	_chdir ("..\\..\\..");
}

/*
* analyzeExpression --
* FP
*	Analyzes the right side of an assignment expression and turns any stream references into appropriate temporary variables that access values fetched from FIFOs.
*   Other (non-stream) references are unchanged.
*		Arguments are: the expression, a list of known input streams, the number of known input streams,
*		a list of known output streams, the number of known output streams, and the output file
*		Parameter right_var_before_dot is a string containing the name of stream on the right side of the assignment in a reduce kernel
*		This parameter is only used for case reduce_index == 5
*		Parameter version conveys information on the version of
*		non-reduce kernel that is being generated (1-D or 2-D)
*		Returns string right_var_before_dot, which contains the name of an input stream. Only modifies this string if it was not defined before entering the function
*/

string analyzeExpression (Expression *expr, Statement *ste, string function_name, DeclVector &funcdecs, vector <PPDirective *> &ppdirectives, StreamInfoVector &declstreams, Decl **streamins, unsigned int ins, Decl **streamouts, unsigned int outs, Decl **streamreduces, unsigned int reduces, Decl** streamvouts, unsigned int vouts, string right_var_before_dot, unsigned int reduce_index, unsigned int version, bool main_function, int replication_factor, int replica_number, stringstream &out) {

// __FIX_ME - Not all expression types have been handled

#ifdef DEBUG
	out << "   In analyzeExpression     ";
#endif

	switch (expr->etype) {
		case ET_Variable:
			{
			int outstream_pos = findVarInArray (streamouts, outs, ((Variable *)(expr))->name);
			int instream_pos = findVarInArray (streamins, ins, ((Variable *)(expr))->name);
			int reduce_pos = findVarInArray (streamreduces, reduces, ((Variable *)(expr))->name);
			int voutstream_pos = findVarInArray (streamvouts, vouts, ((Variable *)(expr))->name);
			// If the variable is not a stream, we should simply print it out
			if ((outstream_pos == -1) && (instream_pos == -1) && (reduce_pos == -1) && (voutstream_pos == -1)) {
				// Check if it is an indexof operator (which gets passed as variable named __indexof_<variablename>
				out << *expr;
				break;
			}
			if ((instream_pos != -1) && (right_var_before_dot.length() == 0)) {
				right_var_before_dot = ((Variable *)(expr))->name->name;
			}
			// Otherwise it's a stream variable
			// How we handle it depends on the reduce_index and whether the variable is a reduce stream or not
			switch (reduce_index) {
			case 0:
			{
				// To find out whether to insert __fp_temp_ in front of the variable name,
				// we need to find out whether the variable is an ordinary stream (in which case we print __fp_temp_ in front of it),
				// or a stream of arrays (in which case we do not print it, because the hardware FIFO serves as a temporary storage)
				ArrayType *temp_array = NULL;
				if (instream_pos != -1) {
					temp_array = (ArrayType *) streamins[instream_pos]->form;
				} else if (outstream_pos != -1) {
					temp_array = (ArrayType *) streamouts[outstream_pos]->form;
				} else if (voutstream_pos != -1) {
					temp_array = (ArrayType *) streamvouts[voutstream_pos]->form;
				}
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << " __fp_temp_";
					}
				} else {
				// For version==2 or reduce index>=3 the structure of the subTypes is different because information on the second dimension of stream size is passed as subtype
// __FIX_ME - This should probably be changed if this code is ever re-written.
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << " __fp_temp_";
					}
				}

				if (version == 1) {
					out << ((Variable *)(expr))->name->name;
				} else if (version == 2) {
					out << *(((Variable *)(expr))->name);
				} else {
					fatal ("Unknown version (not 1 or 2)!!!");
				}
				break;
			}
			case 1:
			{
				out << " __fp_temp_" << ((Variable *)(expr))->name->name;
				break;
			}
			case 2:
			{
				out << " __fp_temp_" << ((Variable *)(expr))->name->name;
				break;
			}
			case 3:
			{
				out << " __fp_temp_" << ((Variable *)(expr))->name->name;
				break;
			}
			case 4:
			{
				out << " __fp_temp_" << ((Variable *)(expr))->name->name;
				break;
			}
			case 5:
			{
				// The action depends on whether the variable is a reduction stream or just a regular stream
				if (reduce_pos == -1) {
					// This means it's not a reduce stream
					out << " __fp_temp_" << ((Variable *)(expr))->name->name;
				} else {
					// Otherwise it's a reduce stream
					if (replication_factor > 1) {
						out << " __fp_temp_" << ((Variable *)(expr))->name->name << "[(" << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " == 1) ? 0 : (brcc_div_result_2)]";
					} else {
						out << " __fp_temp_" << ((Variable *)(expr))->name->name << "[(" << ((Variable *)(expr))->name->name << "_brcc_stream_size_1 == 1) ? 0 : (brcc_div_result_2)]";
					}
				}
				break;
			}
			default:
			{
				fatal ("reduce_index is outside the range 0-5!");
				break;
			}
			}
			break;
			}
		case ET_BinaryExpr:
		{
			string expr_replacement;
			// Check if the expression involves % or / operators and an indexof operand
			if ((((BinaryExpr *)(expr))->bOp == BO_Mod) || (((BinaryExpr *)(expr))->bOp == BO_Div)) {
				if (((BinaryExpr *)(expr))->_leftExpr->etype == ET_BrtIndexofExpr) {
					// Replace any variables that are defined using the #define preprocessing directive
					replacePPDirectives (((BinaryExpr *)(expr))->_rightExpr, ppdirectives);
					// Put the right side of the expression into a string
					stringstream temp_ss;
					right_var_before_dot = analyzeExpression (((BinaryExpr *)(expr))->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, temp_ss);
					if (((reduce_index == 0)&&(version == 1)) || (reduce_index == 1) || (reduce_index == 2)) {
						// 1-D case
						expr_replacement = addModDivCounter (function_name, temp_ss.str(), right_var_before_dot + "_brcc_stream_size_1",  ((BinaryExpr *)(expr))->bOp == BO_Mod, 0, false, replication_factor, replica_number, out);
						out << "(" << expr_replacement << ")";
					} else {
						// 2-D case
						expr_replacement = addModDivCounter (function_name, temp_ss.str(), right_var_before_dot + "_brcc_stream_size_1", ((BinaryExpr *)(expr))->bOp == BO_Mod, 0, (replication_factor > 1), replication_factor, replica_number, out);
						out << "(" << expr_replacement << "))][(("; // removed (int)
						expr_replacement = addModDivCounter (function_name, temp_ss.str(), right_var_before_dot + "_brcc_stream_size_1", ((BinaryExpr *)(expr))->bOp == BO_Mod, 1, (replication_factor > 1), replication_factor, replica_number, out);
						out << expr_replacement << ")";
					}					
					break;
				} else {
// __FIX_ME - Change so that it only gets printed when encountered within kernels
					cerr << "\nWarning: Division or modulo operator found in the code. If one of these operators is found within kernel code, the produced design may be slow because C2H compiler does not produce efficient implementations of modulo and division operators. Plese note that this warning is also issued when these operators are found outside of kernels, in which case it can be disregarded.\n";
				}
			}
			// Add parenthesis everywhere, except for struct references, since C compiler (downstream) can't handle those
			if (((BinaryExpr *)(expr))->bOp != BO_Member) {
				out << "(";
			}
			right_var_before_dot = analyzeExpression (((BinaryExpr *)(expr))->_leftExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			if (((BinaryExpr *)(expr))->bOp != BO_Member) {
				out << ")";
			}
			// There is one caveat with the binary expressions. Access to a structure member is also considered a binary expression
			// Therefore we have to be careful how we analyze that. For instance let the structure be declared as struct point {float x, float y};
			// and let there be an instance struct point x. Now x.x refers to the x memeber of the structure referenced by the x variable
			// If we handle that just like any other expression, we might end up adding iterator variable to both x-es. In other words,
			// if x is a stream, x.x should be converted to x[brcc_iterator].x. Naive approach would convert it into x[brcc_iterator].x[brcc_iterator]
			// That is why we distinguish between these two cases here
// __FIX_ME - we will assume that there are no complex cases, such as structures that contain streams, because such a structure is probably not supported by the original BRCC either
//            If thius assumption is not correct, this code will have to change
			if (((BinaryExpr *)(expr))->bOp == BO_Member) {
				// This means we have a struct reference, so we just print the operator and the right side
				printBinOp (out, ((BinaryExpr *)(expr))->bOp);
#ifdef DEBUG
			out << "\n   printing binop\n " << expr->etype << ";     ";
#endif
				(((BinaryExpr *)(expr))->_rightExpr)->print(out);
			} else {
				out << " ";
				printBinOp (out, ((BinaryExpr *)(expr))->bOp);
				// Relational operations have to be handled separately
				if (((BinaryExpr *)(expr))->bOp == BO_Rel) {
					printRelOp (out, ((RelExpr *)(expr))->rOp);
				} else if (((BinaryExpr *)(expr))->bOp == BO_Assign) {
					// Assign operators have to be handled separately as well
					printAssignOp (out, ((AssignExpr *)expr)->op());
				}
				out << "(";
				right_var_before_dot = analyzeExpression (((BinaryExpr *)(expr))->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			}
			if (((BinaryExpr *)(expr))->bOp != BO_Member) {
				out << ")";
			}
			break;
		}
		case ET_TrinaryExpr:
			out << "(";
			right_var_before_dot = analyzeExpression (((TrinaryExpr *)(expr))->_condExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			out << ")";
			out << " ? ";
			out << "(";
			right_var_before_dot = analyzeExpression (((TrinaryExpr *)(expr))->_trueExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			out << ")";
			out << " : ";
			out << "(";
			right_var_before_dot = analyzeExpression (((TrinaryExpr *)(expr))->_falseExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			out << ")";
			break;
		case ET_ConstructorExpr:
		{
			ConstructorExpr * constructor = (ConstructorExpr *) expr;
			constructor->bType()->printBase(out, 0);
		    out << "(";
			if (constructor->_nExprs > 0) {
				for (unsigned int i=0; i<constructor->_nExprs; i++) {
					if (i>0) {
						out << ", ";
					}
					right_var_before_dot = analyzeExpression (constructor->exprN(i), ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
				}
			}
		    out << ")";
			break;
		}
		case ET_FunctionCall:
		{
			FunctionCall *fcall = (FunctionCall *) expr;
			handleFunctionCall (fcall, ste, funcdecs, declstreams, main_function, out);
			break;
		}
		case ET_IndexExpr:
		{
			IndexExpr *index = (IndexExpr *) expr;
			if (index->array->precedence() < index->precedence())
			{
				out << "(";
			}
			right_var_before_dot = analyzeExpression (index->array, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			if (index->array->precedence() < index->precedence())
			{
				out << ")";
			}
			out << "[(";
			right_var_before_dot = analyzeExpression (index->_subscript, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			out << ")]";
			break;
		}
		case ET_CastExpr:
		{
#ifdef DEBUG
			out << "\nCast expression\n";
#endif
			CastExpr *cast = (CastExpr *) expr;
			out << "(";
			cast->castTo->printType(out,NULL,true,0); 
			out << ") ";
			out << "(";
#ifdef DEBUG
			out << "\nCalling analyzeExpression on the target of the cast\n";
#endif
			right_var_before_dot = analyzeExpression (cast->expr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
#ifdef DEBUG
			out << "\nBack from analyzeExpression. Done with cast expression except for the final closing parenthesis\n";
#endif
			out << ")";
			break;
		}
		case ET_BrtIndexofExpr:
		{
			switch (reduce_index) {
			case 0:
				if (version == 1)
					out << "brcc_iterator_0";
				else
					out << "brcc_iterator_0)][(brcc_iterator_1";
				break;
			case 1:
					out << "brcc_iterator";
				break;
			case 2:
					out << "brcc_iterator_0";
				break;
			case 3:
			case 4:
			case 5:
					out << "brcc_iterator_0)][(brcc_iterator_1";
				break;
			}
			break;
		}
		case ET_UnaryExpr:
		{
			UnaryExpr *unary = (UnaryExpr *)expr;
			printUnaryOp (out, unary->uOp);
			right_var_before_dot = analyzeExpression (unary->_operand, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, version, main_function, replication_factor, replica_number, out);
			break;
		}
		default:
#ifdef DEBUG
			out << "   default type: " << expr->etype << ";     ";
#endif
			expr->print(out);
			break;
	}
	return right_var_before_dot;
}


/*
* fatal --
* FP
*	Terminates the program with an error message
*		
*
*/

void fatal (string msg) {

	cerr << endl << msg <<endl;
	exit (-1);
}

/*
* countSubExprs --
* FP
*	Recursively counts the number of subexpressions separated by commas inside a "binary" expression
*		For instance expression "3 + 4, 5 * 6" will return 2, because there
*		are 2 subexpressions separated by a comma
*		This function is used to count the number of components in a
*		stream declaration. For instance if stream declaration is <10, 20, 30>
*		then it has 3 dimensions of appropriate sizes
*		The function also adds each subexpression to the vector of expressions
*		subexpression is added once the individual expression is reached recursively
*/
unsigned int countSubExprs (BinaryExpr *expr, ExprVector &size_exprs, stringstream &out) {
	
	int subexprs = 0;

	// If the binary operand is not a comma, then it is just an expression,
	// so the number of subexpressions is 1
	if (expr->bOp != BO_Comma) {
		size_exprs.push_back (expr);
		return 1;
#ifdef DEBUG
		expr->print (out);
		out << " -- It's not a comma" << endl;
#endif
	}
	// Travese the expression recursively
	if ((expr->_leftExpr->etype == ET_BinaryExpr)) {
#ifdef DEBUG
		expr->print (out);
		out << " -- Left subexpression is binary" << endl;
#endif
		subexprs += countSubExprs ((BinaryExpr *) expr->_leftExpr, size_exprs, out);
	} else {
		// If the left subexpression is not binary, then it is only a single operand
		size_exprs.push_back (expr->_leftExpr);
		subexprs++;
#ifdef DEBUG
		expr->print (out);
		out << " -- Left subexpression is NOT binary" << endl;
#endif
	}
	if (expr->_rightExpr->etype == ET_BinaryExpr) {
#ifdef DEBUG
		expr->print (out);
		out << " -- Right subexpression is binary" << endl;
#endif
		subexprs += countSubExprs ((BinaryExpr *) expr->_rightExpr, size_exprs, out);
	} else {
		// If the right subexpression is not binary, then it is only a single operand
		size_exprs.push_back (expr->_rightExpr);
		subexprs++;
#ifdef DEBUG
		expr->print (out);
		out << " -- Right subexpression is NOT binary" << endl;
#endif
	}
#ifdef DEBUG
		expr->print (out);
		out << " -- Returning " << subexprs << endl;
#endif
	return subexprs;
};


/*
* countKernelArgs --
* FP
*	Counts the number of in stream, out stream and other (non-stream) arguments to the function
*		Takes a pointer to the function as an input, and returns values by reference
*
*/

void countKernelArgs (FunctionType *ft, unsigned int &ins, unsigned int &outs, unsigned int &reduces, unsigned int &nonstreams, unsigned int &vouts) {

	ins = outs = reduces = nonstreams = vouts = 0;
	for (unsigned int i=0; i<ft->nArgs; i++) {
		if (ft->args[i]->form->isStream()) {
			if ((ft->args[i]->form->getQualifiers() & TQ_Out).tq != 0)
				// It's an out stream
				outs++;
			else if ((ft->args[i]->form->getQualifiers() & TQ_Reduce).tq != 0)
				// It's a reduce stream
				reduces++;
			else if ((ft->args[i]->form->getQualifiers() & TQ_Vout).tq != 0)
				vouts++;
			else
				// Otherwise it has to be an input stream
				ins++;
		} else {
			// If it's not a stream, then it's a regular parameter
			nonstreams++;
		}
	}
}

/*
* findRightStream --
* FP
*	Looks for the first non-reduce stream in an expression and returns a pointer to the stream variable 
*		Takes a pointer to the expression as an input, as well as arrays of pointers to declarations
*		of all in, out, and reduce streams, along with their sizes
*
*/

Variable * findRightStream (Expression *expr, Decl **streamins, unsigned int ins, Decl **streamouts, unsigned int outs, Decl **streamreduces, unsigned int reduces) {

// __FIX_ME - Not all expression types have been handled

	Variable *return_var;
	switch (expr->etype) {
		case ET_Variable:
			{
			int outstream_pos = findVarInArray (streamouts, outs, ((Variable *)(expr))->name);
			int instream_pos = findVarInArray (streamins, ins, ((Variable *)(expr))->name);
			// If it's not an in stream or an out stream, return NULL
			if ((outstream_pos == -1) && (instream_pos == -1)) {
				return NULL;
			}
			// Otherwise it's a stream variable, and we can return a pointer to it
			return ((Variable *)(expr));
			break;
			}
		case ET_BinaryExpr:
			if ((return_var = findRightStream (((BinaryExpr *)(expr))->_leftExpr, streamins, ins, streamouts, outs, streamreduces, reduces)) == NULL) {
				 return findRightStream (((BinaryExpr *)(expr))->_rightExpr, streamins, ins, streamouts, outs, streamreduces, reduces);
			} else {
				return return_var;
			}
			break;
		case ET_TrinaryExpr:
			// We will not look at condition expression (i.e. in expressions of type (x ? y:z) we will not consider x)
			if ((return_var = findRightStream (((TrinaryExpr *)(expr))->_trueExpr, streamins, ins, streamouts, outs, streamreduces, reduces)) == NULL) {
				return findRightStream (((TrinaryExpr *)(expr))->_falseExpr, streamins, ins, streamouts, outs, streamreduces, reduces);
			} else {
				return return_var;
			}
			break;
		case ET_Constant:
			return NULL;
			break;
		case ET_ConstructorExpr:
		{
			ConstructorExpr * constructor = (ConstructorExpr *) expr;
			if (constructor->_nExprs > 0) {
				for (unsigned int i=0; i<constructor->_nExprs; i++) {
					if ((return_var = findRightStream (constructor->exprN(i), streamins, ins, streamouts, outs, streamreduces, reduces)) != NULL)
						return return_var;
				}
			}
			// If wecome here, that means none of the expressions in the constructor were streams, so we return NULL
			return NULL;
			break;
		}
		default:
			cerr << endl << "Expression type " << expr->etype;
			fatal ("In findRightStream: Expression type that has not been handled");
			return NULL; // pointless, but otherwise compiler complains that 'not all control paths return a value'
			break;
	}
	

}


/*
* printInitializations --
* FP
*	Prints variable initializations in the following manner
*		If the variable is an ordinary variable, simply prints out the variable, assignment operator (=) and initializer
*		If the variable is an array, produces a for loop and initializes each element from an initializer list
*		This is done to avoid problems with C2H allocating space for local variables on global stack
*		
*	Inputs:
*		A pointer to the declaration statement, and reference to the vector with pointers to known streams
*		Input variables 'constants' and 'nonconstants' are used to specify whether only variables declared as const should be printed,
*		only variables that are NOT declared as const, or both.
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/

void printInitializations (vector <Decl *> &declarations_with_initializations, bool constants, bool nonconstants, stringstream &out) {

#ifdef DEBUG
	out << "\nInside printInitializations\n";
#endif
	out << "\n";
	vector<Decl *>::iterator iter=declarations_with_initializations.begin();
	for (unsigned int i=0; iter!=declarations_with_initializations.end(); iter++, i++) {
		if (((*iter)->isConst() && constants) || (!(*iter)->isConst() && nonconstants)){
			if (!(*iter)->isArray()) {
				// If it is not an array, we can simply print it out
				out << "\n";
				(*iter)->print(out, false);
				out << ";";
			} else {
				// If it is an array, we have to initialize elements one at a time
				if (((*iter)->initializer->etype == ET_Constant) && (((Constant *)((*iter)->initializer))->ctype == CT_Array)) {
					// Initialize it one element at a time
					ArrayConstant *const_initalizer = ((ArrayConstant *)((*iter)->initializer));
					unsigned int index = 0;
					if (!const_initalizer->items.empty()) {
						ExprVector::const_iterator j = const_initalizer->items.begin();
						for (index=0; j != const_initalizer->items.end(); index++, j++)
						{
							// Find out if the array initializer is 1-D or 2-D
							if (((*j)->etype == ET_Constant) && (((Constant *)(*j))->ctype == CT_Array)) {
								// This means the initializer is 2-D (because the individual initializer element is an array initializer)
								ArrayConstant *const_initalizer_2 = (ArrayConstant *)(*j);
								ExprVector::const_iterator k = const_initalizer_2->items.begin();
								unsigned int index_2 = 0;
								for (index_2=0; k != const_initalizer_2->items.end(); index_2++, k++) {
									out << "\t" << (*iter)->name->name << "[" << index << "][" << index_2 << "] = ";
									(*k)->print(out);
									out << ";\n";
								}
							} else {
								// Otherwise it is 1-D
								out << "\t" << (*iter)->name->name << "[" << index << "] = ";
								(*j)->print(out);
								out << ";\n";
							}
						}
					}

				} else {
					fatal ("Inside printInitializations. Unhandled case: Array initializer is not a constant array initalizer");
				}
			}
		}
    }
	out << "\n";
#ifdef DEBUG
	out << "\nprintInitializations DONE\n";
#endif

}


/*
* handleKernelArgs --
* FP
*	Takes care of various types of kernel arguments and adds the pointers to them to arrays, so that they can be fetched later
*		Takes a pointer to the function declaration as an input, and arrays into which the pointers should be placed, as well as sizes of those arrays
*		The arrays are passed as references to double pointers (althought this is a bit awkward, only other option is a triple pointer,
*		because the original brcc was written using pointers)
*		Parameter reduce_index tells whether the kernel is a reduction kernel (==0 not reduction kernel, !=0 reduction kernel)
*		If it is a reduction kernel, the reduce_index parameter tells which version of declaration should be generated
*		reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
*		reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
*		reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
*		reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
*		reduce_index==5. in streams should be 2-D, and reduce stream should be 2-D with possibly unrelated row and col sizes
*			- eventually 5 should replace 2 and 3
*		Parameter version defines for non-reduce kernels which version of the kernel should be generated
*		version==1 - generate a version with all streams one-dimensional 
*		version==2 - generate a version with all streams two-dimensional, with possibly unrelated dimensions
*		Final argument is the output stream (file) reference, to be able to print the function declaration to an outfile
*/

void handleKernelArgs (Decl* declaration, Decl** &streamins, Decl** &streamouts, Decl** &streamreduces, Decl** &nonstreamargs, Decl** &streamvouts, unsigned int ins, unsigned int outs, unsigned int reduces, unsigned int nonstreams, unsigned int vouts, StreamInfoVector &declstreams, Location &statement_location, unsigned int reduce_index, unsigned int version, int replica_number, stringstream &out) {
#ifdef DEBUG
	out << "\nInside handlekernelArgs\n";
	out << "\nreplica_number is: " << replica_number;
#endif
	ins = outs = reduces = nonstreams = vouts = 0;
	bool first_print = true;
	// First make sure that we either generate reduce kernel, or non-reduce kernel
	assert (!((reduce_index!=0) && (version!=1)));

	FunctionType *ft = (FunctionType *) declaration->form;
	// Record the number of arguments as this number will change as we add new arguments
	unsigned int arguments = ft->nArgs;
	bool need_comma = false;
	for (unsigned int i=0; i<arguments; i++) {
		if (ft->args[i]->form->isStream()) {
			// This vector will hold all expressions for the sizes of individual dimensions of the stream
			ExprVector size_exprs;
			ArrayType *temp_array = (ArrayType *) ft->args[i]->form;
			// We want to convert the stream to an array
			temp_array->type = TT_Array;
			// We only add size arguments for non-reduce arguments, and only if (reduce_index == 0)
			// The second last condition exists because for reduce_index 1, 2, 3, 4, and 5 we use template arguments to forward stream size
			// The last argument means that we do not add size for vout arguments, which by definition have a variable size
// EXPLANATION - Templates referred to in these comments refer to C++ templates
//            This concept originated from the first version of this source-to-source compiler,
//            when the plan was to target a multiprocessor machine. In that initial version
//            this compiler produced C++ code and used templates to pass stream sizes.
//            This is no longer the case in the generated code, but the concept still occurs in
//            intermediate steps.
			if (((ft->args[i]->form->getQualifiers() & TQ_Reduce).tq == 0) && (reduce_index == 0) && !((ft->args[i]->form->getQualifiers() & TQ_Vout).tq != 0)) {
				for (unsigned int j=0; j<version; j++) {
					// For each stream dimension we add an argument to the argument list, which will convey
					// the size for each stream dimension
					ostringstream temp_string;
					// The name of this argument will be <stream_name>_brcc_stream_size_0, brcc_stream_size_1, ...
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_" << j;
					Symbol *stream_size_sym = new Symbol ();
					stream_size_sym->name = temp_string.str();	
					// we will forward their size as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					need_comma = true;
					// We want to convert the stream to an array
					temp_array->type = TT_Array;
					// Also add the same variable to the size_expr expression vector, which will then be stored in the
					// appropriate StreamInfo object
// __FIX_ME - Memory leak!!!
//			  Nobody ever frees this memory, and it is hard to free it because of how
//			  vectors work. Later, a pointer to this Variable will be put into a StreamInfo object
//			  If I try to free memory in a destructor for StreamInfo, vector of StreamInfo objects
//			  deletes some objects when new elements are added, and it destroys the information
//			  That's why it's a bad idea to store pointers to objects in C++, but I don't have
//			  much choice, since the original implementation uses pointers all over the place
//			  Although, in this case I might be able to get away by using a vector of actual expressions
//			  (instead of pointers), and dup the ones that are part of the original framework
					size_exprs.push_back (new Variable (stream_size_sym, statement_location));
				}
			}
			if ((ft->args[i]->form->getQualifiers() & TQ_Out).tq != 0) {
				// It's an out stream
				streamouts [outs] = ft->args[i];
				Type *dummy_type;
				// Even single dimensional declarations have a subType, which is used to store
				// the base type (e.g. int, float, etc. of the variable). Multidimensional
				// declarations have this subtype at the end of the pointer chain.
				// We retain that pointer at the end of the pointer chain.
				dummy_type = temp_array->subType;
#ifdef DEBUG
				out << "\nSubtype " << temp_array->subType->type << "\n";
#endif
				// Add it to the list of declared streams
				StreamInfo *temp_info = new StreamInfo(streamouts[outs], version, true);
				ExprVector::iterator iter=size_exprs.begin();
				for (unsigned int j=0; iter!=size_exprs.end(); iter++, j++) {
					if (!(temp_info->setSize (j, *iter))) {
						out << endl << "j= " << j;
						fatal ("Failed to set size at the above dimension");
					}
					// If we need more than one dimension, we have to add the appropriate sizes in declaration
					if (version > 1) {
						temp_array->size = (*iter)->dup0();
						if ((iter+1)!=size_exprs.end()) {
							temp_array->subType = new ArrayType (TT_Array, NULL);
							temp_array = (ArrayType*) temp_array->subType;
						} else {
							// Finally return the last subtype with unknown purpose (see long comment above)
							temp_array->subType = dummy_type;
						}
					}
				}
				declstreams.push_back (*temp_info);
// __FIXME: For now we will put qualifier to none to remove the out keyword.
//			It seems that it's not possible for out stream to have any other
//			qualifier, but if that changes, this will have to change as well			
				streamouts [outs]->form->getBase()->qualifier = TQ_None;
#ifdef DEBUG
				out << endl << endl << endl;
				out << "Output streams: ";
				out <<  streamouts[outs]->name->name << " ";
				out << "has " << version << " dimensions ";
#endif
				outs++;
			} else if ((ft->args[i]->form->getQualifiers() & TQ_Reduce).tq != 0) {
				streamreduces [reduces] = ft->args[i];
// __FIXME: For now we will put qualifier to none to remove the reduce keyword.
//			It seems that it's not possible for reduce stream to have any other
//			qualifier, but if that changes, this will have to change as well			
				streamreduces [reduces]->form->getBase()->qualifier = TQ_None;				
				// If it's a reduce stream, what we do with it depends on the reduce_index
				// which defines which version of the reduction kernel we're currently handling
				//	reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
				//  reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
				//  reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
				//  reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
				//	reduce_index==5. in streams should be 2-D, and reduce stream should be 2-D with possibly unrelated row and col sizes
				//		- eventually 5 should replace 2 and 3
				switch (reduce_index) {
				case 1:
				case 4:
				{
					// The stream should be turned into a pointer to scalar (base type)
					// At this point it is not enough to change temp_array->type,
					// because the ft->args[i]->form is an object of ArrayType,
					// so its print function gets called.
					// Therefore we delete existing ft->args[i]->form object,
					// and create a new one
					BaseType *temp_type = ft->args[i]->form->getBase ();
					// Actually, we don't delete it, because any object of class Type is added to the global type list
					// in its constructor (see decl.cpp line 102: Type::Type), which are then deleted
					// in the destructor for the project (see project.cpp line 405: Project::~Project)
					// delete ft->args[i]->form;
					// Make it a pointer
					PtrType *pointer = new PtrType();
					pointer->extend(temp_type);
					ft->args[i]->form = pointer;
					// Although it's effectively a scalar, we add it to the list of declared streams
					// This will be a special stream with only 1 dimension which will have size = 0
					// StreamInfo class also contains a parameter that determines whether a stream is a reduce stream
					StreamInfo *temp_info = new StreamInfo(streamreduces[reduces], 1, false, true);
					temp_info->setSize (1, new IntConstant (0, statement_location));
					declstreams.push_back (*temp_info);
					break;
				}
				case 2:
				{
					// The stream should be turned into 1-D array
					temp_array->type = TT_Array;
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					StreamInfo *temp_info = new StreamInfo(streamreduces[reduces], 1, false, true);
					temp_info->setSize (0, temp_array->size);
					declstreams.push_back (*temp_info);
					// Forward the stream's size as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					break;
				}
				case 3:
				{
					// The stream should be turned into 2-D array with ROWS rows and 1 cols
					temp_array->type = TT_Array;
					// In this dimension the size is ROWS
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					// Forward the stream's size as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					Type *dummy_type;
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
					temp_array->subType = new ArrayType (TT_Array, NULL);
					// In this dimension the size is 1
					((ArrayType *)(temp_array->subType))->size = new IntConstant (1, statement_location);
					// Finally return the last subtype  with unknown purpose (see long comment above)
					((ArrayType *)(temp_array->subType))->subType = dummy_type;
					// Add it to the list of declared streams
					StreamInfo *temp_info = new StreamInfo(streamreduces[reduces], 2, false, true);
					temp_info->setSize (0, temp_array->size);
					temp_info->setSize (1, ((ArrayType *)(temp_array->subType))->size);
					declstreams.push_back (*temp_info);
					break;
				}
				case 5:
				{
					// The stream should be turned into 2-D array 
					temp_array->type = TT_Array;
					// In this dimension the size is ROWS
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					// Forward the stream's size (first dimension) as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					Type *dummy_type;
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
					temp_array->subType = new ArrayType (TT_Array, NULL);
					stream_size_sym = new Symbol ();
					temp_string.str("");
					// The name of this size will be <variablename>_brcc_stream_size_1
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_1";
					stream_size_sym->name = temp_string.str();	
					((ArrayType *)(temp_array->subType))->size = new Variable (stream_size_sym, statement_location);
					// Finally return the last subtype  with unknown purpose (see long comment above)
					((ArrayType *)(temp_array->subType))->subType = dummy_type;
					// Add it to the list of declared streams
					StreamInfo *temp_info = new StreamInfo(streamreduces[reduces], 2, false, true);
					temp_info->setSize (0, temp_array->size);
					temp_info->setSize (1, ((ArrayType *)(temp_array->subType))->size);
					declstreams.push_back (*temp_info);
					// Forward the stream's size (Second dimension) as an additional argument
					Decl *new_decl2 = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type2 = new BaseType (BT_Int);
					new_decl2->extend (new_type2);
					ft->addArg (new_decl2);
					break;
				}
				default:
				{
					fatal ("reduce_index is not one of 1, 2, 3, 4, or 5 and reduce stream was encountered");
					break;
				}
				}
				reduces++;
			} else if ((ft->args[i]->form->getQualifiers() & TQ_Vout).tq != 0) {
				// It's a vout argument
#ifdef DEBUG
				out << "\nVOUT kernel argument\n";
#endif
				streamvouts [vouts] = ft->args[i];
				Type *dummy_type;
				// Even single dimensional declarations have a subType, which is used to store
				// the base type (e.g. int, float, etc. of the variable). Multidimensional
				// declarations have this subtype at the end of the pointer chain.
				// We retain that pointer at the end of the pointer chain.
				dummy_type = temp_array->subType;
				// Add it to the list of declared streams
				StreamInfo *temp_info = new StreamInfo(streamvouts[vouts], version, true);
				ExprVector::iterator iter=size_exprs.begin();
				for (unsigned int j=0; iter!=size_exprs.end(); iter++, j++) {
					if (!(temp_info->setSize (j, *iter))) {
						out << endl << "j= " << j;
						fatal ("Failed to set size at the above dimension");
					}
					// If we need more than one dimension, we have to add the appropriate sizes in declaration
					if (version > 1) {
						temp_array->size = (*iter)->dup0();
						if ((iter+1)!=size_exprs.end()) {
							temp_array->subType = new ArrayType (TT_Array, NULL);
							temp_array = (ArrayType*) temp_array->subType;
						} else {
							// Finally return the last subtype with unknown purpose (see long comment above)
							temp_array->subType = dummy_type;
						}
					}
				}
				declstreams.push_back (*temp_info);

#ifdef DEBUG
				out << endl << endl << endl;
				out << "vout streams: ";
				out <<  streamvouts[vouts]->name->name;
#endif


				vouts++;
			} else {
				// Otherwise it has to be an input stream
				streamins [ins] = ft->args[i];
				StreamInfo *temp_info;
				// If it's a reduce stream, what we do with it depends on the reduce_index
				// which defines which version of the reduction kernel we're currently handling
				//	reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
				//  reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
				//  reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
				//  reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
				//	reduce_index==5. in streams should be 2-D, and reduce stream should be 2-D with possibly unrelated row and col sizes
				//		- eventually 5 should replace 2 and 3
				switch (reduce_index) {
				// If this is not a reduction kernel, simply add the current stream to the list of declared streams
				case 0:
				{
					Type *dummy_type;
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
					temp_info = new StreamInfo(streamins[ins], version, false);
					ExprVector::iterator iter=size_exprs.begin();
					for (unsigned int j=0; iter!=size_exprs.end(); iter++, j++) {
						if (!(temp_info->setSize (j, *iter))) {
							out << endl << "j= " << j;
							fatal ("Failed to set size at the above dimension");
						}
						// If we need more than one dimension, we have to add the appropriate sizes in declaration
						if (version > 1) {
							temp_array->size = (*iter)->dup0();
							if ((iter+1)!=size_exprs.end()) {
								temp_array->subType = new ArrayType (TT_Array, NULL);
								temp_array = (ArrayType *) temp_array->subType;
							} else {
								// Finally return the last subtype with unknown purpose (see long comment above)
								temp_array->subType = dummy_type;
							}
						}
					}
					// Add it to the list of declared streams
					declstreams.push_back (*temp_info);
					break;
				}
				case 1:
				case 2:
				{
					// The stream only has 1 dimension
					temp_info = new StreamInfo(streamins[ins], 1, false);
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					if (!(temp_info->setSize (0, temp_array->size))) {
						fatal ("Failed to set size at dimension 0");
					}
					// Add it to the list of declared streams
					declstreams.push_back (*temp_info);
					// Forward the stream's size as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					break;
				}
				case 3:
				case 4:
				{
					// The stream has exactly 2 dimensions
					temp_info = new StreamInfo(streamins[ins], 2, false);
					// The stream should be turned into 2-D array with ROWS rows and COLS cols
					temp_array->type = TT_Array;
					// In this dimension the size is ROWS
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					// Forward the stream's size (first dimension) as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					Type *dummy_type;
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
					temp_array->subType = new ArrayType (TT_Array, NULL);
					stream_size_sym = new Symbol ();
					temp_string.str("");
					// The name of this size will be <variablename>_brcc_stream_size_1
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_1";
					stream_size_sym->name = temp_string.str();	
					((ArrayType *)(temp_array->subType))->size = new Variable (stream_size_sym, statement_location);
					// Finally return the last subtype  with unknown purpose (see long comment above)
					((ArrayType *)(temp_array->subType))->subType = dummy_type;
					// Add it to the list of declared streams
					temp_info->setSize (0, temp_array->size);
					temp_info->setSize (1, ((ArrayType *)(temp_array->subType))->size);
					// Add it to the list of declared streams
					declstreams.push_back (*temp_info);
					// Forward the stream's size (Second dimension) as an additional argument
					Decl *new_decl2 = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type2 = new BaseType (BT_Int);
					new_decl2->extend (new_type2);
					ft->addArg (new_decl2);
					break;
				}
				case 5:
				{
					// The stream has exactly 2 dimensions
					temp_info = new StreamInfo(streamins[ins], 2, false);
					// The stream should be turned into 2-D array with ROWS rows and COLS cols
					temp_array->type = TT_Array;
					// In this dimension the size is ROWS
					Symbol *stream_size_sym = new Symbol ();
					ostringstream temp_string;
					// The name of this size will be <variablename>_brcc_stream_size_0
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_0";
					stream_size_sym->name = temp_string.str();	
					temp_array->size = new Variable (stream_size_sym, statement_location);
					// Forward the stream's size (first dimension) as an additional argument
					Decl *new_decl = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type = new BaseType (BT_Int);
					new_decl->extend (new_type);
					ft->addArg (new_decl);
					Type *dummy_type;
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
					temp_array->subType = new ArrayType (TT_Array, NULL);
					stream_size_sym = new Symbol ();
					temp_string.str("");
					// The name of this size will be <variablename>_brcc_stream_size_1
					temp_string << ft->args[i]->name->name << "_brcc_stream_size_1";
					stream_size_sym->name = temp_string.str();	
					((ArrayType *)(temp_array->subType))->size = new Variable (stream_size_sym, statement_location);
					// Finally return the last subtype  with unknown purpose (see long comment above)
					((ArrayType *)(temp_array->subType))->subType = dummy_type;
					// Add it to the list of declared streams
					temp_info->setSize (0, temp_array->size);
					temp_info->setSize (1, ((ArrayType *)(temp_array->subType))->size);
					// Add it to the list of declared streams
					declstreams.push_back (*temp_info);
					// Forward the stream's size (Second dimension) as an additional argument
					Decl *new_decl2 = new Decl(stream_size_sym);
					// The type of this argument will be integer
					BaseType *new_type2 = new BaseType (BT_Int);
					new_decl2->extend (new_type2);
					ft->addArg (new_decl2);
					break;
				}
				default:
				{
					fatal ("reduce_index is not one of 0, 1, 2, 3, 4, or 5");
					break;
				}
				}
#ifdef DEBUG
				out << endl << endl << endl;
				out << "Input stream: ";
				out <<  streamins[ins]->name->name << " ";
				out << "has " << version << " dimensions ";
#endif
				ins++;
			}
//			ft->args[i]->clear();
		} else {
			// If it's not a stream, then it's a regular parameter
			nonstreamargs [nonstreams++] = ft->args[i];
		}
	}
	out << endl;
#ifdef DEBUG
	out << "Printing declaration!!!" << endl;
#endif
	// We also turn in, out, and vout streams back into streams
	// This is an old code. We used to convert streams into arrays, so we now return them back into streams
	// In the current version we want to convert streams into pointers that are declared as local variables.
// __FIX_ME - This double work should be eliminated eventually.
	for (unsigned int i=0; i<ins; i++) {
		((ArrayType *)(streamins[i]->form))->type = TT_Stream;
	}
	for (unsigned int i=0; i<outs; i++) {
		((ArrayType *)(streamouts[i]->form))->type = TT_Stream;
	}
	// vout actually looks like an array, but we want it to be a stream
	for (unsigned int i=0; i<vouts; i++) {
		((ArrayType *)(streamvouts[i]->form))->type = TT_Stream;
	}

	// We will convert all reduce types back into streams, so that they don't get printed as parameters
	// After we're done with printing, we'll turn them back into whatever they were initially.
	TypeType *reduce_types = new TypeType[reduces];
	if (reduce_types == NULL)
		fatal ("Not enough memory while allocating reduce_types in handleKernelArgs");
	for (unsigned int i=0; i<reduces; i++) {
		// There is one exception, and that is when reduce parameter is scalar
		// This only happens for reduce_index 1 and 4
		if ((reduce_index != 1) && (reduce_index != 4)) {
			reduce_types[i] = ((ArrayType *)(streamreduces[i]->form))->type;
			((ArrayType *)(streamreduces[i]->form))->type = TT_Stream;
		}
	}
	
	// Before printing the declaration, we will temporarily change its name as follows:
	// If reduce_index == 0:
	//   If version = 0 - <name>_1d - 1-D version of the kernel
	//   If version = 1 - <name>_2d - 2-D version of the kernel
	// If reduce_index == 1: <name>_1ds - 1-D into scalar reduction
	// If reduce_index == 2: <name>_1d1d - 1-D into 1-D reduction
	// If reduce_index == 3: <name>_2d1c - 2-D into 1-column reduction
	// If reduce_index == 4: <name>_2ds - 2-D into scalar reduction
	// If reduce_index == 5: <name>_2d2d - 2-D into 2-D reduction
	// We will only do this for kernel functions though.

	if (ft->isKernel()) {
		string preserved_func_name = declaration->name->name;
		switch (reduce_index) {
		case 0:
			if (version == 1)
				declaration->name->name += "_1d";
			else
				declaration->name->name += "_2d";
			break;
		case 1:
			declaration->name->name += "_1ds";
			break;
		case 2:
			declaration->name->name += "_1d1d";
			break;
		case 3:
			declaration->name->name += "_2d1c";
			break;
		case 4:
			declaration->name->name += "_2ds";
			break;
		case 5:
			declaration->name->name += "_2d2d";
			break;
		default:
			fatal ("reduce_index is outside 0-5 (inclusive) range");
			break;
		}
		if (replica_number != -1) {
			declaration->name->name += BRCC_REPLICA_SUFFIX;
			ostringstream tempss;
			tempss << replica_number;
			declaration->name->name += tempss.str();
		}
		declaration->print_no_streams (out, true);

		// After printing the declaration, return the function's name into its original,
		// so that it will be recognized when called
		declaration->name->name = preserved_func_name;
	} else {
		declaration->print_no_streams (out, true);
	}
	// After the declaration was printed, we put back the "out" qualifier for out streams,
	// so that we can later know which arguments were "out" streams
	for (unsigned int i=0; i<outs; i++) {
		streamouts [i]->form->getBase()->qualifier = TQ_Out;
	}

	// Turn reduce streams back into whatever they were initially.
	for (unsigned int i=0; i<reduces; i++) {
		((ArrayType *)(streamreduces[i]->form))->type = reduce_types[i];
	}
	delete [] reduce_types;
}


/*
* handleAssignment --
* FP
*	Takes care of assignments inside kernels
*		If an assignment expression has a stream on the left side, all the stream references are converted into array references
*		Such an assignment is also enclosed inside a for loop to make sure that computation for all the stream elements occurs
*	Inputs:
*		A pointer to the binary expression, a pointer to the statement that contains the expression,
*		arrays containing pointers to in, out and reduce streams, as well as sizes of those arrays
*		Parameter reduce_index tells whether the block is a reduction kernel (==0 not reduction kernel, !=0 reduction kernel)
*		If it is a reduction kernel, the reduce_index parameter tells which version of the reduction kernel should be generated
*		reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
*		reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
*		reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
*		reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/

void handleAssignment (BinaryExpr *binexp, Statement *ste, string function_name, DeclVector &funcdecs, vector <PPDirective *> &ppdirectives, StreamInfoVector &declstreams, Decl **streamouts, unsigned int outs, Decl **streamins, var_info *in_info, unsigned int ins, Decl **streamreduces, unsigned int reduces, Decl** streamvouts, unsigned int vouts, unsigned int reduce_index, unsigned int version, unsigned int statement_count, bool main_function, vector <Decl *> &declarations_with_initializations, int replication_factor, int replica_number, stringstream &out) {

	string left_var_before_dot;
	string left_var_after_dot;
	string::size_type dot_pos;
	Variable *left_var;
	// If the variable on the left side of the assignment (assignments are just binary expressions)
	// appears in the list of the out variables, then this expression should be enclosed inside a for loop,
	// and accesses to stream types should be modified appropriately
	// Assignments to structure elements don't have a "Variable" on the left side, but rather another binary expression
	// (i.e my_struct.elem is a binary expression with "my_struct" on the left side, "." as an operator, and elem on the right side)
	if ((binexp->_leftExpr->etype == ET_Variable) || ((binexp->_leftExpr->etype == ET_BinaryExpr))) {
		if (binexp->_leftExpr->etype == ET_BinaryExpr) {
			// We will only use the part of the variable on the left to identify the variable, because the part on the right
			// is an element inside the structure.
			// The reason for this is that a structure declaration only has a variable name (individual elements are not declared
			// however, inside a program the elements of the structure can be accessed
			// It is possible that the expression on the left has chained struct reference, e.g. array.point.x
			// So we have to go all the way to the left looking for a Variable type
			Expression *temp_expr = binexp->_leftExpr;
			while (temp_expr->etype == ET_BinaryExpr) {
				// Add whatever is on the right side to the right side string
				ostringstream temp_stringstream;
				temp_stringstream << "." << *(((BinaryExpr *)temp_expr)->_rightExpr) << left_var_after_dot;
				left_var_after_dot = temp_stringstream.str();
				// Keep scanning the left expression
				temp_expr = ((BinaryExpr *)temp_expr)->_leftExpr;
			}
			// Once this loop ends, temp_expr should be a Variable
			if (temp_expr->etype == ET_Variable) {
				left_var = (Variable *)(temp_expr);
				left_var_before_dot = ((Variable *)(temp_expr))->name->name;
			} else {
				// This means the variable is not a structure reference so just copy the whole name
				// This is possible in a case of array[x].z - array[x] is not a variable
				// but then we don't care, because there is nothing we have to do about it, because it is surely not
				// a stream reference so we can simply print it out
#ifdef DEBUG
				out << endl << "Just an ordinary expression of etype " << binexp->_leftExpr->etype << ": ";
#endif
				binexp->print (out);
#ifdef DEBUG
				out << "         Leftmost reference in a structure is not a variable       "; 
#endif
				return;
			}
			// left_var_after_dot also includes the dot, so that we don't have to handle it specially
#ifdef DEBUG
			out << "         Left var before dot is:" << left_var_before_dot << ", while after dot is:" << left_var_after_dot << "       "; 
#endif
		} else {
			// This means the variable is not a structure reference so just copy the whole name
			left_var = (Variable *)(binexp->_leftExpr);
			left_var_before_dot = ((Variable *)(binexp->_leftExpr))->name->name;
			left_var_after_dot = "";
		}
		int outstream_pos = findVarInArray (streamouts, outs, left_var->name);
		// If the variable on the left side (the one being assigned to) is not an outstream
		if (outstream_pos == -1) {
			int reduce_pos = findVarInArray (streamreduces, reduces, left_var->name);
			// A hack to make the expression recognize a reduce stream forwarded as a reference
			// This is necessary since the original tool only supports C (not C++), so it doesn't have a simple
			// way to print out a C++ code (such as references)
			// The name was initially changed (& was added to it at the beginning) inside handleKernelArgs
// __FIX_ME - This section of the code is outdated when it comes to reference operator (&). FPGA Brook does not use reference operator any more.
			Symbol *dummy_sym = (left_var->name)->dup();
			dummy_sym->name = "&" + dummy_sym->name;
			int reduce_pos_ref = findVarInArray (streamreduces, reduces, dummy_sym);
			if ((reduce_pos == -1) && (reduce_pos_ref == -1)) {
				// In this case, the assignment is NOT to a reduce stream.
				// Test if the variable is a vout stream
				int vout_pos = findVarInArray (streamvouts, vouts, left_var->name);
				if (vout_pos != -1) {
					// Assignment is to a vout stream
					out << "\t__fp_temp_" << left_var_before_dot;
					out << left_var_after_dot;
					if (binexp->bOp == BO_Assign)
						printAssignOp (out, ((AssignExpr *)binexp)->op());
					else
						fatal ("Unexpected expression type!!! The expression is not BO_Assign");
					// Analyze the right expression
					// We need a recursive function for that
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					return;
				} else {
					// In this case assignment is to a regular variable, so there is no need for intervention on the left side 
					out << endl;
					binexp->_leftExpr->print(out);
					printBinOp (out, binexp->bOp);
					if (((BinaryExpr *)(binexp))->bOp == BO_Assign) {
						// Assign operators have to be handled separately
						printAssignOp (out, ((AssignExpr *)binexp)->op());
					}
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					return;
				} // end if
			} else {
				// In this case, the assignment is to a reduce stream. The code that should be generated depends on the
				// parameter reduce_index
				// reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
				// reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
				// reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
				// reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
				// reduce_index==5. in streams should be 2-D, and reduce stream should be 2-D with possibly unrelated row and col sizes
				//		- eventually 5 should replace 2 and 3
				switch (reduce_index) {
				case 1:
				{
					// reduce stream is scalar
					// Only print the following before the first assignment
					if (statement_count == 0) {
						out << endl << "int brcc_iterator;";
						// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
						// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
						// or they will be replaced with actual declarations
						out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_DECL_END << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
						// Print out constant initializations before the for loops
						printInitializations (declarations_with_initializations, true, false, out);
						out << "for (brcc_iterator=0; brcc_iterator < " << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator++) {" << endl;
						// Print out non-constant initializations after for loops
						printInitializations (declarations_with_initializations, false, true, out);
						// Print tags as a placeholder for operations on modulo and/or division counters
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
						for (unsigned int i=0; i<ins; i++) {
							// Copy input streams into local variables
							// This is because the code may refer to the input value several times,
							// but we can only read it from the FIFO once
							out << "\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
							in_info[i].used = true;
						}
					}
					out << endl << "\tif (brcc_iterator == 0) \n\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot << " = __fp_temp_" << streamins[0]->name->name << left_var_after_dot << ";" << endl;
					out << "\telse \n\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot << " ";
					if (binexp->bOp == BO_Assign)
						printAssignOp (out, ((AssignExpr *)binexp)->op());
					else
						fatal ("Unexpected expression type!!! The expression is not BO_Assign");
					// Analyze the right expression
					// We need a recursive function for that
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					break;
				}
				case 2:
				{
					// Only print the following before the first assignment
					if (statement_count == 0) {
						out << endl << "int brcc_iterator_0, brcc_mod_iterator=0;";
						// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
						// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
						// or they will be replaced with actual declarations
						out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_DECL_END << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
						// Print out constant initializations before for loops
						printInitializations (declarations_with_initializations, true, false, out);
						out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < " << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator_0++, brcc_mod_iterator++) {\n";
						out << "brcc_mod_iterator = ((brcc_mod_iterator == (" <<  streamins[0]->name->name << "_brcc_stream_size_0" << BRCC_DIV_OPERATOR_TO_REPLACE << streamreduces[0]->name->name << "_brcc_stream_size_0 )) ? 0 : brcc_mod_iterator);" << endl;
						// Print out non-constant initializations after for loops
						printInitializations (declarations_with_initializations, false, true, out);
						// Print tags as a placeholder for operations on modulo and/or division counters
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
						for (unsigned int i=0; i<ins; i++) {
							// Copy input streams into local variables
							// This is because the code may refer to the input value several times,
							// but we can only read it from the FIFO once
							out << "\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";		
							in_info[i].used = true;
						}
						// Copy the result to the output FIFO if the count indicates that one element of the output has been produced
						out << "\n\tif ((brcc_mod_iterator == 0) && (brcc_iterator_0 != 0))";
						for (unsigned int i=0; i<reduces; i++) {
							out << "\n\t\t*" << streamreduces[i]->name->name << " = __fp_temp_" << streamreduces[i]->name->name << ";\n";
						}
					}
					// This condition tests if the iterator has reached the number to start accumulating
					// a new reduction (e.g. if input stream has 10 elements and reduce stream has 2 elements, 5 elements of the input
					// should be reduced to produce one element of the output. Therefore, we start with a new reduction every time
					// 5 elements have been processed)
					out << "\t" << "if (brcc_mod_iterator == 0) \n\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot << " = __fp_temp_" <<  streamins[0]->name->name << left_var_after_dot << ";" << endl;
					out << "\telse\n\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot;
					if (binexp->bOp == BO_Assign)
						printAssignOp (out, ((AssignExpr *)binexp)->op());
					else
						fatal ("Unexpected expression type!!! The expression is not BO_Assign");
					// Analyze the right expression
					// We need a recursive function for that
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					break;
				}
				case 3:
				{
					// Only print the following before the first assignment
					if (statement_count == 0) {
						out << endl << "int brcc_iterator_0;";
						out << endl << "int brcc_iterator_1;";
						// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
						// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
						// or they will be replaced with actual declarations
						out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_DECL_END << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
						// Print out constant initializations before for loops
						printInitializations (declarations_with_initializations, true, false, out);
						out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < " << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator_0++) {" << endl;
						// Print tags as a placeholder for operations on modulo and/or division counters
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
						out << "\t" << "for (brcc_iterator_1=0; brcc_iterator_1 < " << streamins[0]->name->name << "_brcc_stream_size_1; brcc_iterator_1++) {" << endl;
						// Print out non-constant initializations after for loops
						printInitializations (declarations_with_initializations, false, true, out);
						// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_1_END << function_name << endl;
						for (unsigned int i=0; i<ins; i++) {
							// Copy input streams into local variables
							// This is because the code may refer to the input value several times,
							// but we can only read it from the FIFO once
							out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";		
							in_info[i].used = true;
						}
					}
					out << "\t\t if (brcc_iterator_1 == 0) \n\t\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot << " = __fp_temp_" <<  streamins[0]->name->name << left_var_after_dot << ";" << endl;
					out << "\t\telse\n\t\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot;
					if (binexp->bOp == BO_Assign)
						printAssignOp (out, ((AssignExpr *)binexp)->op());
					else
						fatal ("Unexpected expression type!!! The expression is not BO_Assign");
					// Analyze the right expression
					// We need a recursive function for that
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					break;
				}
				case 4:
				{
					// reduce stream is scalar
					// Only print the following before the first assignment
					if (statement_count == 0) {
						out << endl << "int brcc_iterator_0;";
						out << endl << "int brcc_iterator_1;";
						// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
						// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
						// or they will be replaced with actual declarations
						out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_DECL_END << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
						// Print out constant initializations before for loops
						printInitializations (declarations_with_initializations, true, false, out);
						out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < " << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator_0++) {" << endl;
						// Print tags as a placeholder for operations on modulo and/or division counters
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
						out << "\t" << "for (brcc_iterator_1=0; brcc_iterator_1 < " << streamins[0]->name->name << "_brcc_stream_size_1; brcc_iterator_1++) {" << endl;
						// Print out non-constant initializations after for loops
						printInitializations (declarations_with_initializations, false, true, out);
						// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
						// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
						out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << endl;
						out << BRCC_MOD_DIV_BLOCK_1_END << function_name << endl;
						for (unsigned int i=0; i<ins; i++) {
							// Copy input streams into local variables
							// This is because the code may refer to the input value several times,
							// but we can only read it from the FIFO once
							out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";		
							in_info[i].used = true;
						}
					}
					out << endl << "\t\tif ((brcc_iterator_0 == 0) && (brcc_iterator_1 == 0)) \n\t\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot << " = __fp_temp_" << streamins[0]->name->name << left_var_after_dot << ";" << endl;
					out << "\telse\n\t\t\t__fp_temp_" << left_var_before_dot << left_var_after_dot;
					if (binexp->bOp == BO_Assign)
						printAssignOp (out, ((AssignExpr *)binexp)->op());
						// out << " = ";
					else
						fatal ("Unexpected expression type!!! The expression is not BO_Assign");
					// Analyze the right expression
					// We need a recursive function for that
					analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
					break;
				}
				case 5:
				{
					// In this case we have to analyze the expression on the right side first to find out the size of the stream on the right
					// This will define the bounds for the loop iterations
					Variable *right_var = findRightStream (binexp->_rightExpr, streamins, ins, streamouts, outs, streamreduces, reduces);
					// If there is a stream variable on the right side, generate appropriate for loop
					if (right_var != NULL) {
						string right_var_before_dot;
						string right_var_after_dot;
						if ((dot_pos = (right_var->name->name).find ('.',0)) == string::npos) {
							// This means there is no dot in the name, so just copy the whole name
							right_var_before_dot = right_var->name->name;
							right_var_after_dot = "";
						} else {
							// Otherwise only use the part of the name on the left side of the dot in the search
							// The reason for this is that a structure is declared as a variable,
							// however, inside a program the elements of the structure can be accessed
							// and cTool does not handle it in any special way, other than naming the variable differently
							// Therefore, we have to look for a dot in the variable name to find it.
							right_var_before_dot = (right_var->name->name).substr (0, dot_pos);
							// left_var_after_dot also includes the dot, so that we don't have to handle it specially
							right_var_after_dot = (right_var->name->name).substr (dot_pos, string::npos);
						}

						// Only print the following before the first assignment
						if (statement_count == 0) {
							out << endl << "int brcc_iterator_0;";
							out << endl << "int brcc_iterator_1;";
							// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
							// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
							// or they will be replaced with actual declarations
							out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
							out << BRCC_MOD_DIV_DECL_END << function_name << endl;
							out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
							out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
							// Print out constant initializations before for loops
							printInitializations (declarations_with_initializations, true, false, out);
							out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < ";
							if (replication_factor > 1) {
								// If the kernel is replicated, insert tags that will help us insert correct values for loop limits later
								out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << "; brcc_iterator_0++) {" << endl;
							} else {
								out << right_var_before_dot << "_brcc_stream_size_0; brcc_iterator_0++) {" << endl;
							}
							// Print tags as a placeholder for operations on modulo and/or division counters
							// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
							out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
							out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
							out << "\t" << "for (brcc_iterator_1=0; brcc_iterator_1 < ";
							if (replication_factor > 1) {
								// If the kernel is replicated, insert tags that will help us insert correct values for loop limits later
								out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1 << "; brcc_iterator_1++) {" << endl;
							} else {
								 out << right_var_before_dot << "_brcc_stream_size_1; brcc_iterator_1++) {" << endl;
							}
							// Print out non-constant initializations after for loops
							printInitializations (declarations_with_initializations, false, true, out);
							// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
							// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
							out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << endl;
							out << BRCC_MOD_DIV_BLOCK_1_END << function_name << endl;
							for (unsigned int i=0; i<ins; i++) {
								// Copy input streams into local variables
								// This is because the code may refer to the input value several times,
								// but we can only read it from the FIFO once
								out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";		
								in_info[i].used = true;
							}
						}

						// Since this is case 5 (2-D reduced into 2-D), we require some special mod and div counters,
						// which will help us implement the reduction correctly
						// mod_div0 will correspond to brcc_iterator_0%(size_of_the_right_hand_stream_dimension0/size_of_the_left_hand_stream_dimension0)
						// mod_div1 will correspond to brcc_iterator_1%(size_of_the_right_hand_stream_dimension1/size_of_the_left_hand_stream_dimension1)
						// mod_div2 will correspond to brcc_iterator_1/(size_of_the_right_hand_stream_dimension1/size_of_the_left_hand_stream_dimension1)							
						string mod_div0, mod_div1, mod_div2;

						stringstream temp_expression;
						if (replication_factor > 1) {
							temp_expression << "( " << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << " " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_0 << " )";
							mod_div0 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", true, 0, false, 1, 0, out);
							temp_expression.str("");
							temp_expression << "( " << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1 << " " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " )";
							mod_div1 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", true, 1, false, 1, 0, out);
							temp_expression.str("");
							temp_expression << "( " << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1 << " " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " )";
							mod_div2 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", false, 1, false, 1, 0, out);

							out << "\t\tif ((( " << mod_div0 << " )== 0) && (( " << mod_div1 << " ) == 0)) \n\t\t\t__fp_temp_" << left_var_before_dot << "[( " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " == 1) ? 0 : " << mod_div2 << "]" << left_var_after_dot << " = __fp_temp_" << streamins[0]->name->name << left_var_after_dot << ";" << endl;
							out << "\t\telse\n\t\t\t__fp_temp_" << left_var_before_dot << "[( " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " == 1) ? 0 : " << mod_div2 << "]" << left_var_after_dot;
							if (binexp->bOp == BO_Assign)
								printAssignOp (out, ((AssignExpr *)binexp)->op());
							else
								fatal ("Unexpected expression type!!! The expression is not BO_Assign");
						} else {
							temp_expression << "( " << right_var_before_dot << "_brcc_stream_size_0 " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << left_var_before_dot << "_brcc_stream_size_0 )";

							mod_div0 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", true, 0, false, replication_factor, replica_number, out);
							temp_expression.str("");
							temp_expression << "( " << right_var_before_dot << "_brcc_stream_size_1 " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << left_var_before_dot << "_brcc_stream_size_1 )";
							mod_div1 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", true, 1, false, replication_factor, replica_number, out);
							temp_expression.str("");
							temp_expression << "( " << right_var_before_dot << "_brcc_stream_size_1 " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << left_var_before_dot << "_brcc_stream_size_1 )";
							mod_div2 = addModDivCounter (function_name, temp_expression.str(), right_var_before_dot + "_brcc_stream_size_1", false, 1, false, replication_factor, replica_number, out);

							out << "\t\tif ((( " << mod_div0 << " )== 0) && (( " << mod_div1 << " ) == 0)) \n\t\t\t__fp_temp_" << left_var_before_dot << "[( " << left_var_before_dot << "_brcc_stream_size_1 == 1) ? 0 : " << mod_div2 << "]" << left_var_after_dot << " = __fp_temp_" << streamins[0]->name->name << left_var_after_dot << ";" << endl;
							out << "\t\telse\n\t\t\t__fp_temp_" << left_var_before_dot << "[( " << left_var_before_dot << "_brcc_stream_size_1 == 1) ? 0 : " << mod_div2 << "]" << left_var_after_dot;
							if (binexp->bOp == BO_Assign)
								printAssignOp (out, ((AssignExpr *)binexp)->op());
							else
								fatal ("Unexpected expression type!!! The expression is not BO_Assign");
						}
						// Analyze the right expression
						// We need a recursive function for that
						analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, right_var_before_dot, reduce_index, 0, main_function, replication_factor, replica_number, out);
					} else {
						// If there is no stream variable on the right side of the expression (should this ever happen?), just print the expression;
						cerr << endl << "Warning: In function handleAssignment, case 5: Assignment with a reduce stream on the left side doesn't have any streams on the right side" << endl;
						cerr << "Statement: " << *ste << endl;
						binexp->print(out);
					}
					break;
				}
				default:
				{
					fatal ("reduce_index is not one of 1, 2, 3, 4, or 5 and reduce stream was encountered");
					break;
				}
				}
				return;
			}
		}
		// If we got to here, the assignment is to an out stream, which is handled by enclosing it into a for loop
		// Introduce an iterator variable for "for" loops
		// Only print the following before the first assignment
		if ((statement_count == 0) && (outs >0) ) {
			out << endl << "int brcc_iterator_0;";
			if (version == 2) {
				out << endl << "int brcc_iterator_1;";
			}
			// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
			// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
			// or they will be replaced with actual declarations
			out << BRCC_MOD_DIV_DECL_BEGIN << function_name << endl;
			out << BRCC_MOD_DIV_DECL_END << function_name << endl;
			out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << endl;
			out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << endl;
			// Print out constant initializations before for loops
			printInitializations (declarations_with_initializations, true, false, out);
			out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < " << left_var_before_dot << "_brcc_stream_size_0" << "; brcc_iterator_0++) {" << endl;
			// Print tags as a placeholder for operations on modulo and/or division counters
			// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
			out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << endl;
			out << BRCC_MOD_DIV_BLOCK_0_END << function_name << endl;
			if (version == 2) {
				// If we are generating two-dimensional version of the kernel, then the expression should be enclosed into 2 for loops
				out << "for (brcc_iterator_1=0; brcc_iterator_1 < " << left_var_before_dot << "_brcc_stream_size_1" << "; brcc_iterator_1++) {" << endl;
			}
			out << "\n";
			// Print out non-constant initializations after for loops
			printInitializations (declarations_with_initializations, false, true, out);
			if (version == 2) {
				// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << endl;
				out << BRCC_MOD_DIV_BLOCK_1_END << function_name << endl;
			}
			out << BRCC_STREAM_ARRAY_POINTER_INIT << function_name << endl;
			// Read all the input variables from their respective FIFOs
			for (unsigned int i=0; i<ins; i++) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Copy input streams into local variables
				// This is because the code may refer to the input value several times,
				// but we can only read it from the FIFO once
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		if (outstream_pos != -1) {
			ArrayType *temp_array = (ArrayType *) streamouts[outstream_pos]->form;
			// For streams that are declared as streams of arrays, we do not add __fp_temp_ prefix
			if (version == 1 && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array)) {
					out << "\t";
					out << left_var_before_dot;
				} else {
					out << "\t__fp_temp_";
					out << left_var_before_dot;
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array)) {
					out << "\t";
					out << left_var_before_dot;
				} else {
					out << "\t__fp_temp_";
					out << left_var_before_dot;
				}
			}
		} else {
			out << "\t__fp_temp_";
			out << left_var_before_dot;
		}
		out << left_var_after_dot;
		if (binexp->bOp == BO_Assign)
			printAssignOp (out, ((AssignExpr *)binexp)->op());
		else
			fatal ("Unexpected expression type!!! The expression is not BO_Assign");
		// Analyze the right expression
		// We need a recursive function for that
		analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
	} else if (binexp->_leftExpr->etype == ET_IndexExpr) {
		// If it's an ET_IndexExpr (i.e. x[y]), we should handle it, because the index y might contain an indexof operator
		IndexExpr *temp_indexexpr = (IndexExpr *) (binexp->_leftExpr);
		out << *(temp_indexexpr->array) << "[";
		analyzeExpression (temp_indexexpr->_subscript, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
		out << "]";
		if (binexp->bOp == BO_Assign)
			printAssignOp (out, ((AssignExpr *)binexp)->op());
		else
			fatal ("Unexpected expression type!!! The expression is not BO_Assign");
		// Analyze the right expression
		// We need a recursive function for that
		analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
	} else {
		// Otherwise, the expression does not require special handling (at least not on the left side), so we can simply print it out
#ifdef DEBUG
		out << endl << "Just an ordinary expression of etype " << binexp->_leftExpr->etype << ": ";
#endif
		binexp->_leftExpr->print (out);
		if (binexp->bOp == BO_Assign)
			printAssignOp (out, ((AssignExpr *)binexp)->op());
		else
			fatal ("Unexpected expression type!!! The expression is not BO_Assign");
		// Analyze the right expression
		// We need a recursive function for that
		analyzeExpression (binexp->_rightExpr, ste, function_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
	}


}

/*
* handleFunctionCall --
* FP
*	Takes care of function calls
*		Analyzes a given function call and determines whether it's a call to streamRead/streamWrite or a kernel call
*		For streamRead/streamWrite, an argument is added to the call that specifies the kernel size
*		For kernel calls an argument is added to the call for each out stream, that specifies the size of the out stream
*	Inputs:
*		A pointer to the function call expression, a pointer to the statement that contains the expression,
*		reference to a vector containing pointers to all known functions, reference to a vector containing pointers to all declared streams
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/

void handleFunctionCall (FunctionCall *fcall, Statement *ste, DeclVector &funcdecs, StreamInfoVector &declstreams, bool main_function, stringstream &out) {

#ifdef DEBUG
	out << "\nInside handleFunctionCall\n";
#endif
	
// __FIX_ME - At this point, we should also verify that the numbers and types of arguments to the kernel match
//			  Although this will be done subsequently by the C2H compiler, it would be very useful to do it here as well,
//			  because we could pinpoint the location in the original source code where the error originates
	// When we scan the arguments in the function call, we will (eventually) determine which function version should be called.
	// We will store this information in variables reduce_index and version. For meanings of these variables, see other functions
	// that use the same variable names (e.g. handleKernelArgs)
	// The variable input_dimensions will be used to store information on the number of dimensions the input stream has
	int replication_factor = -1;
	int input_dimensions = -1;
	// The variable input_dimensions will be used to store information on the number of dimensions the output (reduction) stream has
	// 0 will mean scalar
	int output_dimensions = -1;
	int reduce_index = -1;
	int version = -1;
	int func_loc = -1;
	FunctionType *original_func=NULL;
	// The variable temp_template will be used to form the argument list for kernel calls
	ostringstream temp_template;
	temp_template.str("");
	string variable_2d2d, columns;
	// The following variables will be used to store stream size variable names and the values they should be replaced with
	// The reason for this replacement is that we want to minimize the area of the generated hardware.
	// The generated hardware is smaller if the stream size arguments are constants from its perspective, hence the replacement
	// Of course, the assumption is that stream sizes are constants.
	unsigned int max_vars_to_replace = 2*fcall->nArgs();
	string *original_stream_size_var;
	string *replacement_stream_size_var;
	if (max_vars_to_replace > 0) {
		original_stream_size_var = new string[max_vars_to_replace];
		replacement_stream_size_var = new string[max_vars_to_replace];
	}
	unsigned int num_vars_to_replace = 0;
	// Check if it is a call to streamRead or streamWrite
	string function_name = ((Variable *)(fcall->function))->name->name;
	if ((fcall->function->etype == ET_Variable) && ((function_name == "streamRead") || (function_name == "streamWrite"))) {
		if (function_name == "streamRead") {
			fatal ("Implicit streamRead not fully handled yet");
		} else if (function_name == "streamWrite") {
			// streamWrite consumes a stream
			fatal ("Implicit streamWrite not fully handled yet");
		} else {
			fatal ("Inside handleFunctionCall: Unexpected case 1");
		}
	} else if ((fcall->function->etype == ET_Variable) && (((Variable *)(fcall->function))->name->name == "push")) {
		// If it's a push operation, handle it separately - push will be considered a keyword, meaning no other function
		// in the program should be called push
		// push has to have exactly one argument
		if ((fcall->nArgs() != 1) || (fcall->args[0]->etype != ET_Variable)) {
			fatal ("Each call to push has to have exactly one argument, and that argument has to be a vout stream");
		}
		Variable *stream_var = (Variable *) fcall->args[0];
		// Look for the stream in the vector of declared streams
		int loc = findVarInVector (declstreams, stream_var->name);
		// Check if the stream has been declared
		if (loc == -1) {
			fatal ("A stream in a call to push has not been declared");
		}
		// A push will be renamed into writing the value of the temp variable into the FIFO
		// But only if the stream being pushed is not a stream of arrays
		ArrayType *temp_array = NULL;		
		temp_array = (ArrayType *) (fcall->args[0]->type);
		// For ordinary streams we convert the push function into a write to the FIFO using a pointer
		if (!((temp_array != NULL && temp_array->subType->type == TT_Array) || (temp_array->subType->type == TT_Stream && ((ArrayType*)temp_array->subType)->subType->type == TT_Array))) {
			out << "*" << ((Variable *) fcall->args[0])->name->name << " = __fp_temp_" << ((Variable *) fcall->args[0])->name->name;		
		} else {
			// For streams of arrays, we write to the control port of the FIFO instead (to move to the next array)
			out << "*" << ((Variable *) fcall->args[0])->name->name << "_br2c_ctrl = 0;";
		}
		if (max_vars_to_replace > 0) {
			delete [] original_stream_size_var;
			delete [] replacement_stream_size_var;
		}
		return;
	} else if ((fcall->function->etype == ET_Variable) && (((Variable *)(fcall->function))->name->name == "pop")) {
		// If it's a pop operation, handle it separately - pop will be considered a keyword, meaning no other function
		// in the program should be called pop
		// pop has to have exactly one argument
		if ((fcall->nArgs() != 1) || (fcall->args[0]->etype != ET_Variable)) {
			fatal ("Each call to pop has to have exactly one argument, and that argument has to be a vout stream");
		}
		Variable *stream_var = (Variable *) fcall->args[0];
		// Look for the stream in the vector of declared streams
		int loc = findVarInVector (declstreams, stream_var->name);
		// Check if the stream has been declared
		if (loc == -1) {
			fatal ("A stream in a call to pop has not been declared");
		}
		ArrayType *temp_array = NULL;		
		temp_array = (ArrayType *) (fcall->args[0]->type);	//(ArrayType *) declstreams[loc].stream->form;
		// Pop function is only used for streams of arrays.
		// We handle it by writing to the control port of the FIFO instead (to move to the next array)
		if ((temp_array != NULL && temp_array->subType->type == TT_Array) || (temp_array->subType->type == TT_Stream && ((ArrayType*)temp_array->subType)->subType->type == TT_Array)) {
			out << "*" << ((Variable *) fcall->args[0])->name->name << "_br2c_ctrl = 0;";
		} else {
			fatal ("Pop can only be used with streams of arrays as an argument");
		}
		if (max_vars_to_replace > 0) {
			delete [] original_stream_size_var;
			delete [] replacement_stream_size_var;
		}
		return;
	} else {
		// Otherwise check if the call is to a kernel function
		if (fcall->function->etype == ET_Variable) {
			// We check  whether the call is to a kernel by looking it up in a vector of all known functions
			// Another way to do this would be the following (from FunctionCall::checkKernelCall):
			// if (function->type&&function->type->isKernel())
			func_loc = findVarInVector (funcdecs, ((Variable *)(fcall->function))->name);
			// The second part of the next condition has been commented out because any function could have stream arguments, not just kernels
// __FIX_ME - This is not fully implemented. As a result we don't allow non-kernel functions to have stream arguments
			if ((func_loc != -1) /*&& funcdecs[func_loc]->isKernel()*/) {
				bool template_args_done = false;
				// So, if it is a kernel, we have to add arguments to it for each stream
				original_func = (FunctionType *) (funcdecs[func_loc]->form);
				bool first_stream = true;
				bool two_dim = false;
				unsigned int ins=0, outs=0, reduces=0, nonstreams=0, vouts=0;
				countKernelArgs (original_func, ins, outs, reduces, nonstreams, vouts);
				GraphNode *dataflow_node = NULL;
				if ((funcdecs[func_loc]->isKernel() || funcdecs[func_loc]->isReduce())) {
					// Add the function to the dataflow graph
					GraphNode new_node (funcdecs[func_loc]->name->name);
					if (funcdecs[func_loc]->isReduce()) {
						new_node.setReduce();
					}
					// Find if the current function is supposed to be replicated
					// Search the vector backwards (i.e. the latest pragma with this function name takes precedence)
					for (std::vector<struct replication_info>::reverse_iterator iter_i=globals.replicated_kernels.rbegin(); iter_i<globals.replicated_kernels.rend(); ++iter_i) {
						// Search the vector backwards
						if (iter_i->function_name == function_name) {
							new_node.replication_factor = iter_i->replication_factor;
							replication_factor = iter_i->replication_factor;
							// Once we found one, we are done
							break;
						}
					}
					dataflow_node = &(globals.dataflow.addNode (new_node));
					if (((outs>0) || (vouts>0) || (reduces>0))) {
						// Make all kernels support interrupts (i.e. asynchronous execution)
						// The only exception are kernels that do not have out, vout or reduce streams
						// Since they do not produce any streams, nobody is waiting for data from them so they can be blocking
// __FIX_ME - The above statement is not entirely true. It may be possible that there are other kernels that could be started in parallel,
//				which do not depend on this kernel. For now we will ignore this uncommon case.
					}
				}
				// Look for stream arguments
				for (unsigned int i=0; i<(original_func->nArgs); i++) {
// __FIX_ME - We should do some kind of checking here for argument numbers and types (currently we laev it to C2H to check that)
					if (original_func->args[i]->form->isStream()) {
						// This means that the argument in the current position is a stream
						// Find the size of the stream
						// First of all find the variable
						// We expect one of two things here to be a parameter:
						// 1. A variable
						// 2. Or a unary operation (&variable)
						int loc;
						string stream_name;
						if (fcall->args[i]->etype == ET_Variable ) {
							stream_name = ((Variable *) fcall->args[i])->name->name;
							loc = findVarInVector (declstreams, ((Variable *) fcall->args[i])->name);
						} else if (fcall->args[i]->etype == ET_UnaryExpr ) {
							UnaryExpr *un_exp = ((UnaryExpr *) fcall->args[i]);
							if (un_exp->_operand->etype != ET_Variable) {
								fcall->print(cerr);
								fatal ("Unsupported construct: unary expression in a function call whose operand is not a variable");
							}
							stream_name = ((Variable *) un_exp->_operand)->name->name;
							loc = findVarInVector (declstreams, ((Variable *) un_exp->_operand)->name);
						} else {
							fcall->print(cerr);
							fatal ("Unsupported construct: expression different than a variable or a unary expression in a function call");
						}
						if (original_func->args[i]->form->isReduce() || ((original_func->args[i]->form->getQualifiers() & TQ_Out) != 0) || ((original_func->args[i]->form->getQualifiers() & TQ_Vout) != 0)) {
							// Reduce, out or vout streams are outputs 
							if (dataflow_node != NULL) {
								struct StreamStruct tempStreamStruct;
								tempStreamStruct.stream = stream_name;
								tempStreamStruct.stream_var = original_func->args[i]->name->name;
								if (loc != -1) {
									ArrayType *temp_array = NULL;
									temp_array = (ArrayType *) original_func->args[i]->form;
									if ((temp_array != NULL && temp_array->subType->type == TT_Array) || (temp_array->subType->type == TT_Stream && ((ArrayType*)temp_array->subType)->subType->type == TT_Array)) {	// && ((original_func->args[i]->form->getQualifiers() & TQ_Vout) == 0)) || (temp_array != NULL && temp_array->subType->type == TT_Array && ((ArrayType *)temp_array->subType)->subType->type == TT_Array))  {
										// This means that this is a stream of arrays. Flag it as such
										tempStreamStruct.stream_of_arrays = true;
										if (((ArrayType *)(temp_array->subType))->size->etype != ET_Constant) {
											cerr << endl << "Inside function: ";
											fcall->print(cerr);
											fatal ("Inside handleFunctionCall: Stream of arrays has to have constant array size");
										}
										Constant *constant = (Constant *) ((ArrayType *)(temp_array->subType))->size;
										if (constant->ctype != CT_Int) {
											fatal ("Inside handleFunctionCall: Stream of arrays has to have constant array size. the constant has to be an integer");
										}
										IntConstant *int_constant = (IntConstant *) constant;
										tempStreamStruct.array_size = int_constant->lng;
										tempStreamStruct.word_size = getDataWidth (declstreams[loc].stream->form->getBase()->typemask);
#ifdef DEBUG
										cerr << "\nOriginal expression: ";
										((ArrayType *)(temp_array->subType))->size->print(cerr);
										cerr << "\nConverted: " << tempStreamStruct.array_size;
#endif

									}
								} else {
									fatal ("In handleFunctionCall: Stream not declared (1)");
								}
								dataflow_node->out_streams.push_back (tempStreamStruct);
							}
						} else {
							// Other streams are inputs
							if (dataflow_node != NULL) {
								struct StreamStruct tempStreamStruct;
								tempStreamStruct.stream = stream_name;
								tempStreamStruct.stream_var = original_func->args[i]->name->name;
								if (loc != -1) {
									ArrayType *temp_array = NULL;
									temp_array = (ArrayType *) original_func->args[i]->form; //declstreams[loc].stream->form;
									if ((temp_array != NULL && temp_array->subType->type == TT_Array) || (temp_array->subType->type == TT_Stream && ((ArrayType*)temp_array->subType)->subType->type == TT_Array)) {
										// This means that this is a stream of arrays. Flag it as such
										tempStreamStruct.stream_of_arrays = true;
										if (((ArrayType *)(temp_array->subType))->size->etype != ET_Constant) {
											cerr << endl << "Inside function: ";
											fcall->print(cerr);
											fatal ("Inside handleFunctionCall: Stream of arrays has to have constant array size");
										}
										Constant *constant = (Constant *) ((ArrayType *)(temp_array->subType))->size;
										if (constant->ctype != CT_Int) {
											fatal ("Inside handleFunctionCall: Stream of arrays has to have constant array size. the constant has to be an integer");
										}
										IntConstant *int_constant = (IntConstant *) constant;
										tempStreamStruct.array_size = int_constant->lng;
										tempStreamStruct.word_size = getDataWidth (declstreams[loc].stream->form->getBase()->typemask);
#ifdef DEBUG
										cerr << "\nOriginal expression: ";
										((ArrayType *)(temp_array->subType))->size->print(cerr);
										cerr << "\nConverted: " << tempStreamStruct.array_size;
#endif
									}
								} else {
									fatal ("In handleFunctionCall: Stream not declared (2)");
								}
								dataflow_node->in_streams.push_back (tempStreamStruct);
							}
						}
						// If the kernel is a reduction kernel, first identify which version of the reduction kernel
						// should be called, then add appropriate arguments to the call
						// The first part of the following condition tests whether the function is a reduce kernel
						// The second argument tests that the argument (being passed to the function) that we are
						// currently analyzing is not a reduce argument (meaning it's an input or out stream)
						if (funcdecs[func_loc]->isReduce() && !original_func->args[i]->form->isReduce()) {
							// This will be done simply by checking the first stream argument that is not a reduce stream.
							// We figure out the number of its dimensions and use it to decide what type of reduction
							// should be performed
							// So far I haven't encountered any reduction kernels that have more than one input kernel anyway.
							// The language definiton doesn't really specify if this is allowed or what would be the meaning
							// if all input streams were not of the same dimensionality
							if (loc != -1) {
								unsigned int dims = declstreams[loc].dimensions();
								input_dimensions = dims;
								if (dims == 1) {
									if (first_stream) {
										first_stream = false;
									} else {
										temp_template << ", ";
									}
									temp_template << *(declstreams[loc].getSize(0));
									if (num_vars_to_replace == max_vars_to_replace)
										fatal ("num_vars_to_replace == max_vars_to_replace");
									original_stream_size_var[num_vars_to_replace] = original_func->args[i]->name->name + "_brcc_stream_size_0";
									Expression *replacement_expression = declstreams[loc].getSize(0);
									stringstream replacement_stringstream;
									replacement_expression->print(replacement_stringstream);
									replacement_stream_size_var[num_vars_to_replace] = replacement_stringstream.str();
									num_vars_to_replace++;

									// Convert the size to integer
									stringstream ss_size;
									ss_size << *(declstreams[loc].getSize(0));
									int size_int = -1;
									if (!(ss_size >> size_int)) {
										cerr << "\nError HFC001: Size of dimension 0 of the stream " << declstreams[loc].stream->name->name << " is " << *(declstreams[loc].getSize(0)) << "\n";
										cerr << "\nThis expression could not be converted to integer type\n";
										exit (1);
									}
									// Store the size of the input into the dataflow graph node
									dataflow_node->insize_dim0 = size_int;
								} else if (dims == 2) {
									if (first_stream) {
										first_stream = false;
									} else {
										temp_template << ", ";
									}
									temp_template << *(declstreams[loc].getSize(0)) << ", " << *(declstreams[loc].getSize(1));
									if (num_vars_to_replace == max_vars_to_replace)
										fatal ("num_vars_to_replace == max_vars_to_replace");
									original_stream_size_var[num_vars_to_replace] = original_func->args[i]->name->name + "_brcc_stream_size_0";
									Expression *replacement_expression = declstreams[loc].getSize(0);
									stringstream replacement_stringstream;
									replacement_expression->print(replacement_stringstream);
									replacement_stream_size_var[num_vars_to_replace] = replacement_stringstream.str();
									num_vars_to_replace++;
									if (num_vars_to_replace == max_vars_to_replace)
										fatal ("num_vars_to_replace == max_vars_to_replace");
									original_stream_size_var[num_vars_to_replace] = original_func->args[i]->name->name + "_brcc_stream_size_1";
									replacement_expression = declstreams[loc].getSize(1);
									stringstream replacement_stringstream2;
									replacement_expression->print(replacement_stringstream2);
									replacement_stream_size_var[num_vars_to_replace] = replacement_stringstream2.str();
									num_vars_to_replace++;

									// Convert the size to integer
									stringstream ss_size;
									ss_size << *(declstreams[loc].getSize(0));
									int size_int = -1;
									if (!(ss_size >> size_int)) {
										cerr << "\nError HFC002: Size of dimension 0 of the stream " << declstreams[loc].stream->name->name << " is " << *(declstreams[loc].getSize(0)) << "\n";
										cerr << "\nThis expression could not be converted to integer type\n";
										exit (1);
									}
									// Store the size of the input into the dataflow graph node
									dataflow_node->insize_dim0 = size_int;

									stringstream ss_size2;
									ss_size2 << *(declstreams[loc].getSize(1));
									size_int = -1;
									if (!(ss_size2 >> size_int)) {
										cerr << "\nError HFC003: Size of dimension 1 of the stream " << declstreams[loc].stream->name->name << " is " << *(declstreams[loc].getSize(1)) << "\n";
										cerr << "\nThis expression could not be converted to integer type\n";
										exit (1);
									}
									// Store the size of the input into the dataflow graph node
									dataflow_node->insize_dim1 = size_int;
								} else {
									fatal ("Stream with number of dimensions different than 1 or 2 encountered!!!");
								}
								two_dim = true;
								if (funcdecs[func_loc]->isKernel()) {
									string replace_string;
								}
							}
						} else if (original_func->args[i]->form->isReduce()) {
							// If it's a reduce argument, we will forward its dimensions only if it is a stream
							if ((loc != -1) && (original_func->args[i]->form->type == TT_Stream)) {
								if (first_stream) {
									first_stream = false;
								} else {
									temp_template << ", ";
								}
								temp_template << *(declstreams[loc].getSize(0));
								if (num_vars_to_replace == max_vars_to_replace)
									fatal ("num_vars_to_replace == max_vars_to_replace");
								original_stream_size_var[num_vars_to_replace] = original_func->args[i]->name->name + "_brcc_stream_size_0";
								Expression *replacement_expression = declstreams[loc].getSize(0);
								stringstream replacement_stringstream;
								replacement_expression->print(replacement_stringstream);
								replacement_stream_size_var[num_vars_to_replace] = replacement_stringstream.str();
								num_vars_to_replace++;

								// Convert the size to integer
								stringstream ss_size;
								ss_size << *(declstreams[loc].getSize(0));
								int size_int = -1;
								if (!(ss_size >> size_int)) {
									cerr << "\nError HFC004: Size of dimension 0 of the stream " << declstreams[loc].stream->name->name << " is " << *(declstreams[loc].getSize(0)) << "\n";
									cerr << "\nThis expression could not be converted to integer type\n";
									exit (1);
								}
								// Store the size of the input into the dataflow graph node
								dataflow_node->reducesize_dim0 = size_int;
								if (declstreams[loc].dimensions() == 2) {
									temp_template << ", " << *(declstreams[loc].getSize(1));
									if (num_vars_to_replace == max_vars_to_replace)
										fatal ("num_vars_to_replace == max_vars_to_replace");
									original_stream_size_var[num_vars_to_replace] = original_func->args[i]->name->name + "_brcc_stream_size_1";
									replacement_expression = declstreams[loc].getSize(1);
									stringstream replacement_stringstream;
									replacement_expression->print(replacement_stringstream);
									replacement_stream_size_var[num_vars_to_replace] = replacement_stringstream.str();
									num_vars_to_replace++;
									output_dimensions = 2;
									variable_2d2d = original_func->args[i]->name->name;
									stringstream temp_ss;
									temp_ss << *(declstreams[loc].getSize(1));
									columns = temp_ss.str();

									// Convert the size to integer
									stringstream ss_size2;
									ss_size2 << *(declstreams[loc].getSize(1));
									size_int = -1;
									if (!(ss_size2 >> size_int)) {
										cerr << "\nError HFC005: Size of dimension 1 of the stream " << declstreams[loc].stream->name->name << " is " << *(declstreams[loc].getSize(1)) << "\n";
										cerr << "\nThis expression could not be converted to integer type\n";
										exit (1);
									}
									// Store the size of the input into the dataflow graph node
									dataflow_node->reducesize_dim1 = size_int;
								} else {
									output_dimensions = 1;
								}
								two_dim = true;
							} else {
								// Otherwise, we will take note of the fact that it is not a stream
								// (meaning it's a scalar)
								output_dimensions = 0;
							}
						}
						if ((loc != -1) && !(funcdecs[func_loc]->isReduce())) {
							// If it's not a reduce kernel (i.e. it's an ordinary function, or an ordinary kernel)
							// pass the sizes of its stream arguments as function arguments
							input_dimensions = 1;
							if (declstreams[loc].dimensions() == 2) {
								input_dimensions = 2;
							}
						// Check if the argument is a reduce stream, if it is, no action is necessary (it can be scalar)
						} else if ((loc == -1) && !(funcdecs[func_loc]->isReduce() && original_func->args[i]->form->isReduce())){
							// Otherwise the call is using an undeclared stream, which is illegal
							cerr << "funcdecs[func_loc]->isReduce() returns " << funcdecs[func_loc]->isReduce() << " and original_func->args[i]->form->isReduce() returns " << original_func->args[i]->form->isReduce();
							cerr << endl << "Variable name is: " << ((Variable *) fcall->args[i])->name->name;
							cerr << endl << "Variable name in the function call is: " << original_func->args[i]->name->name;
							fatal ("Kernel stream argument is undeclared!!!");
						}
					}
				}

#ifdef DEBUG
				out << "Function call found!!!";
#endif
			} // Otherwise the function call is not to a kernel, so we don't have to do anything
		} else {
			fatal ("Function call etype is not an ET_Variable type");
		}
	}
	if (temp_template.str() == "") {
		if ((func_loc != -1) && funcdecs[func_loc]->isKernel()) {
			for (int replica_iter=0; replica_iter<((replication_factor==-1)?1:replication_factor); replica_iter++) {
				// If the call is to a kernel function, we should add appropriate suffix, and not print any stream arguments
				fcall->function->print(out);
				stringstream temp_func_name;
				fcall->function->print(temp_func_name);
				if (input_dimensions == 1) {
					out << "_1d";
					temp_func_name << "_1d";
				} else if (input_dimensions == 2) {
					out << "_2d";
					temp_func_name << "_2d";
				} else {
					fatal ("handleFunctionCall: Unknown dimensionality of an input in a kernel call");
				}
				out << BRCC_REPLICA_SUFFIX << replica_iter;
				temp_func_name << BRCC_REPLICA_SUFFIX << replica_iter;
				out << " (";
				globals.kernelnames.push_back(temp_func_name.str().c_str());
				// Also, change the function name in the dataflow graph (only need to do this once)
				if (replica_iter == 0) {
					globals.dataflow.nodes.back().name = temp_func_name.str();
				}
				bool first_print = true;
				// original_func->nArgs contains the number of arguments that the original function expects
				// This includes all the arguments in the original function call, plus the sizes of streams
				// We do not pass any stream arguments, because streams get connected to FIFOs through pointers
				int i;
				for (i=0; i<fcall->nArgs(); i++) {
					if (!original_func->args[i]->form->isStream()) {
						if (first_print)
							first_print = false;
						else
							out << ", ";
						fcall->args[i]->print(out);
					}
				}
				int j=0;
				// Store the name of the called function
				string function_called = temp_func_name.str();
				for ( ; ((unsigned)i)<(original_func->nArgs); i++) {
					// If the argument in the original function declaration has "_brcc_stream_size" in the name,
					// we replace every reference to that argument inside the function with the size of the kernel being
					// passed. This saves area in the accelerator implementation
					if (original_func->args[i]->name->name.find("_brcc_stream_size") != string::npos) {
						// Find the next stream argument
						while (!original_func->args[j]->form->isStream()) j++;
						if (j > fcall->nArgs()) {
							cerr << "\nInappropriate number of arguments in a call to " << (((Variable *)(fcall->function))->name->name) << endl;
							exit (1);
						}
						string variable_to_replace = original_func->args[i]->name->name;
						// The argument in the current position (j) is a stream
						// Find the size of the stream
						// First of all find the variable
						// We expect one of two things here to be a parameter:
						// 1. A variable
						// 2. Or a unary operation (&variable)
						int loc;
						if (fcall->args[j]->etype == ET_Variable ) {
							loc = findVarInVector (declstreams, ((Variable *) fcall->args[j])->name);
						} else if (fcall->args[j]->etype == ET_UnaryExpr ) {
							UnaryExpr *un_exp = ((UnaryExpr *) fcall->args[j]);
							if (un_exp->_operand->etype != ET_Variable) {
								fcall->print(cerr);
								fatal ("Unsupported construct (2): unary expression in a function call whose operand is not a variable");
							}
							loc = findVarInVector (declstreams, ((Variable *) un_exp->_operand)->name);
						} else {
							fcall->print(cerr);
							fatal ("Unsupported construct (2): expression different than a variable or a unary expression in a function call");
						}
						if (loc == -1) {
							cerr << "\nCould not find a stream passed to the kernel in a list of declared streams in a call to " << (((Variable *)(fcall->function))->name->name) << endl;
							exit (1);
						}
						Expression *replacement_expression = declstreams[loc].getSize(0);
						stringstream replacement_stringstream;
						replacement_expression->print(replacement_stringstream);
						replace_stream_size_arguments (function_called, variable_to_replace, replacement_stringstream.str(), out);
						if (input_dimensions  == 2) {
							// 2-D stream, so need to replace another size argument
							if (((unsigned)i) >= original_func->nArgs) {
								cerr << "\nError 3: Inappropriate number of arguments in a call to " << (((Variable *)(fcall->function))->name->name) << endl;
#ifdef DEBUG
								cout << out.str();
								for (unsigned int k=0; k < (original_func->nArgs); k++) {
									cerr << endl << original_func->args[k]->name->name;
								}
								cerr << endl << funcdecs[func_loc]->name->name;
								cerr << endl << "func_loc = " << func_loc;
								cerr << "\nTotal number of functions = " << funcdecs.size();
								for (unsigned int k=0; k < funcdecs.size(); k++) {
									cerr << endl << funcdecs[k]->name->name;
								}
#endif
								exit (1);
							}
							// The second dimension has suffix _1 instead of _0
							variable_to_replace = variable_to_replace.substr(0,variable_to_replace.length()-1) + "1";
							replacement_expression = declstreams[loc].getSize(1);
							replacement_stringstream.str("");
							replacement_expression->print(replacement_stringstream);
							replace_stream_size_arguments (function_called, variable_to_replace, replacement_stringstream.str(), out);
						}
					}
				}
				out << ");\n";
			}
		} else {
#ifdef DEBUG
			out << "\nCalling fcall->print(out)\n";
#endif
			fcall->print(out);
#ifdef DEBUG
			out << "\nBack from fcall->print(out)\n";
#endif
		}
	} else {	// i.e. (temp_template.str() != "")
		string temp_postfix = "";
		switch (output_dimensions) {
		// If reduce_index == 0:
		//   If version = 0 - <name>_1d - 1-D version of the kernel
		//   If version = 1 - <name>_2d - 2-D version of the kernel
		// If reduce_index == 1: <name>_1ds - 1-D into scalar reduction
		// If reduce_index == 2: <name>_1d1d - 1-D into 1-D reduction
		// If reduce_index == 3: <name>_2d1c - 2-D into 1-column reduction
		// If reduce_index == 4: <name>_2ds - 2-D into scalar reduction
		// If reduce_index == 5: <name>_2d2d - 2-D into 2-D reduction
		case 0:
			if (input_dimensions == 1) {
				temp_postfix = "_1ds";
			} else if (input_dimensions == 2) {
				temp_postfix = "_2ds";
			}
			break;
		case 1:
			if (input_dimensions == 1) {
				temp_postfix = "_1d1d";
			} else if (input_dimensions == 2) {
				temp_postfix = "_2d1c";
			}
			break;
		case 2:
			if (input_dimensions == 2) {
				temp_postfix = "_2d2d";
				// In this case we also have to update the declaration of the temporary variable
				// used for storage of the temporary reduction result
				// We extract the number of columns of the result, because the temporary result
				// only needs to hold one row at a time

				// We have to locate the variable to update
				variable_2d2d = "__fp_temp_" + variable_2d2d; 

				// Finally, we go on with replacement in the original string
				// First we locate the actual function (because there are multiple versions of the function
				string temp_storage = out.str();
				size_t func_location = temp_storage.find (funcdecs[func_loc]->name->name+"_2d2d");
				if (func_location == string::npos) {
					cerr << "\nError RKP002: Could not locate kernel function with a pointer variable to initialize\n";
					cerr << "Was looking for \"" << funcdecs[func_loc]->name->name << "\"";
					cout << out.str();
					fatal ("Terminating program!!!");
				}
				// Then we locate the variable inside that function
				string search_string = "*" + variable_2d2d;
				size_t var_location = temp_storage.find(search_string, func_location);
				if (var_location != string::npos) {
					// Now replace the initialization with a real variable
					// HACK to avoid problems with C2H: apparently C2H does not like arrays of size 1,
					// because it takes log2 of this number to determine width of the address port
					// of the memory that is supposed to hold the array
					// We fake it by creating an array with 2 elements instead
					if (columns == "1") {
						columns = "2 /* This array should actually only have 1 element, but C2H has a bug, and this is a workaround */";
					}
					if (replication_factor > 1) {
						// If this reduction kernel is replicated, we need to insert tags that will help us find
						// this location in the code and replace it later
						temp_storage.replace (var_location, search_string.length(), variable_2d2d + "[" + BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 + "]");
					} else {
						temp_storage.replace (var_location, search_string.length(), variable_2d2d + "[" + columns + "]");
					}
				} else {
					cerr << "\nCould not locate FIFO pointer variable to initialize (2)\n";
					cerr << "Was looking for \"" << search_string << "\" in " << temp_storage.substr (func_location, 512);
					cout << out.str();
					fatal ("Terminating program!!!");
				}
				out.str(temp_storage);
				// Put write pointer for out stream to the end
				// (the above command puts that pointer to the beginning of the file),
				out.seekp(0, ios_base::end);				
			}
			break;
		default:
			fatal ("handleFunctionCall: output_dimensions is not in the renge 0-2 (inclusive)!");
			break;
		}
		fcall->function->print(out);
		stringstream temp_func_name;
		fcall->function->print(temp_func_name);
		temp_func_name << temp_postfix;
		temp_func_name << BRCC_REPLICA_SUFFIX << "0";
		globals.kernelnames.push_back(temp_func_name.str().c_str());
		// Also, change the function name in the dataflow graph
		globals.dataflow.nodes.back().name = temp_func_name.str();
		out << temp_postfix;
		if (replication_factor == -1) {
			out << BRCC_REPLICA_SUFFIX << "0";
		} else {
			// If the reduce kernel is replicated, introduce a tag that will help us find this location
			// in the code later when we know how many calls actually have to be generated
			out << BRCC_REDUCE_REPLICA_REPLACEMENT_TAG;
		}		
		out << " (";
		// Print all the non-stream arguments first
		bool first_print = true;
		for (int i=0; i<(fcall->nArgs()); i++) {
			if ((((unsigned)i)<original_func->nArgs) && ((!original_func->args[i]->form->isStream()) || ((original_func->args[i]->form->isReduce())&&(output_dimensions==0)))) {
				if (first_print)
					first_print = false;
				else
					out << ", ";
				fcall->args[i]->print(out);
			}
		}
		for (unsigned int i=0; i<num_vars_to_replace; i++) {
			// We replace every reference to the stream size arguments inside the kernels with the argument being
			// passed. This saves area in the accelerator implementation
			string function_called = (((Variable *)(fcall->function))->name->name) + temp_postfix;
			replace_stream_size_arguments (function_called, original_stream_size_var[i], replacement_stream_size_var[i], out);
		}
		if (first_print)
			first_print = false;
		else
			out << ", ";
		out << ")" ;
	}
	if (max_vars_to_replace > 0) {
		delete [] original_stream_size_var;
		delete [] replacement_stream_size_var;
	}

}


/*
* handleVarDeclaration --
* FP
*	Takes care of variable declarations
*		Analyzes a given declaration and determines whether it's a stream declaration
*		If it is, it is turned into an array, and added to the list of known streams
*	Inputs:
*		A pointer to the declaration statement, and reference to the vector with pointers to known streams
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/

void handleVarDeclaration (DeclStemnt *ste, StreamInfoVector &declstreams, vector <PPDirective *> &ppdirectives, vector <Decl *> &declarations_with_initializations, stringstream &out) {

#ifdef DEBUG
	out << endl << "In handleVarDeclaration" << endl;
#endif
	stringstream init_string;
	init_string.str ("");
	for(DeclVector::iterator iter = ste->decls.begin(); iter != ste->decls.end(); ++iter) {
		if ((*iter)->isStream()) {
			// If declaration is a stream, convert it to array type
			// This vector will hold all expressions for the sizes of individual dimensions of the stream
			ExprVector size_exprs;
#ifdef DEBUG
			out << endl << "     Stream's form has type: " << (**iter).form->type << endl; 
			if (((**iter).form->getQualifiers() & TQ_Iter).tq != 0) {
				out << " Found an iterator declaration ";
			}
#endif
			// We want to convert it to an array
			(**iter).form->type = TT_Array;
			unsigned int dimensions = 1;
			ArrayType *temp_array = (ArrayType *) ((**iter).form);
			// Find out if the size expression has more than one component
			// If it does, that means this stream is multidimensional
			if (temp_array->size->etype == ET_BinaryExpr) {
				// First count the number of dimensions
				// The function countSubExprs also adds each subexpression to the vector of expressions
				dimensions = countSubExprs ((BinaryExpr *)temp_array->size, size_exprs, out);
			} else {
				dimensions = 1;
				size_exprs.push_back (temp_array->size);
			}
#ifdef DEBUG
			out << "        Size of the stream has type: " << temp_array->size->etype << endl; 
#endif
			StreamInfo *temp_info = new StreamInfo(*iter, dimensions);
			ExprVector::iterator expr_iter=size_exprs.begin();
			for (unsigned int j=0; expr_iter!=size_exprs.end(); expr_iter++, j++) {
				replacePPDirectives (*expr_iter, ppdirectives);
				if (!(temp_info->setSize (j, *expr_iter))) {
					out << endl << "j= " << j;
					fatal ("Failed to set size at the above dimension");
				}
				// For each dimension of the stream, we change its expression,
				// because multidimensional stream is declared as <100, 200, 300>,
				// whereas a multidimensional array (which is what we want to convert it into
				// is declared as [100][200][300]
				// In case of a single-dimensional array, the expression will be exchanged by itself
				temp_array->size = *expr_iter;
				Type *dummy_type;
				if (j == 0) {
					// Even single dimensional declarations have a subType, which is used to store
					// the base type (e.g. int, float, etc. of the variable). Multidimensional
					// declarations have this subtype at the end of the pointer chain.
					// We retain that pointer at the end of the pointer chain.
					dummy_type = temp_array->subType;
				}

				if (j < (dimensions-1)) {
					// This means that we haven't handled all dimensions yet, so we should add subType
					// to the temp_array to add another array dimension
					temp_array->subType = new ArrayType (TT_Array, NULL);
					temp_array = (ArrayType *) temp_array->subType;
				} else {
					temp_array->subType = dummy_type;
				}
				assert (j < dimensions);
			}
			// At the same time, add the declaration into the vector of streams
			declstreams.push_back (*temp_info);
			// If the declared variable is an iterator, it should also have an initializer, which is an iter constructor
			// We will remove this initializer, and add a for loop which initializes all elements of the iterator 
			// This way we avoid many problems with memory allocation (compared to approach where we call a function to
			// fill in the elements of the iterator)
// __FIX_ME - This code has not been tested with latest FPGA Brook, therefore iterators are not officially supported
//            A lot of work has been done below to support them, but this was in the inital compiler version
//            when float support was assumed.
			if (((*iter)->form->getQualifiers() & TQ_Iter).tq != 0) {
				// We need 3 pieces of information to generate the initialization list:
				// # of elements to generate, rangeStart, rangeEnd
				// # of elements to generate depends on the # of dimensions
				// if there is only one dimension, the number of elements is equal to that dimension
				if (dimensions == 1) {
					// 1-D iterators can use float, float2, float3 and float4 values
					Expression *size = size_exprs[0];
					// Make sure there is an initializer
					assert ((*iter)->initializer != NULL);
					// Make sure the initializer is a function call
					assert ((*iter)->initializer->etype == ET_FunctionCall);
					FunctionCall *fcall = (FunctionCall *) ((*iter)->initializer);
					// Make sure the function call is to "iter"
					assert (((Variable *)(fcall->function))->name->name == "iter");
					// Make sure that the function call has exactly 2 arguments
					assert (fcall->nArgs() == 2);
					// If the first argument is not a constructor expression, such as float2(0.0,0.5)
					if (fcall->args[0]->etype != ET_ConstructorExpr) {
						// Make sure that the second argument is not a constructor as well
						assert (fcall->args[1]->etype != ET_ConstructorExpr);
						// Generate a for loop to initialize the iterator
						init_string << "for (int brcc_iter_iterator_0=0; brcc_iter_iterator_0<(" << *size << "); brcc_iter_iterator_0++) {" << endl;
						init_string << "\t" << (*iter)->name->name << "[brcc_iter_iterator_0] = ";
						init_string << *(fcall->args[0]) << " + brcc_iter_iterator_0*((" << *(fcall->args[1]) << ")-(" << *(fcall->args[0]) << "))/" << *size << ";";
						init_string	<< endl << "}" << endl;
					} else {
						// Otherwise we have a float2, float3 or float4 constructor, which we handle separately
						// Make sure that the second argument is also a constructor
						assert (fcall->args[1]->etype == ET_ConstructorExpr);
						unsigned int type_num;
						string type_name;
						// We look at the constructor's base type to find out if it's float2, 3 or 4
						if (((ConstructorExpr *)fcall->args[0])->_bType->typemask & BT_Float2) {
							type_name = "float2";
							type_num = 2;
							// Make sure that both arguments have the same type name( i.e it is not OK to have an iterator iter (float2, float4)
							assert (((ConstructorExpr *)fcall->args[1])->_bType->typemask & BT_Float2);
						} else if (((ConstructorExpr *)fcall->args[0])->_bType->typemask & BT_Float3) {
							type_name = "float3";
							type_num = 3;
							// Make sure that both arguments have the same type name( i.e it is not OK to have an iterator iter (float2, float4)
							assert (((ConstructorExpr *)fcall->args[1])->_bType->typemask & BT_Float3);
						} else if (((ConstructorExpr *)fcall->args[0])->_bType->typemask & BT_Float4) {
							type_name = "float4";
							type_num = 4;
							// Make sure that both arguments have the same type name( i.e it is not OK to have an iterator iter (float2, float4)
							assert (((ConstructorExpr *)fcall->args[1])->_bType->typemask & BT_Float4);
						} else {
							cerr << endl << "Type is: " << ((ConstructorExpr *)fcall->args[1])->_bType->typemask;
							fatal ("Type is not one of float2, float3, or float4");
						}
						// Make sure that the number of dimensions matches the number of arguments in both constructors
						assert (type_num == (((ConstructorExpr *)fcall->args[0])->_nExprs));
						assert (type_num == (((ConstructorExpr *)fcall->args[1])->_nExprs));
						// The first constructor contains lower bound, while the second constructor contains higher bound
						// Generate a for loop to initialize the iterator
						init_string << "for (int brcc_iter_iterator_0=0; brcc_iter_iterator_0<(" << *size << "); brcc_iter_iterator_0++) {" << endl;
						init_string << "\t" << (*iter)->name->name << "[brcc_iter_iterator_0] = " << type_name << " (";
						for (unsigned int i=0; i<type_num; i++) {
							if (i != 0) {
								init_string << ", ";
							}
							init_string << *(((ConstructorExpr *)fcall->args[0])->exprN(i)) << " + brcc_iter_iterator_0*((" << *(((ConstructorExpr *)fcall->args[1])->exprN(i)) << ")-(" << *(((ConstructorExpr *)fcall->args[0])->exprN(i)) << "))/" << *size;
						}
						init_string	<< ");" << endl << "}" << endl;
					}
				} else if (dimensions == 2) {
					// This means that we have a 2-D iterator, which should be initialized with 2 float2's
					// Make sure there is an initializer
					assert ((*iter)->initializer != NULL);
					// Make sure the initializer is a function call
					assert ((*iter)->initializer->etype == ET_FunctionCall);
					FunctionCall *fcall = (FunctionCall *) ((*iter)->initializer);
					// Make sure the function call is to "iter"
					assert (((Variable *)(fcall->function))->name->name == "iter");
					// Make sure that the function call has exactly 2 arguments
					assert (fcall->nArgs() == 2);
					// Make sure that both arguments are constructors
					assert (fcall->args[0]->etype == ET_ConstructorExpr);
					assert (fcall->args[1]->etype == ET_ConstructorExpr);
					// Make sure that both constructors are constructors for float2
					assert (((ConstructorExpr *)fcall->args[0])->_bType->typemask & BT_Float2);
					assert (((ConstructorExpr *)fcall->args[1])->_bType->typemask & BT_Float2);
					// Make sure that the number of arguments in both constructors is 2 9for float2
					assert ((((ConstructorExpr *)fcall->args[0])->_nExprs) == 2);
					assert ((((ConstructorExpr *)fcall->args[1])->_nExprs) == 2);

					// The first constructor contains lower bound, while the second constructor contains higher bound
					// Generate two for loops to initialize the iterator
					init_string << "for (int brcc_iter_iterator_0=0; brcc_iter_iterator_0<(" << *(size_exprs[0]) << "); brcc_iter_iterator_0++) {" << endl;
					init_string << "\tfor (int brcc_iter_iterator_1=0; brcc_iter_iterator_1<(" << *(size_exprs[1]) << "); brcc_iter_iterator_1++) {" << endl;
					init_string << "\t\t" << (*iter)->name->name << "[brcc_iter_iterator_0][brcc_iter_iterator_1] = float2 (";
					init_string << *(((ConstructorExpr *)fcall->args[0])->exprN(0)) << " + brcc_iter_iterator_1*((" << *(((ConstructorExpr *)fcall->args[1])->exprN(0)) << ")-(" << *(((ConstructorExpr *)fcall->args[0])->exprN(0)) << "))/" << *(size_exprs[1]) << ", ";
					init_string << *(((ConstructorExpr *)fcall->args[0])->exprN(1)) << " + brcc_iter_iterator_0*((" << *(((ConstructorExpr *)fcall->args[1])->exprN(1)) << ")-(" << *(((ConstructorExpr *)fcall->args[0])->exprN(1)) << "))/" << *(size_exprs[0]);
					init_string	<< ");" << endl << "}}" << endl;
				} else {
					fatal ("The number of dimensions of an iterator is neither 1 nor 2");
				}
				// Delete the old expression for the initializer
				delete (*iter)->initializer;
				// Make the initializer NULL
				(*iter)->initializer = NULL;

				// Print declaration 
				(**iter).print(out, true);
				out << ";";
				out << endl << init_string.str();
			}
		} else {
			// Find out if the declaration has an initializer
			if ((*iter)->initializer != NULL) {
				declarations_with_initializations.push_back ((Decl *)(*iter)->dup());
				if (((Decl *)(*iter))->isConst()) {
					// If the variable is defined as constant, we have to remove the constant qualifier to be able to initialize it at a later time
					// This reduces the ability of the C2H compiler to detect if a constant is used inappropriately, but that's OK, because C2H cannot
					// point to the line in the original code anyway, so such an error would be less useful
// __FIX_ME - Semantic analysis should be performed, and an error should be issued if a variable declared as constant is used inappropriately
					// Notice that the const qualifier is only removed from the original declaration, not from the one that was put into the
					// declarations_with_initializations vector. This is on purpose, because we need to know which variables were declared
					// as constants later ON.
					((Decl *)(*iter))->removeConstQualifier(); 
					if (((Decl *)(*iter))->isConst()) {
						fatal ("It's still a constant");
					}
				}
				// Remove the initializer from the original declaration, so that it does not get "initialized"
				// This is done because C2H does not initalize arrays in "asynchronous" accelerators correctly.
				// It allocates space on stack before "calling" the accelerator. The problem occurs when the accelerator is used in the
				// interrupt mode. The stack space gets freed as soon as the function that called the accelerator
				// returns, so it gets corrupted by subsequent data written onto the stack.
				// We solve this problem by initializing the array manually, element by element
// __FIX_ME - PROBLEM: We also have to do it for regular variables, but only inside kernels (not for regular functions)
//            For now we will do it for all variables in all functions; it should not hurt regular functions much (at all?)
#ifdef DEBUG
				out << "\nThere is an initializer here: " << *((**iter).initializer) << "\n";
#endif
				 delete (*iter)->initializer;
				(*iter)->initializer = NULL;
			}
			// Print declaration 
			(**iter).print(out, true);
			out << ";";
			out << endl << init_string.str();
		}
	}
}


/*
* handleBlockStatement --
* FP
*	Takes care of statements inside blocks
*		Analyzes a given statement. It may call itself recursively in case of statements nested in blocks
*		Almost any program will have nested blocks, because of for loops, etc.
*	Inputs:
*		A pointer to the block statement, and reference to vectors with pointers to known function declarations and streams
*		Arrays containing pointers to in, out and reduce streams, as well as sizes of those arrays
*		Parameter reduce_index tells whether the block is a reduction kernel (==0 not reduction kernel, !=0 reduction kernel)
*		If it is a reduction kernel, the reduce_index parameter tells which version of the reduction kernel should be generated
*		reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
*		reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
*		reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
*		reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/
void handleBlockStatement (Statement *block_ste, string function_name, DeclVector &funcdecs, StreamInfoVector &declstreams, Decl **streamouts, unsigned int outs, Decl **streamins, var_info *in_info, unsigned int ins, Decl **streamreduces, unsigned int reduces, Decl** streamvouts, unsigned int vouts, vector <PPDirective *> &ppdirectives, unsigned int reduce_index, unsigned int version, unsigned int &statement_count, bool main_function, vector <Decl *> &declarations_with_initializations, int replication_factor, int replica_number, stringstream &out) {

#ifdef DEBUG
	out << "\nInside handleBlockStatement\n";
#endif

	string function_suffix("");
	// If there are streams associated with the current block (i.e. it is some kind of a kernel), we may need to know which version of the function
	// we are currently generating. For the meanings of these suffixes, search elsewhere in the code
	if ((ins > 0) || (outs > 0) || (reduces > 0)) {
		switch (reduce_index) {
		case 0:
			if (version == 1)
				function_suffix = "_1d";
			else
				function_suffix = "_2d";
			break;
		case 1:
			function_suffix = "_1ds";
			break;
		case 2:
			function_suffix = "_1d1d";
			break;
		case 3:
			function_suffix = "_2d1c";
			break;
		case 4:
			function_suffix = "_2ds";
			break;
		case 5:
			function_suffix = "_2d2d";
			break;
		default:
			fatal ("reduce_index is outside 0-5 (inclusive) range");
			break;
		}
	}
#ifdef DEBUG
	out << "\nInside handleBlockStatement: function " << function_name << function_suffix << " replication factor is " << replication_factor << " replica number is " << replica_number;
#endif
	if (replica_number != -1) {
		function_suffix += BRCC_REPLICA_SUFFIX;
		ostringstream temp;
		temp << replica_number;
		function_suffix += temp.str();
	}
	string temp_func_name = function_name+function_suffix;

	// Right after all the declarations, we should print out 'for' loops that iterate over all stream elements.
	// Actually we should enclose the declarations into for loops as well, because they sometimes contain 
	// initialization of variables that should be initialized for every stream element
	if (block_ste->type != ST_DeclStemnt) {
		// However, we still have to print out enclosing for loops if it is the first assignment in the kernel
		// We have to find out dimensions of:
		//										the input stream if this is a reduction kernel
		//										the out stream if this is a regular kernel
		// These dimensions will help us determine what kind of 'for' loop should be generated to enclose statements inside the block
		if (statement_count == 0) {
			if (reduce_index != 0) {
				if (streamins == NULL)
					fatal ("Inside handleBlockStatement: streamins is NULL");
				if (streamins[0] == NULL)
					fatal ("Inside handleBlockStatement: streamins[0] is NULL");
				if (streamreduces == NULL)
					fatal ("Inside handleBlockStatement: streamreduces is NULL");
				if (streamreduces[0] == NULL)
					fatal ("Inside handleBlockStatement: streamreduces[0] is NULL");
			}
			switch (reduce_index) {
			case 0:
			{
				// In this case this is a regular kernel. Therefore we will use dimensions of an out stream to determine loop boundaries
				// We will assume that there is one or more out stream in a kernel
				// If there is more than one out stream, we will asume that all of them have the same dimensions
				// This should be true according to Brook specification
				// If there are no out streams, we will use the in streams to determine dimensions, and will also
				// assume that all in streams have the same dimensions
				// If there are no in or out streams, we will assume that this is a regular function (not a kernel),
				// so there is no need to print for loops.
				Symbol temp_sym;
				temp_sym.name = function_name;
				int func_loc = findVarInVector (funcdecs, &temp_sym);
				if (func_loc == -1) {
					fatal ("Could not find the function " + function_name + " in the list of declared functions");
				} else {
#ifdef DEBUG
					cout << "\nFound function " << function_name << " in the list of declared functions";
#endif
				}
				bool is_kernel = funcdecs[func_loc]->isKernel();
				if (((ins > 0) || (outs > 0)) && is_kernel) {
					string stream_name = (outs > 0) ? streamouts[0]->name->name : streamins[0]->name->name;
					// Add for loops, unless there are no out streams (in that case it's likely a streamWrite-like function)
					if (outs > 0) {
						out << endl << "int brcc_iterator_0;";
						if (version == 2) {
							out << endl << "int brcc_iterator_1;";
						}
						// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
						// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
						// or they will be replaced with actual declarations
						out << BRCC_MOD_DIV_DECL_BEGIN << function_name << function_suffix << endl;
						out << BRCC_MOD_DIV_DECL_END << function_name << function_suffix << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << function_suffix << endl;
						out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << function_suffix << endl;
						// Print out constant initializations before for loops
						printInitializations (declarations_with_initializations, true, false, out);
						// 2-D streams have to be handled slightly differently (that's what (version == 2) tests)
						// If we are generating two-dimensional version of the kernel, then the expression should be enclosed into 2 for loops
						if (version == 2) {
							if (replication_factor > 1) {
								// If replication factor is greater than 1 (i.e. there is replication) and this is a 2-D stream,
								// index ranges get a bit more complex, which is what these statements handle
								// Essentially, this takes care of the fact that elements arrive in the row-major order
								// and that they are distributed in a round robin fashion
								// Complications occur when replication factor does not divide the number of columns evenly
								// Since we don't know (at this stage) whether this is true or not, we simply generate general code
//__FIX_ME - Potential for optimization - see the comments above this line
								out << endl << "for (brcc_iterator_0 = " << replica_number << BRCC_DIV_OPERATOR_TO_REPLACE << stream_name << "_brcc_stream_size_1 , brcc_iterator_1 = " << replica_number << BRCC_MOD_OPERATOR_TO_REPLACE << stream_name << "_brcc_stream_size_1 ; brcc_iterator_0 < " << stream_name << "_brcc_stream_size_0; ) {" << endl;
								out << "while (brcc_iterator_1 >= " << stream_name << "_brcc_stream_size_1) {" << endl;
								out << "\tbrcc_iterator_0++;" << endl;
								out << BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_BEGIN << function_name << function_suffix;
								out << BRCC_INDEXOF_REPLICATED_UPDATE_BLOCK_END << function_name << function_suffix;
								out << "\n\tbrcc_iterator_1 -= " << stream_name << "_brcc_stream_size_1;" << endl;
								out << "}" << endl;
								out << "if (brcc_iterator_0 < " << stream_name << "_brcc_stream_size_0) {" << endl;
							} else {
								out << endl << "for (brcc_iterator_0 = " << replica_number << "; brcc_iterator_0 < " << stream_name << "_brcc_stream_size_0" << "; brcc_iterator_0+=" << replication_factor << ") {" << endl;
							}
							// Print tags as a placeholder for operations on modulo and/or division counters
							// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
							out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
							out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;				
							if (replication_factor > 1) {
								out << "for ( ; brcc_iterator_1 < " << stream_name << "_brcc_stream_size_1" << "; brcc_iterator_1+=" << replication_factor << ") {" << endl;
							} else {
								out << "for (brcc_iterator_1=0; brcc_iterator_1 < " << stream_name << "_brcc_stream_size_1" << "; brcc_iterator_1++) {" << endl;
							}
							// Print out non-constant initializations after for loops
							printInitializations (declarations_with_initializations, false, true, out);
							// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
							// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
							out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << function_suffix << endl;
							out << BRCC_MOD_DIV_BLOCK_1_END << function_name << function_suffix << endl;
						} else {
							out << endl << "for (brcc_iterator_0=" << replica_number << "; brcc_iterator_0 < " << stream_name << "_brcc_stream_size_0" << "; brcc_iterator_0+=" << replication_factor << ") {" << endl;
							// Print tags as a placeholder for operations on modulo and/or division counters
							// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
							out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
							out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;				
							// Print out non-constant initializations after for loops
							printInitializations (declarations_with_initializations, false, true, out);
						}
						out << BRCC_STREAM_ARRAY_POINTER_INIT << function_name << function_suffix << endl;
						for (unsigned int i=0; i<ins; i++) { 
							ArrayType *temp_array = (ArrayType *) streamins[i]->form;
							// Store input stream elements into local variables
							// This is because the code may refer to the input value several times,
							// but we can only read it from the FIFO once
							// For streams that are declared as streams of arrays, we do not perform reads here
							if (version == 1 && reduce_index < 3) {
								if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
									out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
								}
							} else {
								if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
									out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
								}
							}
							in_info[i].used = true;
						}

					}
				} else {
					// Print all initializations for non-kernel functions
					printInitializations (declarations_with_initializations, true, true, out);
				}
				break;
			}
			case 1:
			{
				out << endl << "int brcc_iterator;";
				// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
				// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
				// or they will be replaced with actual declarations
				out << BRCC_MOD_DIV_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_DECL_END << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << function_suffix << endl;
				// Print out constant initializations before for loops
				printInitializations (declarations_with_initializations, true, false, out);
				out << "for (brcc_iterator=0; brcc_iterator < ";
				if (replication_factor > 1) {
					out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << function_name << function_suffix;
				} else {
					out << streamins[0]->name->name << "_brcc_stream_size_0";
				}
				out << "; brcc_iterator++) {" << endl;
				// Print out non-constant initializations after for loops
				printInitializations (declarations_with_initializations, false, true, out);
				// Print tags as a placeholder for operations on modulo and/or division counters
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;
				break;
			}
			case 2:
			{
				out << endl << "int brcc_iterator_0, brcc_mod_iterator=0;";
				if (replication_factor > 1) {
					out << endl << BRCC_REDUCE_MODULO_ITERATOR_TAG_0 << function_name << function_suffix;
				}
				// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
				// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
				// or they will be replaced with actual declarations
				out << BRCC_MOD_DIV_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_DECL_END << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << function_suffix << endl;
				// Print out constant initializations before for loops
				printInitializations (declarations_with_initializations, true, false, out);
				out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < ";
				if (replication_factor > 1) {
					out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << function_name << function_suffix;
				} else {
					out << streamins[0]->name->name << "_brcc_stream_size_0";
				}
				out << "; brcc_iterator_0++, brcc_mod_iterator++) {\n";
				out << "brcc_mod_iterator = ((brcc_mod_iterator == (";
				if (replication_factor > 1) {
					out << BRCC_REDUCE_MODULO_USAGE_TAG_0 << function_name << function_suffix;
				} else {
					out << streamins[0]->name->name << "_brcc_stream_size_0" << BRCC_DIV_OPERATOR_TO_REPLACE << streamreduces[0]->name->name << "_brcc_stream_size_0";
				}
				out << " )) ? 0 : brcc_mod_iterator);" << endl;
				// Print out non-constant initializations after for loops
				printInitializations (declarations_with_initializations, false, true, out);
				// Print tags as a placeholder for operations on modulo and/or division counters
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;

				// Copy the result to the output FIFO if the count indicates that one element of the output has been produced
				out << "\n\tif ((brcc_mod_iterator == 0) && (brcc_iterator_0 != 0)) {";
				for (unsigned int i=0; i<reduces; i++) {
					out << "\n\t\t*" << streamreduces[i]->name->name << " = __fp_temp_" << streamreduces[i]->name->name << ";\n";
				}
				if (replication_factor > 1) {
					out << "\t\t" << BRCC_REDUCE_MODULO_UPDATE_TAG_0 << function_name << function_suffix << "\n";
				}
				out << "\t}\n";
				break;
			}
			case 3:
			case 4:
			{
				out << endl << "int brcc_iterator_0;";
				out << endl << "int brcc_iterator_1;";
				if (replication_factor > 1) {
					out << endl << BRCC_REDUCE_MODULO_ITERATOR_TAG_0 << function_name << function_suffix;
				}
				// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
				// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
				// or they will be replaced with actual declarations
				out << BRCC_MOD_DIV_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_DECL_END << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << function_suffix << endl;
				// Print out constant initializations before for loops
				printInitializations (declarations_with_initializations, true, false, out);
				out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < " << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator_0++) {" << endl;
				// Print tags as a placeholder for operations on modulo and/or division counters
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;
				out << "\t" << "for (brcc_iterator_1=0; brcc_iterator_1 < " << streamins[0]->name->name << "_brcc_stream_size_1; brcc_iterator_1++) {" << endl;
				// Print out non-constant initializations after for loops
				printInitializations (declarations_with_initializations, false, true, out);
				// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_BLOCK_1_END << function_name << function_suffix << endl;
				break;
			}
			case 5:
			{
				out << endl << "int brcc_iterator_0;";
				out << endl << "int brcc_iterator_1;";
				// Print tags as placeholders for declaration of variables that may be needed to handle modulo and/or division operators
				// These tags will be handled in the second pass: either they will be removed if there are no modulo/div operators,
				// or they will be replaced with actual declarations
				out << BRCC_MOD_DIV_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_DECL_END << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_AFTER_DECL_END << function_name << function_suffix << endl;
				// Print out constant initializations before for loops
				printInitializations (declarations_with_initializations, true, false, out);
				// We will use the dimensions of the first in stream encountered. In all programs shipped with Brook there is only one in stream
				// for any case where there is a reduce kernel. Therefore, the dimensions of this stream are going to be used as loop iteration limits
				if (ins > 0) {
					out << endl << "for (brcc_iterator_0=0; brcc_iterator_0 < ";
					if (replication_factor > 1) {
						// If the kernel is replicated, insert tags that will help us insert correct values for loop limits later
						out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << "; brcc_iterator_0++) {" << endl;
					} else {
						out << streamins[0]->name->name << "_brcc_stream_size_0; brcc_iterator_0++) {" << endl;
					}
					// Print tags as a placeholder for operations on modulo and/or division counters
					// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
					out << BRCC_MOD_DIV_BLOCK_0_BEGIN << function_name << function_suffix << endl;
					out << BRCC_MOD_DIV_BLOCK_0_END << function_name << function_suffix << endl;
					out << "\t" << "for (brcc_iterator_1=0; brcc_iterator_1 < ";
					if (replication_factor > 1) {
						// If the kernel is replicated, insert tags that will help us insert correct values for loop limits later
						out << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1 << "; brcc_iterator_1++) {" << endl;
					} else {
						out << streamins[0]->name->name << "_brcc_stream_size_1; brcc_iterator_1++) {" << endl;
					}
				} else if (reduces > 0) {
					cerr << "\nHandleBlockStatement ERROR 4324556: Reduction kernel \"" << function_name << "\" has no input stream, which is not supported!!!\n";
					exit (1);
				} else {
					fatal ("Unexpected case in handleBlockStatement: neither is (ins > 0), nor (reduces > 0)!!!");
				}
				// Print out non-constant initializations after for loops
				printInitializations (declarations_with_initializations, false, true, out);
				// Print tags as a placeholder for operations on modulo and/or division counters (second dimension)
				// The tags will be replaced by actual code, or removed (if no variables are needed) in the second pass
				out << BRCC_MOD_DIV_BLOCK_1_BEGIN << function_name << function_suffix << endl;
				out << BRCC_MOD_DIV_BLOCK_1_END << function_name << function_suffix << endl;
				break;
			}
			default:
			{
				fatal ("handleBlockStatement: Unknown case!");
				break;
			}
			}
			statement_count++;
		}
	}

	// The statement_count variable will keep track of the statements inside the block
	// This is only needed to distinguish between the first and all other iterations
#ifdef DEBUG
	out << endl << "statement_count = " << statement_count << endl;
#endif
	switch (block_ste->type) {
	// The statement could be another block
	case ST_Block:
	{
#ifdef DEBUG
		out << "\nFound block: calling handleBlock\n";
#endif

		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		handleBlock ((Block *)(block_ste), function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, true, replication_factor, replica_number, out);
		break;
	}
	// We analyze expressions because they may contain stream references, which should be converted into array references
	case ST_ExpressionStemnt:
	{
#ifdef DEBUG
		out << "\nFound an expression\n";
#endif

		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		Expression *expr = ((ExpressionStemnt *) block_ste)->expression;

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		findVarsUsed (expr, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;
		switch (expr->etype) {
		case ET_BinaryExpr:
			{
			// Assignments seem to be simply binary expressions
			BinaryExpr *binexp = (BinaryExpr *) expr;
#ifdef DEBUG
			out << endl << "Left: " << binexp->_leftExpr->etype << ", right: " << binexp->_rightExpr->etype << endl;
#endif
			handleAssignment (binexp, block_ste, temp_func_name, funcdecs, ppdirectives, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			break;
			}
		// Function calls have to be checked
		// If it is a call to kernel, it should be augmented with parameters containing sizes of out streams
		case ET_FunctionCall:
			{
#ifdef DEBUG
				out << "\nFound a function call: calling handleFunctionCall\n";
#endif
				handleFunctionCall (((FunctionCall *)expr), block_ste, funcdecs, declstreams, main_function, out);
				break;
			}
		default:
#ifdef DEBUG
	out << "\nDefault expression type: simply printing the expression\n";
#endif
			expr->print(out);
			break;
		}
		break;
	}
	case ST_DeclStemnt:
	{
		// Handle declarations in non-kernel functions
#ifdef DEBUG
		out << "DECLARATION!!!";
#endif
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		handleVarDeclaration (((DeclStemnt *)block_ste), declstreams, ppdirectives, declarations_with_initializations, out);
		break;
	}
	case ST_ForStemnt:
	{
#ifdef DEBUG
		out << "\nFound a for statement\n";
#endif
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		// If the statement is a for statement, we print the header of the statement, and then call this function
		// recursively to handle the for statement block 
		ForStemnt *for_ste = (ForStemnt *) block_ste;
		for (LabelVector::const_iterator j=for_ste->labels.begin(); j != for_ste->labels.end(); j++)
			(*j)->print(out, 0);

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		// Look for variables used in any of the three parts of the 'for' loop (init, cond, incr)
		findVarsUsed (for_ste->init, block_ste, funcdecs, streamins, ins, new_info);
		findVarsUsed (for_ste->cond, block_ste, funcdecs, streamins, ins, new_info);
		findVarsUsed (for_ste->incr, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;

		out << "for (";

		if (for_ste->init)
			out << *(for_ste->init);
		out << "; ";

		if (for_ste->cond)
			out << *(for_ste->cond);
		out << "; ";

		if (for_ste->incr)
			out << *(for_ste->incr);
		out << ") {\n";

		//Call this function recursively to handle the block inside the for loop
		handleBlockStatement (for_ste->block, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
		if (for_ste->block->needSemicolon())
			out << ";\n";
		out << "}\n";
		break;
	}
	case ST_IfStemnt:
	{
#ifdef DEBUG
		out << "\nFound an if statement\n";
#endif
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		IfStemnt *if_ste = (IfStemnt *) block_ste;

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		findVarsUsed (if_ste->cond, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;

		out << "if (";
		analyzeExpression (if_ste->cond, block_ste, temp_func_name, funcdecs, ppdirectives, declstreams, streamins, ins, streamouts, outs, streamreduces, reduces, streamvouts, vouts, "", reduce_index, version, main_function, replication_factor, replica_number, out);
		out << ") {" << endl;
		if (if_ste->thenBlk != NULL) {
			handleBlockStatement (if_ste->thenBlk, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			if (if_ste->thenBlk->needSemicolon())
				out << ";\n";
		}
		if (if_ste->elseBlk != NULL) {
			out << "} else {" << endl;
			handleBlockStatement (if_ste->elseBlk, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			if (if_ste->elseBlk->needSemicolon())
				out << ";\n";
		}
		out << "}" << endl;
		break;
	}
	case ST_SwitchStemnt:
	{
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		SwitchStemnt *sw_ste = (SwitchStemnt *) block_ste;

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		findVarsUsed (sw_ste->cond, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;


		out << "switch (";
		sw_ste->cond->print (out);
		out << ") {" << endl;
		if (sw_ste->block != NULL) {
			handleBlockStatement (sw_ste->block, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			if (sw_ste->block->needSemicolon())
				out << ";\n";
		}
		out << "}" << endl;
		break;
	}
	case ST_WhileStemnt:
	{
#ifdef DEBUG
		out << "\nFound a while statement\n";
#endif
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		WhileStemnt *while_ste = (WhileStemnt *) block_ste;

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		findVarsUsed (while_ste->cond, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;


		out << "while (" << *(while_ste->cond) << ") {\n";
		if (while_ste->block != NULL) {
			handleBlockStatement (while_ste->block, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			if (while_ste->block->needSemicolon())
				out << ";\n";
		} else {
			out << ";";
		}
		out << "}\n";
		break;
	}
	case ST_DoWhileStemnt:
	{
#ifdef DEBUG
		out << "\nFound a do-while statement\n";
#endif
		// First print label(s) (if any) associated with the statement
		LabelVector::const_iterator    j;
		for (j=block_ste->labels.begin(); j != block_ste->labels.end(); j++)
			(*j)->print(out,1);
		DoWhileStemnt *dowhile_ste = (DoWhileStemnt *) block_ste;

		// Find if any variables are being used by this statement
		bool *new_info = new bool[ins];
		for (unsigned int i=0; i<ins; i++)
			new_info[i] = false;
		findVarsUsed (dowhile_ste->cond, block_ste, funcdecs, streamins, ins, new_info);
		for (unsigned int i=0; i<ins; i++) {
			// Copy any input streams that may be used by this expression into local variables
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			// So, if the variable hasn't been used before, but it is used by the current expression
			if (!in_info[i].used && new_info[i]) {
				ArrayType *temp_array = (ArrayType *) streamins[i]->form;
				// Then print it out, and take note of the fact that it's been used
				// For streams that are declared as streams of arrays, we do not perform reads here
				if (version == 1 && reduce_index < 3) {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				} else {
					if (!((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array))) {
						out << "\t\t__fp_temp_" << streamins[i]->name->name << " = *" << streamins[i]->name->name << ";\n";
					}
				}
				in_info[i].used = true;
			}
		}
		delete [] new_info;

		out << "do {\n";
		if (dowhile_ste->block != NULL) {
			handleBlockStatement (dowhile_ste->block, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
			if (dowhile_ste->block->needSemicolon())
				out << ";\n";
		} else {
			out << ";";
		}
		out << "} while (" << *(dowhile_ste->cond) << ");\n";
		break;

	}
	default:
	{
#ifdef DEBUG
		out << "\nFound default statement case, statement type is " << block_ste->type << ". The statement will be simply printed\n";
#endif
		// If the statement is not an expresion or a declaration, simply print it
		block_ste->print(out, 0);
		break;
	}
	}
}


/*
* handleBlock --
* FP
*	Takes care of blocks
*		Analyzes a given block and statements inside the block. It may call itself recursively (through handleBlockStatement)
*		in case of nested blocks. Almost any program will have nested blocks, because of for loops etc.
*	Inputs:
*		A pointer to the block statement, and the name of the function this block is a part of
*		Reference to vectors with pointers to known function declarations and streams
*		Arrays containing pointers to in, out and reduce streams, as well as sizes of those arrays
*		Parameter reduce_index tells whether the block is a reduction kernel (==0 not reduction kernel, !=0 reduction kernel)
*		If it is a reduction kernel, the reduce_index parameter tells which version of the reduction kernel should be generated
*		reduce_index==1. in streams should be 1-D, and reduce stream should be a scalar
*		reduce_index==2. in streams should be 1-D, and reduce stream should be 1-D
*		reduce_index==3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
*		reduce_index==4. in streams should be 2-D, and reduce stream should be a scalar
*		Final argument is the output stream reference, to be able to print the appropriate code to an outfile
*/
void handleBlock (Block *ste, string function_name, DeclVector &funcdecs, StreamInfoVector &declstreams, Decl **streamouts, unsigned int outs, Decl **streamins, var_info *in_info, unsigned int ins, Decl **streamreduces, unsigned int reduces, Decl** streamvouts, unsigned int vouts, vector <PPDirective *> &ppdirectives, unsigned int reduce_index, unsigned int version, unsigned int &statement_count, bool main_function, bool block_inside_block, int replication_factor, int replica_number, stringstream &out) {

	vector <Decl *> declarations_with_initializations;

	// Always start a block with an open curly bracket
	out << " {" << endl;

	// This is a good spot to print stream declarations.
	// Streams are usually passed as arguments to kernels. We convert them to local pointers.
	// C2H Pragmas will be used to "connect" these pointers to their respective FIFOs
	// We only print declarations once at the beginning of the block
	if (statement_count == 0) {
		out << "\n";
		for (unsigned int i=0; i<ins; i++) {
			out << "volatile ";
			streamins[i]->form->printBase(out, 0);
// __FIX_ME - This is a little hack, but reasonable. Probability that anyone will ever call some variable inside their program
//            876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843 is infinitesimal
//            We use similar names this throughout the code
			out << " *" << streamins[i]->name->name <<  " = 876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_";
			out << function_name << ";\n";
			// For each input stream, we also create a "local copy" of that stream.
			// This is because the code may refer to the input value several times,
			// but we can only read it from the FIFO once
			streamins[i]->form->printBase(out, 0);
			out << " __fp_temp_" << streamins[i]->name->name << ";\n";
			// For streams that are declared as streams of arrays, we also need a control port, which will trigger reads/writes from/to the FIFO
			ArrayType *temp_array = (ArrayType *) streamins[i]->form;

			if (version == 1  && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array)) {
					out << "volatile char  *" << streamins[i]->name->name << "_br2c_ctrl;\n";
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array)) {
					out << "volatile char  *" << streamins[i]->name->name << "_br2c_ctrl;\n";
				}
			}
		}
		for (unsigned int i=0; i<outs; i++) {
			out << "volatile ";
			// Temporarily put qualifier to none to remove the out keyword
			streamouts[i]->form->getBase()->qualifier = TQ_None;
			streamouts[i]->form->printBase(out, 0);
			out << " *" << streamouts[i]->name->name << " = 876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_";
			out << function_name << ";\n";
			// For each out stream, we also create a "local copy" of that stream.
			// This is because the code may perform many operations to produce an element
			// We only want to write to the output FIFO when the computation is complete
			streamouts[i]->form->printBase(out, 0);
			out << " __fp_temp_" << streamouts[i]->name->name << ";\n";		
			// Return the out qualifier
			streamouts[i]->form->getBase()->qualifier = TQ_Out;
			// For streams that are declared as streams of arrays, we also need a control port, which will trigger reads/writes from/to the FIFO
			ArrayType *temp_array = (ArrayType *) streamouts[i]->form;
			if (version == 1 && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array)) {
					out << "volatile char  *" << streamouts[i]->name->name << "_br2c_ctrl;\n";
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array)) {
					out << "volatile char  *" << streamouts[i]->name->name << "_br2c_ctrl;\n";
				}
			}
		}
		for (unsigned int i=0; i<vouts; i++) {
			out << "volatile ";
			streamvouts[i]->form->printBase(out, 0);
			out << " *" << streamvouts[i]->name->name << " = 876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_";
			out << function_name << ";\n";
			// For each out stream, we also create a "local copy" of that stream.
			// This is because the code may perform many operations to produce an element
			// We only want to write to the output FIFO when the computation is complete
			streamvouts[i]->form->printBase(out, 0);
			out << " __fp_temp_" << streamvouts[i]->name->name << ";\n";		
			// For streams that are declared as streams of arrays, we also need a control port, which will trigger reads/writes from/to the FIFO
			ArrayType *temp_array = (ArrayType *) streamvouts[i]->form;
			if (version == 1 && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array)) {
					out << "volatile char  *" << streamvouts[i]->name->name << "_br2c_ctrl;\n";
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array)) {
					out << "volatile char  *" << streamvouts[i]->name->name << "_br2c_ctrl;\n";
				}
			}
		}
		for (unsigned int i=0; i<reduces; i++) {
			// We don't have a FIFO when the reduce argument is a scalar
			// (cases when reduce_index is 1 or 4)
			if ((reduce_index != 1) && (reduce_index != 4)) {
				out << "volatile ";
				streamreduces[i]->form->printBase(out, 0);
				out << " *" << streamreduces[i]->name->name << " = 876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_";
				out << function_name << ";\n";
			}
			// For each reduce stream, we also create a "local copy" of that stream.
			// This is because the code may perform many operations to produce an element
			// We only want to write to the output FIFO when the computation is complete
			// For all cases other than reduce_index == 5, we allocate a temporary scalar variable
			if (reduce_index == 5) {
				// For reduce_index == 5, we have to allocate a piece of local memory that will be used to store the result
				// For now we will make the temporary variable a pointer. However, since C2H does not support dynamic memory allocation,
				// we will deal with this later by creating instances of the reduce kernel that are customized to the size of the input stream
				streamreduces[i]->form->printBase(out, 0);
				out << " *__fp_temp_" << streamreduces[i]->name->name << ";\n";
			} else {
				streamreduces[i]->form->printBase(out, 0);
				out << " __fp_temp_" << streamreduces[i]->name->name << ";\n";
			} // end if
		} // end for
	} // end if

	// Analyze the statements inside the block
	Statement *func_ste;
#ifdef DEBUG
	int i=0;
#endif
	for (func_ste = ste->head; func_ste; func_ste = func_ste->next) {
		handleBlockStatement (func_ste, function_name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, reduce_index, version, statement_count, main_function, declarations_with_initializations, replication_factor, replica_number, out);
		if (func_ste->needSemicolon())
			out << ";" << endl;
		else
			out << endl;
#ifdef DEBUG
		out << endl << "BLOCKSTATEMENT_NUMBER " << i << ": ";
		i++;
		switch (func_ste->type) {
			case 0:	out << "ST_NullStemnt"; break;
			case 1:	out << "ST_DeclStemnt"; break;
			case 2:	out << "ST_TypedefStemnt"; break;
			case 3:	out << "ST_ExpressionStemnt"; break;
			case 4:	out << "ST_IfStemnt"; break;
			case 5:	out << "ST_SwitchStemnt"; break;
			case 6:	out << "ST_ForStemnt"; break;
			case 7:	out << "ST_WhileStemnt"; break;
			case 8:	out << "ST_DoWhileStemnt"; break;
			case 9:	out << "ST_ContinueStemnt"; break;
			case 10:	out << "ST_BreakStemnt"; break;
			case 11:	out << "ST_GotoStemnt"; break;
			case 12:	out << "ST_ReturnStemnt"; break;
			case 13:	out << "ST_Block"; break;
			case 14:	out << "ST_BRTKernel"; break; // Doesn't seem to be recognized!!!!!!!!!!!!!
			case 15:	out << "ST_BRTScatter"; break;
			case 16:	out << "ST_FileLineStemnt"; break;
			case 17:	out << "ST_InclStemnt"; break;
			case 18:	out << "ST_EndInclStemnt"; break;
			case 19:	out << "ST_PPDirective"; break;
			default: out << "Unknown statement type"; break;
		}
		out << endl;
#endif
	}

#ifdef DEBUG
	out << endl << "reduce_index = " << reduce_index << ", version = " << version;
#endif

	// This is a good spot to write out elements to their FIFOs
	// But only if this is the outermost block in the function
	if (!block_inside_block) {
		for (unsigned int i=0; i<outs; i++) {
			ArrayType *temp_array = (ArrayType *) streamouts[i]->form;
			// Copy the results to outputs

			if (version == 1 && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array)) {
					// For streams that are declared as streams of arrays, we do this by writing to the control port
					out << "\n*" << streamouts[i]->name->name << "_br2c_ctrl = 0;\n";
				} else {
					out << "\n*" << streamouts[i]->name->name << " = __fp_temp_" << streamouts[i]->name->name << ";\n";
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array)) {
					// For streams that are declared as streams of arrays, we do this by writing to the control port
					out << "\n*" << streamouts[i]->name->name << "_br2c_ctrl = 0;\n";
				} else {
					out << "\n*" << streamouts[i]->name->name << " = __fp_temp_" << streamouts[i]->name->name << ";\n";
				}
			}
		}
		// Also we need to switch to the next input element here
		for (unsigned int i=0; i<ins; i++) {
			ArrayType *temp_array = (ArrayType *) streamins[i]->form;
			// For streams that are declared as streams of arrays, switch to the next input element
			// But only if this is not streamWrite-type kernel, which handles it differently (through pop operator)
			if (version == 1 && reduce_index < 3) {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (outs != 0)) {
					out << "*" << streamins[i]->name->name << "_br2c_ctrl = 0;\n";
				}
			} else {
				if ((temp_array->subType != NULL) && (temp_array->subType->type == TT_Array) && (((ArrayType *)temp_array->subType)->subType->type == TT_Array) && (outs != 0)) {
					out << "*" << streamins[i]->name->name << "_br2c_ctrl = 0;\n";
				}
			}
		}
		// We have to output the result of reduction for case 5 here (although it is conditional)
		// The condition will be tested at runtime.
		if (reduce_index == 5) {
			if (replication_factor > 1) {
				out << "\n\t\tif (((brcc_mod_div_iterator_0 == (( " << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_0 << " " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_0 << " )-1))) && ((brcc_mod_div_iterator_1 == (( " << BRCC_REDUCE_TOTAL_ELEMENTS_TAG_1 << " " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << BRCC_REDUCE_OUTPUT_ELEMENTS_TAG_1 << " )-1)))) {";
			} else {
				out << "\n\t\tif (((brcc_mod_div_iterator_0 == (( " << streamins[0]->name->name << "_brcc_stream_size_0 " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << streamreduces[0]->name->name << "_brcc_stream_size_0 )-1))) && ((brcc_mod_div_iterator_1 == (( " << streamins[0]->name->name << "_brcc_stream_size_1 " << BRCC_DIV_OPERATOR_TO_REPLACE << " " << streamreduces[0]->name->name << "_brcc_stream_size_1 )-1)))) {";
			}
			for (unsigned int i=0; i<reduces; i++) {
				// Copy the results to outputs
				out << "\n\t\t\t*" << streamreduces[i]->name->name << " = __fp_temp_" << streamreduces[i]->name->name << "[(" << streamreduces[0]->name->name << "_brcc_stream_size_1 == 1) ? 0 : (brcc_div_result_2)];\n";
			}
			out << "\t\t}";
		}
	} // end if 
	// Always finish a block with closed curly bracket
	out << endl << "}" << endl;

	// Depending on the value of reduce_index, outputing reduce variables should be done at different times.
	// This is a good spot to write reduce elements to their FIFOs
	// But only if this is the outermost block in the function
	if (!block_inside_block) {
		switch (reduce_index) {
		case 1:
		case 3:
		// Although we normally output results for case 2 elsewhere, we still have to output the last element here.
		case 2:
			for (unsigned int i=0; i<reduces; i++) {
				// Copy the results to outputs
				out << "\n*" << streamreduces[i]->name->name << " = __fp_temp_" << streamreduces[i]->name->name << ";\n";
			}
			break;
		} // end switch
	} // end if
	out << endl;
}


/*
* handleProgramStatement --
* FP
*	Takes care of generic statements inside the program (not the ones embedded inside the blocks)
*	Other than the program itself (Classes project and TransUnit), class Statement 
*	is the highest element in the hierarchy representing the program
*	This function does NOT call itself recursively
*		Analyzes a given statement and depending on the statement type calls appropriate functions to
*		analyze different statement types
*	Inputs:
*		A pointer to the statement, a reference to the vector with pointers to all known functions
*		and the output stream reference, for printing the code to an outfile
*/
void handleProgramStatement (Statement *ste, DeclVector &funcdecs, vector <PPDirective *> &ppdirectives, stringstream &out) {

	// The statement_count variable will keep track of the assignments inside the block
	// This is only needed to distinguish between the first and all other assignments
	unsigned int statement_count = 0;
	bool main_function = false;
#ifdef DEBUG
	out << "Inside handleProgramStatement" << endl;
#endif
	switch (ste->type) {
	case ST_Block:
#ifdef DEBUG
		out << "Inside ST_Block" << endl;
#endif
		if (ste->isFuncDef()) {
			FunctionDef *function;
			function = (FunctionDef *) ste;
			Decl *declaration = function->decl;
			funcdecs.push_back (declaration);
			StreamInfoVector declstreams;
			Decl **streamins=NULL, **streamouts=NULL, **nonstreamargs=NULL, **streamreduces=NULL, **streamvouts=NULL;
			var_info *in_info;
			unsigned int ins=0, outs=0, nonstreams=0, reduces=0, vouts=0;
			if (declaration == NULL) {
				cerr << endl << "FunctionDef with a NULL declaration pointer" << endl;
				exit (1);
			}
			if (declaration->isKernel()) {
				// At this point we know that the function is a kernel
#ifdef DEBUG
				out << endl << endl << endl;
				out << " " << "KERNEL FOUND" << " ";
				out << endl << endl << endl;
#endif
				// Make sure that the kernel's type is function (so that we can cast it later without problems)
				if (declaration->form->isFunction()) {
					// Make sure that it's return type is void
					BaseType *bt = declaration->form->getBase();
					if (!(bt->typemask & BT_Void)) {
						cerr << endl << "Return type of a kernel " << declaration->name->name << " is not void" << endl;
						exit (1);
					}
					FunctionType *ft = (FunctionType *) declaration->form;
#ifdef DEBUG
					out << "This kernel has " << ft->nArgs << " arguments: " << endl;
#endif
					countKernelArgs (ft, ins, outs, reduces, nonstreams, vouts);
#ifdef DEBUG
					out << endl << endl << endl;
					out << "There are " << ins << " ins, " << outs << " outs, " << vouts << " vouts and " << nonstreams << " other arguments";
					out << endl << endl << endl;
#endif
					if (ins != 0) {
						streamins = new (Decl *[ins]);
						in_info = new var_info[ins];
					}
					if (outs != 0)
						streamouts = new (Decl *[outs]);
					if (reduces != 0)
						streamreduces = new (Decl *[reduces]);
					if (nonstreams != 0)
						nonstreamargs = new (Decl *[nonstreams]);
					if (vouts != 0)
						streamvouts = new (Decl *[vouts]);
					// Find if the current function is supposed to be replicated
					// By default, there should be only one replica
					int replication_factor=1;
					// Search the vector backwards (i.e. the latest pragma with this function name takes precedence
					for (std::vector<struct replication_info>::reverse_iterator iter_i=globals.replicated_kernels.rbegin(); iter_i<globals.replicated_kernels.rend(); ++iter_i) {
						// Search the vector backwards
						if (iter_i->function_name == declaration->name->name) {
							replication_factor = iter_i->replication_factor;
							break;
						}
					}
					if (declaration->isReduce()) {
						// If the kernel is a reduce kernel, we should generate three different versions of the kernel
						// We declare an array of pointers to FunctionDef. reduce_kernel[0] will point to the original code
						// (which will not be modified in case we need it at some later point)
						// whereas 5 copies of the code will be created, each one implementing different implementation:
						//  1. Takes a 1-D stream and produces a scalar
						//	2. in streams should be 1-D, and reduce stream should be 1-D
						//	3. in streams should be 2-D, and reduce stream should be 2-D with the number of cols = 1 - CURRENTLY UNUSED (the generated code is never called)
						//	4. in streams should be 2-D, and reduce stream should be a scalar
						//	5. in streams should be 2-D, and reduce stream should be 2-D with possibly unrelated row and col sizes

						FunctionDef **reduce_kernel = new FunctionDef*[6];
						reduce_kernel[0] = function;
						for (unsigned int i=1; i<6; i++) {
							for (unsigned int var_i=0; var_i<ins; var_i++)
								in_info[var_i].used = false;
							reduce_kernel[i] = (FunctionDef *) function->dup0();
							out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432\n";
							handleKernelArgs (reduce_kernel[i]->decl, streamins, streamouts, streamreduces, nonstreamargs, streamvouts, ins, outs, reduces, nonstreams, vouts, declstreams, ste->location, i, 1, 0, out);
							// A function definition is always a block, so the following cast is safe
							handleBlock (reduce_kernel[i], declaration->name->name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, i, 1, statement_count, main_function, false, replication_factor, 0, out);
							// Handle closing curly brackets at the end of for loops.
							if (i > 1) {
								if (i == 4) {
									out << "}\n";
									for (unsigned int i=0; i<reduces; i++) {
										// Copy the results to outputs
										out << "\n*" << streamreduces[i]->name->name << " = __fp_temp_" << streamreduces[i]->name->name << ";\n";
									}
									out << "}\n";
								} else if (i == 2) {
									out << "}\n";
								} else {
									out << "}}" << endl;
								}
							} else {
								if (outs > 0) {
									out << "}" << endl;
								}
							}
							statement_count = 0;
							out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432\n";
						}
						// Delete all the structures created in this part of the code
						for (unsigned int i=1; i<6; i++) {
							delete reduce_kernel[i];
						}
						delete [] reduce_kernel;
						// Free up memory
						if (streamins != NULL) {
							delete [] streamins;
							delete [] in_info;
							streamins = NULL;
							in_info = NULL;
						}
						if (streamouts != NULL) {
							delete [] streamouts;
							streamouts = NULL;
						}
						if (streamreduces != NULL) {
							delete [] streamreduces;
							streamreduces = NULL;
						}
						if (nonstreamargs != NULL) {
							delete [] nonstreamargs;
							nonstreamargs = NULL;
						}
						if (streamvouts != NULL) {
							delete [] streamvouts;
							streamvouts = NULL;
						}
						// This will exit the case statement
						break;
					}

					// If the kernel is not a reduce kernel, we handle it differently
					// For each kernel we define 2 versions of a kernel:
					//		1. Kernel that handles only 1-dimensional streams
					//		2. Kernel that handles only 2-dimensional streams
					// Create 2 copies of the kernel for this purpose (or actually 2 times the replication factor)
					FunctionDef **kernel = new FunctionDef*[2*replication_factor+1];
					// kernel[0] will preserve the original function
					// We only want to modify this function once, in all other cases we will create duplicates that we will work on
					kernel[0] = function;
					for (int replica=0; replica<2*replication_factor; replica++) {
						kernel[replica+1] = (FunctionDef *) function->dup0();
					}
					for (int replica=0; replica<replication_factor; replica++) {
						statement_count = 0;
						out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432\n";
						for (unsigned int i=0; i<ins; i++) {
							in_info[i].used = false;
						}
						handleKernelArgs (kernel[2*replica]->decl, streamins, streamouts, streamreduces, nonstreamargs, streamvouts, ins, outs, reduces, nonstreams, vouts, declstreams, ste->location, 0, 1, replica, out);
						handleBlock (kernel[2*replica], declaration->name->name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, 0, 1, statement_count, main_function, false, replication_factor, replica, out);
						// Handle closing curly brackets at the end of for loops.
						// But only if there is at least one out stream (otherwise there is no 'for' loop)
						if (outs > 0) {
							out << "}" << endl;
						}
						out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432\n";
					}
					for (int replica=0; replica<replication_factor; replica++) {
						statement_count = 0;
						out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432\n";
						for (unsigned int i=0; i<ins; i++) {
							in_info[i].used = false;
						}
						handleKernelArgs (kernel[2*replica+1]->decl, streamins, streamouts, streamreduces, nonstreamargs, streamvouts, ins, outs, reduces, nonstreams, vouts, declstreams, ste->location, 0, 2, replica, out);
						handleBlock (kernel[2*replica+1], declaration->name->name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, 0, 2, statement_count, main_function, false, replication_factor, replica, out);
						// Handle closing curly brackets at the end of for loops.
						// But only if there is at least one out stream (otherwise there are no 'for' loop)
						if (outs > 0) {
							out << "}}" << endl;
						}
						if (replication_factor > 1) {
							out << "}" << endl;
						}
						out << "\n3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432\n";
					}
					// Delete all the structures created in this part of the code
					for (int replica=0; replica<2*replication_factor; replica++) {
						delete kernel[replica+1];
					}
					delete [] kernel;

					// Free up memory
					if (streamins != NULL) {
						delete [] streamins;
						delete [] in_info;
						streamins = NULL;
						in_info = NULL;
					}
					if (streamouts != NULL) {
						delete [] streamouts;
						streamouts = NULL;
					}
					if (streamreduces != NULL) {
						delete [] streamreduces;
						streamreduces = NULL;
					}
					if (nonstreamargs != NULL) {
						delete [] nonstreamargs;
						nonstreamargs = NULL;
					}
					if (streamvouts != NULL) {
						delete [] streamvouts;
						streamvouts = NULL;
					}
					// This will exit the case statement
					break;


#ifdef DEBUG
					out << endl << endl << endl;
					out << "Other arguments: ";
					for (unsigned int i=0; i<nonstreams; i++) {
						out <<  nonstreamargs[i]->name->name << " ";
					}
					out << endl << endl << endl;
#endif
				} else {
					cerr << endl << "The type of the kernel " << declaration->name->name << " is not function type" << endl;
					exit (1);
				}
			} else {
				// It's a regular (not kernel) declaration
				// Since some of its arguments might still be streams, we have to replace any such arguments into arrays
				if (declaration->form->isFunction()) {
					bool main_function = false;
					FunctionType *ft = (FunctionType *) declaration->form;
					if (declaration->name->name == "main") {	
						main_function = true;
#ifdef DEBUG
						out << "\nfunctiondef FOUND!!!!!!!!\n";
#endif
					}

#ifdef DEBUG
					out << "This function has " << ft->nArgs << " arguments: " << endl;
#endif
					countKernelArgs (ft, ins, outs, reduces, nonstreams, vouts);
#ifdef DEBUG
					out << endl << endl << endl;
					out << "There are " << ins << " ins, " << outs << " outs, " << vouts << " vouts and " << nonstreams << " other arguments";
					out << endl << endl << endl;
#endif
					if (ins != 0) {
						streamins = new (Decl *[ins]);
						in_info = new var_info[ins];
					}
					if (outs != 0)
						streamouts = new (Decl *[outs]);
					if (reduces != 0)
						streamreduces = new (Decl *[reduces]);
					if (nonstreams != 0)
						nonstreamargs = new (Decl *[nonstreams]);
					if (vouts != 0)
						streamvouts = new (Decl *[vouts]);

					handleKernelArgs (declaration, streamins, streamouts, streamreduces, nonstreamargs, streamvouts, ins, outs, reduces, nonstreams, vouts, declstreams, ste->location, 0, 1, -1, out);
#ifdef DEBUG
					out << endl << endl << endl;
					out << "Other arguments: ";
					for (unsigned int i=0; i<nonstreams; i++) {
						out <<  nonstreamargs[i]->name->name << " ";
					}
					out << endl << endl << endl;
#endif
				}
				out << endl;
			}
			// A function definition is always a block, so the following cast is safe
			handleBlock (((Block *)ste), declaration->name->name, funcdecs, declstreams, streamouts, outs, streamins, in_info, ins, streamreduces, reduces, streamvouts, vouts, ppdirectives, 0, 0, statement_count, main_function, false, -1, -1, out);
			// Free up memory
			if (streamins != NULL) {
				delete [] streamins;
				delete [] in_info;
				streamins = NULL;
				in_info = NULL;
			}
			if (streamouts != NULL) {
				delete [] streamouts;
				streamouts = NULL;
			}
			if (streamreduces != NULL) {
				delete [] streamreduces;
				streamreduces = NULL;
			}
			if (nonstreamargs != NULL) {
				delete [] nonstreamargs;
				nonstreamargs = NULL;
			}
			if (streamvouts != NULL) {
				delete [] streamvouts;
				streamvouts = NULL;
			}
		} else {
			// If statement is a block, but not a function definition (is this even possible at this hierarchy level?), simply print it out
			ste->print(out, 0);
		}
		break;
	case ST_DeclStemnt:
	{
#ifdef DEBUG
		out << "Inside ST_DeclStemnt" << endl;
#endif
		// Declarations could be kernels or functions taking stream arguments
		// Therefore, they should be handled by replacing any stream arguments into array arguments
		DeclVector decls = ((DeclStemnt *) ste)->decls;
		if (!decls.empty()) {
			for (DeclVector::iterator iter=decls.begin(); iter != decls.end(); iter++) {
				if ((*iter)->form != NULL && (*iter)->form->isFunction()) {
#ifdef DEBUG
					out << "Declaration is a function" << endl;
#endif
					Decl **streamins=NULL, **streamouts=NULL, **nonstreamargs=NULL, **streamreduces=NULL, **streamvouts=NULL;
					StreamInfoVector declstreams;
					unsigned int ins=0, outs=0, nonstreams=0, reduces=0, vouts=0;
					// If the declaration is a function (kernel or not), we should analyze its arguments
					// to see if any of them is a stream, and handle them appropriately
// __FIX_ME - Passing streams to ordinary functions has never been tested and likely wouldn't work
//            The code and comments referring to such functionality originated from the first version
//            of this source-to-source compiler, when the plan was to target a multiprocessor machine
//            In that compiler, streams were converted to arrays.
					FunctionType *ft = (FunctionType *) ((*iter)->form);
#ifdef DEBUG
					out << "This kernel has " << ft->nArgs << " arguments: " << endl;
#endif
					countKernelArgs (ft, ins, outs, reduces, nonstreams, vouts);
#ifdef DEBUG
					out << endl << endl << endl;
					out << "There are " << ins << " ins, " << outs << " outs, " << vouts << " vouts and " << nonstreams << " other arguments";
					out << endl << endl << endl;
#endif
					if (ins != 0)
						streamins = new (Decl *[ins]);
					if (outs != 0)
						streamouts = new (Decl *[outs]);
					if (reduces != 0)
						streamreduces = new (Decl *[reduces]);
					if (nonstreams != 0)
						nonstreamargs = new (Decl *[nonstreams]);
					if (vouts != 0)
						streamvouts = new (Decl *[vouts]);


// __FIX_ME - We will assume that there are no forward declarations of reduction kernels. This is a simplifying assumption.
//			  If this functionality is needed in the future, it should be handled similarly to the ordinary function declarations above.
					handleKernelArgs ((*iter), streamins, streamouts, streamreduces, nonstreamargs, streamvouts, ins, outs, reduces, nonstreams, vouts, declstreams, ste->location, 0, 1, -1, out);
#ifdef DEBUG
					out << endl << endl << endl;
					out << "Other arguments: ";
					for (unsigned int i=0; i<nonstreams; i++) {
						out <<  nonstreamargs[i]->name->name << " ";
					}
					out << endl << endl << endl;
#endif
					if (streamins != NULL) {
						delete [] streamins;
						streamins = NULL;
					}
					if (streamouts != NULL) {
						delete [] streamouts;
						streamouts = NULL;
					}
					if (streamreduces != NULL) {
						delete [] streamreduces;
						streamreduces = NULL;
					}
					if (nonstreamargs != NULL) {
						delete [] nonstreamargs;
						nonstreamargs = NULL;
					}
					if (streamvouts != NULL) {
						delete [] streamvouts;
						streamvouts = NULL;
					}
				} else {
					// If it's not a function, simply print it out
					ste->print (out, 0);
#ifdef DEBUG
					out << "Declaration is NOT a function" << endl;
#endif
				}
			}
			// Declarations require semicolon
			out << ";" << endl;
		} else {
			ste->print (cerr, 0);
			fatal ("Empty declaration");
		}
		break;
	}
	case ST_PPDirective:
	{
#ifdef DEBUG
		out << "Inside ST_PPDirective case" << endl;
#endif
		ppdirectives.push_back ((PPDirective *)ste);
		size_t pos;
		string directive = ((PPDirective *)ste)->directive;
		pos = directive.find('#');
		if (directive.substr(pos+1, 6) == "pragma") {
			if ((pos = directive.find_first_of(BRCC_REPLICATE_PRAGMA, pos+7)) != string::npos) {
				// Extract the name of the function and the replication factor
				string func_name;
				int replication_factor;
				pos = pos + strlen(BRCC_REPLICATE_PRAGMA);
				// Find first non-whitespace character
				while ((pos != directive.length()) && ((directive[pos] == ' ') || (directive[pos] == '\t') || (directive[pos] == '\n'))) {
					pos++;
				}
				// Find first whitespace character
				size_t pos2 = pos+1;
				while ((pos2 != directive.length()) && ((directive[pos2] != ' ') && (directive[pos2] != '\t') && (directive[pos2] != '\n'))) {
					pos2++;
				}
				// pos and pos2 now point to beginning and end (actually the character after the end) of the kernel to be replicated
				func_name = directive.substr(pos, pos2-pos);
				pos = pos2;
				// Find first non-whitespace character
				while ((pos != directive.length()) && ((directive[pos] == ' ') || (directive[pos] == '\t') || (directive[pos] == '\n'))) {
					pos++;
				}
				// pos now points to thew replication factor
				stringstream temp_ss;
				temp_ss.str(directive.substr(pos));
				if (!(temp_ss >> replication_factor)) {
					cerr << "\nError: Could not locate the number containing a replication factor into an integer in a preprocessor directive \"" << directive << "\"" << endl;
					exit (1);
				}
				// Add the replication factor to the globals
				struct replication_info rinfo;
				rinfo.function_name = func_name;
				rinfo.replication_factor = replication_factor;
				globals.replicated_kernels.push_back (rinfo);
#ifdef DEBUG
				out << "\nFound replication pragma\n";
				out << "Function: \"" << func_name << "\"\n";
				out << "Replication factor: " << replication_factor;
#endif
				// No need to print out the replication pragma because it is not used by C2H. All replication is handled in this compiler.
				break;
			}
		}
#ifdef DEBUG
		out << "Size of ppdirectives is: " << ppdirectives.size();
#endif
		// If the statement is not any of the above types, it doesn't require special handling, so simply print it out
		ste->print(out, 0);
		break;
	}
	default:
	{
#ifdef DEBUG
		out << "Inside default case" << endl;
#endif
		// If the statement is not any of the above types, it doesn't require special handling, so simply print it out
		ste->print(out, 0);
		break;
	}
	}
}


/*
 * main --
 *
 *      The main function parses the arguments to get the input Brook file, compiles the requested file
 *      and drives the other functions to emit C2H code, Verilog code, and SOPC system description.
 */

int main(int argc, char *argv[]) {
	Project *proj;
	TransUnit *tu;

	// Parse command-line arguments
	parse_args(argc, argv);
	if (globals.verbose) {
		cerr << "***Compiling " << globals.sourcename << "\n";
	}

	// Read CONFIGURATION.cfg file to determine values for customizable parameters
	readConfigFile ();

	proj = new Project();
	// Parse the input file
	tu = proj->parse(globals.sourcename, false, NULL, false, NULL, NULL, NULL);
	// If parsing was successful
	if (tu) {
		// Output .c file
		ofstream out;
		// Output .dot files for non-replicated and replicated streaming dataflow graphs.
		ofstream dot, dot_replicated;

		out.open(globals.coutputname);
		if (out.fail()) {
			cerr << "***Unable to open " << globals.coutputname << "\n";
			exit(1);
		}
		dot.open(globals.dataflowdotfilename);
		if (dot.fail()) {
			cerr << "***Unable to open " << globals.dataflowdotfilename << "\n";
			exit(1);
		}
		dot_replicated.open(globals.dataflowreplicateddotfilename);
		if (dot_replicated.fail()) {
			cerr << "***Unable to open " << globals.dataflowreplicateddotfilename << "\n";
			exit(1);
		}

		// Print headers that should be included in all files
		out << "#include <stdlib.h>" << endl;
		out << "#include <stdio.h>" << endl;
		out << "#include \"system.h\"" << endl;

		// Stream that will hold C2H pragma statements
		ostringstream pragma_stream;

		// temp_out will hold the majority of the output program before we print it to the actual output file
		stringstream temp_out;
		Statement * ste;
		// Vector for function declarations.
		DeclVector funcdecs;

		// Vector of pointers to pre-processor directives (mainly used to handle defines)
		vector <PPDirective *> ppdirectives;
		int i;
		// Traverse all the program statements
		for (i=1,ste=tu->head; ste; i++,ste=ste->next) {
#ifdef DEBUG
			out << "STATEMENT_NO " << i << "  (";
			switch (ste->type) {
				case 0:	out << "ST_NullStemnt"; break;
				case 1:	out << "ST_DeclStemnt"; break;
				case 2:	out << "ST_TypedefStemnt"; break;
				case 3:	out << "ST_ExpressionStemnt"; break;
				case 4:	out << "ST_IfStemnt"; break;
				case 5:	out << "ST_SwitchStemnt"; break;
				case 6:	out << "ST_ForStemnt"; break;
				case 7:	out << "ST_WhileStemnt"; break;
				case 8:	out << "ST_DoWhileStemnt"; break;
				case 9:	out << "ST_ContinueStemnt"; break;
				case 10:	out << "ST_BreakStemnt"; break;
				case 11:	out << "ST_GotoStemnt"; break;
				case 12:	out << "ST_ReturnStemnt"; break;
				case 13:	out << "ST_Block"; break;
				case 14:	out << "ST_BRTKernel"; break; // Doesn't seem to be recognized!!!!!!!!!!!!!
				case 15:	out << "ST_BRTScatter"; break;
				case 16:	out << "ST_FileLineStemnt"; break;
				case 17:	out << "ST_InclStemnt"; break;
				case 18:	out << "ST_EndInclStemnt"; break;
				case 19:	out << "ST_PPDirective"; break;
				default: out << "Unknown statement type"; break;
			}
			out << endl;
#endif

			// Program statement processing is actually done in the handleProgramStatement function
			handleProgramStatement (ste, funcdecs, ppdirectives, temp_out);
#ifdef DEBUG
			out << ")" << ":" << *ste << endl;
#endif
		}

#ifdef DEBUG
		for (unsigned int h=0; h<funcdecs.size(); h++) {
			cerr << "\n" << funcdecs[h]->name->name;
		}
		out << "CHECKPOINT 1";
		out << endl << temp_out.str();
		globals.dataflow.debugPrint();
#endif

		// After dataflow analysis determines input and output streams, call connect function to add edges
		// to form correct streaming dataflow graph. See Graph::Connect inside of graph.cpp for details.
		globals.dataflow.Connect ();

#ifdef DEBUG
		globals.dataflow.debugPrintEdges ();
		cout << "\nReplicating!";
#endif
	
		// Pass the dataflow graph to the Graph::Replicate method to perform kernel replication
		globals.replicated.Replicate (globals.dataflow);

		// Output both graphs to appropriate files
		std::stringstream dot_ss, dot_replicated_ss;
		globals.dataflow.printDot (dot_ss);
		globals.replicated.printDot (dot_replicated_ss);
		dot << dot_ss.str();
		dot_replicated << dot_replicated_ss.str();

#ifdef DEBUG
		cout << "\nReplicated!";
		globals.replicated.debugPrint();
		globals.replicated.debugPrintEdges();
#endif

		// After the graph has been replicated, we can generate replicas for reduction streams
		generateReductionReplicas (temp_out);

		// Next, we can generate C2H pragmas that specify how pointers inside kernels connect to FIFO's
		generatePragmas (pragma_stream, temp_out);
	
#ifdef DEBUG
		cout << "\nCollectors:";
		for (unsigned int i=0; i< globals.collectors.size(); i++) {
			cout << endl << globals.collectors[i].name << ", ins: " << globals.collectors[i].inputs << ", outs: " << globals.collectors[i].outputs;
		}
		cout << "\nDistributors:";
		for (unsigned int i=0; i< globals.distributors.size(); i++) {
			cout << endl << globals.distributors[i].name << ", ins: " << globals.distributors[i].inputs << ", outs: " << globals.distributors[i].outputs;
		}
		cout << "\nCollectors/Distributors:";
		for (unsigned int i=0; i< globals.collectors_distributors.size(); i++) {
			cout << endl << globals.collectors_distributors[i].name << ", ins: " << globals.collectors_distributors[i].inputs << ", outs: " << globals.collectors_distributors[i].outputs;
		}
#endif



		// Before we print out the program, we will get rid of any versions of kernel functions that are not used
		// We will do this by finding tags that were inserted before and after every such function
		// If there is any FIFO variable that hasn't been initialized with a pointer to FIFO, such function
		// can be removed. We will recognize variables that have not been initialized by another tag.

		string temp_storage = temp_out.str();
		size_t f_start, f_end, v_uninit;
		string str_begin = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_BEGIN_7762905432";
		string str_end = "3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432";
		string str_var = "876532_BRCC_INTERNAL_VAR_REPLACE_INIT_ADDRESS_111843_";
		f_start = temp_storage.find (str_begin);
		while (f_start != string::npos) {
			f_end = temp_storage.find ("3165248_BRCC_INTERNAL_VAR_REPLACE_KERNEL_FUNCTION_END_7762905432", f_start+1);
			if (f_end == string::npos) {
				fatal ("Could not find a matching kernel end tag!");
			}
			v_uninit = temp_storage.find (str_var, f_start+1);
			// In case the uninitialized variable is between start and end, we have to eliminate the whole function
			if ((v_uninit != string::npos) && (v_uninit < f_end)) {
				temp_storage.replace (f_start, (f_end+str_end.length())-f_start, "");
			} else {
				// Otherwise we just have to remove the begin and end tags
				// Be careful to first eliminate the end tag, because if we eliminate the begin first,
				// it will change the position of the end
				temp_storage.replace (f_end, str_end.length(), "");
				temp_storage.replace (f_start, str_begin.length(), "");
			}
			f_start = temp_storage.find (str_begin, f_start);
		}
		temp_out.str(temp_storage);
		// Put write pointer for out stream to the end
		// (the above command puts that pointer to the beginning of the file),
		temp_out.seekp(0, ios_base::end);

		// There might be a modulo or division operator in the code that we may want to get rid of
		// This function only handles modulo and division operators with constant operands
		replaceBRCCDivMod (temp_out);

		// Also get rid of the BRCC_MOD_DIV_DECL, BRCC_MOD_DIV_BLOCK, and other tags leftover from the first compiler pass
		removeTags (temp_out);

		// Output C2H pragmas to the top of the outpu file
		out << "\n// ****************** START C2H PRAGMAS ***************************";
		out << pragma_stream.str() << "\n// ****************** END C2H PRAGMAS ***************************\n\n";

		// Output the rest of the program
		out << temp_out.str();
		
		// Close all the output files
		out.close();
		dot.close();
		dot_replicated.close();
#ifdef DEBUG
		for (unsigned int i=0; i<globals.replicated_kernels.size(); i++) {
			cout << "\nFound replication pragma\n";
			cout << "Function: \"" << globals.replicated_kernels[i].function_name << "\"\n";
			cout << "Replication factor: " << globals.replicated_kernels[i].replication_factor;
		}
#endif

		// The following function takes care of all the SOPC components needed to support the application.
		createSOPCFiles ();

   } else {
	   cerr << "***Unable to parse " << globals.sourcename << endl;
	   exit(1);
   }

   if (globals.verbose) {
	   cerr << "***Successfully compiled " << globals.sourcename << "\n";
   }
   delete proj;
   exit(0);
   return 0;
}