#include "Utils.h"
#include "Phreeqc.h"
#include "phqalloc.h"
#include "Temperature.h"
#include "Exchange.h"
#include "GasPhase.h"
#include "Reaction.h"
#include "PPassemblage.h"
#include "SSassemblage.h"
#include "cxxKinetics.h"
#include "Solution.h"

#if defined(PHREEQCI_GUI)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

/*   
     Calling sequence 

Initialization:
---------------

build_tally_table();                   finds all elements (rows),
                                       possible return values (columns),
				       allocates space
get_tally_table_rows_columns(int *rows, int *columns)
				       returns number of rows and columns in table
get_tally_table_row_heading(int row, char *string)
				       row is C row number
				       returns row descripter for row
get_tally_table_column_heading(int column, int *type, char *string)
				       column is C column number
				       returns column heading for column

Each call to phreeqc:
---------------------

zero_tally_table();                    initialize table to 0s
set_reaction_moles(n_user, moles)      n_user is reservoir number
                                       moles is number of moles of reaction to add

int set_reaction_temperature(n_user, tc)

fill_tally_table(int *n_user, int index_conservative, int n_buffer)
                                       n_user is reservoir number
				       index_conservative is solution number
				           where conservative mixing is stored
                                       slot is 0 for initial

   run phreeqc here.

fill_tally_table(int *n_user, int index_conservative, int n_buffer)
                                       n_user is reservoir number
				       index_conservative is solution number
				           where conservative mixing is stored
                                       slot is 1 for final
store_tally_table(LDBLE *array, int row_dim, int col_dim, LDBLE fill_factor) 
                                       row_dim is Fortran dimension
                                       col_dim is Fortran dimension
				       array is space from Fortran
				       stores conservative mixing (column 0)
				       stores reaction (column 1)
				       difference between slot 1 and slot 0 for
				       all other entities (columns 2-n)

Finalization:
-------------
int free_tally_table(void);       Frees space

*/
/* ---------------------------------------------------------------------- */
int Phreeqc::
get_all_components(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Counts components in any defined solution, gas_phase, exchanger,
 *   surface, or pure_phase_assemblage
 *
 *   Returns n_comp, which is total, including H, O, elements, and Charge
 *           names contains character strings with names of components
 */
	int i, j;
/*
 *   Accumulate all aqueous components
 */
	add_all_components_tally();

	// add secondary master species
	for (i = 0; i < (int)master.size(); i++)
	{
		if (master[i]->total > 0.0 && master[i]->s->type == AQ && master[i]->primary == TRUE)
		{
			for (int j = i + 1; j < (int)master.size(); j++)
			{
				if (master[j]->elt->primary == master[i])
				{
					master[j]->total = 1.0;
				}
				else
				{
					break;
				}
			}
		}
	}


/*
 *   Count components + Alkalinity + total_h + total_o
 */
	tally_count_component = 3;
	for (i = 0; i < (int)master.size(); i++)
	{
		if (master[i]->total > 0.0 && master[i]->s->type == AQ)
		{
			tally_count_component++;
		}
	}
/*
 *   Put information in buffer.
 *   Buffer contains an entry for every primary master
 *   species that can be used in the transport problem.
 */
	t_buffer = (class tally_buffer *) PHRQ_malloc(
		(size_t)tally_count_component * sizeof(class tally_buffer));

	// store alkalinity
	j = 0;
	t_buffer[j].name = string_hsave("Alkalinity");
	t_buffer[j].master = master_bsearch("Alkalinity");
	t_buffer[j].gfw = t_buffer[j].master->elt->gfw;
	j++;		

	// store total_h
	t_buffer[j].name = string_hsave("Total_H");
	t_buffer[j].master = NULL;
	compute_gfw("H", &(t_buffer[j].gfw));
	j++;

	// store total_o
	t_buffer[j].name = string_hsave("Total_O");
	t_buffer[j].master = NULL;
	compute_gfw("O", &(t_buffer[j].gfw));
	j++;

	for (i = 0; i < (int)master.size(); i++)
	{
		if (master[i]->total > 0.0 && master[i]->s->type == AQ)
		{
			t_buffer[j].name = master[i]->elt->name;
			t_buffer[j].master = master[i];
			t_buffer[j].gfw = master[i]->elt->gfw;
			j++;
		}
	}
	/*
	 *  Return value
	 */
	/**n_comp = count_component;*/
	count_tally_table_rows = tally_count_component;
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
store_tally_table(LDBLE * l_array, int row_dim_in, int col_dim, LDBLE fill_factor)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	int row_dim = row_dim_in + 1;
	//if (tally_table == NULL)
	if (tally_table.size() == 0)
	{
		input_error++;
		error_msg("Tally table not defined, get_tally_table_rows_columns",
				  CONTINUE);
		return (ERROR);
	}
	if (count_tally_table_rows > row_dim)
	{
		input_error++;
		error_msg
			("Too many tally table rows for Fortran storage, store_tally_table",
			 CONTINUE);
		return (ERROR);
	}
	if (count_tally_table_columns > col_dim)
	{
		input_error++;
		error_msg
			("Too many tally table columns for Fortran storage, store_tally_table",
			 CONTINUE);
		return (ERROR);
	}
	/*
	 * store conservative mixing solution
	 */
	for (j = 0; j < count_tally_table_rows; j++)
	{
		l_array[j] = tally_table[0].total[1][j].moles;
	}
	/*
	 * store reaction solution
	 */
	for (j = 0; j < count_tally_table_rows; j++)
	{
		l_array[row_dim + j] = tally_table[1].total[1][j].moles;
	}
	/*
	 *   Calculate deltas
	 */

	diff_tally_table();

	/*
	 * store deltas for all other columns
	 */
	for (i = 2; i < count_tally_table_columns; i++)
	{
		for (j = 0; j < count_tally_table_rows; j++)
		{
			l_array[i * row_dim + j] =
				tally_table[i].total[2][j].moles / fill_factor;
		}
	}

	/*
	 * Add row for total moles of reactant
	 */
	for (size_t i = 0; i < count_tally_table_columns; i++)
	{
		l_array[i * (size_t)row_dim + count_tally_table_rows] =
				tally_table[i].moles / fill_factor;
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
get_tally_table_rows_columns(int *rows, int *columns)
/* ---------------------------------------------------------------------- */
{
	*rows = 0;
	*columns = 0;
	if (tally_table.size() == 0)
	{
		input_error++;
		error_msg("tally table not defined, get_tally_table_rows_columns",
				  CONTINUE);
		return (ERROR);
	}
	*rows = (int)count_tally_table_rows;
	*columns = (int)count_tally_table_columns;
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
get_tally_table_row_heading(int row, char *string)
/* ---------------------------------------------------------------------- */
{
	/*
	 *  row is C row number
	 */
	strcpy(string, "");
	if (tally_table.size() == 0)
	{
		input_error++;
		error_msg("Tally table not defined, get_tally_table row_heading",
				  CONTINUE);
		return (ERROR);
	}
	if (row >= count_tally_table_rows)
	{
		input_error++;
		error_msg
			("Row exceeds tally table size, get_tally_table row_heading",
			 CONTINUE);
		return (ERROR);
	}
	strcpy(string, t_buffer[row].name);
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
get_tally_table_column_heading(int column, int *type, char *string)
/* ---------------------------------------------------------------------- */
{
	/*
	 *  column is C column number
	 */
	*type = -1;
	strcpy(string, "");
	if (tally_table.size() == 0)
	{
		input_error++;
		error_msg("tally table not defined, get_tally_table_column_heading",
				  CONTINUE);
		return (ERROR);
	}
	if (column >= count_tally_table_columns)
	{
		input_error++;
		error_msg
			("column exceeds tally table size, get_tally_table_column_heading",
			 CONTINUE);
		return (ERROR);
	}
	strcpy(string, tally_table[column].name);
	*type = tally_table[column].type;
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
free_tally_table(void)
/* ---------------------------------------------------------------------- */
{
	int i, k;
	if (tally_table.size() == 0)
		return (OK);
	for (i = 0; i < count_tally_table_columns; i++)
	{
		if (tally_table[i].formula.size() != 0)
			tally_table[i].formula.clear();
		for (k = 0; k < 3; k++)
		{
			tally_table[i].total[k] = (class tally_buffer *) free_check_null(
				tally_table[i].total[k]);
		}
	}
	//tally_table = (class tally *) free_check_null(tally_table);
	t_buffer = (class tally_buffer *) free_check_null(t_buffer);
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
zero_tally_table(void)
/* ---------------------------------------------------------------------- */
{
	int i, j, k;
	for (i = 0; i < count_tally_table_columns; i++)
	{
		tally_table[i].moles = 0.0;
		for (j = 0; j < count_tally_table_rows; j++)
		{
			for (k = 0; k < 3; k++)
			{
				tally_table[i].total[k][j].moles = 0;
			}
		}
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
diff_tally_table(void)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	/*
	   output_msg("Difference\n\n");
	 */
	for (i = 0; i < count_tally_table_columns; i++)
	{
		for (j = 0; j < count_tally_table_rows; j++)
		{
			tally_table[i].total[2][j].moles =
				tally_table[i].total[1][j].moles -
				tally_table[i].total[0][j].moles;
		}

		/*
		   output_msg(sformatf( "Column %d\t%s\tType: %d\n", i, tally_table[i].name, tally_table[i].type));
		   for (j = 0; j < count_tally_table_rows; j++) {
		   output_msg(sformatf( "\t%d\t%s\t%e\n", j, tally_table[i].total[2][j].name, (double) tally_table[i].total[2][j].moles));
		   }
		 */
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
print_tally_table(void)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	output_msg(sformatf( "Tally_table\n\n"));
	for (i = 0; i < count_tally_table_columns; i++)
	{
		output_msg(sformatf( "%s\tType: %d\n", tally_table[i].name,
				   tally_table[i].type));
		output_msg(sformatf( "\n"));
		output_msg(sformatf( "\t%15s\t%15s\t%15s\n", "Initial",
				   "Final", "Difference"));
		for (j = 0; j < count_tally_table_rows; j++)
		{
			output_msg(sformatf( "%5s\t%15g\t%15g\t%15g\n",
					   t_buffer[j].name, tally_table[i].total[0][j].moles,
					   tally_table[i].total[1][j].moles,
					   tally_table[i].total[2][j].moles));
		}
		output_msg(sformatf( "\n"));
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
fill_tally_table(int *n_user, int index_conservative, int n_buffer)
/* ---------------------------------------------------------------------- */
{
/*
 *   Routine accumulates elements from all solutions, phases, gas phases,
 *   exchangers, and surfaces. 
 */
	int found;
	LDBLE moles;
	//const char* cptr;
	/*
	 *  Cycle through tally table columns
	 */
	for (int i = 0; i < count_tally_table_columns; i++)
	{
		switch (tally_table[i].type)
		{
		case Solution:
/*
 *   fill solution
 */
			if (n_user[Solution] < 0 || n_buffer == 0)
				break;
			{
				cxxSolution *solution_ptr = NULL;;
				if (i == 0)
				{
					solution_ptr = Utilities::Rxn_find(Rxn_solution_map, index_conservative);
				}
				else if (i == 1)
				{
					solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user[Solution]);
				}
				else
				{
					error_msg("Solution is not in first two columns of tally_table", STOP);
				}
				if (solution_ptr == NULL)
					break;
				/*
				 *   Add secondary master species
				 */


				xsolution_zero();

				// adds primary master species
				add_solution(solution_ptr, 1.0, 1.0);

				// adds secondary master species
				cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin();
				for ( ; jit != solution_ptr->Get_totals().end(); jit++)
				{
					class master *master_ptr = master_bsearch(jit->first.c_str());
					master_ptr->total = jit->second;
				}

				// Fill table
				master_to_tally_table(tally_table[i].total[n_buffer]);
				
				// Add alkalinity
				tally_table[i].total[n_buffer][0].moles = solution_ptr->Get_total_alkalinity();

				// Add total_h
				tally_table[i].total[n_buffer][1].moles = solution_ptr->Get_total_h();

				// Add total_o
				tally_table[i].total[n_buffer][2].moles = solution_ptr->Get_total_o();
			}
			break;
		case Reaction:
			/*
			 *   fill reaction
			 */
			if (n_user[Reaction] < 0)
				break;
			{
				cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user[Reaction]);
				if (reaction_ptr == NULL)
					break;
				count_elts = 0;
				paren_count = 0;
				if (n_buffer == 1)
				{
					moles = reaction_ptr->Get_steps()[0];
				}
				else
				{
					moles = 0.0;
				}
				reaction_calc(reaction_ptr);
				add_elt_list(reaction_ptr->Get_elementList(), moles);
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Pure_phase:
			/*
			 *   fill an equilibrium phase
			 */
			if (n_user[Pure_phase] < 0)
				break;
			{
				cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n_user[Pure_phase]);
				if (pp_assemblage_ptr == NULL)
					break;
				std::map<std::string, cxxPPassemblageComp>::iterator it;
				it =  pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
				for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
				{
					if (string_hsave(it->second.Get_name().c_str()) ==
						tally_table[i].name)
						break;
					if (strcmp_nocase(it->second.Get_name().c_str(),
						tally_table[i].name) == 0)
						break;
				}
				if (it == pp_assemblage_ptr->Get_pp_assemblage_comps().end())
					break;
				count_elts = 0;
				paren_count = 0;
				moles = it->second.Get_moles();
				tally_table[i].moles = moles;
				add_elt_list(tally_table[i].formula, moles);
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Exchange:
			{
				/*
				*   fill exchange
				*/
				if (n_user[Exchange] < 0)
					break;
				cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user[Exchange]);
				if (exchange_ptr == NULL)
					break;
				count_elts = 0;
				paren_count = 0;
				for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++)
				{
					add_elt_list(exchange_ptr->Get_exchange_comps()[j].Get_totals(), 1.0);
				}
				elt_list_combine();
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Surface:
			/*
			 *   fill surface
			 */
			if (n_user[Surface] < 0)
				break;
			{
				cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user[Surface]);
				if (surface_ptr == NULL)
					break;
				count_elts = 0;
				paren_count = 0;
				for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++)
				{
					add_elt_list(surface_ptr->Get_surface_comps()[j].Get_totals(), 1.0);
				}
				elt_list_combine();
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Ss_phase:
			if (n_user[Ss_phase] < 0)
				break;
			{
				/*
				*   fill an solid solution phase
			    */

				cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, n_user[Ss_phase]);
				if (ss_assemblage_ptr == NULL)
					break;
				found = FALSE;
				moles = 0.0;
				std::vector<cxxSS *> ss_ptrs = ss_assemblage_ptr->Vectorize();
				for (size_t j = 0; j < ss_ptrs.size(); j++)
				{
					cxxSS * ss_ptr = ss_ptrs[j];
					cxxSScomp * comp_ptr = NULL;
					size_t k;
					for (k = 0; k < ss_ptr->Get_ss_comps().size(); k++)
					{
						comp_ptr = &(ss_ptr->Get_ss_comps()[k]);
						int l;
						class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
						if (phase_ptr->name == tally_table[i].name)
							break;
						if (strcmp_nocase(phase_ptr->name, tally_table[i].name) == 0)
							break;
					}
					if (k < ss_ptr->Get_ss_comps().size() && comp_ptr)
					{
						moles = comp_ptr->Get_moles();
						found = TRUE;
						break;
					}
				}
				if (found == FALSE)
					break;
				count_elts = 0;
				paren_count = 0;
				tally_table[i].moles = moles;
				add_elt_list(tally_table[i].formula, moles);
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Gas_phase:
			/*
			 *   fill in gas phase
			 */
			if (n_user[Gas_phase] < 0)
				break;
			{
				cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, n_user[Gas_phase]);
				if (gas_phase_ptr == NULL)
					break;
				count_elts = 0;
				paren_count = 0;
				const std::vector<cxxGasComp> *gc = &(gas_phase_ptr->Get_gas_comps());
				for (size_t l = 0; l < gc->size(); l++)
				{
					int k;
					class phase *phase_ptr = phase_bsearch((*gc)[l].Get_phase_name().c_str(), &k, FALSE);

					add_elt_list(phase_ptr->next_elt, (*gc)[l].Get_moles());
				}
				elt_list_combine();
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
				break;
			}
		case Kinetics:
			{
				/*
				*   fill in kinetics
				*/
				if (n_user[Kinetics] < 0)
					break;
				cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user[Kinetics]);
				if (kinetics_ptr == NULL)
					break;
				cxxKineticsComp * kinetics_comp_ptr = NULL;
				size_t j;
				for (j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
				{
					kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
					if (string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()) == tally_table[i].name)
						break;
					if (strcmp_nocase
						(kinetics_comp_ptr->Get_rate_name().c_str(), tally_table[i].name) == 0)
						break;
				}
				if (j >= kinetics_ptr->Get_kinetics_comps().size())
					break;
				moles = 0.0;
				if (kinetics_comp_ptr)
				{
					moles = kinetics_comp_ptr->Get_m();
				}
				tally_table[i].moles = moles;
				count_elts = 0;
				paren_count = 0;
				add_elt_list(tally_table[i].formula, moles);
				elt_list_to_tally_table(tally_table[i].total[n_buffer]);
			}
			break;
		case Mix:
			break;
		case Temperature:
		case Pressure:
			break;
		case UnKnown:
			break;
		}
	}

	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
elt_list_to_tally_table(class tally_buffer *buffer_ptr)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	for (i = 0; i < count_tally_table_rows; i++)
	{
		buffer_ptr[i].moles = 0.0;
	}
	/*
	 * copy element list amounts to buffer in tally table
	 * for column number
	 */

	for (j = 0; j < count_elts; j++)
	{
		if (elt_list[j].elt->primary->s == s_h2o)
			continue;
		if (elt_list[j].elt->primary->s == s_hplus)
			continue;
		if (elt_list[j].elt->primary->s == s_h3oplus)
			continue;
		if (elt_list[j].elt->primary->type != AQ)
			continue;
		for (i = 0; i < count_tally_table_rows; i++)
		{
			if (buffer_ptr[i].master != NULL)
			{
				if (elt_list[j].elt->primary ==
					buffer_ptr[i].master->elt->primary)
				{
					buffer_ptr[i].moles = elt_list[j].coef;
					break;
				}
			}
		}
		if (i >= count_tally_table_rows)
		{
			error_msg("Should not be here in elt_list_to_tally_table", STOP);
		}
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
master_to_tally_table(class tally_buffer *buffer_ptr)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	for (i = 0; i < count_tally_table_rows; i++)
	{
		buffer_ptr[i].moles = 0.0;
	}
	/*
	 * copy element list amounts to buffer in tally table
	 * for column number
	 */
	for (j  = 0; j < (int)master.size(); j++)
	{
		if (master[j]->total <= 0)
			continue;
		if (master[j]->elt->primary->s == s_h2o)
			continue;
		if (master[j]->elt->primary->s == s_hplus)
			continue;
		if (master[j]->elt->primary->s == s_h3oplus)
			continue;
		if (master[j]->elt->primary->type != AQ)
			continue;
		for (i = 0; i < count_tally_table_rows; i++)
		{
			if (master[j] ==  buffer_ptr[i].master)
			{
				buffer_ptr[i].moles = master[j]->total;
				break;
			}
		}
		if (i >= count_tally_table_rows)
		{
			error_msg("Should not be here in master_to_tally_table", STOP);
		}
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
build_tally_table(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Routine accumulates elements from all solutions, phases, gas phases,
 *   exchangers, and surfaces. Counts number of aqueous components
 *   to transport. Stores in global variable tally_count_component.
 *   Also calculates a number greater than all user numbers and
 *   stores in global variable first_user_number.
 */
	int j, k, l, p, save_print_use;
	size_t n;
	int count_tt_pure_phase, count_tt_ss_phase, count_tt_kinetics;
	class phase *phase_ptr;
	char token[MAX_LENGTH];
	const char* cptr;
/*
 *  make list of all elements in all entitites
 *  defines the number of rows in the table
 */
	get_all_components();

	save_print_use = pr.use;
	pr.use = FALSE;
/*
 *  find nuber of columns
 */
	count_tally_table_columns = 0;
/*
 *   add one for conservative mixing
 */
	n = count_tally_table_columns;
	extend_tally_table();
	tally_table[n].name = string_hsave("Solution_conservative");
	tally_table[n].type = Solution;
/*
 *   add one for mixing plus reaction
 */
	n = count_tally_table_columns;
	extend_tally_table();
	tally_table[n].name = string_hsave("Solution_reaction");
	tally_table[n].type = Solution;
/*
 *   add one for reactions
 */
	if (Rxn_reaction_map.size() > 0)
	{
		n = count_tally_table_columns;
		extend_tally_table();
		tally_table[n].name = string_hsave("Reaction");
		tally_table[n].type = Reaction;
	}
/*
 *   add one for exchangers
 */
	if (Rxn_exchange_map.size() > 0)
	{
		n = count_tally_table_columns;
		extend_tally_table();
		tally_table[n].name = string_hsave("Exchange");
		tally_table[n].type = Exchange;
	}
/*
 *   add one for surface
 */
	if (Rxn_surface_map.size() > 0)
	{
		n = count_tally_table_columns;
		extend_tally_table();
		tally_table[n].name = string_hsave("Surface");
		tally_table[n].type = Surface;
	}
/*
 *   add one for gases
 */
	if (Rxn_gas_phase_map.size() > 0)
	{
		n = count_tally_table_columns;
		extend_tally_table();
		tally_table[n].name = string_hsave("Gas_phase");
		tally_table[n].type = Gas_phase;
	}
/*
 *   Count pure phases
 */
	count_tt_pure_phase = 0;
	if (Rxn_pp_assemblage_map.size() > 0)
	{
		/* 
		 * Go through all pure phases in pure phase assemblages
		 */
		std::map<int, cxxPPassemblage>::iterator it;
		for (it = Rxn_pp_assemblage_map.begin(); it != Rxn_pp_assemblage_map.end(); it++)
		{
			cxxPPassemblage * pp_assemblage_ptr = &(it->second);
			std::map<std::string, cxxPPassemblageComp>::iterator jit;
			jit =  pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
			for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++)
			{
				cxxPPassemblageComp * comp_ptr = &(jit->second);
				int l;
				class phase * phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE);
				/* 
				 * check if already in tally_table
				 */
				for (k = 1; k < count_tally_table_columns; k++)
				{
					if (tally_table[k].type == Pure_phase &&
						tally_table[k].name == phase_ptr->name &&
						tally_table[k].add_formula ==
						string_hsave(comp_ptr->Get_add_formula().c_str()))
						break;
				}
				if (k < count_tally_table_columns)
					continue;
				/*
				 * Add to table
				 */
				count_tt_pure_phase++;
				n = count_tally_table_columns;
				extend_tally_table();
				tally_table[n].name = phase_ptr->name;
				tally_table[n].type = Pure_phase;
				tally_table[n].add_formula = string_hsave(comp_ptr->Get_add_formula().c_str());
				count_elts = 0;
				paren_count = 0;
				if (comp_ptr->Get_add_formula().size() > 0)
				{
					strcpy(token, comp_ptr->Get_add_formula().c_str());
					cptr = &(token[0]);
					get_elts_in_species(&cptr, 1.0);
				}
				else
				{
					strcpy(token, phase_ptr->formula);
					add_elt_list(phase_ptr->next_elt, 1.0);
				}
				elt_list_combine();
				tally_table[n].formula = elt_list_vsave();
			}
		}
	}
/*
 *   Add solid-solution pure phases
 */
	count_tt_ss_phase = 0;
	if (Rxn_ss_assemblage_map.size() > 0)
	{
		/* 
		 * Go through all components of all solid solutions in solid-solution assemblages
		 */
		std::map<int, cxxSSassemblage>::iterator it;
		for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++)
		{
			cxxSSassemblage *ss_assemblage_ptr = &(it->second);
			std::vector<cxxSS *> ss_ptrs = ss_assemblage_ptr->Vectorize();
			for (j = 0; j < (int) ss_ptrs.size(); j++)
			{
				cxxSS * ss_ptr = ss_ptrs[j];
				for (k = 0; k < (int) ss_ptr->Get_ss_comps().size(); k++)
				{
					cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]);
					int l;
					class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
					/* 
					 * check if already in tally_table
					 */
					for (l = 1; l < count_tally_table_columns; l++)
					{
						if (tally_table[l].type == Ss_phase &&
							tally_table[l].name == phase_ptr->name)
							break;
					}
					if (l < count_tally_table_columns)
						continue;
					/*
					 * Add to table
					 */
					count_tt_ss_phase++;
					n = count_tally_table_columns;
					extend_tally_table();
					tally_table[n].name = phase_ptr->name;
					tally_table[n].type = Ss_phase;
					count_elts = 0;
					paren_count = 0;
					strcpy(token, phase_ptr->formula);
					add_elt_list(phase_ptr->next_elt, 1.0);
					elt_list_combine();
					tally_table[n].formula = elt_list_vsave();
				}
			}
		}
	}
/*
 *   Add kinetic reactants
 */
	count_tt_kinetics = 0;
	if (Rxn_kinetics_map.size() > 0)
	{
		std::map<int, cxxKinetics>::iterator it;
		for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++)
		{
			cxxKinetics *kinetics_ptr = &(it->second);
			for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++)
			{
				cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
				/* 
				 * check if already in tally_table
				 */
				for (l = 1; l < count_tally_table_columns; l++)
				{
					if (tally_table[l].type == Kinetics &&
						tally_table[l].name == string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()))
						break;
				}
				if (l < count_tally_table_columns)
					continue;
				/*
				 * Add to table
				 */
				count_tt_kinetics++;
				n = count_tally_table_columns;
				extend_tally_table();
				tally_table[n].name = string_hsave(kinetics_comp_ptr->Get_rate_name().c_str());
				tally_table[n].type = Kinetics;
				/*
				 * get formula for kinetic component
				 */
				count_elts = 0;
				paren_count = 0;
				phase_ptr = NULL;
				if (kinetics_comp_ptr->Get_namecoef().size() == 1)
				{
					strcpy(token, kinetics_comp_ptr->Get_namecoef().begin()->first.c_str());
					phase_ptr = phase_bsearch(token, &p, FALSE);
				}
				if (phase_ptr != NULL)
				{
					add_elt_list(phase_ptr->next_elt, 1.0);
				}
				else
				{
					cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin();
					for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++)
					{
						std::string name = it->first;
						LDBLE coef = it->second;
						cptr = name.c_str();
						get_elts_in_species(&cptr, 1.0 * coef);
					}
				}
				elt_list_combine();
				tally_table[n].formula = elt_list_vsave();
			}
		}
	}

	pr.use = save_print_use;
	return (OK);
}

