1 /* 2 * Copyright (c) 2003-2018, John Wiegley. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of New Artisans LLC nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /** 33 * @addtogroup math 34 */ 35 36 /** 37 * @file balance.h 38 * @author John Wiegley 39 * 40 * @ingroup math 41 * 42 * @brief Basic type for adding multiple commodities together 43 * 44 * Unlike the amount_t class, which throws an exception if amounts of 45 * differing commodities are added or subtracted, the balance_t class 46 * is designed to allow this, tracking the amounts of each component 47 * commodity separately. 48 */ 49 #ifndef _BALANCE_H 50 #define _BALANCE_H 51 52 #include "amount.h" 53 54 namespace ledger { 55 56 DECLARE_EXCEPTION(balance_error, std::runtime_error); 57 58 /** 59 * @class balance_t 60 * 61 * @brief A wrapper around amount_t allowing addition of multiple commodities. 62 * 63 * The balance_t class is appopriate for keeping a running balance 64 * where amounts of multiple commodities may be involved. 65 */ 66 class balance_t 67 : public equality_comparable<balance_t, 68 equality_comparable<balance_t, amount_t, 69 equality_comparable<balance_t, double, 70 equality_comparable<balance_t, unsigned long, 71 equality_comparable<balance_t, long, 72 additive<balance_t, 73 additive<balance_t, amount_t, 74 additive<balance_t, double, 75 additive<balance_t, unsigned long, 76 additive<balance_t, long, 77 multiplicative<balance_t, amount_t, 78 multiplicative<balance_t, double, 79 multiplicative<balance_t, unsigned long, 80 multiplicative<balance_t, long> > > > > > > > > > > > > > 81 { 82 public: 83 typedef std::unordered_map<commodity_t *, amount_t> amounts_map; 84 typedef std::vector<const amount_t *> amounts_array; 85 86 amounts_map amounts; 87 88 /** 89 * Constructors. balance_t supports similar forms of construction 90 * to amount_t. 91 * 92 * balance_t() creates an empty balance to which amounts or other 93 * balances may be added or subtracted. 94 * 95 * balance_t(amount_t) constructs a balance whose starting value is 96 * equal to the given amount. 97 * 98 * balance_t(double), balance_t(unsigned long) and balance_t(long) 99 * will construct an amount from their arguments and then construct 100 * a balance whose starting value is equal to that amount. This 101 * initial balance will have no commodity. 102 * 103 * balance_t(string) and balance_t(const char *) both convert from a 104 * string representation of an amount to a balance whose initial 105 * value is that amount. This is the proper way to initialize a 106 * balance like '$100.00'. 107 */ balance_t()108 balance_t() { 109 TRACE_CTOR(balance_t, ""); 110 } balance_t(const amount_t & amt)111 balance_t(const amount_t& amt) { 112 if (amt.is_null()) 113 throw_(balance_error, 114 _("Cannot initialize a balance from an uninitialized amount")); 115 if (! amt.is_realzero()) 116 amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); 117 TRACE_CTOR(balance_t, "const amount_t&"); 118 } 119 balance_t(const double val); 120 balance_t(const unsigned long val); 121 balance_t(const long val); 122 balance_t(const string & val)123 explicit balance_t(const string& val) { 124 amount_t temp(val); 125 amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); 126 TRACE_CTOR(balance_t, "const string&"); 127 } balance_t(const char * val)128 explicit balance_t(const char * val) { 129 amount_t temp(val); 130 amounts.insert(amounts_map::value_type(&temp.commodity(), temp)); 131 TRACE_CTOR(balance_t, "const char *"); 132 } 133 134 /** 135 * Destructor. Destroys all of the accumulated amounts in the 136 * balance. 137 */ ~balance_t()138 ~balance_t() { 139 TRACE_DTOR(balance_t); 140 } 141 142 /** 143 * Assignment and copy operators. An balance may be assigned or copied. 144 */ balance_t(const balance_t & bal)145 balance_t(const balance_t& bal) : amounts(bal.amounts) { 146 TRACE_CTOR(balance_t, "copy"); 147 } 148 149 balance_t& operator=(const balance_t& bal) { 150 if (this != &bal) 151 amounts = bal.amounts; 152 return *this; 153 } 154 balance_t& operator=(const amount_t& amt) { 155 if (amt.is_null()) 156 throw_(balance_error, 157 _("Cannot assign an uninitialized amount to a balance")); 158 159 amounts.clear(); 160 if (! amt.is_realzero()) 161 amounts.insert(amounts_map::value_type(&amt.commodity(), amt)); 162 163 return *this; 164 } 165 166 balance_t& operator=(const string& str) { 167 return *this = balance_t(str); 168 } 169 balance_t& operator=(const char * str) { 170 return *this = balance_t(str); 171 } 172 173 /** 174 * Comparison operators. Balances are fairly restrictive in terms 175 * of how they may be compared. They may be compared for equality 176 * or inequality, but this is all, since the concept of "less than" 177 * or "greater than" makes no sense when amounts of multiple 178 * commodities are involved. 179 * 180 * Balances may also be compared to amounts, in which case the sum 181 * of the balance must equal the amount exactly. 182 * 183 * If a comparison between balances is desired, the balances must 184 * first be rendered to value equivalent amounts using the `value' 185 * method, to determine a market valuation at some specific moment 186 * in time. 187 */ 188 bool operator==(const balance_t& bal) const { 189 return amounts == bal.amounts; 190 } 191 bool operator==(const amount_t& amt) const { 192 if (amt.is_null()) 193 throw_(balance_error, 194 _("Cannot compare a balance to an uninitialized amount")); 195 196 if (amt.is_realzero()) 197 return amounts.empty(); 198 else 199 return amounts.size() == 1 && amounts.begin()->second == amt; 200 } 201 202 template <typename T> 203 bool operator==(const T& val) const { 204 return *this == amount_t(val); 205 } 206 207 /** 208 * Binary arithmetic operators. Balances support addition and 209 * subtraction of other balances or amounts, but multiplication and 210 * division are restricted to uncommoditized amounts only. 211 */ 212 balance_t& operator+=(const balance_t& bal); 213 balance_t& operator+=(const amount_t& amt); 214 balance_t& operator+=(const double val) { 215 return *this += amount_t(val); 216 } 217 balance_t& operator+=(const unsigned long val) { 218 return *this += amount_t(val); 219 } 220 balance_t& operator+=(const long val) { 221 return *this += amount_t(val); 222 } 223 224 balance_t& operator-=(const balance_t& bal); 225 balance_t& operator-=(const amount_t& amt); 226 balance_t& operator-=(const double val) { 227 return *this -= amount_t(val); 228 } 229 balance_t& operator-=(const unsigned long val) { 230 return *this -= amount_t(val); 231 } 232 balance_t& operator-=(const long val) { 233 return *this -= amount_t(val); 234 } 235 236 balance_t& operator*=(const amount_t& amt); 237 balance_t& operator*=(const double val) { 238 return *this *= amount_t(val); 239 } 240 balance_t& operator*=(const unsigned long val) { 241 return *this *= amount_t(val); 242 } 243 balance_t& operator*=(const long val) { 244 return *this *= amount_t(val); 245 } 246 247 balance_t& operator/=(const amount_t& amt); 248 balance_t& operator/=(const double val) { 249 return *this /= amount_t(val); 250 } 251 balance_t& operator/=(const unsigned long val) { 252 return *this /= amount_t(val); 253 } 254 balance_t& operator/=(const long val) { 255 return *this /= amount_t(val); 256 } 257 258 /** 259 * Unary arithmetic operators. There are only a few unary methods 260 * support on balance: 261 * 262 * negate(), also unary minus (- x), returns a balance all of whose 263 * component amounts have been negated. In order words, it inverts 264 * the sign of all member amounts. 265 * 266 * abs() returns a balance where no component amount is negative. 267 * 268 * reduce() reduces the values in a balance to their most basic 269 * commodity forms, for amounts that utilize "scaling commodities". 270 * For example, a balance of 1h and 1m after reduction will be 271 * 3660s. 272 * 273 * unreduce(), if used with amounts that use "scaling commodities", 274 * yields the most compact form greater than 1.0 for each component 275 * amount. That is, a balance of 10m and 1799s will unreduce to 276 * 39.98m. 277 * 278 * value(optional<datetime_t>) returns the total historical value for 279 * a balance -- the default moment returns a value based on the most 280 * recently known price -- based on the price history of its 281 * component commodities. See amount_t::value for an example. 282 * 283 * Further, for the sake of efficiency and avoiding temporary 284 * objects, the following methods support "in-place" variants act on 285 * the balance itself and return a reference to the result 286 * (`*this'): 287 * 288 * in_place_negate() 289 * in_place_reduce() 290 * in_place_unreduce() 291 */ negated()292 balance_t negated() const { 293 balance_t temp(*this); 294 temp.in_place_negate(); 295 return temp; 296 } in_place_negate()297 void in_place_negate() { 298 foreach (amounts_map::value_type& pair, amounts) 299 pair.second.in_place_negate(); 300 } 301 balance_t operator-() const { 302 return negated(); 303 } 304 abs()305 balance_t abs() const { 306 balance_t temp; 307 foreach (const amounts_map::value_type& pair, amounts) 308 temp += pair.second.abs(); 309 return temp; 310 } 311 rounded()312 balance_t rounded() const { 313 balance_t temp(*this); 314 temp.in_place_round(); 315 return temp; 316 } in_place_round()317 void in_place_round() { 318 foreach (amounts_map::value_type& pair, amounts) 319 pair.second.in_place_round(); 320 } 321 roundto(int places)322 balance_t roundto(int places) const { 323 balance_t temp(*this); 324 temp.in_place_roundto(places); 325 return temp; 326 } 327 in_place_roundto(int places)328 void in_place_roundto(int places) { 329 foreach (amounts_map::value_type& pair, amounts) 330 pair.second.in_place_roundto(places); 331 } 332 truncated()333 balance_t truncated() const { 334 balance_t temp(*this); 335 temp.in_place_truncate(); 336 return temp; 337 } in_place_truncate()338 void in_place_truncate() { 339 foreach (amounts_map::value_type& pair, amounts) 340 pair.second.in_place_truncate(); 341 } 342 floored()343 balance_t floored() const { 344 balance_t temp(*this); 345 temp.in_place_floor(); 346 return temp; 347 } in_place_floor()348 void in_place_floor() { 349 foreach (amounts_map::value_type& pair, amounts) 350 pair.second.in_place_floor(); 351 } 352 ceilinged()353 balance_t ceilinged() const { 354 balance_t temp(*this); 355 temp.in_place_ceiling(); 356 return temp; 357 } in_place_ceiling()358 void in_place_ceiling() { 359 foreach (amounts_map::value_type& pair, amounts) 360 pair.second.in_place_ceiling(); 361 } 362 363 unrounded()364 balance_t unrounded() const { 365 balance_t temp(*this); 366 temp.in_place_unround(); 367 return temp; 368 } in_place_unround()369 void in_place_unround() { 370 foreach (amounts_map::value_type& pair, amounts) 371 pair.second.in_place_unround(); 372 } 373 reduced()374 balance_t reduced() const { 375 balance_t temp(*this); 376 temp.in_place_reduce(); 377 return temp; 378 } in_place_reduce()379 void in_place_reduce() { 380 // A temporary must be used here because reduction may cause 381 // multiple component amounts to collapse to the same commodity. 382 balance_t temp; 383 foreach (const amounts_map::value_type& pair, amounts) 384 temp += pair.second.reduced(); 385 *this = temp; 386 } 387 unreduced()388 balance_t unreduced() const { 389 balance_t temp(*this); 390 temp.in_place_unreduce(); 391 return temp; 392 } in_place_unreduce()393 void in_place_unreduce() { 394 // A temporary must be used here because unreduction may cause 395 // multiple component amounts to collapse to the same commodity. 396 balance_t temp; 397 foreach (const amounts_map::value_type& pair, amounts) 398 temp += pair.second.unreduced(); 399 *this = temp; 400 } 401 402 optional<balance_t> 403 value(const datetime_t& moment = datetime_t(), 404 const commodity_t * in_terms_of = NULL) const; 405 406 /** 407 * Truth tests. An balance may be truth test in two ways: 408 * 409 * is_nonzero(), or operator bool, returns true if a balance's 410 * display value is not zero. 411 * 412 * is_zero() returns true if an balance's display value is zero. 413 * Thus, a balance containing $0.0001 is considered zero if the 414 * current display precision for dollars is two decimal places. 415 * 416 * is_realzero() returns true if an balance's actual value is zero. 417 * Thus, a balance containing $0.0001 is never considered realzero. 418 * 419 * is_empty() returns true if a balance has no amounts within it. 420 * This can occur after a balance has been default initialized, or 421 * if the exact amount it contains is subsequently subtracted from 422 * it. 423 */ 424 operator bool() const { 425 return is_nonzero(); 426 } 427 is_nonzero()428 bool is_nonzero() const { 429 if (is_empty()) 430 return false; 431 432 foreach (const amounts_map::value_type& pair, amounts) 433 if (pair.second.is_nonzero()) 434 return true; 435 return false; 436 } 437 is_zero()438 bool is_zero() const { 439 if (is_empty()) 440 return true; 441 442 foreach (const amounts_map::value_type& pair, amounts) 443 if (! pair.second.is_zero()) 444 return false; 445 return true; 446 } 447 is_realzero()448 bool is_realzero() const { 449 if (is_empty()) 450 return true; 451 452 foreach (const amounts_map::value_type& pair, amounts) 453 if (! pair.second.is_realzero()) 454 return false; 455 return true; 456 } 457 is_empty()458 bool is_empty() const { 459 return amounts.size() == 0; 460 } single_amount()461 bool single_amount() const { 462 return amounts.size() == 1; 463 } 464 465 /** 466 * Conversion methods. A balance can be converted to an amount, but 467 * only if contains a single component amount. 468 */ string()469 operator string() const { 470 return to_string(); 471 } to_string()472 string to_string() const { 473 std::ostringstream buf; 474 print(buf); 475 return buf.str(); 476 } 477 to_amount()478 amount_t to_amount() const { 479 if (is_empty()) 480 throw_(balance_error, _("Cannot convert an empty balance to an amount")); 481 else if (amounts.size() == 1) 482 return amounts.begin()->second; 483 else 484 throw_(balance_error, 485 _("Cannot convert a balance with multiple commodities to an amount")); 486 return amount_t(); 487 } 488 489 /** 490 * Commodity-related methods. Balances support two 491 * commodity-related methods: 492 * 493 * commodity_count() returns the number of different commodities 494 * stored in the balance. 495 * 496 * commodity_amount(optional<commodity_t>) returns an (optional) 497 * amount for the given commodity within the balance; if no 498 * commodity is specified, it returns the (optional) uncommoditized 499 * component of the balance. If no matching element can be found, 500 * boost::none is returned. 501 */ commodity_count()502 std::size_t commodity_count() const { 503 return amounts.size(); 504 } 505 506 optional<amount_t> 507 commodity_amount(const optional<const commodity_t&>& commodity = none) const; 508 509 amounts_map::iterator find_by_name(const commodity_t& comm); 510 amounts_map::const_iterator find_by_name(const commodity_t& comm) const; 511 number()512 balance_t number() const { 513 balance_t temp; 514 foreach (const amounts_map::value_type& pair, amounts) 515 temp += pair.second.number(); 516 return temp; 517 } 518 519 /** 520 * Annotated commodity methods. The amounts contained by a balance 521 * may use annotated commodities. The `strip_annotations' method 522 * will return a balance all of whose component amount have had 523 * their commodity annotations likewise stripped. See 524 * amount_t::strip_annotations for more details. 525 */ 526 balance_t strip_annotations(const keep_details_t& what_to_keep) const; 527 528 /** 529 * Given a balance, insert a commodity-wise sort of the amounts into the 530 * given amounts_array. 531 */ 532 void sorted_amounts(amounts_array& sorted) const; 533 534 /** 535 * Iteration primitives. `map_sorted_amounts' allows one to visit 536 * each amount in balance in the proper order for displaying to the 537 * user. Mostly used by `print' and other routinse where the sort 538 * order of the amounts' commodities is significant. 539 */ 540 void map_sorted_amounts(function<void(const amount_t&)> fn) const; 541 542 /** 543 * Printing methods. A balance may be output to a stream using the 544 * `print' method. There is also a global operator<< defined which 545 * simply calls print for a balance on the given stream. There is 546 * one form of the print method, which takes two required arguments 547 * and one arguments with a default value: 548 * 549 * print(ostream, int first_width, int latter_width) prints a 550 * balance to the given output stream, using each commodity's 551 * default display characteristics. The first_width parameter 552 * specifies the width that should be used for printing amounts 553 * (since they are likely to vary in width). The latter_width, if 554 * specified, gives the width to be used for each line after the 555 * first. This is useful when printing in a column which falls at 556 * the right-hand side of the screen. 557 * 558 * In addition to the width constraints, balances will also print 559 * with commodities in alphabetized order, regardless of the 560 * relative amounts of those commodities. There is no option to 561 * change this behavior. 562 */ 563 void print(std::ostream& out, 564 const int first_width = -1, 565 const int latter_width = -1, 566 const uint_least8_t flags = AMOUNT_PRINT_NO_FLAGS) const; 567 568 /** 569 * Debugging methods. There are two methods defined to help with 570 * debugging: 571 * 572 * dump(ostream) dumps a balance to an output stream. There is 573 * little different from print(), it simply surrounds the display 574 * value with a marker, for example "BALANCE($1.00, DM 12.00)". 575 * This code is used by other dumping code elsewhere in Ledger. 576 * 577 * valid() returns true if the amounts within the balance are valid. 578 */ dump(std::ostream & out)579 void dump(std::ostream& out) const { 580 out << "BALANCE("; 581 bool first = true; 582 foreach (const amounts_map::value_type& pair, amounts) { 583 if (first) 584 first = false; 585 else 586 out << ", "; 587 pair.second.print(out); 588 } 589 out << ")"; 590 } 591 valid()592 bool valid() const { 593 foreach (const amounts_map::value_type& pair, amounts) 594 if (! pair.second.valid()) { 595 DEBUG("ledger.validate", "balance_t: ! pair.second.valid()"); 596 return false; 597 } 598 return true; 599 } 600 }; 601 602 inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) { 603 bal.print(out, 12); 604 return out; 605 } 606 607 void put_balance(property_tree::ptree& pt, const balance_t& bal); 608 609 balance_t average_lot_prices(const balance_t& bal); 610 611 } // namespace ledger 612 613 #endif // _BALANCE_H 614