RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
common/currency.h
00001 /* currency.h                                                     -*- C++ -*-
00002    Jeremy Barnes, 10 October 2012
00003    Copyright (c) 2012 Datacratic Inc.  All rights reserved.
00004 
00005 */
00006 
00007 #ifndef __types__currency_h__
00008 #define __types__currency_h__
00009 
00010 
00011 #include "jml/utils/exc_assert.h"
00012 #include "jml/utils/compact_vector.h"
00013 #include "jml/utils/unnamed_bool.h"
00014 #include "jml/db/persistent.h"
00015 #include "soa/jsoncpp/json.h"
00016 #include "soa/types/value_description.h"
00017 
00018 namespace RTBKIT {
00019 
00020 
00021 enum class CurrencyCode : std::uint32_t {
00022     CC_NONE = 'N' << 24 | 'O' << 16 | 'N' << 8 | 'E',
00023     CC_USD = 'U' << 24 | 'S' << 16 | 'D' << 8  | 0      // micro dollars
00024 };
00025 
00026 std::string toString(CurrencyCode code);
00027 CurrencyCode parseCurrencyCode(const std::string & code);
00028 
00029 CurrencyCode jsonDecode(const Json::Value & j, CurrencyCode * = 0);
00030 Json::Value jsonEncode(CurrencyCode code);
00031 
00032 
00033 /*****************************************************************************/
00034 /* AMOUNT                                                                    */
00035 /*****************************************************************************/
00036 
00037 struct Amount {
00038     Amount(CurrencyCode currencyCode = CurrencyCode::CC_NONE, int64_t value = 0)
00039         : value(value), currencyCode(currencyCode)
00040     {
00041         if (currencyCode == CurrencyCode::CC_NONE)
00042             ExcAssertEqual(value, 0);
00043     }
00044 
00045     Amount(const std::string & currencyStr, int64_t value = 0)
00046         : value(value), currencyCode(parseCurrency(currencyStr))
00047     {
00048         if (currencyCode == CurrencyCode::CC_NONE)
00049             ExcAssertEqual(value, 0);
00050     }
00051 
00052     bool isZero() const { return value == 0; }
00053     bool isNonNegative() const { return value >= 0; }
00054     bool isNegative() const { return value < 0; }
00055 
00057     Amount limit(const Amount & other) const
00058     {
00059         assertCurrencyIsCompatible(other);
00060         return Amount(currencyCode, std::min(value, other.value));
00061     }
00062 
00063     JML_IMPLEMENT_OPERATOR_BOOL(!isZero());
00064 
00065     Amount & operator += (const Amount & other)
00066     {
00067         if (!other)
00068             return *this;
00069         else if (!*this)
00070             *this = other;
00071         else {
00072             ExcAssertEqual(currencyCode, other.currencyCode);
00073             value += other.value;
00074         }
00075         return *this;
00076     }
00077     
00078     Amount operator + (const Amount & other) const
00079     {
00080         Amount result = *this;
00081         result += other;
00082         return result;
00083     }
00084 
00085     Amount & operator -= (const Amount & other)
00086     {
00087         if (!other)
00088             return *this;
00089         else if (!*this)
00090             *this = -other;
00091         else {
00092             if (currencyCode != other.currencyCode) {
00093                 using namespace std;
00094                 cerr << this->toString() << " - " << other.toString() << endl;
00095             }
00096             ExcAssertEqual(currencyCode, other.currencyCode);
00097             value -= other.value;
00098         }
00099         return *this;
00100     }
00101     
00102     Amount operator - (const Amount & other) const
00103     {
00104         Amount result = *this;
00105         result -= other;
00106         return result;
00107     }
00108 
00109     bool currencyIsCompatible(const Amount & other) const
00110     {
00111         if (currencyCode == other.currencyCode) return true;
00112         if (currencyCode == CurrencyCode::CC_NONE && value == 0) return true;
00113         if (other.currencyCode == CurrencyCode::CC_NONE && other.value == 0) return true;
00114         return false;
00115     }
00116 
00117     void assertCurrencyIsCompatible(const Amount & other) const;
00118 
00124     bool operator == (const Amount & other) const
00125     {
00126         return value == other.value
00127             && (currencyCode == other.currencyCode
00128                 || (value == 0
00129                     && (currencyCode == CurrencyCode::CC_NONE
00130                         || other.currencyCode == CurrencyCode::CC_NONE)));
00131     }
00132 
00133     bool operator != (const Amount & other) const
00134     {
00135         return ! operator == (other);
00136     }
00137 
00138     bool operator < (const Amount & other) const;
00139     bool operator <= (const Amount & other) const;
00140     bool operator > (const Amount & other) const;
00141     bool operator >= (const Amount & other) const;
00142 
00143     Amount operator - () const
00144     {
00145         Amount result = *this;
00146         result.value = -result.value;
00147         return result;
00148     }
00149 
00150     static std::string getCurrencyStr(CurrencyCode currencyCode);
00151     std::string getCurrencyStr() const;
00152     static CurrencyCode parseCurrency(const std::string & currency);
00153 
00154     std::string toString() const;
00155 
00156     Json::Value toJson() const;
00157     static Amount fromJson(const Json::Value & json);
00158     static Amount parse(const std::string & value);
00159 
00160     void serialize(ML::DB::Store_Writer & store) const;
00161     void reconstitute(ML::DB::Store_Reader & store);
00162 
00163     int64_t value;
00164     CurrencyCode currencyCode;
00165 };
00166 
00167 std::ostream & operator << (std::ostream & stream, Amount amount);
00168 
00169 IMPL_SERIALIZE_RECONSTITUTE(Amount);
00170 
00171 struct MicroUSD : public Amount {
00172     MicroUSD(int64_t value = 0)
00173         : Amount(CurrencyCode::CC_USD, value)
00174     {
00175     }
00176 
00177     MicroUSD(Amount amount)
00178         : Amount(amount)
00179     {
00180         if (amount)
00181             ExcAssertEqual(currencyCode, CurrencyCode::CC_USD);
00182     }
00183 
00184     operator int64_t () const { return value; }
00185 };
00186 
00187 struct USD : public Amount {
00188     USD(double value = 0.0)
00189         : Amount(CurrencyCode::CC_USD, value * 1000000)
00190     {
00191     }
00192 
00193     USD(Amount amount)
00194         : Amount(amount)
00195     {
00196         if (amount)
00197             ExcAssertEqual(currencyCode, CurrencyCode::CC_USD);
00198     }
00199     
00200     operator double () const { return value * 0.000001; }
00201 };
00202 
00203 struct USD_CPM : public Amount {
00204     USD_CPM(double amountInDollarsCPM = 0.0)
00205         : Amount(CurrencyCode::CC_USD, amountInDollarsCPM * 1000)
00206     {
00207     }
00208 
00209     USD_CPM(Amount amount)
00210         : Amount(amount)
00211     {
00212         if (amount)
00213             ExcAssertEqual(currencyCode, CurrencyCode::CC_USD);
00214     }
00215 
00216     operator double () const { return value * 0.001; }
00217 };
00218 
00219 struct MicroUSD_CPM : public Amount {
00220     MicroUSD_CPM(int64_t amountInMicroDollarsCPM = 0.0)
00221         : Amount(CurrencyCode::CC_USD, amountInMicroDollarsCPM / 1000)
00222     {
00223     }
00224 
00225     MicroUSD_CPM(Amount amount)
00226         : Amount(amount)
00227     {
00228         if (amount)
00229             ExcAssertEqual(currencyCode, CurrencyCode::CC_USD);
00230     }
00231 
00232     operator int64_t () const { return value * 1000; }
00233 };
00234 
00235 /*****************************************************************************/
00236 /* CURRENCY POOL                                                             */
00237 /*****************************************************************************/
00238 
00243 struct CurrencyPool {
00244 
00245     void clear()
00246     {
00247         currencyAmounts.clear();
00248     }
00249 
00250     bool empty() const
00251     {
00252         return currencyAmounts.empty();
00253     }
00254 
00255     CurrencyPool()
00256     {
00257     }
00258 
00259     CurrencyPool(const Amount & amount)
00260         : currencyAmounts(1, amount)
00261     {
00262         if (!amount)
00263             currencyAmounts.clear();
00264     }
00265 
00266     CurrencyPool & operator += (const Amount & amount)
00267     {
00268         if (!amount) return *this;
00269 
00270         for (auto & am: currencyAmounts) {
00271             if (am.currencyCode == amount.currencyCode) {
00272                 am += amount;
00273                 return *this;
00274             }
00275         }
00276 
00277         currencyAmounts.push_back(amount);
00278         std::sort(currencyAmounts.begin(), currencyAmounts.end(),
00279                   [] (Amount am1, Amount am2)
00280                   { return am1.currencyCode < am2.currencyCode; });
00281 
00282         return *this;
00283     }
00284 
00285     CurrencyPool & operator -= (const Amount & amount)
00286     {
00287         return operator += (-amount);
00288     }
00289 
00290     CurrencyPool operator + (const Amount & amount) const
00291     {
00292         CurrencyPool result = *this;
00293         result += amount;
00294         return result;
00295     }
00296 
00297     CurrencyPool operator - (const Amount & amount) const
00298     {
00299         CurrencyPool result = *this;
00300         result -= amount;
00301         return result;
00302     }
00303 
00304     CurrencyPool & operator += (const CurrencyPool & other)
00305     {
00306         for (auto & am: other.currencyAmounts)
00307             operator += (am);
00308         return *this;
00309     }
00310 
00311     CurrencyPool & operator -= (const CurrencyPool & other)
00312     {
00313         for (auto & am: other.currencyAmounts)
00314             operator -= (am);
00315         return *this;
00316     }
00317 
00318     CurrencyPool operator + (const CurrencyPool & spend) const
00319     {
00320         CurrencyPool result = *this;
00321         result += spend;
00322         return result;
00323     }
00324 
00325     CurrencyPool operator - (const CurrencyPool & spend) const
00326     {
00327         CurrencyPool result = *this;
00328         result -= spend;
00329         return result;
00330     }
00331 
00335     CurrencyPool limit(const CurrencyPool & other) const
00336     {
00337         CurrencyPool result;
00338 
00339         for (auto & am: other.currencyAmounts) {
00340             Amount a = getAvailable(am.currencyCode).limit(am);
00341             if (a)
00342                 result.currencyAmounts.push_back(a);
00343         }
00344 
00345         return result;
00346     }
00347 
00349     CurrencyPool nonNegative() const
00350     {
00351         CurrencyPool result;
00352 
00353         for (auto & am: currencyAmounts) {
00354             if (am.isNonNegative())
00355                 result.currencyAmounts.push_back(am);
00356         }
00357 
00358         return result;
00359     }
00360 
00361     bool operator == (const Amount & other) const
00362     {
00363         return operator == (CurrencyPool(other));
00364     }
00365     bool operator != (const Amount & other) const
00366     {
00367         return ! operator == (other);
00368     }
00369 
00370     bool operator == (const CurrencyPool & other) const;
00371     bool operator != (const CurrencyPool & other) const
00372     {
00373         return ! operator == (other);
00374     }
00375 
00379     bool hasAvailable(const Amount & amount) const;
00380 
00382     Amount getAvailable(const CurrencyCode & currency) const;
00383 
00384     bool isNonNegative() const
00385     {
00386         for (auto & am: currencyAmounts)
00387             if (!am.isNonNegative())
00388                 return false;
00389         return true;
00390     }
00391 
00392     bool isZero() const
00393     {
00394         for (auto & am: currencyAmounts)
00395             if (!am.isZero())
00396                 return false;
00397         return true;
00398     }
00399 
00400     void serialize(ML::DB::Store_Writer & store) const;
00401     void reconstitute(ML::DB::Store_Reader & store);
00402 
00403     ML::compact_vector<Amount, 1> currencyAmounts;  
00404 
00405     Json::Value toJson() const;
00406     std::string toString() const;
00407 
00408     static CurrencyPool fromJson(const Json::Value & json);
00409 
00410     bool isSameOrPastVersion(const CurrencyPool & otherPool) const;
00411 };
00412 
00413 std::ostream & operator << (std::ostream & stream, CurrencyPool pool);
00414 
00415 IMPL_SERIALIZE_RECONSTITUTE(CurrencyPool);
00416 
00417 
00418 /*****************************************************************************/
00419 /* LINE ITEMS                                                                */
00420 /*****************************************************************************/
00421 
00424 struct LineItems {
00425 
00426     bool isZero() const
00427     {
00428         for (const auto & e: entries)
00429             if (!e.second.isZero())
00430                 return false;
00431         return true;
00432     }
00433 
00434     void clear()
00435     {
00436         entries.clear();
00437     }
00438 
00439     bool empty() const
00440     {
00441         for (auto & e: entries) {
00442             if (!e.second.empty())
00443                 return false;
00444         }
00445         return true;
00446     }
00447 
00449     CurrencyPool total() const;
00450     
00451     CurrencyPool & operator [] (std::string item)
00452     {
00453         return entries[item];
00454     }
00455 
00456     CurrencyPool operator [] (std::string item) const
00457     {
00458         auto it = entries.find(item);
00459         if (it == entries.end())
00460             return CurrencyPool();
00461         return it->second;
00462     }
00463 
00464     static LineItems fromJson(const Json::Value & json);
00465     Json::Value toJson() const;
00466 
00467     bool operator == (const LineItems & other) const;
00468     bool operator != (const LineItems & other) const
00469     {
00470         return ! operator == (other);
00471     }
00472 
00473     LineItems & operator += (const LineItems & other);
00474 
00475     void serialize(ML::DB::Store_Writer & store) const;
00476     void reconstitute(ML::DB::Store_Reader & store);
00477 
00478     std::map<std::string, CurrencyPool> entries;
00479 };
00480 
00481 inline std::ostream & operator << (std::ostream & stream, const LineItems & li)
00482 {
00483     return stream << li.toJson();
00484 }
00485 
00486 IMPL_SERIALIZE_RECONSTITUTE(LineItems);
00487 
00488 Datacratic::ValueDescriptionT<LineItems> *
00489 getDefaultDescription(LineItems * = 0);
00490 
00491 Datacratic::ValueDescriptionT<CurrencyCode> *
00492 getDefaultDescription(CurrencyCode * = 0);
00493 
00494 Datacratic::ValueDescriptionT<CurrencyPool> *
00495 getDefaultDescription(CurrencyPool * = 0);
00496 
00497 Datacratic::ValueDescriptionT<Amount> *
00498 getDefaultDescription(Amount * = 0);
00499 
00500 
00501 } // namespace RTBKIT
00502 
00503 
00504 #endif /* __types__currency_h__ */
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator