![]() |
RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
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__ */