Commit bfd8cf6a authored by ferhat's avatar ferhat
Browse files

- SymbolGaussElim.cc is discarded from the preprocessor (all simulations are...

- SymbolGaussElim.cc is discarded from the preprocessor (all simulations are now implemented in simulate)
- Correction in model_info.m

git-svn-id: https://www.dynare.org/svn/dynare/trunk@2358 ac1d8469-bf42-47a9-8791-bf33cf982152
parent 892d6524
......@@ -147,10 +147,11 @@ NumConstNode::eval(const eval_context_type &eval_context) const throw (EvalExcep
}
void
NumConstNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
NumConstNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
CompileCode.write(&FLDC, sizeof(FLDC));
double vard=atof(datatree.num_constants.get(id).c_str());
//double vard=id;
#ifdef DEBUGC
cout << "FLDC " << vard << "\n";
#endif
......@@ -427,7 +428,7 @@ VariableNode::eval(const eval_context_type &eval_context) const throw (EvalExcep
}
void
VariableNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
VariableNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
int i, lagl;
#ifdef DEBUGC
......@@ -892,7 +893,7 @@ UnaryOpNode::eval(const eval_context_type &eval_context) const throw (EvalExcept
}
void
UnaryOpNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
UnaryOpNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<UnaryOpNode *>(this));
if (it != temporary_terms.end())
......@@ -1164,8 +1165,6 @@ BinaryOpNode::computeTemporaryTerms(map<NodeID, int> &reference_count,
reference_count[this2]++;
if (reference_count[this2] * cost(temporary_terms, false) > MIN_COST_C)
{
if(this2->idx==2280)
cout << "==>Curr_block= " << Curr_block << " equation= " << equation << " first_occurence[this2].first=" << first_occurence[this2].first << " first_occurence[this2].second=" << first_occurence[this2].second << "\n";
temporary_terms.insert(this2);
ModelBlock->Block_List[first_occurence[this2].first].Temporary_Terms_in_Equation[first_occurence[this2].second]->insert(this2);
}
......@@ -1226,7 +1225,7 @@ BinaryOpNode::eval(const eval_context_type &eval_context) const throw (EvalExcep
}
void
BinaryOpNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
BinaryOpNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
// If current node is a temporary term
temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<BinaryOpNode *>(this));
......@@ -1580,8 +1579,6 @@ TrinaryOpNode::computeTemporaryTerms(map<NodeID, int> &reference_count,
reference_count[this2]++;
if (reference_count[this2] * cost(temporary_terms, false) > MIN_COST_C)
{
if(this2->idx==2280)
cout << "==>Curr_block= " << Curr_block << " equation= " << equation << " first_occurence[this2].first=" << first_occurence[this2].first << " first_occurence[this2].second=" << first_occurence[this2].second << "\n";
temporary_terms.insert(this2);
ModelBlock->Block_List[first_occurence[this2].first].Temporary_Terms_in_Equation[first_occurence[this2].second]->insert(this2);
}
......@@ -1613,7 +1610,7 @@ TrinaryOpNode::eval(const eval_context_type &eval_context) const throw (EvalExce
void
TrinaryOpNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type,
const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
// If current node is a temporary term
temporary_terms_type::const_iterator it = temporary_terms.find(const_cast<TrinaryOpNode *>(this));
......@@ -1788,7 +1785,7 @@ UnknownFunctionNode::eval(const eval_context_type &eval_context) const throw (Ev
}
void
UnknownFunctionNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const
UnknownFunctionNode::compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const
{
cerr << "UnknownFunctionNode::compile: operation impossible!" << endl;
exit(EXIT_FAILURE);
......
......@@ -33,7 +33,6 @@ OBJS = \
IncidenceMatrix.o \
BlockTriangular.o \
Model_Graph.o \
SymbolGaussElim.o \
DynareMain.o \
DynareMain2.o
......
......@@ -125,7 +125,7 @@ ModFile::evalAllExpressions()
cout << "error in evaluation of variable\n";
}
}
if(mod_file_struct.load_params_and_steady_state_present && init_values.size()<symbol_table.endo_nbr+symbol_table.exo_nbr+symbol_table.exo_det_nbr)
if(mod_file_struct.load_params_and_steady_state_present && int(init_values.size())<symbol_table.endo_nbr+symbol_table.exo_nbr+symbol_table.exo_det_nbr)
{
for(j=0;j <symbol_table.endo_nbr; j++)
{
......@@ -200,7 +200,7 @@ ModFile::evalAllExpressions()
}
}
}
if(init_values.size()<symbol_table.endo_nbr+symbol_table.exo_nbr+symbol_table.exo_det_nbr)
if(int(init_values.size())<symbol_table.endo_nbr+symbol_table.exo_nbr+symbol_table.exo_det_nbr)
{
cout << "\nWarning: Uninitialized variable: \n";
cout << "Endogenous\n";
......@@ -246,7 +246,7 @@ ModFile::evalAllExpressions()
cout << "error in evaluation of pound\n";
}
}
if(model_tree.local_variables_table.size()!=symbol_table.model_local_variable_nbr+symbol_table.modfile_local_variable_nbr)
if(int(model_tree.local_variables_table.size())!=symbol_table.model_local_variable_nbr+symbol_table.modfile_local_variable_nbr)
{
cout << "Warning: Unitilialized pound: \n";
cout << "Local variable in a model\n";
......@@ -398,7 +398,7 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all) const
mOutputFile << "logname_ = '" << basename << ".log';" << endl;
mOutputFile << "diary " << basename << ".log" << endl;
mOutputFile << "options_.model_mode = " << model_tree.mode << ";\n";
if (model_tree.mode == eSparseMode)
if (model_tree.mode == eSparseMode || model_tree.mode == eSparseDLLMode)
{
mOutputFile << "addpath " << basename << ";\n";
mOutputFile << "delete('" << basename << "_static.m');\n";
......@@ -408,7 +408,7 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all) const
if (model_tree.equation_number() > 0)
{
if (model_tree.mode == eDLLMode)
if (model_tree.mode == eDLLMode || model_tree.mode == eSparseDLLMode)
{
mOutputFile << "if exist('" << basename << "_static.c')" << endl;
mOutputFile << " clear " << basename << "_static" << endl;
......@@ -425,7 +425,6 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all) const
mOutputFile << "erase_compiled_function('" + basename +"_dynamic');" << endl;
}
}
cout << "Processing outputs ...";
symbol_table.writeOutput(mOutputFile);
......@@ -448,9 +447,10 @@ ModFile::writeOutputFiles(const string &basename, bool clear_all) const
mOutputFile << "save('" << basename << "_results.mat', 'oo_', 'M_', 'options_');" << endl;
mOutputFile << "diary off" << endl;
if (model_tree.mode == eSparseMode)
if (model_tree.mode == eSparseMode || model_tree.mode == eSparseDLLMode)
mOutputFile << "rmpath " << basename << ";\n";
mOutputFile << endl << "disp(['Total computing time : ' dynsec2hms(toc) ]);" << endl;
mOutputFile.close();
cout << "done\n";
}
......@@ -73,7 +73,7 @@ ModelTree::writeDerivative(ostream &output, int eq, int symb_id, int lag,
}
void
ModelTree::compileDerivative(ofstream &code_file, int eq, int symb_id, int lag, ExprNodeOutputType output_type, map_idx_type map_idx) const
ModelTree::compileDerivative(ofstream &code_file, int eq, int symb_id, int lag, ExprNodeOutputType output_type, map_idx_type &map_idx) const
{
first_derivatives_type::const_iterator it = first_derivatives.find(make_pair(eq, variable_table.getID(eEndogenous, symb_id, lag)));
if (it != first_derivatives.end())
......@@ -1082,7 +1082,7 @@ end:
void
ModelTree::writeModelEquationsCodeOrdered(const string file_name, const Model_Block *ModelBlock, const string bin_basename, ExprNodeOutputType output_type) const
ModelTree::writeModelEquationsCodeOrdered(const string file_name, const Model_Block *ModelBlock, const string bin_basename, ExprNodeOutputType output_type, map_idx_type map_idx) const
{
struct Uff_l
{
......@@ -1107,7 +1107,8 @@ ModelTree::writeModelEquationsCodeOrdered(const string file_name, const Model_Bl
map<NodeID, int> reference_count;
map<int,int> ModelBlock_Aggregated_Size, ModelBlock_Aggregated_Number;
int prev_Simulation_Type=-1;
SymbolicGaussElimination SGE;
//SymbolicGaussElimination SGE;
bool file_open=false;
temporary_terms_type::const_iterator it_temp=temporary_terms.begin();
//----------------------------------------------------------------------
string main_name=file_name;
......@@ -1180,14 +1181,15 @@ ModelTree::writeModelEquationsCodeOrdered(const string file_name, const Model_Bl
code_file.write(reinterpret_cast<char *>(&v),sizeof(v));
v=block_triangular.ModelBlock->Block_List[j].Max_Lead;
code_file.write(reinterpret_cast<char *>(&v),sizeof(v));
if (ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_COMPLETE || ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_SIMPLE)
{
//if (ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_COMPLETE || ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_SIMPLE)
//{
int u_count_int=0;
Write_Inf_To_Bin_File(file_name, bin_basename, j, u_count_int,SGE.file_open);
Write_Inf_To_Bin_File(file_name, bin_basename, j, u_count_int,file_open,
ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_COMPLETE || ModelBlock->Block_List[j].Simulation_Type==SOLVE_TWO_BOUNDARIES_SIMPLE);
v=u_count_int;
code_file.write(reinterpret_cast<char *>(&v),sizeof(v));
SGE.file_is_open();
}
file_open=true;
//}
}
for (k1 = 0; k1 < ModelBlock_Aggregated_Size[k0]; k1++)
{
......@@ -1821,7 +1823,7 @@ ModelTree::reform(const string name1) const
void
ModelTree::Write_Inf_To_Bin_File(const string &dynamic_basename, const string &bin_basename, const int &num,
int &u_count_int, bool &file_open) const
int &u_count_int, bool &file_open, bool is_two_boundaries) const
{
int j;
std::ofstream SaveCode;
......@@ -1850,19 +1852,22 @@ ModelTree::Write_Inf_To_Bin_File(const string &dynamic_basename, const string &b
u_count_int++;
}
}
for (j=0;j<block_triangular.ModelBlock->Block_List[num].Size;j++)
if(is_two_boundaries)
{
int eqr1=j;
int varr=block_triangular.ModelBlock->Block_List[num].Size*(block_triangular.periods
+block_triangular.incidencematrix.Model_Max_Lead_Endo);
int k1=0;
SaveCode.write(reinterpret_cast<char *>(&eqr1), sizeof(eqr1));
SaveCode.write(reinterpret_cast<char *>(&varr), sizeof(varr));
SaveCode.write(reinterpret_cast<char *>(&k1), sizeof(k1));
SaveCode.write(reinterpret_cast<char *>(&eqr1), sizeof(eqr1));
u_count_int++;
for (j=0;j<block_triangular.ModelBlock->Block_List[num].Size;j++)
{
int eqr1=j;
int varr=block_triangular.ModelBlock->Block_List[num].Size*(block_triangular.periods
+block_triangular.incidencematrix.Model_Max_Lead_Endo);
int k1=0;
SaveCode.write(reinterpret_cast<char *>(&eqr1), sizeof(eqr1));
SaveCode.write(reinterpret_cast<char *>(&varr), sizeof(varr));
SaveCode.write(reinterpret_cast<char *>(&k1), sizeof(k1));
SaveCode.write(reinterpret_cast<char *>(&eqr1), sizeof(eqr1));
u_count_int++;
}
}
//cout << "u_count_int=" << u_count_int << "\n";
for (j=0;j<block_triangular.ModelBlock->Block_List[num].Size;j++)
{
int varr=block_triangular.ModelBlock->Block_List[num].Variable[j];
......@@ -2103,7 +2108,7 @@ ModelTree::writeSparseDynamicMFile(const string &dynamic_basename, const string
ofstream mDynamicModelFile;
ostringstream tmp, tmp1, tmp_eq;
int prev_Simulation_Type, tmp_i;
SymbolicGaussElimination SGE;
//SymbolicGaussElimination SGE;
bool OK;
chdir(basename.c_str());
string filename = dynamic_basename + ".m";
......@@ -2121,7 +2126,7 @@ ModelTree::writeSparseDynamicMFile(const string &dynamic_basename, const string
mDynamicModelFile << "%/\n";
int i, k, Nb_SGE=0;
bool printed = false, skip_head, open_par=false;
bool skip_head, open_par=false;
if (computeJacobian || computeJacobianExo || computeHessian)
{
mDynamicModelFile << "function [varargout] = " << dynamic_basename << "(varargin)\n";
......@@ -2413,10 +2418,6 @@ ModelTree::writeSparseDynamicMFile(const string &dynamic_basename, const string
if (open_par)
mDynamicModelFile << " end\n";
open_par=false;
if (!printed)
{
printed = true;
}
Nb_SGE++;
int nze, m;
for (nze=0,m=0;m<=block_triangular.ModelBlock->Block_List[i].Max_Lead+block_triangular.ModelBlock->Block_List[i].Max_Lag;m++)
......@@ -2453,8 +2454,6 @@ ModelTree::writeSparseDynamicMFile(const string &dynamic_basename, const string
writeModelEquationsOrdered_M( block_triangular.ModelBlock, dynamic_basename);
chdir("..");
if (printed)
cout << "done\n";
}
void
......@@ -3074,9 +3073,10 @@ ModelTree::writeStaticFile(const string &basename) const
switch (mode)
{
case eStandardMode:
case eSparseDLLMode:
/*case eSparseDLLMode:*/
writeStaticMFile(basename + "_static");
break;
case eSparseDLLMode:
case eSparseMode:
// create a directory to store all files
#ifdef _WIN32
......@@ -3117,7 +3117,7 @@ ModelTree::writeDynamicFile(const string &basename) const
#else
mkdir(basename.c_str(), 0777);
#endif
writeModelEquationsCodeOrdered(basename + "_dynamic", block_triangular.ModelBlock, basename, oCDynamicModelSparseDLL);
writeModelEquationsCodeOrdered(basename + "_dynamic", block_triangular.ModelBlock, basename, oCDynamicModelSparseDLL, map_idx);
block_triangular.Free_Block(block_triangular.ModelBlock);
block_triangular.incidencematrix.Free_IM();
//block_triangular.Free_IM_X(block_triangular.First_IM_X);
......
......@@ -596,7 +596,7 @@ ParsingDriver::add_to_row(NodeID v)
void
ParsingDriver::steady()
{
if (mod_file->model_tree.mode == eSparseMode)
if (mod_file->model_tree.mode == eSparseMode || mod_file->model_tree.mode == eSparseDLLMode)
mod_file->addStatement(new SteadySparseStatement(options_list));
else
mod_file->addStatement(new SteadyStatement(options_list));
......
This diff is collapsed.
......@@ -159,7 +159,7 @@ public:
};
virtual double eval(const eval_context_type &eval_context) const throw (EvalException) = 0;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const = 0;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const = 0;
};
//! Object used to compare two nodes (using their indexes)
......@@ -186,7 +186,7 @@ public:
virtual void collectExogenous(set<pair<int, int> > &result) const;
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
};
//! Symbol or variable node
......@@ -214,7 +214,7 @@ public:
map_idx_type &map_idx) const;
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
};
//! Unary operator node
......@@ -243,7 +243,7 @@ public:
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
static double eval_opcode(UnaryOpcode op_code, double v) throw (EvalException);
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
};
//! Binary operator node
......@@ -273,7 +273,7 @@ public:
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
static double eval_opcode(double v1, BinaryOpcode op_code, double v2) throw (EvalException);
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
virtual NodeID get_arg1() { return(arg1);};
virtual NodeID get_arg2() { return(arg2);};
};
......@@ -310,7 +310,7 @@ public:
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) throw (EvalException);
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
};
//! Unknown function node
......@@ -337,7 +337,7 @@ public:
virtual void collectExogenous(set<pair<int, int> > &result) const;
virtual void collectTemporary_terms(const temporary_terms_type &temporary_terms, Model_Block *ModelBlock, int Curr_Block) const;
virtual double eval(const eval_context_type &eval_context) const throw (EvalException);
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type map_idx) const;
virtual void compile(ofstream &CompileCode, bool lhs_rhs, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, map_idx_type &map_idx) const;
};
//! For one lead/lag of one block, stores mapping of information between original model and block-decomposed model
......
......@@ -23,6 +23,8 @@
using namespace std;
#include <ostream>
#include <ctime>
#include "SymbolTable.hh"
#include "NumericalConstants.hh"
......
......@@ -32,7 +32,6 @@ using namespace std;
#include "NumericalConstants.hh"
#include "DataTree.hh"
#include "BlockTriangular.hh"
#include "SymbolGaussElim.hh"
//! The three in which ModelTree can work
enum ModelTreeMode
......@@ -85,7 +84,7 @@ private:
//! Write derivative of an equation w.r. to a variable
void writeDerivative(ostream &output, int eq, int symb_id, int lag, ExprNodeOutputType output_type, const temporary_terms_type &temporary_terms, SymbolType type) const;
//! Write derivative code of an equation w.r. to a variable
void compileDerivative(ofstream &code_file, int eq, int symb_id, int lag, ExprNodeOutputType output_type, map_idx_type map_idx) const;
void compileDerivative(ofstream &code_file, int eq, int symb_id, int lag, ExprNodeOutputType output_type, map_idx_type &map_idx) const;
//! Computes temporary terms
void computeTemporaryTerms(int order);
void computeTemporaryTermsOrdered(int order, Model_Block *ModelBlock);
......@@ -109,7 +108,7 @@ private:
//! Writes the Block reordred structure of the static model in M output
void writeModelStaticEquationsOrdered_M(Model_Block *ModelBlock, const string &static_basename) const;
//! Writes the code of the Block reordred structure of the model in virtual machine bytecode
void writeModelEquationsCodeOrdered(const string file_name, const Model_Block *ModelBlock, const string bin_basename, ExprNodeOutputType output_type) const;
void writeModelEquationsCodeOrdered(const string file_name, const Model_Block *ModelBlock, const string bin_basename, ExprNodeOutputType output_type, map_idx_type map_idx) const;
//! Writes static model file (Matlab version)
void writeStaticMFile(const string &static_basename) const;
//! Writes static model file (C version)
......@@ -173,7 +172,7 @@ public:
BlockTriangular block_triangular;
//! Adds informations for simulation in a binary file
void Write_Inf_To_Bin_File(const string &dynamic_basename, const string &bin_basename,
const int &num, int &u_count_int, bool &file_open) const;
const int &num, int &u_count_int, bool &file_open, bool is_two_boundaries) const;
//! Returns the number of equations in the model
int equation_number() const;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment