/src/quantlib/ql/experimental/commodities/unitofmeasureconversionmanager.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
32 | | bool matches(const UnitOfMeasureConversion& c1, |
33 | 0 | const UnitOfMeasureConversion& c2) { |
34 | 0 | return c1.commodityType() == c2.commodityType() && |
35 | 0 | ((c1.source() == c2.source() && c1.target() == c2.target()) |
36 | 0 | || (c1.source() == c2.target() && c1.target() == c2.source())); |
37 | 0 | } |
38 | | |
39 | | bool matches(const UnitOfMeasureConversion& c, |
40 | | const CommodityType& commodityType, |
41 | | const UnitOfMeasure& source, |
42 | 0 | const UnitOfMeasure& target) { |
43 | 0 | return c.commodityType() == commodityType && |
44 | 0 | ((c.source() == source && c.target() == target) |
45 | 0 | || (c.source() == target && c.target() == source)); |
46 | 0 | } |
47 | | |
48 | | bool matches(const UnitOfMeasureConversion& c, |
49 | | const CommodityType& commodityType, |
50 | 0 | const UnitOfMeasure& source) { |
51 | 0 | return c.commodityType() == commodityType && |
52 | 0 | (c.source() == source || c.target() == source); |
53 | 0 | } |
54 | | |
55 | | } |
56 | | |
57 | 0 | UnitOfMeasureConversionManager::UnitOfMeasureConversionManager() { |
58 | 0 | addKnownConversionFactors(); |
59 | 0 | } |
60 | | |
61 | 0 | void UnitOfMeasureConversionManager::add(const UnitOfMeasureConversion& c) { |
62 | | // not fast, but hopefully we won't have a lot of entries. |
63 | 0 | for (auto i = data_.begin(); i != data_.end(); ++i) { |
64 | 0 | if (matches(*i, c)) { |
65 | 0 | data_.erase(i); |
66 | 0 | break; |
67 | 0 | } |
68 | 0 | } |
69 | |
|
70 | 0 | data_.push_back(c); |
71 | 0 | } |
72 | | |
73 | | UnitOfMeasureConversion UnitOfMeasureConversionManager::lookup( |
74 | | const CommodityType& commodityType, |
75 | | const UnitOfMeasure& source, |
76 | | const UnitOfMeasure& target, |
77 | 0 | UnitOfMeasureConversion::Type type) const { |
78 | 0 | if (type == UnitOfMeasureConversion::Direct) { |
79 | 0 | return directLookup(commodityType,source,target); |
80 | 0 | } else if (!source.triangulationUnitOfMeasure().empty()) { |
81 | 0 | const UnitOfMeasure& link = source.triangulationUnitOfMeasure(); |
82 | 0 | if (link == target) |
83 | 0 | return directLookup(commodityType,source,link); |
84 | 0 | else |
85 | 0 | return UnitOfMeasureConversion::chain( |
86 | 0 | directLookup(commodityType,source,link), |
87 | 0 | lookup(commodityType,link,target)); |
88 | 0 | } else if (!target.triangulationUnitOfMeasure().empty()) { |
89 | 0 | const UnitOfMeasure& link = target.triangulationUnitOfMeasure(); |
90 | 0 | if (source == link) |
91 | 0 | return directLookup(commodityType,link,target); |
92 | 0 | else |
93 | 0 | return UnitOfMeasureConversion::chain( |
94 | 0 | lookup(commodityType,source,link), |
95 | 0 | directLookup(commodityType,link,target)); |
96 | 0 | } else { |
97 | 0 | return smartLookup(commodityType,source,target); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | void UnitOfMeasureConversionManager::clear() { |
102 | 0 | data_.clear(); |
103 | 0 | addKnownConversionFactors(); |
104 | 0 | } |
105 | | |
106 | 0 | void UnitOfMeasureConversionManager::addKnownConversionFactors() { |
107 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
108 | 0 | MBUnitOfMeasure(), |
109 | 0 | BarrelUnitOfMeasure(), |
110 | 0 | 1000)); |
111 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
112 | 0 | BarrelUnitOfMeasure(), |
113 | 0 | GallonUnitOfMeasure(), |
114 | 0 | 42)); |
115 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
116 | 0 | GallonUnitOfMeasure(), |
117 | 0 | MBUnitOfMeasure(), |
118 | 0 | 1000 * 42)); |
119 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
120 | 0 | LitreUnitOfMeasure(), |
121 | 0 | GallonUnitOfMeasure(), |
122 | 0 | 3.78541)); |
123 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
124 | 0 | BarrelUnitOfMeasure(), |
125 | 0 | LitreUnitOfMeasure(), |
126 | 0 | 158.987)); |
127 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
128 | 0 | KilolitreUnitOfMeasure(), |
129 | 0 | BarrelUnitOfMeasure(), |
130 | 0 | 6.28981)); |
131 | 0 | add(UnitOfMeasureConversion(NullCommodityType(), |
132 | 0 | TokyoKilolitreUnitOfMeasure(), |
133 | 0 | BarrelUnitOfMeasure(), |
134 | 0 | 6.28981)); |
135 | 0 | } |
136 | | |
137 | | UnitOfMeasureConversion UnitOfMeasureConversionManager::directLookup( |
138 | | const CommodityType& commodityType, |
139 | | const UnitOfMeasure& source, |
140 | 0 | const UnitOfMeasure& target) const { |
141 | |
|
142 | 0 | for (const auto& i : data_) { |
143 | 0 | if (matches(i, commodityType, source, target)) { |
144 | 0 | return i; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | // Here, the code used to look for conversions with null |
149 | | // commodity type as a fall-back. However, this would only |
150 | | // affect direct lookups and not other matches being tried in |
151 | | // the smart-lookup loop. To implement the complete fall-back |
152 | | // strategy, we should either duplicate the loop (as we would |
153 | | // duplicate it here---smelly) or change the 'matches' |
154 | | // functions so that a null commodity type matches. However, |
155 | | // in the second case we would also have to take care that |
156 | | // conversions with a null type be at the end of the list so |
157 | | // that they don't supersede specific types. We'll have to |
158 | | // think a bit about this, so no fall-back for the time being. |
159 | | |
160 | 0 | QL_FAIL("no direct conversion available from " |
161 | 0 | << commodityType.code() << " " << source.code() |
162 | 0 | << " to " << target.code()); |
163 | 0 | } |
164 | | |
165 | | UnitOfMeasureConversion UnitOfMeasureConversionManager::smartLookup( |
166 | | const CommodityType& commodityType, |
167 | | const UnitOfMeasure& source, |
168 | | const UnitOfMeasure& target, |
169 | 0 | list<string> forbidden) const { |
170 | |
|
171 | 0 | try { |
172 | 0 | return directLookup(commodityType,source,target); |
173 | 0 | } catch (Error&) { |
174 | 0 | ; // no direct conversion available; turn to smart lookup. |
175 | 0 | } |
176 | | |
177 | | // The source unit is forbidden to subsequent lookups in order |
178 | | // to avoid cycles. |
179 | 0 | forbidden.push_back(source.code()); |
180 | |
|
181 | 0 | for (const auto& i : data_) { |
182 | | // we look for conversion data which involve our source unit... |
183 | 0 | if (matches(i, commodityType, source)) { |
184 | 0 | const UnitOfMeasure& other = source == i.source() ? i.target() : i.source(); |
185 | 0 | if (find(forbidden.begin(),forbidden.end(), |
186 | 0 | other.code()) == forbidden.end()) { |
187 | | // if we can get to the target from here... |
188 | 0 | try { |
189 | 0 | UnitOfMeasureConversion tail = |
190 | 0 | smartLookup(commodityType,other,target); |
191 | | // ..we're done. |
192 | 0 | return UnitOfMeasureConversion::chain(i, tail); |
193 | 0 | } catch (Error&) { |
194 | | // otherwise, we just discard this conversion. |
195 | 0 | ; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | // if the loop completed, we have no way to return the |
202 | | // requested conversion. |
203 | 0 | QL_FAIL("no conversion available for " |
204 | 0 | << commodityType.code() << " from " |
205 | 0 | << source.code() << " to " << target.code()); |
206 | 0 | } |
207 | | |
208 | | } |
209 | | |