Coverage Report

Created: 2025-08-05 06:45

/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