// ToyBoxFDTDbezhig, version 1.0
//      An if-I-can-do-it-you-can-do-it FDTD! 
// Copyright (C) 1998, 1999 Laurie E. Miller, Paul Hayes, Matthew O'Keefe 

// 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 2
//     of the License, or any later version, with the following conditions
//     attached in addition to any and all conditions of the GNU
//     General Public License:
//     When reporting or displaying any results or animations created
//     using this code or modification of this code, make the appropriate
//     citation referencing ToyBoxFDTDbezhig by name and including the 
//     version number.  
//
// 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., 59 Temple Place, Suite 330, Boston, MA  
//     02111-1307  USA

// Contacting the authors:
//
// Laurie E. Miller, Paul Hayes, Matthew O'Keefe
// Department of Electrical and Computer Engineering
//      200 Union Street S. E.
//      Minneapolis, MN 55455
//
// lemiller@lcse.umn.edu
// info@cemtach.org
// 
// http://cemtach.org
// http://cemtach.org/software/ToyBoxFDTD/ToyBoxFDTD.html


// MEA: This file is derived from the modified version of "ToyBoxFDTDbezhig.cpp" found in 
//      /home/maiello/Development/FDTDDevelopment/FDTDDevelopment-Linux/VolumnRendering
//
//






#include <math.h> 
#include <stdio.h>         
#include <float.h>
#include <malloc.h>
#include <stdlib.h>

// Only one selection allowed at a time...

     // The original plane-wave test
//#define ORIGINAL_PLANE_WAVE_TEST

     // Un-comment if we are simulating a guassian pulsed electrical current flow through a thin conductor running in the "x" direction
//#define SIMULATE_Y_CONDUCTING_ELEMENT_TEST

     // Un-comment if we are simulating a guassian ramped (continuous) electrical current flow through a thin conductor running in the "x" direction
#define SIMULATE_Y_CONDUCTING_ELEMENT_TEST_CONTINUOUS




#ifdef ORIGINAL_PLANE_WAVE_TEST

// constants that turn on/off the new features
//#define PMC
          // If uncommented, this line includes the PMC part of the code at 
          //     compile time.  If this line is commented out, the open sides of
          //     the waveguide revert to PEC and the guide becomes rectangular
          //     rather than parallel-plate.
//#define SINUSOIDAL_PULSE_STIMULUS
          // If uncommented, this line includes the sinusoidal pulse as the 
          //     stimulus for the simulation.  If this line is commented out,
          //     the stimulus will be a simplified continuous plane wave. 



//#define FIXED_SCALING_VALUE 100.0   //(This is my static override of the original scaling mechanisms listed above)

// program control constants

#define FREQUENCY 10.0e9
          // frequency of the stimulus in Hertz
#define GUIDE_WIDTH 0.0229
          // meters -- section of the width of the guide to be simulated
          //    -- the parallel plate waveguide is actually infinite in width
#define GUIDE_HEIGHT 0.0102
          // interior height of the guide in meters
#define LENGTH_IN_WAVELENGTHS 4.5
          // length of the waveguide in wavelengths of the stimulus


#endif 


#ifdef SIMULATE_Y_CONDUCTING_ELEMENT_TEST


#define FREQUENCY 10.0e9
          // frequency of the stimulus in Hertz
#define GUIDE_WIDTH 0.04
          // meters -- section of the width of the guide to be simulated
          //    -- the parallel plate waveguide is actually infinite in width
#define GUIDE_HEIGHT 0.04
          // interior height of the guide in meters
#define LENGTH_IN_WAVELENGTHS 2.0
          // length of the waveguide in wavelengths of the stimulus

#endif 

#ifdef SIMULATE_Y_CONDUCTING_ELEMENT_TEST_CONTINUOUS


#define FREQUENCY 10.0e9
          // frequency of the stimulus in Hertz
#define GUIDE_WIDTH 0.04
          // meters -- section of the width of the guide to be simulated
          //    -- the parallel plate waveguide is actually infinite in width
#define GUIDE_HEIGHT 0.04
          // interior height of the guide in meters
#define LENGTH_IN_WAVELENGTHS 2.0
          // length of the waveguide in wavelengths of the stimulus

#endif 



// physical constants
#define LIGHT_SPEED     299792458.0       
          // speed of light in a vacuum in meters/second
#define LIGHT_SPEED_SQUARED 89875517873681764.0        
          // m^2/s^2
#define MU_0            (M_PI*4.0e-7)         
          // permeability of free space in henry/meter
#define EPSILON_0       (1.0/(MU_0*LIGHT_SPEED_SQUARED))      
          // permittivity of free space in farad/meter






/////////////////////////////////////////////////////////////////////////////
// variable declarations (file local)
static int i,j,k;     
           // indices of the 3D array of cells
static int allocatedBytes = 0;          
           // a counter to track number of bytes allocated
static int iteration = 0;          
           // counter to track how many timesteps have been computed
static double stimulus = 0.0;       
           // value of the stimulus at a given timestep
static  double currentSimulatedTime = 0.0;
           // time in simulated seconds that the simulation has progressed
static  double totalSimulatedTime = 0.0;       
           // time in seconds that will be simulated by the program
static  double omega;       
           // angular frequency in radians/second
static  double lambda;       
           // wavelength of the stimulus in meters
static  double dx, dy, dz; 
           // space differentials (or dimensions of a single cell) in meters
static  double dt;
           // time differential (how much time between timesteps) in seconds
static  double dtmudx, dtepsdx;       
           // physical constants used in the field update equations 
static  double dtmudy, dtepsdy;       
           // physical constants used in the field update equations 
static  double dtmudz, dtepsdz;       
           // physical constants used in the field update equations 


// variable declarations (global)
double ***ex, ***ey, ***ez;  
           // pointers to the arrays of ex, ey, and ez values
double ***hx, ***hy, ***hz;
           // pointers to the arrays of hx, hy, and hz values
int nx, ny, nz;         
           // total number of cells along the x, y, and z axes, respectively

double ***Jx, ***Jy, ***Jz;           
unsigned char *** PEC;
   
// **** FEM version of original FDTD algorythm ****************************************************************************************************


typedef double temp_array_50_50[50][50];
temp_array_50_50 * array_50_50_tmp;         //(used by a lot of functions!)

#define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })



	//Variable size matrix multiply code in "C" from
	//  https://rosettacode.org/wiki/Matrix_multiplication

/* Make the data structure self-contained.  Element at row i and col j
   is x[i * w + j].  More often than not, though,  you might want
   to represent a matrix some other way */
typedef struct { int h, w; double *x;} matrix_t, *matrix;

inline double dot(double *a, double *b, int len, int step)
{
	double r = 0;
	while (len--) {
		r += *a++ * *b;
		b += step;
	}
	return r;
}

matrix mat_new(int h, int w)
{				//MEA: Changed malloc() to calloc()
	matrix r = (matrix_t *) calloc(sizeof(matrix_t) + sizeof(double) * w * h, sizeof(char));
	r->h = h, r->w = w;
	r->x = (double*)(r + 1);
	return r;
}

matrix mat_mul(matrix a, matrix b)
{
	matrix r;
	double *p, *pa;
	int i, j;
	if (a->w != b->h) return 0;

	r = mat_new(a->h, b->w);
	p = r->x;
	for (pa = a->x, i = 0; i < a->h; i++, pa += a->w)
		for (j = 0; j < b->w; j++)
			*p++ = dot(pa, b->x + j, a->w, b->w);
	return r;
}

	//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_mulab_run(matrix a, matrix b, matrix r)
{
	double *p, *pa;
	int i, j;

	p = r->x;
	for (pa = a->x, i = 0; i < a->h; i++, pa += a->w)
		for (j = 0; j < b->w; j++)
			*p++ = dot(pa, b->x + j, a->w, b->w) ;
}

	//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_addab_run(matrix a, matrix b, matrix r)
{
	int i;

	for(i = 0; i < a->h * a->w; i++)
		(r->x)[i] = (a->x)[i] + (b->x)[i];
}

//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_subab_run(matrix a, matrix b, matrix r)
{
	int i;

	for(i = 0; i < a->h * a->w; i++)
		(r->x)[i] = (a->x)[i] - (b->x)[i];
}

//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_transpose_run(matrix a, matrix r)
{
	int i,j;
	temp_array_50_50 * pa_x;
	temp_array_50_50 * pr_x;

	pa_x = ( temp_array_50_50 *) a->x;
	pr_x = ( temp_array_50_50 *) r->x;

	for(i = 0; i < 50; i++)
		for(j = 0; j < 50; j++)
			(* pr_x)[j][i] = (* pr_x)[i][j];

}
//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_3D_array_to_mat_run(double ***  pMesh, char axis, int axis_index, matrix r)
{
	int i,j,k;
	temp_array_50_50 * pr_x;

	pr_x = ( temp_array_50_50 *) r->x;

	if(axis == 'x')
	{
        for(j = 0; j < 50; j++)
            for(k = 0; k < 50; k++)
                (* pr_x)[j][k] = pMesh[axis_index][j][k];
	}
	else if(axis == 'y')
	{
        for(i = 0; i < 50; i++)
            for(k = 0; k < 50; k++)
                (* pr_x)[i][k] = pMesh[i][axis_index][k];
	}
	else //axis == 'z'
	{
        for(i = 0; i < 50; i++)
            for(j = 0; j < 50; j++)
                (* pr_x)[i][j] = pMesh[i][j][axis_index];
	}
}

//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_3D_array_to_mat_transpose_run(double ***  pMesh, char axis, int axis_index, matrix r)
{
    int i,j,k;
    temp_array_50_50 * pr_x;

    pr_x = ( temp_array_50_50 *) r->x;

    if(axis == 'x')
    {
        for(j = 0; j < 50; j++)
            for(k = 0; k < 50; k++)
                (* pr_x)[k][j] = pMesh[axis_index][j][k];
    }
    else if(axis == 'y')
    {
        for(i = 0; i < 50; i++)
            for(k = 0; k < 50; k++)
                (* pr_x)[k][i] = pMesh[i][axis_index][k];
    }
    else //axis == 'z'
    {
        for(i = 0; i < 50; i++)
            for(j = 0; j < 50; j++)
                (* pr_x)[j][i] = pMesh[i][j][axis_index];
    }
}

//MEA: Added this to be used in runtime mode (no bounds checks).
void mat_mat_to_3D_array_run(matrix a,  double ***  pMesh, char axis, int axis_index)
{
	int i,j,k;
	temp_array_50_50 * pa_x;

	pa_x = ( temp_array_50_50 *) a->x;


    if(axis == 'x')
    {
        for(j = 0; j < 50; j++)
            for(k = 0; k < 50; k++)
                pMesh[axis_index][j][k] = (* pa_x)[j][k];
    }
    else if(axis == 'y')
    {
        for(i = 0; i < 50; i++)
            for(k = 0; k < 50; k++)
                pMesh[i][axis_index][k] = (* pa_x)[i][k];
    }
    else //axis == 'z'
    {
        for(i = 0; i < 50; i++)
            for(j = 0; j < 50; j++)
                pMesh[i][j][axis_index] = (* pa_x)[i][j];
    }

}




void mat_show(matrix a)
{
	int i, j;
	double *p = a->x;
	for (i = 0; i < a->h; i++, putchar('\n'))
		for (j = 0; j < a->w; j++)
			printf("\t%7.3f", *p++);
	putchar('\n');
}

void mat_demo(void)
{
//#define ORIGINAL_MAT_DEMO
#ifdef  ORIGINAL_MAT_DEMO
	double da[] = {	1, 1,  1,   1,
			2, 4,  8,  16,
			3, 9, 27,  81,
			4,16, 64, 256	};
	double db[] = {     4.0,   -3.0,  4.0/3,
			-13.0/3, 19.0/4, -7.0/3,
			  3.0/2,   -2.0,  7.0/6,
			 -1.0/6,  1.0/4, -1.0/6};

	matrix_t a = { 4, 4, da }, b = { 4, 3, db };
	matrix c = mat_mul(&a, &b);

	/* mat_show(&a), mat_show(&b); */
	mat_show(c);
	 free(c);

	 	 //Produces
	 // 1.000	  0.000	 -0.000
	 // 0.000	  1.000	 -0.000
	 // 0.000	  0.000	  1.000
	 // 0.000	  0.000	  0.000
	 //
	 // Matches exactly with Octave.

#else
	 	 // Test matrix multiply of a vector...

	double da[] = {	1, 1,  1,   1,
			2, 4,  8,  16,
			3, 9, 27,  81,
			4,16, 64, 256	};
	double db[] = {     4.0,
			-13.0/3,
			  3.0/2,
			 -1.0/6};

	matrix_t a = { 4, 4, da }, b = { 4, 1, db };
	matrix c = mat_mul(&a, &b);

	/* mat_show(&a), mat_show(&b); */
	mat_show(c);
	 free(c);

		 //Produces
	 	 //  1.000
	 	 //  0.000
	 	 //  0.000
	 	 //  0.000
	 	 //
	     // Matches exactly with Octave.


#endif

}

//Matrix inverse. From https://followtutorials.com/2011/09/numerical-methods-inverse-of-nxn-matrix-using-c.html
//                  			(Keywords: "Programming Techniques - Numerical Methods: Inverse of nxn matrix using C")

//#define DEMO_MATRIX_INVERSE
#ifdef DEMO_MATRIX_INVERSE
		//(If used, called from Simulation.cc).
		//Don't use larger the 3x3 in this demo. Also a "0" on the diagonal fails (this is addressed one of the comments
		// in link above.. We never will have a "0" on the diagonal so ignore this problem.
		//3x3 checked against octave, OK.
		//
		//  Reference  https://blog.gtiwari333.com/2009/12/c-c-code-gauss-jordan-method-for.html   is cited as
		// as a solution for "0" on the diagonal but is misleading! Code fixes only a "0" in a specific position.
		// (It also has multiple typo mistakes!)
		// Octave must use a more elaborate method like the following to handle this condition.
		//
		// https://www.mathsisfun.com/algebra/matrix-inverse-row-operations-gauss-jordan.html
		//
		// Finally, note that I had to modify the code so as to reference variable "double foo[][]" array representation.
		// so it is compatible with the "matrix_mul()" routine above.

void demo_matrix_inverse(void){

   // float matrix[10][10], ratio,a;
	float matrix[100], ratio,a;
    int i, j, k, n;
    printf("Enter order of matrix: ");
    scanf("%d", &n);
    printf("Enter the matrix: \n");
    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
        //    scanf("%f", &matrix[i][j]);
        	scanf("%f", &matrix[i*2*n+j]);
        }
    }