/* ---------------------------------------------------------------------- */
void Phreeqc::
add_all_components_tally(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Routine accumulates elements from all solutions, phases, gas phases,
 *   exchangers, and surfaces. Counts number of aqueous components
 *   to transport. Stores in global variable tally_count_component.
 *   Also calculates a number greater than all user numbers and
 *   stores in global variable first_user_number.
 */
	int save_print_use;

	save_print_use = pr.use;
	pr.use = FALSE;
/*
 *   Delete solutions less than -1
 */

/*
 *   add all solutions
 */
	xsolution_zero();
	{
		std::map<int, cxxSolution>::iterator it = Rxn_solution_map.begin();
		for (; it != Rxn_solution_map.end(); it++)
		{
			add_solution(&it->second, 1.0 / it->second.Get_mass_water(), 1.0);
		}
	}
/*
 *   add all irrev reactions
 */
	{
		std::map<int, cxxReaction>::iterator it = Rxn_reaction_map.begin();
		for (; it != Rxn_reaction_map.end(); it++)
		{
			add_reaction(&it->second, 1, 1.0);
		}
	}
/*
 *   Add pure phases
 */
	{
		std::map<int, cxxPPassemblage>::iterator it = Rxn_pp_assemblage_map.begin();
		for (; it != Rxn_pp_assemblage_map.end(); it++)
		{
			add_pp_assemblage(&(it->second));
		}
	}
/*
 *   Exchangers
 */
	{
		std::map<int, cxxExchange>::iterator it = Rxn_exchange_map.begin();
		for (; it != Rxn_exchange_map.end(); it++)
		{
			add_exchange(&it->second);
		}
	}
/*
 *   Surfaces
 */
	{
		std::map<int, cxxSurface>::iterator it = Rxn_surface_map.begin();
		for (; it != Rxn_surface_map.end(); it++)
		{
			add_surface(&it->second);
		}
	}
/*
 *   Gases
 */
	{
		std::map<int, cxxGasPhase>::iterator it = Rxn_gas_phase_map.begin();
		for ( ; it != Rxn_gas_phase_map.end(); it++)
		{
			add_gas_phase(&it->second);
		}
	}
/*
 *   Add solid-solution pure phases
 */
	{
		std::map<int, cxxSSassemblage>::iterator it;
		for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++)
		{
			add_ss_assemblage(&(it->second));
		}
	}
