1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2008 J. Erik Radmall 5 Copyright (C) 2009 StatPro Italia srl 6 7 This file is part of QuantLib, a free-software/open-source library 8 for financial quantitative analysts and developers - http://quantlib.org/ 9 10 QuantLib is free software: you can redistribute it and/or modify it 11 under the terms of the QuantLib license. You should have received a 12 copy of the license along with this program; if not, please email 13 <quantlib-dev@lists.sf.net>. The license is also available online at 14 <http://quantlib.org/license.shtml>. 15 16 This program is distributed in the hope that it will be useful, but WITHOUT 17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 FOR A PARTICULAR PURPOSE. See the license for more details. 19 */ 20 21 #include <ql/experimental/commodities/unitofmeasureconversionmanager.hpp> 22 #include <ql/experimental/commodities/petroleumunitsofmeasure.hpp> 23 #include <ql/errors.hpp> 24 #include <algorithm> 25 26 using namespace std; 27 28 namespace QuantLib { 29 30 namespace { 31 matches(const UnitOfMeasureConversion & c1,const UnitOfMeasureConversion & c2)32 bool matches(const UnitOfMeasureConversion& c1, 33 const UnitOfMeasureConversion& c2) { 34 return c1.commodityType() == c2.commodityType() && 35 ((c1.source() == c2.source() && c1.target() == c2.target()) 36 || (c1.source() == c2.target() && c1.target() == c2.source())); 37 } 38 matches(const UnitOfMeasureConversion & c,const CommodityType & commodityType,const UnitOfMeasure & source,const UnitOfMeasure & target)39 bool matches(const UnitOfMeasureConversion& c, 40 const CommodityType& commodityType, 41 const UnitOfMeasure& source, 42 const UnitOfMeasure& target) { 43 return c.commodityType() == commodityType && 44 ((c.source() == source && c.target() == target) 45 || (c.source() == target && c.target() == source)); 46 } 47 matches(const UnitOfMeasureConversion & c,const CommodityType & commodityType,const UnitOfMeasure & source)48 bool matches(const UnitOfMeasureConversion& c, 49 const CommodityType& commodityType, 50 const UnitOfMeasure& source) { 51 return c.commodityType() == commodityType && 52 (c.source() == source || c.target() == source); 53 } 54 55 } 56 UnitOfMeasureConversionManager()57 UnitOfMeasureConversionManager::UnitOfMeasureConversionManager() { 58 addKnownConversionFactors(); 59 } 60 add(const UnitOfMeasureConversion & c)61 void UnitOfMeasureConversionManager::add(const UnitOfMeasureConversion& c) { 62 // not fast, but hopefully we won't have a lot of entries. 63 for (list<UnitOfMeasureConversion>::iterator i = data_.begin(); 64 i != data_.end(); ++i) { 65 if (matches(*i, c)) { 66 data_.erase(i); 67 break; 68 } 69 } 70 71 data_.push_back(c); 72 } 73 lookup(const CommodityType & commodityType,const UnitOfMeasure & source,const UnitOfMeasure & target,UnitOfMeasureConversion::Type type) const74 UnitOfMeasureConversion UnitOfMeasureConversionManager::lookup( 75 const CommodityType& commodityType, 76 const UnitOfMeasure& source, 77 const UnitOfMeasure& target, 78 UnitOfMeasureConversion::Type type) const { 79 if (type == UnitOfMeasureConversion::Direct) { 80 return directLookup(commodityType,source,target); 81 } else if (!source.triangulationUnitOfMeasure().empty()) { 82 const UnitOfMeasure& link = source.triangulationUnitOfMeasure(); 83 if (link == target) 84 return directLookup(commodityType,source,link); 85 else 86 return UnitOfMeasureConversion::chain( 87 directLookup(commodityType,source,link), 88 lookup(commodityType,link,target)); 89 } else if (!target.triangulationUnitOfMeasure().empty()) { 90 const UnitOfMeasure& link = target.triangulationUnitOfMeasure(); 91 if (source == link) 92 return directLookup(commodityType,link,target); 93 else 94 return UnitOfMeasureConversion::chain( 95 lookup(commodityType,source,link), 96 directLookup(commodityType,link,target)); 97 } else { 98 return smartLookup(commodityType,source,target); 99 } 100 } 101 clear()102 void UnitOfMeasureConversionManager::clear() { 103 data_.clear(); 104 addKnownConversionFactors(); 105 } 106 addKnownConversionFactors()107 void UnitOfMeasureConversionManager::addKnownConversionFactors() { 108 add(UnitOfMeasureConversion(NullCommodityType(), 109 MBUnitOfMeasure(), 110 BarrelUnitOfMeasure(), 111 1000)); 112 add(UnitOfMeasureConversion(NullCommodityType(), 113 BarrelUnitOfMeasure(), 114 GallonUnitOfMeasure(), 115 42)); 116 add(UnitOfMeasureConversion(NullCommodityType(), 117 GallonUnitOfMeasure(), 118 MBUnitOfMeasure(), 119 1000 * 42)); 120 add(UnitOfMeasureConversion(NullCommodityType(), 121 LitreUnitOfMeasure(), 122 GallonUnitOfMeasure(), 123 3.78541)); 124 add(UnitOfMeasureConversion(NullCommodityType(), 125 BarrelUnitOfMeasure(), 126 LitreUnitOfMeasure(), 127 158.987)); 128 add(UnitOfMeasureConversion(NullCommodityType(), 129 KilolitreUnitOfMeasure(), 130 BarrelUnitOfMeasure(), 131 6.28981)); 132 add(UnitOfMeasureConversion(NullCommodityType(), 133 TokyoKilolitreUnitOfMeasure(), 134 BarrelUnitOfMeasure(), 135 6.28981)); 136 } 137 directLookup(const CommodityType & commodityType,const UnitOfMeasure & source,const UnitOfMeasure & target) const138 UnitOfMeasureConversion UnitOfMeasureConversionManager::directLookup( 139 const CommodityType& commodityType, 140 const UnitOfMeasure& source, 141 const UnitOfMeasure& target) const { 142 143 for (list<UnitOfMeasureConversion>::const_iterator i = data_.begin(); 144 i != data_.end(); ++i) { 145 if (matches(*i, commodityType, source, target)) { 146 return *i; 147 } 148 } 149 150 // Here, the code used to look for conversions with null 151 // commodity type as a fall-back. However, this would only 152 // affect direct lookups and not other matches being tried in 153 // the smart-lookup loop. To implement the complete fall-back 154 // strategy, we should either duplicate the loop (as we would 155 // duplicate it here---smelly) or change the 'matches' 156 // functions so that a null commodity type matches. However, 157 // in the second case we would also have to take care that 158 // conversions with a null type be at the end of the list so 159 // that they don't supersede specific types. We'll have to 160 // think a bit about this, so no fall-back for the time being. 161 162 QL_FAIL("no direct conversion available from " 163 << commodityType.code() << " " << source.code() 164 << " to " << target.code()); 165 } 166 smartLookup(const CommodityType & commodityType,const UnitOfMeasure & source,const UnitOfMeasure & target,list<string> forbidden) const167 UnitOfMeasureConversion UnitOfMeasureConversionManager::smartLookup( 168 const CommodityType& commodityType, 169 const UnitOfMeasure& source, 170 const UnitOfMeasure& target, 171 list<string> forbidden) const { 172 173 try { 174 return directLookup(commodityType,source,target); 175 } catch (Error&) { 176 ; // no direct conversion available; turn to smart lookup. 177 } 178 179 // The source unit is forbidden to subsequent lookups in order 180 // to avoid cycles. 181 forbidden.push_back(source.code()); 182 183 for (list<UnitOfMeasureConversion>::const_iterator i = data_.begin(); 184 i != data_.end(); ++i) { 185 // we look for conversion data which involve our source unit... 186 if (matches(*i, commodityType, source)) { 187 const UnitOfMeasure& other = 188 source == i->source() ? i->target() : i->source(); 189 if (find(forbidden.begin(),forbidden.end(), 190 other.code()) == forbidden.end()) { 191 // if we can get to the target from here... 192 try { 193 UnitOfMeasureConversion tail = 194 smartLookup(commodityType,other,target); 195 // ..we're done. 196 return UnitOfMeasureConversion::chain(*i,tail); 197 } catch (Error&) { 198 // otherwise, we just discard this conversion. 199 ; 200 } 201 } 202 } 203 } 204 205 // if the loop completed, we have no way to return the 206 // requested conversion. 207 QL_FAIL("no conversion available for " 208 << commodityType.code() << " from " 209 << source.code() << " to " << target.code()); 210 } 211 212 } 213 214