#else

void matrix_inverse(double  * matrix , int n){
	double ratio, a;
	int i, j, k;
#endif

    for(i = 0; i < n; i++){
        for(j = n; j < 2*n; j++){
            if(i==(j-n))
        //        matrix[i][j] = 1.0;
            	matrix[i*2*n+j] = 1.0;
            else
         //       matrix[i][j] = 0.0;
            	matrix[i*2*n+j] = 0.0;
        }
    }
    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            if(i!=j){
               // ratio = matrix[j][i]/matrix[i][i];
            	ratio = matrix[j*2*n + i]/matrix[i*2*n+i];
                for(k = 0; k < 2*n; k++){
           //         matrix[j][k] -= ratio * matrix[i][k];
                	matrix[j*2*n+k] -= ratio * matrix[i*2*n+k];
                }
            }
        }
    }
    for(i = 0; i < n; i++){
       // a = matrix[i][i];
    	a = matrix[i*2*n+i];
        for(j = 0; j < 2*n; j++){
       //     matrix[i][j] /= a;
        	matrix[i*2*n+j] /= a;
        }
    }

#ifdef DEMO_MATRIX_INVERSE
    printf("The inverse matrix is: \n");
    for(i = 0; i < n; i++){
        for(j = n; j < 2*n; j++){
      //      printf("%.2f", matrix[i][j]);
        	printf("%.2f", matrix[i*2*n+j]);
            printf("\t");
        }
        printf("\n");
    }
#endif

}


matrix Me_inv_S;     //Inverse(Me) * S
matrix Mh_inv_S_T;  //Inverse(Mh) * S_T

	// variable declarations (global) work matrix's

matrix  m_tmp_1,  m_tmp_2,  m_tmp_3;



	//FEM required parameters and matrixes via ./Documentation/StallcupIsaacA2018.pdf
int n_max;
double d_max;




void Init_ToyFDTDbezhig_FEM(void)
{

	  // wavelength in meters:
	  lambda = LIGHT_SPEED/FREQUENCY;
	  // angular frequency in radians/second:
	  omega = 2.0*M_PI*FREQUENCY;

	  // set ny and dy:
	  // start with a small ny:
	  ny = 3;
	  // calculate dy from the guide width and ny:
	  dy = GUIDE_WIDTH/ny;
	  // until dy is less than a twenty-fifth of a wavelength,
	  //     increment ny and recalculate dy:
	  while(dy >= lambda/25.0)
	    {
	      ny++;
	      dy = GUIDE_WIDTH/ny;
	    }

	  // set nz and dz:
	  // start with a small nz:
	  nz = 3;
	  // calculate dz from the guide height and nz:
	  dz = GUIDE_HEIGHT/nz;
	  // until dz is less than a twenty-fifth of a wavelength,
	  //     increment nz and recalculate dz:
	  while(dz >= lambda/25.0)
	    {
	      nz++;
	      dz = GUIDE_HEIGHT/nz;
	    }

	  // set dx, nx, and dt:
	  // set dx equal to dy or dz, whichever is smaller:
	  dx = (dy < dz) ? dy : dz;
	  // choose nx to make the guide LENGTH_IN_WAVELENGTHS
	  //     wavelengths long:
	  nx = (int)(LENGTH_IN_WAVELENGTHS*lambda/dx);
	  // chose dt for Courant stability:
	  dt = 1.0/(LIGHT_SPEED*sqrt(1/(dx*dx) + 1.0/(dy*dy) + 1.0/(dz*dz)));

	  //In the case of FEM, base simulation size on largest of phyical dimension calculated above.
	  n_max = max(max(nx, ny), nz);
	  d_max = max(max(dx, dy), dz);

	  //As per information obtained from ./Documentation/StallcupIsaacA2018.pdf...

	  int i,j;

	  double recp_dt = 1.0 / dt;

	  // **** Create "Me_inv_S: ****


	  matrix S_tmp = mat_new(n_max, n_max);
	  	  //Size to be used with matrix_inverse()
	  matrix Me_inv_tmp1 = mat_new(n_max, 2*n_max);


	  	  //Trick to view a 1-D array as a 2-D array  so as t debug in Eclipse.
	  	  //(See https://stackoverflow.com/questions/14808908/pointer-to-2d-arrays-in-c)
	  typedef double view_temp_Me_inv_tmp1[50][100];
	  view_temp_Me_inv_tmp1 * view_Me_inv_tmp1;  //Assumes  n_max computed to 50.
	  view_Me_inv_tmp1 = (view_temp_Me_inv_tmp1 *) Me_inv_tmp1->x;


	  //Populate the temporary Electric Mass Matrix for inversion.
	  // (EPSILON_0  and d_max and recp_dt  similar to FDTD Equation 3.4 and 3.5 of StallcupIsaccA2018.pdf)
	  // (d_max and recp_dt similar to FEM Equation 4.13 of StallcupIsaccA2018.pdf)
	  //(i => row, j => column)
	  for(i=0; i<n_max; i++)
		  for(j=0; j<n_max; j++)
		  {
			  	  if(i == j)
			  	  {
						  //First the center of the tri-diagonal
					  Me_inv_tmp1->x [i*2*n_max + j] = d_max * EPSILON_0 *  recp_dt * (2.0/3.0);
						  //The lower part of the tri-diagonal.
					  if(i  != 0)
					  {
						  Me_inv_tmp1->x[i*2*n_max + j - 1] = d_max * EPSILON_0 *  recp_dt * (1.0/6.0);
					  }
						  //The upper part of the tri-diagonal.
					  if(i != (n_max-1))
					  {
						  Me_inv_tmp1->x[i*2*n_max + j + 1] = d_max * EPSILON_0 *  recp_dt * (1.0/6.0);
					  }
			  	  }
		  }

	  	  //Invert the Electric Mass Matrix.
	      //Verified with "Verify_Me_inv_tmp1_x.m")
	  matrix_inverse(Me_inv_tmp1->x, n_max);
	  	  //A second Me_inv temporary work matrix to
	  	  //be used with mat_mul()
	  matrix Me_inv_tmp2 = mat_new(n_max, n_max);




	  temp_array_50_50 * pMe_inv_tmp2_x = (temp_array_50_50 *)Me_inv_tmp2->x;

	  	  //Transfer result of Electric Mass Matrix inversion to
	  	  //a form that can be used by mat_mul();
	    for(i = 0; i < n_max; i++)
	        for(j = 0; j < n_max; j++)
	        	(* pMe_inv_tmp2_x)[i][j] = (* view_Me_inv_tmp1)[i][j+50] ;


	    free(Me_inv_tmp1);

			  //(For viewing S_tmp in the Debug Expression window.)
		 array_50_50_tmp =  (temp_array_50_50 *)  S_tmp->x;

	    //Populate the temporary S matrix..
	  	  //(i => row, j => column)
	  	  for(i=0; i<n_max; i++)
	  		  for(j=0; j<n_max; j++)
	  		  {
	  			  	  if(i == j)
	  			  	  {
	  						  //First the center of the tri-diagonal
	  			  		  S_tmp->x [i*n_max + j] = -1.0;

	  					     //The lower part of the tri-diagonal.
	  					  if(i  != 0)
	  			  		  {
	  						 S_tmp->x [i*n_max + j - 1] = 1.0;
	  					  }

	  			  	  }
	  		  }

	  	  	  //Determine the operation matrix, Inverse(Me) * S
	  	Me_inv_S = mat_mul(Me_inv_tmp2, S_tmp);


	  	 	 //(For viewing Me_inv_S in the Debug Expression window.)
	  	array_50_50_tmp =  (temp_array_50_50 *)  Me_inv_S->x;

	  	free(S_tmp);




		  // **** Create "Mh_inv_S_T: ****


	  matrix S_T_tmp = mat_new(n_max, n_max);
		  //Size to be used with matrix_inverse()
	  matrix Mh_inv_tmp1 = mat_new(n_max, 2*n_max);

	  	  //Trick to view a 1-D array as a 2-D array  so as t debug in Eclipse.
	  	  //(See https://stackoverflow.com/questions/14808908/pointer-to-2d-arrays-in-c)
	  typedef double view_temp_Mh_inv_tmp1[50][100];
	  view_temp_Mh_inv_tmp1 * view_Mh_inv_tmp1;  //Assumes  n_max computed to 50.
	  view_Mh_inv_tmp1 = (view_temp_Mh_inv_tmp1 *) Mh_inv_tmp1->x;


	  //Populate the temporary Magnetic Mass Matrix for inversion.
	  // (MU_0  and d_max and recp_dt  similar to FDTD Equation 3.4 and 3.5 of StallcupIsaccA2018.pdf)
	  // (d_max and recp_dt similar to FEM Equation 4.13 of StallcupIsaccA2018.pdf)
	  //(i => row, j => column)
	  for(i=0; i<n_max; i++)
		  for(j=0; j<n_max; j++)
		  {
			  	  if(i == j)
			  	  {
						  //The identity matix scaled by d_max.
					  Mh_inv_tmp1->x [i*2*n_max + j] = d_max * MU_0 *  recp_dt;
			  	  }
		  }

	  	  //Invert the Magnetic Mass Matrix.
	  matrix_inverse(Mh_inv_tmp1->x, n_max);
	  	  //A second Mh_inv temporary work matrix to
	  	  //be used with mat_mul()
	  matrix Mh_inv_tmp2 = mat_new(n_max, n_max);


	  temp_array_50_50 * pMh_inv_tmp2_x = (temp_array_50_50 *)Mh_inv_tmp2->x;

	  	  //Transfer result of Magnetic Mass Matrix inversion to
	  	  //a form that can be used by mat_mul();
	    for(i = 0; i < n_max; i++)
	        for(j = 0; j < n_max; j++)
	        	(* pMh_inv_tmp2_x)[i][j] = (* view_Mh_inv_tmp1)[i][j+50] ;


	    free(Mh_inv_tmp1);

			  //(For viewing S_T_tmp in the Debug Expression window.)
		 array_50_50_tmp =  (temp_array_50_50 *)  S_T_tmp->x;

	    //Populate the temporary S_T matrix..
	  	  //(i => row, j => column)
	  	  for(i=0; i<n_max; i++)
	  		  for(j=0; j<n_max; j++)
	  		  {
	  			  	  if(i == j)
	  			  	  {
	  						  //First the center of the tri-diagonal
	  			  		  S_T_tmp->x [i*n_max + j] = -1.0;

						  //The upper part of the tri-diagonal.
	  			  		  if(i != (n_max-1))
	  			  		  {
	  			  			 S_T_tmp->x [i*n_max + j +1] = 1.0;
	  			  		  }

	  			  	  }
	  		  }

	  	  	  //Determine the operation matrix, Inverse(Mh) * S_T
	  	Mh_inv_S_T = mat_mul(Mh_inv_tmp2, S_T_tmp);


	  	 	 //(For viewing Mh_inv_S_T in the Debug Expression window.)
	  		 //Verified with Verify_Mh_inv_S_T_x.m
	  	array_50_50_tmp =  (temp_array_50_50 *)  Mh_inv_S_T->x;

	  	free(S_T_tmp);





	    	// Allocate work arrays.
	  	 m_tmp_1  = mat_new(n_max, n_max);
	  	 m_tmp_2  = mat_new(n_max, n_max);
	  	 m_tmp_3  = mat_new(n_max, n_max);


		  // Allocate memory for the E field arrays:

		  // allocate the array of ex components:
		  ex = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      ex[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  ex[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      ex[i][j][k] = 0.0;
			    }
			}
		    }


		  // allocate the array of ey components:
		  ey = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      ey[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  ey[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      ey[i][j][k] = 0.0;
			    }
			}
		    }


		  // allocate the array of ez components:
		  ez = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      ez[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  ez[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      ez[i][j][k] = 0.0;
			    }
			}
		    }


		  // Allocate the H field arrays workspace:


		  // allocate the array of hx components:
		  hx = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      hx[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  hx[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      hx[i][j][k] = 0.0;
			    }
			}
		    }


		  // allocate the array of hy components:
		  hy = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      hy[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  hy[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      hy[i][j][k] = 0.0;
			    }
			}
		    }


		  // allocate the array of hz components:
		  hz = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      hz[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  hz[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      hz[i][j][k] = 0.0;
			    }
			}
		    }



		  // Allocate memory for the J source arrays:

		  // allocate the array of Jx components:
		  Jx = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      Jx[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  Jx[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      Jx[i][j][k] = 0.0;
			    }
			}
		    }

		  // allocate the array of Jy components:
		  Jy = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      Jy[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  Jy[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      Jy[i][j][k] = 0.0;
			    }
			}
		    }


		  // allocate the array of Jz components:
		  Jz = (double ***)malloc((n_max)*sizeof(double **));
		  for(i=0; i<(n_max); i++)
		    {
		      Jz[i] = (double **)malloc((n_max)*sizeof(double *));
		      for(j=0; j<(n_max); j++)
			{
			  Jz[i][j] = (double *)malloc((n_max)*sizeof(double));
			  for(k=0; k<(n_max); k++)
			    {
			      Jz[i][j][k] = 0.0;
			    }
			}
		    }

		  // constants used in the field update equations:
		  //NOTE: This is used only if DEBUG_MESH_CALCULATIONS_<xx>
		  //           is defined below.
		dtmudx = dt/(MU_0*d_max);
		dtepsdx = dt/(EPSILON_0*d_max);
		dtmudy = dt/(MU_0*d_max);
		dtepsdy = dt/(EPSILON_0*d_max);
		dtmudz = dt/(MU_0*d_max);
		dtepsdz = dt/(EPSILON_0*d_max);

}


void DoInteration_FEM(void)
{
    iteration++;

    // time in simulated seconds that the simulation has progressed:
    currentSimulatedTime = dt*(double)iteration;


#ifdef SIMULATE_Y_CONDUCTING_ELEMENT_TEST_CONTINUOUS

//#define CONDUCTOR_RUNNING_IN_X_AXIS
#ifdef CONDUCTOR_RUNNING_IN_X_AXIS

    for(i=10; i<(n_max-10); i++)
	{     //(Scaling is arbitrary in this test. What ever gives us the best visual effect)
	  // (See http://en.wikipedia.org/wiki/Gaussian_function or sub-directory "Documentation")

	  if(currentSimulatedTime <= 1.155499e-10)
	    Jx[i][n_max/2][n_max/2] =  (1.0/(.5*sqrt(2.0*M_PI)))*
	      exp(-((currentSimulatedTime - (50*2.265686119e-12))*(currentSimulatedTime - (50*2.265686119e-12)))/(2.0*(10*2.265686119e-12)*(10*2.265686119e-12)));
	  else
	    Jx[i][n_max/2][n_max/2] = .79788456;

	}
#endif

#define CONDUCTOR_RUNNING_IN_Y_AXIS
#ifdef CONDUCTOR_RUNNING_IN_Y_AXIS

    for(j=10; j<(n_max-10); j++)
    {     //(Scaling is arbitrary in this test. What ever gives us the best visual effect)
      // (See http://en.wikipedia.org/wiki/Gaussian_function or sub-directory "Documentation")

      if(currentSimulatedTime <= 1.155499e-10)
        Jy[n_max/2][j][n_max/2] =  (1.0/(.5*sqrt(2.0*M_PI)))*
          exp(-((currentSimulatedTime - (50*2.265686119e-12))*(currentSimulatedTime - (50*2.265686119e-12)))/(2.0*(10*2.265686119e-12)*(10*2.265686119e-12)));
      else
        Jy[n_max/2][j][n_max/2] = .79788456;

    }
#endif

//#define CONDUCTOR_RUNNING_IN_Z_AXIS
#ifdef CONDUCTOR_RUNNING_IN_Z_AXIS

    for(k=10; k<(n_max-10); k++)
    {     //(Scaling is arbitrary in this test. What ever gives us the best visual effect)
      // (See http://en.wikipedia.org/wiki/Gaussian_function or sub-directory "Documentation")

      if(currentSimulatedTime <= 1.155499e-10)
        Jz[n_max/2][n_max/2][k] =  (1.0/(.5*sqrt(2.0*M_PI)))*
          exp(-((currentSimulatedTime - (50*2.265686119e-12))*(currentSimulatedTime - (50*2.265686119e-12)))/(2.0*(10*2.265686119e-12)*(10*2.265686119e-12)));
      else
        Jz[n_max/2][n_max/2][k] = .79788456;

    }
#endif

#endif

      	  //Refer to Equation  2.6 and Equation 4.13 of StallcupIsaccA2018.pdf

//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB



    /////////////////////////////////////////////////////////////////////////
    // Update the interior of the mesh:

    // Update all the H field vector components within the mesh:

    // Update the hx values:
//    for(i=0; i<(nx-1); i++)
//	{
//	  for(j=0; j<(ny); j++)
//	    {
//	      for(k=0; k<(nz); k++)
//		{
//		  hx[i][j][k] += (dtmudz*(ey[i+1][j][k+1] - ey[i+1][j][k]) -
//		                  dtmudy*(ez[i+1][j+1][k] - ez[i+1][j][k]));
//		}
//	    }
//	}
//#define DEBUG_MESH_CALCULATIONS_HX
#ifdef  DEBUG_MESH_CALCULATIONS_HX

    for(i=0; i<(n_max-1); i++)
   	{
   	  for(j=0; j<(n_max-1); j++)
   	    {
   	      for(k=0; k<(n_max-1); k++)
   		{
   		  hx[i][j][k] += (dtmudz*(ey[i+1][j][k+1] - ey[i+1][j][k]) -
   		                  dtmudy*(ez[i+1][j+1][k] - ez[i+1][j][k]));
   		}
   	    }
   	}



#else
    for(i=0; i<(n_max-1); i++)
    {
    	 // Create temp ey  matrix.
        mat_3D_array_to_mat_run(ey, 'y', i, m_tmp_1);
    	 // Create temp ez matrix
        mat_3D_array_to_mat_run(ez, 'z', i, m_tmp_2);
    	// Subtract m_tmp_1 from m_tmp_2
    	mat_subab_run(m_tmp_1, m_tmp_2, m_tmp_1);
    	//Apply FEM transformation
    	mat_mulab_run(Mh_inv_S_T, m_tmp_1, m_tmp_2);
    	// Do time iteration.
    	mat_3D_array_to_mat_run(hx, 'x', i, m_tmp_1);
    	mat_subab_run(m_tmp_1, m_tmp_2, m_tmp_1);
    	mat_mat_to_3D_array_run(m_tmp_1, hx, 'x', i);

     }
#endif


    // Update the hy values:
//    for(i=0; i<(nx); i++)
//	{
//	  for(j=0; j<(ny-1); j++)
//	    {
//	      for(k=0; k<(nz); k++)
//		{
//		  hy[i][j][k] +=  (dtmudx*(ez[i+1][j+1][k] - ez[i][j+1][k]) -
 //   		                   dtmudz*(ex[i][j+1][k+1] - ex[i][j+1][k]));
//		}
//	    }
//	}
#define DEBUG_MESH_CALCULATIONS_HY
#ifdef  DEBUG_MESH_CALCULATIONS_HY

	for(i=0; i<(n_max-1); i++)
	{
	  for(j=0; j<(n_max-1); j++)
		{
		  for(k=0; k<(n_max-1); k++)
		{
		  hy[i][j][k] +=  (dtmudx*(ez[i+1][j+1][k] - ez[i][j+1][k]) -
							   dtmudz*(ex[i][j+1][k+1] - ex[i][j+1][k]));
		}
		}
	}

#else

#endif



    // Update the hz values:
 //   for(i=0; i<(nx); i++)
//	{
//	  for(j=0; j<(ny); j++)
//	    {
//	      for(k=0; k<(nz-1); k++)
//		{
//		  hz[i][j][k] +=  (dtmudy*(ex[i][j+1][k+1] - ex[i][j][k+1]) -
//    		                   dtmudx*(ey[i+1][j][k+1] - ey[i][j][k+1]));
//		}
//	    }
//	}
#define DEBUG_MESH_CALCULATIONS_HZ
#ifdef  DEBUG_MESH_CALCULATIONS_HZ

    for(i=0; i<(n_max-1); i++)
	{
	  for(j=0; j<(n_max-1); j++)
	    {
	      for(k=0; k<(n_max-1); k++)
		{
		  hz[i][j][k] +=  (dtmudy*(ex[i][j+1][k+1] - ex[i][j][k+1]) -
    		                   dtmudx*(ey[i+1][j][k+1] - ey[i][j][k+1]));
		}
	    }
	}

#else

#endif



    // Update the E field vector components.


    // Update the ex values:
//    for(i=0; i<(nx); i++)
//	{
//	  for(j=1; j<(ny); j++)
//	    {
//	      for(k=1; k<(nz); k++)
//		{
//		    ex[i][j][k] = (dtepsdy*(hz[i][j][k-1] - hz[i][j-1][k-1]) -
//				   dtepsdz*(hy[i][j-1][k] - hy[i][j-1][k-1])) - Jx[i][j][k];
//		}
//	    }
//	}


#define DEBUG_MESH_CALCULATIONS_EX
#ifdef  DEBUG_MESH_CALCULATIONS_EX

    for(i=0; i<(n_max-1); i++)
	{
	  for(j=1; j<(n_max-1); j++)
	    {
	      for(k=1; k<(n_max-1); k++)
		{
		    ex[i][j][k] = (dtepsdy*(hz[i][j][k-1] - hz[i][j-1][k-1]) -
				   dtepsdz*(hy[i][j-1][k] - hy[i][j-1][k-1])) - Jx[i][j][k];
		}
	    }
	}

#else

#endif





    // Update the ey values:
//    for(i=1; i<(nx); i++)
//	{
//	  for(j=0; j<(ny); j++)
//	    {
//	      for(k=1; k<(nz); k++)
//		{
//		    ey[i][j][k] = (dtepsdz*(hx[i-1][j][k] - hx[i-1][j][k-1]) -
//				   dtepsdx*(hz[i][j][k-1] - hz[i-1][j][k-1]))- Jy[i][j][k];
//		}
//	    }
//	}
#define DEBUG_MESH_CALCULATIONS_EY
#ifdef  DEBUG_MESH_CALCULATIONS_EY

    for(i=1; i<(n_max-1); i++)
	{
	  for(j=0; j<(n_max-1); j++)
	    {
	      for(k=1; k<(n_max-1); k++)
		{
		    ey[i][j][k] = (dtepsdz*(hx[i-1][j][k] - hx[i-1][j][k-1]) -
				   dtepsdx*(hz[i][j][k-1] - hz[i-1][j][k-1]))- Jy[i][j][k];
		}
	    }
	}

#else

#endif


    // Update the ez values:
//    for(i=1; i<(nx); i++)
//	{
//	  for(j=1; j<(ny); j++)
//	    {
//	      for(k=0; k<(nz); k++)
//		{
//		    ez[i][j][k] = (dtepsdx*(hy[i][j-1][k] - hy[i-1][j-1][k]) -
//				   dtepsdy*(hx[i-1][j][k] - hx[i-1][j-1][k])) - Jz[i][j][k];
//		}
//	    }
//	}


#define DEBUG_MESH_CALCULATIONS_EZ
#ifdef  DEBUG_MESH_CALCULATIONS_EZ

    for(i=1; i<(n_max-1); i++)
	{
	  for(j=1; j<(n_max-1); j++)
	    {
	      for(k=0; k<(n_max-1); k++)
		{
		    ez[i][j][k] = (dtepsdx*(hy[i][j-1][k] - hy[i-1][j-1][k]) -
				   dtepsdy*(hx[i-1][j][k] - hx[i-1][j-1][k])) - Jz[i][j][k];
		}
	    }
	}

#else

#endif




//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
























      	  	  	  // Update all the H field vector components:
      	  	  	  // (Position)

      	  // Update the hxz values:
//      mat_subab_run(hx_, hz_, hxz_a);
//      mat_mulab_run(Me_inv_S, hxz_a, ey_m);

      	  // Update the hyx values:
//      mat_subab_run(hy_, hx_, hyx_a);
//      mat_mulab_run(Me_inv_S, hyx_a, ez_m);

      	  // Update the hzy values:
 //     mat_subab_run(hz_, hy_, hzy_a);
//      mat_mulab_run(Me_inv_S, hzy_a, ex_m);

      	  	  	  // Update the E field vector components.
      	  	  	  // (Position)

      	  // Update the exz values:
//      mat_subab_run(ex_, ez_, exz_a);
//      mat_mulab_run(Mh_inv_S_T, exz_a, hy_m);

      	  // Update the eyx values:
 //     mat_subab_run(ey_, ex_, eyx_a);
 //     mat_mulab_run(Mh_inv_S_T, eyx_a, hz_m);

      	  // Update the ezy values:
 //     mat_subab_run(ez_, ey_, ezy_a);
//      mat_mulab_run(Mh_inv_S_T, ezy_a, hx_m);


      	  	  	  // Update all the H field vector components:
      	  	  	  // (Time)

		  // Update the hy values:
 //     mat_subab_run(hy_, ey_m, hy_);

		  // Update the hz values:
//      mat_subab_run(hz_, ez_m, hz_);

		  // Update the hx values:
  //    mat_subab_run(hx_, ex_m, hx_);

      	  	  	  // Update the E field vector components.
           	  	  // (Time)

      	  // Update the ey values:
 //     mat_addab_run(ey_, hy_m, ey_);

      	  // Update the ez values:
 //     mat_addab_run(ez_, hz_m, ez_);

      	  // Update the ex values:
  //    mat_addab_run(ex_, hx_m, ex_);








}