/*
 *   Add elements in kinetic reactions
 */
	{
		std::map<int, cxxKinetics>::iterator it = Rxn_kinetics_map.begin();
		for ( ; it != Rxn_kinetics_map.end(); it++)
		{
			calc_dummy_kinetic_reaction_tally(&(it->second));
			add_kinetics(&(it->second));
		}
	}
/*
 *   reset pr.use
 */
	pr.use = save_print_use;
	return;
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
calc_dummy_kinetic_reaction_tally(cxxKinetics *kinetics_ptr)
/* ---------------------------------------------------------------------- */
{
/*
 *    Go through kinetic components and add positive amount of each reactant
 */
	LDBLE coef;
	const char* cptr;
	class phase *phase_ptr;
/*
 *   Go through list and generate list of elements and
 *   coefficient of elements in reaction
 */
	kinetics_ptr->Get_totals().clear();
	count_elts = 0;
	paren_count = 0;
	for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
	{
		cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
		coef = 1.0;
/*
 *   Reactant is a pure phase, copy formula into token
 */
		phase_ptr = NULL;
		if (kinetics_comp_ptr->Get_namecoef().size() == 1)
		{
			std::string name = kinetics_comp_ptr->Get_namecoef().begin()->first;
			int j;
			phase_ptr = phase_bsearch(name.c_str(), &j, FALSE);
		}
		if (phase_ptr != NULL)
		{
			add_elt_list(phase_ptr->next_elt, coef);
		}
		else
		{
			cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin();
			for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++)
			{
				cptr = it->first.c_str();
				get_elts_in_species(&cptr, coef);
			}
		}
	}
	kinetics_ptr->Set_totals(elt_list_NameDouble());

	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
