Minsky
godleyTable.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2012
3  @author Russell Standish
4  This file is part of Minsky.
5 
6  Minsky is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Minsky is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Minsky. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "cairoItems.h"
20 #include "minsky.h"
21 #include "godleyTable.h"
22 #include "port.h"
23 #include "flowCoef.h"
24 #include "godleyExport.h"
25 #include "assetClass.rcd"
26 #include "godleyTable.rcd"
27 #include "minsky_epilogue.h"
28 using namespace minsky;
29 
30 const char* GodleyTable::initialConditions="Initial Conditions";
31 
33 {
35 }
36 
37 bool GodleyTable::initialConditionRow(unsigned row) const
38 {
39  if (row>=rows()) return false;
40  const string& label=cell(row,0);
41  static const size_t initialConditionsSz=strlen(initialConditions);
42  size_t i, j;
43  // trim any leading whitespaces
44  for (i=0; isspace(label[i]); ++i);
45  // compare case insensitively
46  for (j=0; j<initialConditionsSz && i<label.size() &&
47  toupper(label[i])==toupper(initialConditions[j]); ++i, ++j);
48  return j==initialConditionsSz;
49 }
50 
51 bool GodleyTable::singularRow(unsigned row, unsigned col)
52 {
53  for (size_t c=0; c<cols(); ++c)
54  if (c!=col && !cell(row, c).empty())
55  return false;
56  return true;
57 }
58 
59 void GodleyTable::insertRow(unsigned row)
60 {
61  if (row<=data.size())
62  {
63  data.insert(data.begin()+row, vector<string>(cols()));
64  markEdited();
65  }
66 }
67 
68 void GodleyTable::insertCol(unsigned col)
69 {
70  if (col>=m_assetClass.size())
71  m_assetClass.resize(cols(), noAssetClass);
72  m_assetClass.insert(m_assetClass.begin()+col,
73  col==0? noAssetClass: m_assetClass[col-1]);
74  for (unsigned row=0; row<data.size(); ++row)
75  {
76  for (size_t i=data[row].size(); i<col; ++i)
77  data[row].insert(data[row].end(), "");
78  data[row].insert(data[row].begin()+col, "");
79  }
80  markEdited();
81 }
82 
83 void GodleyTable::deleteCol(unsigned col)
84 {
85  if (col>=m_assetClass.size())
86  m_assetClass.resize(cols(), noAssetClass);
87  m_assetClass.erase(m_assetClass.begin()+col-1);
88  if (col>0 && col<=data[0].size())
89  {
90  for (unsigned row=0; row<rows(); ++row)
91  data[row].erase(data[row].begin()+col-1);
92  markEdited();
93  }
94  // insert extra empty column if an asset class gets emptied out of this
96 }
97 
98 void GodleyTable::moveRow(int row, int n)
99 {
100  if (n==0 || row<0 || row>=int(rows()) || row+n<0 || row+n>=int(rows()))
101  return;
102  vector<string> rowToMove;
103  rowToMove.swap(data[row]);
104  for ( ; abs(n)>0; n=n>0? n-1:n+1)
105  rowToMove.swap(data[row+n]);
106  rowToMove.swap(data[row]);
107 }
108 
109 void GodleyTable::moveCol(int col, int n)
110 {
111  if (n==0 || col<0 || col>=int(cols()) || col+n<0 || col+n>=int(cols()))
112  return;
113  auto targetAssetClass=_assetClass(col+n);
114  for (size_t row=0; row<rows(); ++row)
115  {
116  string cellToMove;
117  cellToMove.swap(data[row][col]);
118  for (int i=n; abs(i)>0; i=i>0? i-1:i+1)
119  cellToMove.swap(data[row][col+i]);
120  cellToMove.swap(data[row][col]);
121  }
122  auto ac=_assetClass(col);
123  for (int i=n; abs(i)>0; i=i>0? i-1:i+1)
124  swap(ac, m_assetClass[col+i]);
125  swap(ac, m_assetClass[col]);
126 
127  _assetClass(col+n, targetAssetClass);
128  // insert extra empty column if an asset class gets emptied out of this
130  // save text in currently highlighted column heading. For tickets 1058/1094/1122/1127.
131  savedText=data[0][col];
132 }
133 
135 {
136  if (_assetClass(col)!=equity) return;
137  for (unsigned r=1; r<rows(); ++r)
138  {
139  cell(r,col)="";
140  auto sum=rowSumAsMap(r);
141  cell(r,col)=stringify(sum);
142  }
143 }
144 
145 
146 vector<string> GodleyTable::getColumnVariables() const
147 {
148  set<string> uvars; //set for uniqueness checking
149  vector<string> vars;
150  for (size_t c=1; c<cols(); ++c)
151  {
152  const string var=trimWS(cell(0,c));
153  if (!var.empty())
154  {
155  // disable duplicate column test on equity columns (feature #174)
156  if (_assetClass(c)!=AssetClass::equity && !uvars.insert(var).second)
157  throw error("Duplicate column label detected");
158  vars.push_back(var);
159  }
160  }
161  return vars;
162 }
163 
164 vector<string> GodleyTable::getVariables() const
165 {
166  vector<string> vars;
167  set<string> uvars; //set for uniqueness checking
168  for (size_t r=1; r<rows(); ++r)
169  if (!initialConditionRow(r))
170  for (size_t c=1; c<cols(); ++c)
171  {
172  const FlowCoef fc(cell(r,c));
173  if (!fc.name.empty() && uvars.insert(fc.name).second)
174  vars.push_back(fc.name);
175  }
176  return vars;
177 }
178 
180 {
181  if (col==0) return noAssetClass;
182  return col<m_assetClass.size()? m_assetClass[col]: noAssetClass;
183 }
184 
186 (size_t col, GodleyTable::AssetClass cls)
187 {
188  if (col==0) return noAssetClass; // don't set column 0 asset class
189  if (col>=m_assetClass.size())
190  m_assetClass.resize(cols(), noAssetClass);
191  assert(cols()>col);
192  m_assetClass[col]=cls;
193  return _assetClass(col);
194 }
195 
197  assert(cols()>=3);
199 }
200 
201 string GodleyTable::assetClass(TCL_args args)
202 {
203  const int col=args;
204  if (args.count)
205  {
207  (col, AssetClass(classdesc::enumKey<AssetClass>((char*)args)));
208  markEdited();
209  }
210  return classdesc::enumKey<AssetClass>(_assetClass(col));
211 }
212 
213 map<string,double> GodleyTable::rowSumAsMap(int row) const
214 {
215  if (row==0)
216  throw runtime_error("rowSum not valid for stock var names");
217 
218  // accumulate the total for each variable
219  map<string,double> sum;
220 
221  for (size_t c=1; c<cols(); ++c)
222  {
223  const FlowCoef fc(cell(row,c));
224  if (!fc.name.empty()||initialConditionRow(row))
225  {
226  // apply accounting relation to the initial condition row
227  if (signConventionReversed(c))
228  sum[fc.name]-=fc.coef;
229  else
230  sum[fc.name]+=fc.coef;
231  }
232  }
233 
234  return sum;
235 }
236 
237 string GodleyTable::stringify(const map<string,double>& sum)
238 {
239  // create symbolic representation of each term
240  ostringstream ret;
241  for (auto i=sum.begin(); i!=sum.end(); ++i)
242  if (i->second!=0)
243  {
244  if (!ret.str().empty() &&i->second>0)
245  ret<<"+";
246  if (i->second==-1)
247  ret<<"-";
248  else if (i->second!=1)
249  abs(i->second)>5*std::numeric_limits<double>::epsilon()? ret<<i->second : ret<<0; // only display decimals if sum of row is larger than 5*2.22045e-16. for ticket 1244
250  abs(i->second)>5*std::numeric_limits<double>::epsilon()? ret<<i->first : ret<<"";
251  }
252 
253  //if completely empty, substitute a zero
254  if (ret.str().empty())
255  return "0";
256  return ret.str();
257 
258 }
259 
260 std::vector<std::string> GodleyTable::getColumn(unsigned col) const
261 {
262  std::vector<std::string> r;
263  for (unsigned row=0; row<rows(); ++row)
264  r.push_back(cell(row,col));
265  return r;
266 }
267 
268 void GodleyTable::setDEmode(bool mode)
269 {
270  if (mode==doubleEntryCompliant) return;
271  doubleEntryCompliant=true; // to allow signConventionReversed to work
272  for (size_t r=1; r<rows(); ++r)
273  if (!initialConditionRow(r))
274  for (size_t c=1; c<cols(); ++c)
275  if (signConventionReversed(c))
276  {
277  string& formula=cell(r,c);
278  unsigned start=0;
279  while (start<formula.length() && isspace(formula[start])) start++;
280  if (start==formula.length()) continue; // empty cell
281  if (formula[start]=='-')
282  formula.erase(start,1); // turns a negative into a positive
283  else
284  formula.insert(start,"-");
285  }
287 }
288 
290 {
291  for (int i=1;;++i)
292  {
293  string trialName="Godley"+to_string(i);
294 
295  if (!cminsky().model->findAny
296  (&Group::items,
297  [&](const ItemPtr& i)
298  {
299  auto g=dynamic_cast<GodleyIcon*>(i.get());
300  return g && g->table.title == trialName;
301  }))
302  {
303  title = trialName;
304  break;
305  }
306  }
307 }
308 
309 void GodleyTable::exportToLaTeX(const string& filename) const
310 {
311  ofstream f(filename);
312  minsky::exportToLaTeX(f, *this);
313  if (!f) throw runtime_error("cannot save to "+filename);
314 }
315 
316 void GodleyTable::exportToCSV(const string& filename) const
317 {
318  ofstream f(filename);
319  minsky::exportToCSV(f, *this);
320  if (!f) throw runtime_error("cannot save to "+filename);
321 }
322 
324 {
325  const unsigned numRows=rows()>1? rows(): 1;
326  map<AssetClass,Data> tmpCols;
327  for (unsigned c=1; c<cols(); ++c)
328  if (_assetClass(c)==noAssetClass)
329  tmpCols[asset].push_back(getColumn(c));
330  else
331  tmpCols[_assetClass(c)].push_back(getColumn(c));
332 
333  // add empty column if asset class not present, and count number of cols
334  unsigned numCols=1;
335  for (int ac=asset; ac<=equity; ++ac)
336  {
337  auto& tc=tmpCols[AssetClass(ac)];
338  // strip out any blank columns
339  tc.erase(remove_if(tc.begin(), tc.end(), [](const vector<string>& x)
340  {return x.empty() || x[0].empty();}), tc.end());
341  // ensure at least one column is present in an asset class
342  if (tc.empty())
343  tc.emplace_back(numRows);
344 
345  numCols+=tc.size();
346  }
347 
348  resize(numRows, numCols);
349  unsigned col=1;
350  for (int ac=asset; ac<=equity; ++ac)
351  for (auto& colData: tmpCols[AssetClass(ac)])
352  {
353  for (unsigned row=0; row<rows(); ++row)
354  cell(row,col)=colData[row];
355  _assetClass(col,AssetClass(ac));
356  col++;
357  }
358 }
359 
360 void GodleyTable::rename(const std::string& from, const std::string& to)
361 {
362  // if no stock vars found, check flow var cells.
363  for (size_t r=0; r<rows(); ++r)
364  for (size_t c=1; c<cols(); ++c)
365  {
366  FlowCoef fc(cell(r,c));
367  if (!fc.name.empty() && fc.name==from)
368  {
369  fc.name=to;
370  cell(r,c)=fc.str();
371  }
372  }
373 }
374 
375 void GodleyTable::renameFlows(const std::string& from, const std::string& to)
376 {
377  for (size_t r=1; r<rows(); ++r)
378  for (size_t c=1; c<cols(); ++c)
379  {
380  FlowCoef fc(cell(r,c));
381  if (!fc.name.empty() && fc.name==from)
382  {
383  fc.name=to;
384  cell(r,c)=fc.str();
385  }
386  }
387 }
388 
389 void GodleyTable::renameStock(const std::string& from, const std::string& to)
390 {
391  for (size_t c=1; c<cols(); ++c)
392  {
393  FlowCoef fc(cell(0,c));
394  if (!fc.name.empty() && fc.name==from)
395  {
396  fc.name=to;
397  cell(0,c)=fc.str();
398  }
399  }
400 }
std::size_t cols() const
Definition: godleyTable.h:115
function f
Definition: canvas.m:1
void renameFlows(const std::string &from, const std::string &to)
rename all instances of a flow variable
Definition: godleyTable.cc:375
bool singleEquity() const
Check whether more than one equity column is present irrespective of single or multiple equity column...
Definition: godleyTable.cc:196
void deleteCol(unsigned col)
delete col before col
Definition: godleyTable.cc:83
void orderAssetClasses()
reorders columns into assets/liabilities and equities. Adds empty columns if an asset class is not pr...
Definition: godleyTable.cc:323
void exportToLaTeX(std::ostream &f, const GodleyTable &g)
Definition: godleyExport.cc:84
std::vector< std::string > getColumn(unsigned col) const
get column data
Definition: godleyTable.cc:260
vector< AssetClass > m_assetClass
class of each column (used in DE compliant mode)
Definition: godleyTable.h:45
std::string name
Definition: flowCoef.h:30
void moveCol(int col, int n)
Definition: godleyTable.cc:109
std::shared_ptr< Item > ItemPtr
Definition: item.h:57
bool initialConditionRow(unsigned row) const
returns true if row is an "Initial Conditions" row
Definition: godleyTable.cc:37
static std::string stringify(const std::map< std::string, double > &)
Definition: godleyTable.cc:237
string & cell(unsigned row, unsigned col)
Definition: godleyTable.h:144
std::string str() const
Definition: flowCoef.cc:81
std::size_t rows() const
Definition: godleyTable.h:114
void markEdited()
indicate model has been changed since last saved
Definition: minsky.h:173
void renameStock(const std::string &from, const std::string &to)
rename a stock variable
Definition: godleyTable.cc:389
void exportToCSV(const std::string &filename) const
Definition: godleyTable.cc:316
void moveRow(int row, int n)
move row row down by n places (up if -ve)
Definition: godleyTable.cc:98
void insertRow(unsigned row)
insert row at row
Definition: godleyTable.cc:59
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
void resize(unsigned rows, unsigned cols)
Definition: godleyTable.h:118
std::string trimWS(const std::string &s)
Definition: str.h:49
string assetClass(ecolab::TCL_args args)
Definition: godleyTable.cc:201
static void markEdited()
mark model as having changed
Definition: godleyTable.cc:32
const Minsky & cminsky()
const version to help in const correctness
Definition: minsky.h:549
void balanceEquity(int col)
insert A-L into the equity column c, such that A-L-E=0
Definition: godleyTable.cc:134
std::map< std::string, double > rowSumAsMap(int row) const
Definition: godleyTable.cc:213
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::GodleyTable)
std::vector< std::string > getColumnVariables() const
get the set of column labels, in column order
Definition: godleyTable.cc:146
void insertCol(unsigned col)
insert col at col
Definition: godleyTable.cc:68
represents a numerical coefficient times a variable (a "flow")
Definition: flowCoef.h:27
void setDEmode(bool doubleEntryCompliant)
toggle flow signs according to double entry compliant mode
Definition: godleyTable.cc:268
GodleyAssetClass::AssetClass AssetClass
Definition: godleyTable.h:56
void rename(const std::string &from, const std::string &to)
rename all instances of a variable
Definition: godleyTable.cc:360
std::string title
Definition: godleyTable.h:60
const vector< AssetClass > & _assetClass() const
class of each column (used in DE compliant mode)
Definition: godleyTable.h:78
std::string savedText
save text in currently highlighted column heading for renaming all variable instances and to enable u...
Definition: godleyTable.h:177
string to_string(CONST84 char *x)
Definition: minskyTCLObj.h:33
void exportToCSV(std::ostream &s, const GodleyTable &g)
Definition: godleyExport.cc:61
Minsky & minsky()
global minsky object
Definition: minskyTCL.cc:51
void exportToLaTeX(const std::string &filename) const
Definition: godleyTable.cc:309
static const char * initialConditions
Definition: godleyTable.h:62
bool signConventionReversed(int col) const
The usual mathematical sign convention is reversed in double entry book keeping conventions if the as...
Definition: godleyTable.h:94
std::vector< std::string > getVariables() const
get the vector of unique variable names from the interior of the table, in row, then column order ...
Definition: godleyTable.cc:164
bool singularRow(unsigned row, unsigned col)
return true if row is empty apart from a value in column col
Definition: godleyTable.cc:51