// *******************************************************************************************************************************************************

void Init_ToyFDTDbezhig(void)
{

  /////////////////////////////////////////////////////////////////////////////
  // setting up the problem to be modeled
  //
  // Modified from David K. Cheng, Field and Wave Electromagnetics, 2nd ed., 
  //     pages 554-555. 
  // Parallel plate waveguide, interior height = 1.02cm, infinite width of which
  //     2.29 cm is simulated.
  //
  // Choosing nx, ny, and nz:
  // There should be at least 20 cells per wavelength in each direction, 
  //     but we'll go with 25 so the animation will look prettier.    
  // The number of cells along the width of the guide and the width of 
  //    those cells should fit the simulated guide width exactly, so that ny*dy 
  //    = GUIDE_WIDTH meters.  
  //    The same should be true for nz*dz = GUIDE_HEIGHT meters.  
  // dx is chosen to be dy or dz -- whichever is smaller
  // nx is chosen to make the guide LENGTH_IN_WAVELENGTHS 
  //     wavelengths long.  
  // 
  // dt is chosen for Courant stability; the time step must be kept small 
  //     enough so that the plane wave only travels one cell length 
  //     (one dx) in a single timestep.  Otherwise FDTD cannot keep up 
  //     with the signal propagation, since FDTD computes a cell only from 
  //     it's immediate neighbors.  

  // wavelength in meters:
  lambda = LIGHT_SPEED/FREQUENCY; 
  // angular frequency in radians/second:
  omega = 2.0*M_PI*FREQUENCY; 

  // set ny and dy:
  // start with a small ny:
  ny = 3;  
  // calculate dy from the guide width and ny:
  dy = GUIDE_WIDTH/ny;
  // until dy is less than a twenty-fifth of a wavelength,
  //     increment ny and recalculate dy:
  while(dy >= lambda/25.0)
    {
      ny++;
      dy = GUIDE_WIDTH/ny;
    }

  // set nz and dz:
  // start with a small nz:
  nz = 3;  
  // calculate dz from the guide height and nz:
  dz = GUIDE_HEIGHT/nz;
  // until dz is less than a twenty-fifth of a wavelength,
  //     increment nz and recalculate dz:
  while(dz >= lambda/25.0)
    {
      nz++;
      dz = GUIDE_HEIGHT/nz;
    }

  // set dx, nx, and dt:
  // set dx equal to dy or dz, whichever is smaller:
  dx = (dy < dz) ? dy : dz;
  // choose nx to make the guide LENGTH_IN_WAVELENGTHS 
  //     wavelengths long:  
  nx = (int)(LENGTH_IN_WAVELENGTHS*lambda/dx);
  // chose dt for Courant stability:
  dt = 1.0/(LIGHT_SPEED*sqrt(1/(dx*dx) + 1.0/(dy*dy) + 1.0/(dz*dz)));
 

  // constants used in the field update equations:
  dtmudx = dt/(MU_0*dx);
  dtepsdx = dt/(EPSILON_0*dx);
  dtmudy = dt/(MU_0*dy);
  dtepsdy = dt/(EPSILON_0*dy);
  dtmudz = dt/(MU_0*dz);
  dtepsdz = dt/(EPSILON_0*dz);
  



  /////////////////////////////////////////////////////////////////////////////
  // memory allocation for the FDTD mesh:
  // There is a separate array for each of the six vector components, 
  //      ex, ey, ez, hx, hy, and hz.
  // The mesh is set up so that tangential E vectors form the outer faces of 
  //     the simulation volume.  There are nx*ny*nz cells in the mesh, but 
  //     there are nx*(ny+1)*(nz+1) ex component vectors in the mesh.
  //     There are (nx+1)*ny*(nz+1) ey component vectors in the mesh.
  //     There are (nx+1)*(ny+1)*nz ez component vectors in the mesh.
  // If you draw out a 2-dimensional slice of the mesh, you'll see why
  //     this is.  For example if you have a 3x3x3 cell mesh, and you 
  //     draw the E field components on the z=0 face, you'll see that
  //     the face has 12 ex component vectors, 3 in the x-direction
  //     and 4 in the y-direction.  That face also has 12 ey components,
  //     4 in the x-direction and 3 in the y-direction.  

  // Allocate memory for the E field arrays:

  // allocate the array of ex components:
  ex = (double ***)malloc((nx)*sizeof(double **));
  for(i=0; i<(nx); i++)
    {  
      ex[i] = (double **)malloc((ny+1)*sizeof(double *));
      for(j=0; j<(ny+1); j++)
	{
	  ex[i][j] = (double *)malloc((nz+1)*sizeof(double));
	  for(k=0; k<(nz+1); k++)
	    {
	      ex[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx)*(ny+1)*(nz+1) * sizeof(double));
  
  // allocate the array of ey components:
  ey = (double ***)malloc((nx+1)*sizeof(double **));
  for(i=0; i<(nx+1); i++)
    {  
      ey[i] = (double **)malloc((ny)*sizeof(double *));
      for(j=0; j<(ny); j++)
	{
	  ey[i][j] = (double *)malloc((nz+1)*sizeof(double));
	  for(k=0; k<(nz+1); k++)
	    {
	      ey[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx+1)*(ny)*(nz+1) * sizeof(double));

  // allocate the array of ez components:
  ez = (double ***)malloc((nx+1)*sizeof(double **));
  for(i=0; i<(nx+1); i++)
    {  
      ez[i] = (double **)malloc((ny+1)*sizeof(double *));
      for(j=0; j<(ny+1); j++)
	{
	  ez[i][j] = (double *)malloc((nz)*sizeof(double));
	  for(k=0; k<(nz); k++)
	    {
	      ez[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx+1)*(ny+1)*(nz) * sizeof(double));

  // Allocate the H field arrays:
  // Since the H arrays are staggered half a step off 
  //     from the E arrays in every direction, the H 
  //     arrays are one cell smaller in the x, y, and z 
  //     directions than the corresponding E arrays. 
  // By this arrangement, the outer faces of the mesh
  //     consist of E components only, and the H 
  //     components lie only in the interior of the mesh.  

  // allocate the array of hx components:
  hx = (double ***)malloc((nx-1)*sizeof(double **));
  for(i=0; i<(nx-1); i++)
    {  
      hx[i] = (double **)malloc((ny)*sizeof(double *));
      for(j=0; j<(ny); j++)
	{
	  hx[i][j] = (double *)malloc((nz)*sizeof(double));
	  for(k=0; k<(nz); k++)
	    {
	      hx[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx-1)*(ny)*(nz) * sizeof(double));
  
  // allocate the array of hy components:
  hy = (double ***)malloc((nx)*sizeof(double **));
  for(i=0; i<(nx); i++)
    {  
      hy[i] = (double **)malloc((ny-1)*sizeof(double *));
      for(j=0; j<(ny-1); j++)
	{
	  hy[i][j] = (double *)malloc((nz)*sizeof(double));
	  for(k=0; k<(nz); k++)
	    {
	      hy[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx)*(ny-1)*(nz) * sizeof(double));
  
  // allocate the array of hz components:
  hz = (double ***)malloc((nx)*sizeof(double **));
  for(i=0; i<(nx); i++)
    {  
      hz[i] = (double **)malloc((ny)*sizeof(double *));
      for(j=0; j<(ny); j++)
	{
	  hz[i][j] = (double *)malloc((nz-1)*sizeof(double));
	  for(k=0; k<(nz-1); k++)
	    {
	      hz[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx)*(ny)*(nz-1) * sizeof(double));




  // Allocate memory for the J source arrays:

  // allocate the array of Jx components:
  Jx = (double ***)malloc((nx)*sizeof(double **));
  for(i=0; i<(nx); i++)
    {  
      Jx[i] = (double **)malloc((ny+1)*sizeof(double *));
      for(j=0; j<(ny+1); j++)
	{
	  Jx[i][j] = (double *)malloc((nz+1)*sizeof(double));
	  for(k=0; k<(nz+1); k++)
	    {
	      Jx[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx)*(ny+1)*(nz+1) * sizeof(double));
  
  // allocate the array of Jy components:
  Jy = (double ***)malloc((nx+1)*sizeof(double **));
  for(i=0; i<(nx+1); i++)
    {  
      Jy[i] = (double **)malloc((ny)*sizeof(double *));
      for(j=0; j<(ny); j++)
	{
	  Jy[i][j] = (double *)malloc((nz+1)*sizeof(double));
	  for(k=0; k<(nz+1); k++)
	    {
	      Jy[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx+1)*(ny)*(nz+1) * sizeof(double));

  // allocate the array of Jz components:
  Jz = (double ***)malloc((nx+1)*sizeof(double **));
  for(i=0; i<(nx+1); i++)
    {  
      Jz[i] = (double **)malloc((ny+1)*sizeof(double *));
      for(j=0; j<(ny+1); j++)
	{
	  Jz[i][j] = (double *)malloc((nz)*sizeof(double));
	  for(k=0; k<(nz); k++)
	    {
	      Jz[i][j][k] = 0.0;
	    }
	}
    }
  allocatedBytes += ( (nx+1)*(ny+1)*(nz) * sizeof(double));

  // allocate the array of PEC flags:
  PEC = (unsigned char ***)malloc((nx+1)*sizeof(unsigned char **));
  for(i=0; i<(nx+1); i++)
    {  
      PEC[i] = (unsigned char **)malloc((ny+1)*sizeof(unsigned char *));
      for(j=0; j<(ny+1); j++)
	{
	  PEC[i][j] = (unsigned char *)malloc((nz)*sizeof(unsigned char));
	  for(k=0; k<(nz); k++)
	    {
	      PEC[i][j][k] = 0;
	    }
	}
    }





}


void DoInteration(void)
{
	int idx;


      iteration++;

      // time in simulated seconds that the simulation has progressed:
      currentSimulatedTime = dt*(double)iteration;  



  
#ifdef ORIGINAL_PLANE_WAVE_TEST


      /////////////////////////////////////////////////////////////////////////
      // Compute the stimulus: a sinusoidal pulse emanates from the x=0 face:
      //     The length of the guide lies in the x-direction, the width of the 
      //     guide lies in the y-direction, and the height of the guide lies
      //     in the z-direction.  So the guide is sourced by all the ez 
      //     components on the stimulus face.  
      // The pulse is one complete wavelength of a shifted sinusoid that 
      //     ranges from zero to one in intensity.  So the stimulus varies 
      //     sinusoidally from zero to one to zero again and then terminates
      //     --the x=0 face becomes a PEC thereafter.  
      //
      // compute the current stimulus value:
#ifdef SINUSOIDAL_PULSE_STIMULUS
      if (currentSimulatedTime <= 1.0/FREQUENCY)
	{
	  stimulus = .5*(1.0 + sin(omega*currentSimulatedTime - M_PI*.5));
	}
      else
	{
	  stimulus = 0.0;
	}
#else
      stimulus = sin(omega*currentSimulatedTime);
#endif
      // set all vectors on the x=0 face to the value of stimulus:
      for (i=0; i<(1); i++)
	{ 
	  for(j=0; j<(ny+1); j++)
	    {
	      for(k=0; k<nz; k++)
		{
		  ez[i][j][k] = stimulus;
		}
	    }
	}

#endif 

#ifdef SIMULATE_Y_CONDUCTING_ELEMENT_TEST



      for(i=10; i<(nx-10); i++)
	{     //(Scaling is arbitrary in this test. What ever gives us the best visual effect)
	  // (See http://en.wikipedia.org/wiki/Gaussian_function or sub-directory "Documentation")

	   Jx[i][ny/2][nz/2]   = (1.0/(.5*sqrt(2.0*M_PI)))*
	    exp(-((currentSimulatedTime - (50*2.265686119e-12))*(currentSimulatedTime - (50*2.265686119e-12)))/(2.0*(10*2.265686119e-12)*(10*2.265686119e-12)));
	  PEC[i][ny/2][nz/2] = 1;
	  
	

	}
 

#endif

#ifdef SIMULATE_Y_CONDUCTING_ELEMENT_TEST_CONTINUOUS


      for(i=10; i<(nx-10); i++)
	{     //(Scaling is arbitrary in this test. What ever gives us the best visual effect)
	  // (See http://en.wikipedia.org/wiki/Gaussian_function or sub-directory "Documentation")

	  if(currentSimulatedTime <= 1.155499e-10)
	    Jx[i][ny/2][nz/2] =  (1.0/(.5*sqrt(2.0*M_PI)))*
	      exp(-((currentSimulatedTime - (50*2.265686119e-12))*(currentSimulatedTime - (50*2.265686119e-12)))/(2.0*(10*2.265686119e-12)*(10*2.265686119e-12)));
	  else
	    Jx[i][ny/2][nz/2] = .79788456;

	    
	  PEC[i][ny/2][nz/2] = 1;
	  
	

	}
 

#endif


	
      /////////////////////////////////////////////////////////////////////////
      // Update the interior of the mesh:
      //    all vector components except those on the faces of the mesh.  
      //
      // Update all the H field vector components within the mesh:
      //     Since all H vectors are internal, all H values are updated here.  
      //     Note that the normal H vectors on the faces of the mesh are not
      //     computed here, and in fact were never allocated -- the normal
      //     H components on the faces of the mesh are never used to update
      //     any other value, so they are left out of the memory allocation 
      //     entirely.  

      // Update the hx values:
      for(i=0; i<(nx-1); i++)
	{  
	  for(j=0; j<(ny); j++)
	    {
	      for(k=0; k<(nz); k++)
		{
		  hx[i][j][k] += (dtmudz*(ey[i+1][j][k+1] - ey[i+1][j][k]) - 
		                  dtmudy*(ez[i+1][j+1][k] - ez[i+1][j][k]));
		}
	    }
	}

      // Update the hy values:
      for(i=0; i<(nx); i++)
	{  
	  for(j=0; j<(ny-1); j++)
	    {
	      for(k=0; k<(nz); k++)
		{
		  hy[i][j][k] +=  (dtmudx*(ez[i+1][j+1][k] - ez[i][j+1][k]) -
      		                   dtmudz*(ex[i][j+1][k+1] - ex[i][j+1][k]));
		}
	    }
	}

      // Update the hz values:
      for(i=0; i<(nx); i++)
	{  
	  for(j=0; j<(ny); j++)
	    {
	      for(k=0; k<(nz-1); k++)
		{
		  hz[i][j][k] +=  (dtmudy*(ex[i][j+1][k+1] - ex[i][j][k+1]) -
      		                   dtmudx*(ey[i+1][j][k+1] - ey[i][j][k+1]));
		}
	    }
	}

      // Update the E field vector components.  
      // The values on the faces of the mesh are not updated here; they 
      //      are handled by the boundary condition computation 
      //      (and stimulus computation).  

      // Update the ex values:
      for(i=0; i<(nx); i++)
	{  
	  for(j=1; j<(ny); j++)
	    {
	      for(k=1; k<(nz); k++)
		{

		  if(PEC == 0)
		    ex[i][j][k] += (dtepsdy*(hz[i][j][k-1] - hz[i][j-1][k-1]) -
				  dtepsdz*(hy[i][j-1][k] - hy[i][j-1][k-1]));
		  else
		    ex[i][j][k] = (dtepsdy*(hz[i][j][k-1] - hz[i][j-1][k-1]) -
				   dtepsdz*(hy[i][j-1][k] - hy[i][j-1][k-1])) - Jx[i][j][k];
		}
	    }
	}      

      // Update the ey values:
      for(i=1; i<(nx); i++)
	{  
	  for(j=0; j<(ny); j++)
	    {
	      for(k=1; k<(nz); k++)
		{
		  if(PEC == 0)
		    ey[i][j][k] += (dtepsdz*(hx[i-1][j][k] - hx[i-1][j][k-1]) -
				    dtepsdx*(hz[i][j][k-1] - hz[i-1][j][k-1]));
		  else
		    ey[i][j][k] = (dtepsdz*(hx[i-1][j][k] - hx[i-1][j][k-1]) -
				   dtepsdx*(hz[i][j][k-1] - hz[i-1][j][k-1]))- Jy[i][j][k];
		}
	    }
	}

      // Update the ez values:
      for(i=1; i<(nx); i++)
	{  
	  for(j=1; j<(ny); j++)
	    {
	      for(k=0; k<(nz); k++)
		{
		  if(PEC == 0)
		    ez[i][j][k] += (dtepsdx*(hy[i][j-1][k] - hy[i-1][j-1][k]) -
				    dtepsdy*(hx[i-1][j][k] - hx[i-1][j-1][k]));
		  else
		    ez[i][j][k] = (dtepsdx*(hy[i][j-1][k] - hy[i-1][j-1][k]) -
				   dtepsdy*(hx[i-1][j][k] - hx[i-1][j-1][k])) - Jz[i][j][k];
		}
	    }
	}
      

      /////////////////////////////////////////////////////////////////////////
      // Compute the boundary conditions:

#ifdef PMC
      // Compute the PMC symmetry boundaries:
      // j = 0 face, j = ny face
      for(i=0; i<nx; i++)
	{	
	  for(k=0; k<(nz+1); k++)
	    {
	      ex[i][0][k] = ex[i][ny-1][k];
	      ex[i][ny][k] = ex[i][1][k];
	    }
	}
      for(i=0; i<(nx+1); i++)
	{	
	  for(k=0; k<nz; k++)
	    {
	      ez[i][0][k] = ez[i][ny-1][k];
	      ez[i][ny][k] = ez[i][1][k];
	    }
	}
#endif

      // Compute the PEC boundaries:
      //
      // OK, so I'm yanking your chain on this one.  The PEC condition is 
      // enforced by setting the tangential E field components on the PEC
      // faces of the mesh to zero every timestep (except the stimulus 
      // face).  But the lazy/efficient way out is to initialize those 
      // vectors to zero and never compute them again, which is exactly 
      // what happens in this code.  



  
  
}	