extend_tally_table(void)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	/* 
	 * adds another column to tally_table
	 * increments number of columns
	 */
	//tally_table = (class tally *) PHRQ_realloc((void *) tally_table,
	//	(count_tally_table_columns + 1) * sizeof(class tally));
	//if (tally_table == NULL)
	//	malloc_error();
	tally_table.resize(count_tally_table_columns + 1);
	for (i = 0; i < 3; i++)
	{
		tally_table[count_tally_table_columns].total[i] = (class tally_buffer *)
			PHRQ_malloc(count_tally_table_rows * sizeof(class tally_buffer));
		if (tally_table[count_tally_table_columns].total[i] == NULL)
			malloc_error();
		for (j = 0; j < count_tally_table_rows; j++)
		{
			tally_table[count_tally_table_columns].total[i][j].name =
				t_buffer[j].name;
			tally_table[count_tally_table_columns].total[i][j].master =
				t_buffer[j].master;
		}
	}
	tally_table[count_tally_table_columns].name = NULL;
	tally_table[count_tally_table_columns].type = UnKnown;
	tally_table[count_tally_table_columns].add_formula = NULL;
	tally_table[count_tally_table_columns].moles = 0.0;
	count_tally_table_columns++;
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
set_reaction_moles(int n_user, LDBLE moles)
/* ---------------------------------------------------------------------- */
{
	cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user);
	if (reaction_ptr == NULL)
		return (ERROR);
	std::vector<LDBLE> v;
	v.push_back(moles);
	reaction_ptr->Set_steps(v);
	reaction_ptr->Set_countSteps(1);
	reaction_ptr->Set_equalIncrements(true);
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
set_reaction_temperature(int n_user, LDBLE tc)
/* ---------------------------------------------------------------------- */
{
	cxxTemperature * temperature_ptr = Utilities::Rxn_find(Rxn_temperature_map, n_user);
	if (temperature_ptr == NULL)
		return (ERROR);
	temperature_ptr->Get_temps().clear();
	temperature_ptr->Get_temps().push_back(tc);
	temperature_ptr->Set_equalIncrements(false);
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
set_kinetics_time(int n_user, LDBLE step)
/* ---------------------------------------------------------------------- */
{
	cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user);

	if (kinetics_ptr == NULL)
		return (ERROR);
	kinetics_ptr->Get_steps().clear();
	kinetics_ptr->Get_steps().push_back(step);
	kinetics_ptr->Set_equalIncrements(false);
	return (OK);
}

