| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** @file | ||
| 2 | * @brief Piecewise function class | ||
| 3 | *//* | ||
| 4 | * Copyright 2007 Michael Sloan <mgsloan@gmail.com> | ||
| 5 | * | ||
| 6 | * This library is free software; you can redistribute it and/or | ||
| 7 | * modify it either under the terms of the GNU Lesser General Public | ||
| 8 | * License version 2.1 as published by the Free Software Foundation | ||
| 9 | * (the "LGPL") or, at your option, under the terms of the Mozilla | ||
| 10 | * Public License Version 1.1 (the "MPL"). If you do not alter this | ||
| 11 | * notice, a recipient may use your version of this file under either | ||
| 12 | * the MPL or the LGPL. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the LGPL along with this library | ||
| 15 | * in the file COPYING-LGPL-2.1; if not, output to the Free Software | ||
| 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 17 | * You should have received a copy of the MPL along with this library | ||
| 18 | * in the file COPYING-MPL-1.1 | ||
| 19 | * | ||
| 20 | * The contents of this file are subject to the Mozilla Public License | ||
| 21 | * Version 1.1 (the "License"); you may not use this file except in | ||
| 22 | * compliance with the License. You may obtain a copy of the License at | ||
| 23 | * http://www.mozilla.org/MPL/ | ||
| 24 | * | ||
| 25 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY | ||
| 26 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for | ||
| 27 | * the specific language governing rights and limitations. | ||
| 28 | * | ||
| 29 | */ | ||
| 30 | |||
| 31 | #ifndef LIB2GEOM_SEEN_PIECEWISE_H | ||
| 32 | #define LIB2GEOM_SEEN_PIECEWISE_H | ||
| 33 | |||
| 34 | #include <vector> | ||
| 35 | #include <map> | ||
| 36 | #include <utility> | ||
| 37 | #include <boost/concept_check.hpp> | ||
| 38 | #include <2geom/concepts.h> | ||
| 39 | #include <2geom/math-utils.h> | ||
| 40 | #include <2geom/sbasis.h> | ||
| 41 | |||
| 42 | |||
| 43 | namespace Geom { | ||
| 44 | /** | ||
| 45 | * @brief Function defined as discrete pieces. | ||
| 46 | * | ||
| 47 | * The Piecewise class manages a sequence of elements of a type as segments and | ||
| 48 | * the ’cuts’ between them. These cuts are time values which separate the pieces. | ||
| 49 | * This function representation allows for more interesting functions, as it provides | ||
| 50 | * a viable output for operations such as inversion, which may require multiple | ||
| 51 | * SBasis to properly invert the original. | ||
| 52 | * | ||
| 53 | * As for technical details, while the actual SBasis segments begin on the first | ||
| 54 | * cut and end on the last, the function is defined throughout all inputs by ex- | ||
| 55 | * tending the first and last segments. The exact switching between segments is | ||
| 56 | * arbitrarily such that beginnings (t=0) have preference over endings (t=1). This | ||
| 57 | * only matters if it is discontinuous at the location. | ||
| 58 | * \f[ | ||
| 59 | * f(t) \rightarrow \left\{ | ||
| 60 | * \begin{array}{cc} | ||
| 61 | * s_1,& t <= c_2 \\ | ||
| 62 | * s_2,& c_2 <= t <= c_3\\ | ||
| 63 | * \ldots \\ | ||
| 64 | * s_n,& c_n <= t | ||
| 65 | * \end{array}\right. | ||
| 66 | * \f] | ||
| 67 | * | ||
| 68 | * @ingroup Fragments | ||
| 69 | */ | ||
| 70 | template <typename T> | ||
| 71 | class Piecewise { | ||
| 72 | BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept); | ||
| 73 | |||
| 74 | public: | ||
| 75 | std::vector<double> cuts; | ||
| 76 | std::vector<T> segs; | ||
| 77 | //segs[i] stretches from cuts[i] to cuts[i+1]. | ||
| 78 | |||
| 79 | 296433 | Piecewise() {} | |
| 80 | |||
| 81 | 30900 | explicit Piecewise(const T &s) { | |
| 82 |
1/2✓ Branch 1 taken 30900 times.
✗ Branch 2 not taken.
|
30900 | push_cut(0.); |
| 83 |
1/2✓ Branch 1 taken 30900 times.
✗ Branch 2 not taken.
|
30900 | push_seg(s); |
| 84 |
1/2✓ Branch 1 taken 30900 times.
✗ Branch 2 not taken.
|
30900 | push_cut(1.); |
| 85 | 30900 | } | |
| 86 | |||
| 87 | unsigned input_dim(){return 1;} | ||
| 88 | |||
| 89 | typedef typename T::output_type output_type; | ||
| 90 | |||
| 91 | ✗ | explicit Piecewise(const output_type & v) { | |
| 92 | ✗ | push_cut(0.); | |
| 93 | ✗ | push_seg(T(v)); | |
| 94 | ✗ | push_cut(1.); | |
| 95 | ✗ | } | |
| 96 | |||
| 97 | 118800 | inline void reserve(unsigned i) { segs.reserve(i); cuts.reserve(i + 1); } | |
| 98 | |||
| 99 | 528840 | inline T const& operator[](unsigned i) const { return segs[i]; } | |
| 100 | 325800 | inline T& operator[](unsigned i) { return segs[i]; } | |
| 101 | 35100 | inline output_type operator()(double t) const { return valueAt(t); } | |
| 102 | 35100 | inline output_type valueAt(double t) const { | |
| 103 | 35100 | unsigned n = segN(t); | |
| 104 | 35100 | return segs[n](segT(t, n)); | |
| 105 | } | ||
| 106 | ✗ | inline output_type firstValue() const { | |
| 107 | ✗ | return valueAt(cuts.front()); | |
| 108 | } | ||
| 109 | ✗ | inline output_type lastValue() const { | |
| 110 | ✗ | return valueAt(cuts.back()); | |
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * The size of the returned vector equals n_derivs+1. | ||
| 115 | */ | ||
| 116 | std::vector<output_type> valueAndDerivatives(double t, unsigned n_derivs) const { | ||
| 117 | unsigned n = segN(t); | ||
| 118 | std::vector<output_type> ret, val = segs[n].valueAndDerivatives(segT(t, n), n_derivs); | ||
| 119 | double mult = 1; | ||
| 120 | for(unsigned i = 0; i < val.size(); i++) { | ||
| 121 | ret.push_back(val[i] * mult); | ||
| 122 | mult /= cuts[n+1] - cuts[n]; | ||
| 123 | } | ||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | //TODO: maybe it is not a good idea to have this? | ||
| 128 | Piecewise<T> operator()(SBasis f); | ||
| 129 | Piecewise<T> operator()(Piecewise<SBasis>f); | ||
| 130 | |||
| 131 | 2274108 | inline unsigned size() const { return segs.size(); } | |
| 132 | 502800 | inline bool empty() const { return segs.empty(); } | |
| 133 | inline void clear() { | ||
| 134 | segs.clear(); | ||
| 135 | cuts.clear(); | ||
| 136 | } | ||
| 137 | |||
| 138 | /**Convenience/implementation hiding function to add segment/cut pairs. | ||
| 139 | * Asserts that basic size and order invariants are correct | ||
| 140 | */ | ||
| 141 | 35100 | inline void push(const T &s, double to) { | |
| 142 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 35100 times.
|
35100 | assert(cuts.size() - segs.size() == 1); |
| 143 | 35100 | push_seg(s); | |
| 144 | 35100 | push_cut(to); | |
| 145 | 35100 | } | |
| 146 | 145098 | inline void push(T &&s, double to) { | |
| 147 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 72582 times.
|
145098 | assert(cuts.size() - segs.size() == 1); |
| 148 | 145098 | push_seg(s); | |
| 149 | 145098 | push_cut(to); | |
| 150 | 145098 | } | |
| 151 | //Convenience/implementation hiding function to add cuts. | ||
| 152 | 869931 | inline void push_cut(double c) { | |
| 153 |
3/6✓ Branch 1 taken 325182 times.
✓ Branch 2 taken 109827 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 325182 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
869931 | ASSERT_INVARIANTS(cuts.empty() || c > cuts.back()); |
| 154 | 869931 | cuts.push_back(c); | |
| 155 | 869931 | } | |
| 156 | //Convenience/implementation hiding function to add segments. | ||
| 157 | 578298 | inline void push_seg(const T &s) { segs.push_back(s); } | |
| 158 | 115200 | inline void push_seg(T &&s) { segs.emplace_back(s); } | |
| 159 | |||
| 160 | /**Returns the segment index which corresponds to a 'global' piecewise time. | ||
| 161 | * Also takes optional low/high parameters to expedite the search for the segment. | ||
| 162 | */ | ||
| 163 | 70200 | inline unsigned segN(double t, int low = 0, int high = -1) const { | |
| 164 |
1/2✓ Branch 0 taken 35100 times.
✗ Branch 1 not taken.
|
70200 | high = (high == -1) ? size() : high; |
| 165 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 35100 times.
|
70200 | if(t < cuts[0]) return 0; |
| 166 |
2/2✓ Branch 2 taken 19200 times.
✓ Branch 3 taken 15900 times.
|
70200 | if(t >= cuts[size()]) return size() - 1; |
| 167 |
2/2✓ Branch 0 taken 18900 times.
✓ Branch 1 taken 2700 times.
|
43200 | while(low < high) { |
| 168 | 37800 | int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences | |
| 169 | 37800 | double mv = cuts[mid]; | |
| 170 |
2/2✓ Branch 0 taken 2700 times.
✓ Branch 1 taken 16200 times.
|
37800 | if(mv < t) { |
| 171 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2700 times.
|
5400 | if(t < cuts[mid + 1]) return mid; else low = mid + 1; |
| 172 |
2/2✓ Branch 0 taken 3000 times.
✓ Branch 1 taken 13200 times.
|
32400 | } else if(t < mv) { |
| 173 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3000 times.
|
6000 | if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1; |
| 174 | } else { | ||
| 175 | 26400 | return mid; | |
| 176 | } | ||
| 177 | } | ||
| 178 | 5400 | return low; | |
| 179 | } | ||
| 180 | |||
| 181 | /**Returns the time within a segment, given the 'global' piecewise time. | ||
| 182 | * Also takes an optional index parameter which may be used for efficiency or to find the time on a | ||
| 183 | * segment outside its range. If it is left to its default, -1, it will call segN to find the index. | ||
| 184 | */ | ||
| 185 | 82200 | inline double segT(double t, int i = -1) const { | |
| 186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 41100 times.
|
82200 | if(i == -1) i = segN(t); |
| 187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 41100 times.
|
82200 | assert(i >= 0); |
| 188 | 82200 | return (t - cuts[i]) / (cuts[i+1] - cuts[i]); | |
| 189 | } | ||
| 190 | |||
| 191 | ✗ | inline double mapToDomain(double t, unsigned i) const { | |
| 192 | ✗ | return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i] | |
| 193 | } | ||
| 194 | |||
| 195 | //Offsets the piecewise domain | ||
| 196 | ✗ | inline void offsetDomain(double o) { | |
| 197 | ✗ | assert(std::isfinite(o)); | |
| 198 | ✗ | if(o != 0) | |
| 199 | ✗ | for(unsigned i = 0; i <= size(); i++) | |
| 200 | ✗ | cuts[i] += o; | |
| 201 | ✗ | } | |
| 202 | |||
| 203 | //Scales the domain of the function by a value. 0 will result in an empty Piecewise. | ||
| 204 | ✗ | inline void scaleDomain(double s) { | |
| 205 | ✗ | assert(s > 0); | |
| 206 | ✗ | if(s == 0) { | |
| 207 | ✗ | cuts.clear(); segs.clear(); | |
| 208 | ✗ | return; | |
| 209 | } | ||
| 210 | ✗ | for(unsigned i = 0; i <= size(); i++) | |
| 211 | ✗ | cuts[i] *= s; | |
| 212 | } | ||
| 213 | |||
| 214 | //Retrieves the domain in interval form | ||
| 215 |
1/2✓ Branch 4 taken 9600 times.
✗ Branch 5 not taken.
|
9600 | inline Interval domain() const { return Interval(cuts.front(), cuts.back()); } |
| 216 | |||
| 217 | //Transforms the domain into another interval | ||
| 218 | 47400 | inline void setDomain(Interval dom) { | |
| 219 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 47400 times.
|
47400 | if(empty()) return; |
| 220 | /* dom can not be empty | ||
| 221 | if(dom.empty()) { | ||
| 222 | cuts.clear(); segs.clear(); | ||
| 223 | return; | ||
| 224 | }*/ | ||
| 225 | 47400 | double cf = cuts.front(); | |
| 226 | 47400 | double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf); | |
| 227 |
2/2✓ Branch 1 taken 116100 times.
✓ Branch 2 taken 47400 times.
|
163500 | for(unsigned i = 0; i <= size(); i++) |
| 228 | 116100 | cuts[i] = (cuts[i] - cf) * s + o; | |
| 229 | //fix floating point precision errors. | ||
| 230 | 47400 | cuts[0] = dom.min(); | |
| 231 | 47400 | cuts[size()] = dom.max(); | |
| 232 | } | ||
| 233 | |||
| 234 | //Concatenates this Piecewise function with another, offsetting time of the other to match the end. | ||
| 235 | 45300 | inline void concat(const Piecewise<T> &other) { | |
| 236 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 45300 times.
|
45300 | if(other.empty()) return; |
| 237 | |||
| 238 |
2/2✓ Branch 1 taken 10500 times.
✓ Branch 2 taken 34800 times.
|
45300 | if(empty()) { |
| 239 | 10500 | cuts = other.cuts; segs = other.segs; | |
| 240 | 10500 | return; | |
| 241 | } | ||
| 242 | |||
| 243 |
1/2✓ Branch 7 taken 34800 times.
✗ Branch 8 not taken.
|
34800 | segs.insert(segs.end(), other.segs.begin(), other.segs.end()); |
| 244 | 34800 | double t = cuts.back() - other.cuts.front(); | |
| 245 | 34800 | cuts.reserve(cuts.size() + other.size()); | |
| 246 |
2/2✓ Branch 1 taken 54000 times.
✓ Branch 2 taken 34800 times.
|
88800 | for(unsigned i = 0; i < other.size(); i++) |
| 247 | 54000 | push_cut(other.cuts[i + 1] + t); | |
| 248 | } | ||
| 249 | |||
| 250 | //Like concat, but ensures continuity. | ||
| 251 | ✗ | inline void continuousConcat(const Piecewise<T> &other) { | |
| 252 | ✗ | boost::function_requires<AddableConcept<typename T::output_type> >(); | |
| 253 | ✗ | if(other.empty()) return; | |
| 254 | |||
| 255 | ✗ | if(empty()) { segs = other.segs; cuts = other.cuts; return; } | |
| 256 | |||
| 257 | ✗ | typename T::output_type y = segs.back().at1() - other.segs.front().at0(); | |
| 258 | ✗ | double t = cuts.back() - other.cuts.front(); | |
| 259 | ✗ | reserve(size() + other.size()); | |
| 260 | ✗ | for(unsigned i = 0; i < other.size(); i++) | |
| 261 | ✗ | push(other[i] + y, other.cuts[i + 1] + t); | |
| 262 | } | ||
| 263 | |||
| 264 | //returns true if the Piecewise<T> meets some basic invariants. | ||
| 265 | 136800 | inline bool invariants() const { | |
| 266 | // segs between cuts | ||
| 267 |
2/8✗ Branch 2 not taken.
✓ Branch 3 taken 68400 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 68400 times.
|
136800 | if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty()))) |
| 268 | ✗ | return false; | |
| 269 | // cuts in order | ||
| 270 |
2/2✓ Branch 1 taken 142200 times.
✓ Branch 2 taken 68400 times.
|
421200 | for(unsigned i = 0; i < segs.size(); i++) |
| 271 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 142200 times.
|
284400 | if(cuts[i] >= cuts[i+1]) |
| 272 | ✗ | return false; | |
| 273 | 136800 | return true; | |
| 274 | } | ||
| 275 | |||
| 276 | }; | ||
| 277 | |||
| 278 | /** | ||
| 279 | * ... | ||
| 280 | * \return ... | ||
| 281 | * \relates Piecewise | ||
| 282 | */ | ||
| 283 | template<typename T> | ||
| 284 | ✗ | inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f) { | |
| 285 | ✗ | boost::function_requires<FragmentConcept<T> >(); | |
| 286 | |||
| 287 | ✗ | if(f.empty()) return typename FragmentConcept<T>::BoundsType(); | |
| 288 | ✗ | typename FragmentConcept<T>::BoundsType ret(bounds_fast(f[0])); | |
| 289 | ✗ | for(unsigned i = 1; i < f.size(); i++) | |
| 290 | ✗ | ret.unionWith(bounds_fast(f[i])); | |
| 291 | ✗ | return ret; | |
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * ... | ||
| 296 | * \return ... | ||
| 297 | * \relates Piecewise | ||
| 298 | */ | ||
| 299 | template<typename T> | ||
| 300 | 300 | inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &f) { | |
| 301 | 300 | boost::function_requires<FragmentConcept<T> >(); | |
| 302 | |||
| 303 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 300 times.
|
300 | if(f.empty()) return typename FragmentConcept<T>::BoundsType(); |
| 304 |
1/2✓ Branch 3 taken 300 times.
✗ Branch 4 not taken.
|
300 | typename FragmentConcept<T>::BoundsType ret(bounds_exact(f[0])); |
| 305 |
2/2✓ Branch 1 taken 11700 times.
✓ Branch 2 taken 300 times.
|
12000 | for(unsigned i = 1; i < f.size(); i++) |
| 306 |
2/4✓ Branch 3 taken 11700 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 11700 times.
✗ Branch 7 not taken.
|
11700 | ret.unionWith(bounds_exact(f[i])); |
| 307 | 300 | return ret; | |
| 308 | } | ||
| 309 | |||
| 310 | /** | ||
| 311 | * ... | ||
| 312 | * \return ... | ||
| 313 | * \relates Piecewise | ||
| 314 | */ | ||
| 315 | template<typename T> | ||
| 316 | ✗ | inline typename FragmentConcept<T>::BoundsType bounds_local(const Piecewise<T> &f, const OptInterval &_m) { | |
| 317 | ✗ | boost::function_requires<FragmentConcept<T> >(); | |
| 318 | |||
| 319 | ✗ | if(f.empty() || !_m) return typename FragmentConcept<T>::BoundsType(); | |
| 320 | ✗ | Interval const &m = *_m; | |
| 321 | ✗ | if(m.isSingular()) return typename FragmentConcept<T>::BoundsType(f(m.min())); | |
| 322 | |||
| 323 | ✗ | unsigned fi = f.segN(m.min()), ti = f.segN(m.max()); | |
| 324 | ✗ | double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti); | |
| 325 | |||
| 326 | ✗ | if(fi == ti) return bounds_local(f[fi], Interval(ft, tt)); | |
| 327 | |||
| 328 | ✗ | typename FragmentConcept<T>::BoundsType ret(bounds_local(f[fi], Interval(ft, 1.))); | |
| 329 | ✗ | for(unsigned i = fi + 1; i < ti; i++) | |
| 330 | ✗ | ret.unionWith(bounds_exact(f[i])); | |
| 331 | ✗ | if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt))); | |
| 332 | |||
| 333 | ✗ | return ret; | |
| 334 | } | ||
| 335 | |||
| 336 | /** | ||
| 337 | * Returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time. | ||
| 338 | * \relates Piecewise | ||
| 339 | */ | ||
| 340 | template<typename T> | ||
| 341 | 18000 | T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) { | |
| 342 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9000 times.
|
18000 | assert(i < a.size()); |
| 343 | 18000 | double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]); | |
| 344 | 18000 | return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth ); | |
| 345 | } | ||
| 346 | |||
| 347 | /**Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c); | ||
| 348 | * Further subdivides the Piecewise<T> such that there is a cut at every value in c. | ||
| 349 | * Precondition: c sorted lower to higher. | ||
| 350 | * | ||
| 351 | * //Given Piecewise<T> a and b: | ||
| 352 | * Piecewise<T> ac = a.partition(b.cuts); | ||
| 353 | * Piecewise<T> bc = b.partition(a.cuts); | ||
| 354 | * //ac.cuts should be equivalent to bc.cuts | ||
| 355 | * | ||
| 356 | * \relates Piecewise | ||
| 357 | */ | ||
| 358 | template<typename T> | ||
| 359 | 136800 | Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) { | |
| 360 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 68400 times.
|
136800 | assert(pw.invariants()); |
| 361 |
3/4✓ Branch 1 taken 9600 times.
✓ Branch 2 taken 58800 times.
✓ Branch 4 taken 9600 times.
✗ Branch 5 not taken.
|
136800 | if(c.empty()) return Piecewise<T>(pw); |
| 362 | |||
| 363 | 117600 | Piecewise<T> ret = Piecewise<T>(); | |
| 364 |
1/2✓ Branch 3 taken 58800 times.
✗ Branch 4 not taken.
|
117600 | ret.reserve(c.size() + pw.cuts.size() - 1); |
| 365 | |||
| 366 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 58800 times.
|
117600 | if(pw.empty()) { |
| 367 | ✗ | ret.cuts = c; | |
| 368 | ✗ | for(unsigned i = 0; i < c.size() - 1; i++) | |
| 369 | ✗ | ret.push_seg(T()); | |
| 370 | ✗ | return ret; | |
| 371 | } | ||
| 372 | |||
| 373 | 117600 | unsigned si = 0, ci = 0; //Segment index, Cut index | |
| 374 | |||
| 375 | //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment | ||
| 376 |
3/6✓ Branch 1 taken 58800 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 58800 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 58800 times.
|
117600 | while(ci < c.size() && c[ci] < pw.cuts.front()) { |
| 377 | ✗ | bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front()); | |
| 378 | ✗ | ret.push_cut(c[ci]); | |
| 379 | ✗ | ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) ); | |
| 380 | ✗ | ci++; | |
| 381 | } | ||
| 382 | |||
| 383 |
1/2✓ Branch 2 taken 58800 times.
✗ Branch 3 not taken.
|
117600 | ret.push_cut(pw.cuts[0]); |
| 384 | 117600 | double prev = pw.cuts[0]; //previous cut | |
| 385 | //Loop which handles cuts within the Piecewise<T> domain | ||
| 386 | //Should have the cuts = segs + 1 invariant | ||
| 387 |
5/6✓ Branch 1 taken 265200 times.
✓ Branch 2 taken 58800 times.
✓ Branch 4 taken 265200 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 265200 times.
✓ Branch 7 taken 58800 times.
|
648000 | while(si < pw.size() && ci <= c.size()) { |
| 388 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 265200 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 265200 times.
|
530400 | if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest |
| 389 | ✗ | ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end()); | |
| 390 | ✗ | ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end()); | |
| 391 | ✗ | return ret; | |
| 392 |
5/6✓ Branch 1 taken 265200 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 132600 times.
✓ Branch 6 taken 132600 times.
✓ Branch 7 taken 132600 times.
✓ Branch 8 taken 132600 times.
|
530400 | }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) { //no more cuts within this segment, finalize |
| 393 |
2/2✓ Branch 1 taken 6000 times.
✓ Branch 2 taken 126600 times.
|
265200 | if(prev > pw.cuts[si]) { //segment already has cuts, so portion is required |
| 394 |
3/9✓ Branch 2 taken 6000 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 6000 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 6000 times.
✗ Branch 10 not taken.
|
12000 | ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0)); |
| 395 | } else { //plain copy is fine | ||
| 396 |
1/2✓ Branch 2 taken 126600 times.
✗ Branch 3 not taken.
|
253200 | ret.push_seg(pw[si]); |
| 397 | } | ||
| 398 |
1/2✓ Branch 2 taken 132600 times.
✗ Branch 3 not taken.
|
265200 | ret.push_cut(pw.cuts[si + 1]); |
| 399 | 265200 | prev = pw.cuts[si + 1]; | |
| 400 | 265200 | si++; | |
| 401 |
2/2✓ Branch 2 taken 123600 times.
✓ Branch 3 taken 9000 times.
|
265200 | } else if(c[ci] == pw.cuts[si]){ //coincident |
| 402 | //Already finalized the seg with the code immediately above | ||
| 403 | 247200 | ci++; | |
| 404 | } else { //plain old subdivision | ||
| 405 |
2/4✓ Branch 4 taken 9000 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9000 times.
✗ Branch 8 not taken.
|
18000 | ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]); |
| 406 | 18000 | prev = c[ci]; | |
| 407 | 18000 | ci++; | |
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | //input cuts extend further than this Piecewise<T>, extend the last segment. | ||
| 412 |
2/2✓ Branch 1 taken 58800 times.
✓ Branch 2 taken 58800 times.
|
235200 | while(ci < c.size()) { |
| 413 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 58800 times.
|
117600 | if(c[ci] > prev) { |
| 414 | ✗ | ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]); | |
| 415 | ✗ | prev = c[ci]; | |
| 416 | } | ||
| 417 | 117600 | ci++; | |
| 418 | } | ||
| 419 | 117600 | return ret; | |
| 420 | 117600 | } | |
| 421 | |||
| 422 | /** | ||
| 423 | * Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)]. | ||
| 424 | * \relates Piecewise | ||
| 425 | */ | ||
| 426 | template<typename T> | ||
| 427 | ✗ | Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) { | |
| 428 | ✗ | if(pw.empty() || from == to) return Piecewise<T>(); | |
| 429 | |||
| 430 | ✗ | Piecewise<T> ret; | |
| 431 | |||
| 432 | ✗ | double temp = from; | |
| 433 | ✗ | from = std::min(from, to); | |
| 434 | ✗ | to = std::max(temp, to); | |
| 435 | |||
| 436 | ✗ | unsigned i = pw.segN(from); | |
| 437 | ✗ | ret.push_cut(from); | |
| 438 | ✗ | if(i == pw.size() - 1 || to <= pw.cuts[i + 1]) { //to/from inhabit the same segment | |
| 439 | ✗ | ret.push(elem_portion(pw, i, from, to), to); | |
| 440 | ✗ | return ret; | |
| 441 | } | ||
| 442 | ✗ | ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 )); | |
| 443 | ✗ | i++; | |
| 444 | ✗ | unsigned fi = pw.segN(to, i); | |
| 445 | ✗ | ret.reserve(fi - i + 1); | |
| 446 | ✗ | if (to == pw.cuts[fi]) fi-=1; | |
| 447 | |||
| 448 | ✗ | ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi); //copy segs | |
| 449 | ✗ | ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1); //and their cuts | |
| 450 | |||
| 451 | ✗ | ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi))); | |
| 452 | ✗ | if(to != ret.cuts.back()) ret.push_cut(to); | |
| 453 | ✗ | ret.invariants(); | |
| 454 | ✗ | return ret; | |
| 455 | ✗ | } | |
| 456 | |||
| 457 | //TODO: seems like these should be mutating | ||
| 458 | /** | ||
| 459 | * ... | ||
| 460 | * \return ... | ||
| 461 | * \relates Piecewise | ||
| 462 | */ | ||
| 463 | template<typename T> | ||
| 464 | 600 | Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) { | |
| 465 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 600 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
600 | if(f.empty()) return f; |
| 466 | 600 | Piecewise<T> ret; | |
| 467 |
1/2✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
|
600 | ret.reserve(f.size()); |
| 468 |
1/2✓ Branch 2 taken 600 times.
✗ Branch 3 not taken.
|
600 | ret.push_cut(f.cuts[0]); |
| 469 |
2/2✓ Branch 1 taken 23400 times.
✓ Branch 2 taken 600 times.
|
24000 | for(unsigned i=0; i<f.size(); i++){ |
| 470 |
2/6✗ Branch 2 not taken.
✓ Branch 3 taken 23400 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 23400 times.
✗ Branch 8 not taken.
|
23400 | if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) { |
| 471 |
1/2✓ Branch 3 taken 23400 times.
✗ Branch 4 not taken.
|
23400 | ret.push(f[i], f.cuts[i+1]); |
| 472 | } | ||
| 473 | } | ||
| 474 | 600 | return ret; | |
| 475 | 600 | } | |
| 476 | |||
| 477 | //TODO: seems like these should be mutating | ||
| 478 | /** | ||
| 479 | * ... | ||
| 480 | * \return ... | ||
| 481 | * \relates Piecewise | ||
| 482 | */ | ||
| 483 | template<typename T> | ||
| 484 | Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) { | ||
| 485 | if(f.empty()) return f; | ||
| 486 | Piecewise<T> ret; | ||
| 487 | ret.reserve(f.size()); | ||
| 488 | ret.push_cut(f.cuts[0]); | ||
| 489 | double last = f.cuts[0]; // last cut included | ||
| 490 | for(unsigned i=0; i<f.size(); i++){ | ||
| 491 | if (f.cuts[i+1]-f.cuts[i] >= tol) { | ||
| 492 | ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]); | ||
| 493 | last = f.cuts[i+1]; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | |||
| 499 | /** | ||
| 500 | * ... | ||
| 501 | * \return ... | ||
| 502 | * \relates Piecewise | ||
| 503 | */ | ||
| 504 | template<typename T> | ||
| 505 | std::vector<double> roots(const Piecewise<T> &pw) { | ||
| 506 | std::vector<double> ret; | ||
| 507 | for(unsigned i = 0; i < pw.size(); i++) { | ||
| 508 | std::vector<double> sr = roots(pw[i]); | ||
| 509 | for (double & j : sr) ret.push_back(j * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]); | ||
| 510 | |||
| 511 | } | ||
| 512 | return ret; | ||
| 513 | } | ||
| 514 | |||
| 515 | //IMPL: OffsetableConcept | ||
| 516 | /** | ||
| 517 | * ... | ||
| 518 | * \return \f$ a + b = \f$ | ||
| 519 | * \relates Piecewise | ||
| 520 | */ | ||
| 521 | template<typename T> | ||
| 522 | ✗ | Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) { | |
| 523 | ✗ | boost::function_requires<OffsetableConcept<T> >(); | |
| 524 | //TODO:empty | ||
| 525 | ✗ | Piecewise<T> ret; | |
| 526 | ✗ | ret.segs.reserve(a.size()); | |
| 527 | ✗ | ret.cuts = a.cuts; | |
| 528 | ✗ | for(unsigned i = 0; i < a.size();i++) | |
| 529 | ✗ | ret.push_seg(a[i] + b); | |
| 530 | ✗ | return ret; | |
| 531 | ✗ | } | |
| 532 | template<typename T> | ||
| 533 | Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) { | ||
| 534 | boost::function_requires<OffsetableConcept<T> >(); | ||
| 535 | //TODO: empty | ||
| 536 | Piecewise<T> ret; | ||
| 537 | ret.segs.reserve(a.size()); | ||
| 538 | ret.cuts = a.cuts; | ||
| 539 | for(unsigned i = 0; i < a.size();i++) | ||
| 540 | ret.push_seg(a[i] - b); | ||
| 541 | return ret; | ||
| 542 | } | ||
| 543 | template<typename T> | ||
| 544 | ✗ | Piecewise<T>& operator+=(Piecewise<T>& a, typename T::output_type b) { | |
| 545 | ✗ | boost::function_requires<OffsetableConcept<T> >(); | |
| 546 | |||
| 547 | ✗ | if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; } | |
| 548 | |||
| 549 | ✗ | for(unsigned i = 0; i < a.size();i++) | |
| 550 | ✗ | a[i] += b; | |
| 551 | ✗ | return a; | |
| 552 | } | ||
| 553 | template<typename T> | ||
| 554 | 10200 | Piecewise<T>& operator-=(Piecewise<T>& a, typename T::output_type b) { | |
| 555 | 10200 | boost::function_requires<OffsetableConcept<T> >(); | |
| 556 | |||
| 557 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 10200 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
10200 | if(a.empty()) { a.push_cut(0.); a.push(T(-b), 1.); return a; } |
| 558 | |||
| 559 |
2/2✓ Branch 1 taken 35700 times.
✓ Branch 2 taken 10200 times.
|
45900 | for(unsigned i = 0;i < a.size();i++) |
| 560 | 35700 | a[i] -= b; | |
| 561 | 10200 | return a; | |
| 562 | } | ||
| 563 | |||
| 564 | //IMPL: ScalableConcept | ||
| 565 | /** | ||
| 566 | * ... | ||
| 567 | * \return \f$ -a = \f$ | ||
| 568 | * \relates Piecewise | ||
| 569 | */ | ||
| 570 | template<typename T> | ||
| 571 | ✗ | Piecewise<T> operator-(Piecewise<T> const &a) { | |
| 572 | ✗ | boost::function_requires<ScalableConcept<T> >(); | |
| 573 | |||
| 574 | ✗ | Piecewise<T> ret; | |
| 575 | ✗ | ret.segs.reserve(a.size()); | |
| 576 | ✗ | ret.cuts = a.cuts; | |
| 577 | ✗ | for(unsigned i = 0; i < a.size();i++) | |
| 578 | ✗ | ret.push_seg(- a[i]); | |
| 579 | ✗ | return ret; | |
| 580 | ✗ | } | |
| 581 | /** | ||
| 582 | * ... | ||
| 583 | * \return \f$ a * b = \f$ | ||
| 584 | * \relates Piecewise | ||
| 585 | */ | ||
| 586 | template<typename T> | ||
| 587 | ✗ | Piecewise<T> operator*(Piecewise<T> const &a, double b) { | |
| 588 | ✗ | boost::function_requires<ScalableConcept<T> >(); | |
| 589 | |||
| 590 | ✗ | if(a.empty()) return Piecewise<T>(); | |
| 591 | |||
| 592 | ✗ | Piecewise<T> ret; | |
| 593 | ✗ | ret.segs.reserve(a.size()); | |
| 594 | ✗ | ret.cuts = a.cuts; | |
| 595 | ✗ | for(unsigned i = 0; i < a.size();i++) | |
| 596 | ✗ | ret.push_seg(a[i] * b); | |
| 597 | ✗ | return ret; | |
| 598 | ✗ | } | |
| 599 | /** | ||
| 600 | * ... | ||
| 601 | * \return \f$ a * b = \f$ | ||
| 602 | * \relates Piecewise | ||
| 603 | */ | ||
| 604 | template<typename T> | ||
| 605 | Piecewise<T> operator*(Piecewise<T> const &a, T b) { | ||
| 606 | boost::function_requires<ScalableConcept<T> >(); | ||
| 607 | |||
| 608 | if(a.empty()) return Piecewise<T>(); | ||
| 609 | |||
| 610 | Piecewise<T> ret; | ||
| 611 | ret.segs.reserve(a.size()); | ||
| 612 | ret.cuts = a.cuts; | ||
| 613 | for(unsigned i = 0; i < a.size();i++) | ||
| 614 | ret.push_seg(a[i] * b); | ||
| 615 | return ret; | ||
| 616 | } | ||
| 617 | /** | ||
| 618 | * ... | ||
| 619 | * \return \f$ a / b = \f$ | ||
| 620 | * \relates Piecewise | ||
| 621 | */ | ||
| 622 | template<typename T> | ||
| 623 | Piecewise<T> operator/(Piecewise<T> const &a, double b) { | ||
| 624 | boost::function_requires<ScalableConcept<T> >(); | ||
| 625 | |||
| 626 | //FIXME: b == 0? | ||
| 627 | if(a.empty()) return Piecewise<T>(); | ||
| 628 | |||
| 629 | Piecewise<T> ret; | ||
| 630 | ret.segs.reserve(a.size()); | ||
| 631 | ret.cuts = a.cuts; | ||
| 632 | for(unsigned i = 0; i < a.size();i++) | ||
| 633 | ret.push_seg(a[i] / b); | ||
| 634 | return ret; | ||
| 635 | } | ||
| 636 | template<typename T> | ||
| 637 | 600 | Piecewise<T>& operator*=(Piecewise<T>& a, double b) { | |
| 638 | 600 | boost::function_requires<ScalableConcept<T> >(); | |
| 639 | |||
| 640 |
2/2✓ Branch 1 taken 24000 times.
✓ Branch 2 taken 600 times.
|
24600 | for(unsigned i = 0; i < a.size();i++) |
| 641 |
0/2✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
24000 | a[i] *= b; |
| 642 | 600 | return a; | |
| 643 | } | ||
| 644 | template<typename T> | ||
| 645 | ✗ | Piecewise<T>& operator/=(Piecewise<T>& a, double b) { | |
| 646 | ✗ | boost::function_requires<ScalableConcept<T> >(); | |
| 647 | |||
| 648 | //FIXME: b == 0? | ||
| 649 | |||
| 650 | ✗ | for(unsigned i = 0; i < a.size();i++) | |
| 651 | ✗ | a[i] /= b; | |
| 652 | ✗ | return a; | |
| 653 | } | ||
| 654 | |||
| 655 | //IMPL: AddableConcept | ||
| 656 | /** | ||
| 657 | * ... | ||
| 658 | * \return \f$ a + b = \f$ | ||
| 659 | * \relates Piecewise | ||
| 660 | */ | ||
| 661 | template<typename T> | ||
| 662 | 300 | Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) { | |
| 663 | 300 | boost::function_requires<AddableConcept<T> >(); | |
| 664 | |||
| 665 |
2/4✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 300 times.
✗ Branch 7 not taken.
|
300 | Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts); |
| 666 | 300 | Piecewise<T> ret; | |
| 667 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 300 times.
|
300 | assert(pa.size() == pb.size()); |
| 668 |
1/2✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
|
300 | ret.segs.reserve(pa.size()); |
| 669 |
1/2✓ Branch 1 taken 300 times.
✗ Branch 2 not taken.
|
300 | ret.cuts = pa.cuts; |
| 670 |
2/2✓ Branch 1 taken 21000 times.
✓ Branch 2 taken 300 times.
|
21300 | for (unsigned i = 0; i < pa.size(); i++) |
| 671 |
2/4✓ Branch 4 taken 21000 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21000 times.
✗ Branch 8 not taken.
|
21000 | ret.push_seg(pa[i] + pb[i]); |
| 672 | 600 | return ret; | |
| 673 | 300 | } | |
| 674 | /** | ||
| 675 | * ... | ||
| 676 | * \return \f$ a - b = \f$ | ||
| 677 | * \relates Piecewise | ||
| 678 | */ | ||
| 679 | template<typename T> | ||
| 680 | 9600 | Piecewise<T> operator-(Piecewise<T> const &a, Piecewise<T> const &b) { | |
| 681 | 9600 | boost::function_requires<AddableConcept<T> >(); | |
| 682 | |||
| 683 |
2/4✓ Branch 2 taken 9600 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 9600 times.
✗ Branch 7 not taken.
|
9600 | Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts); |
| 684 | 9600 | Piecewise<T> ret = Piecewise<T>(); | |
| 685 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 9600 times.
|
9600 | assert(pa.size() == pb.size()); |
| 686 |
1/2✓ Branch 2 taken 9600 times.
✗ Branch 3 not taken.
|
9600 | ret.segs.reserve(pa.size()); |
| 687 |
1/2✓ Branch 1 taken 9600 times.
✗ Branch 2 not taken.
|
9600 | ret.cuts = pa.cuts; |
| 688 |
2/2✓ Branch 1 taken 9600 times.
✓ Branch 2 taken 9600 times.
|
19200 | for (unsigned i = 0; i < pa.size(); i++) |
| 689 |
2/4✓ Branch 4 taken 9600 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9600 times.
✗ Branch 8 not taken.
|
9600 | ret.push_seg(pa[i] - pb[i]); |
| 690 | 19200 | return ret; | |
| 691 | 9600 | } | |
| 692 | template<typename T> | ||
| 693 | inline Piecewise<T>& operator+=(Piecewise<T> &a, Piecewise<T> const &b) { | ||
| 694 | a = a+b; | ||
| 695 | return a; | ||
| 696 | } | ||
| 697 | template<typename T> | ||
| 698 | inline Piecewise<T>& operator-=(Piecewise<T> &a, Piecewise<T> const &b) { | ||
| 699 | a = a-b; | ||
| 700 | return a; | ||
| 701 | } | ||
| 702 | |||
| 703 | /** | ||
| 704 | * ... | ||
| 705 | * \return \f$ a \cdot b = \f$ | ||
| 706 | * \relates Piecewise | ||
| 707 | */ | ||
| 708 | template<typename T1,typename T2> | ||
| 709 | 300 | Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) { | |
| 710 | //function_requires<MultiplicableConcept<T1> >(); | ||
| 711 | //function_requires<MultiplicableConcept<T2> >(); | ||
| 712 | |||
| 713 |
1/2✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
|
300 | Piecewise<T1> pa = partition(a, b.cuts); |
| 714 |
1/2✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
|
300 | Piecewise<T2> pb = partition(b, a.cuts); |
| 715 | 300 | Piecewise<T2> ret = Piecewise<T2>(); | |
| 716 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 300 times.
|
300 | assert(pa.size() == pb.size()); |
| 717 |
1/2✓ Branch 2 taken 300 times.
✗ Branch 3 not taken.
|
300 | ret.segs.reserve(pa.size()); |
| 718 |
1/2✓ Branch 1 taken 300 times.
✗ Branch 2 not taken.
|
300 | ret.cuts = pa.cuts; |
| 719 |
2/2✓ Branch 1 taken 21000 times.
✓ Branch 2 taken 300 times.
|
21300 | for (unsigned i = 0; i < pa.size(); i++) |
| 720 |
2/4✓ Branch 4 taken 21000 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21000 times.
✗ Branch 8 not taken.
|
21000 | ret.push_seg(pa[i] * pb[i]); |
| 721 | 600 | return ret; | |
| 722 | 300 | } | |
| 723 | |||
| 724 | /** | ||
| 725 | * ... | ||
| 726 | * \return \f$ a \cdot b \f$ | ||
| 727 | * \relates Piecewise | ||
| 728 | */ | ||
| 729 | template<typename T> | ||
| 730 | ✗ | inline Piecewise<T>& operator*=(Piecewise<T> &a, Piecewise<T> const &b) { | |
| 731 | ✗ | a = a * b; | |
| 732 | ✗ | return a; | |
| 733 | } | ||
| 734 | |||
| 735 | Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k); | ||
| 736 | //TODO: replace divide(a,b,k) by divide(a,b,tol,k)? | ||
| 737 | //TODO: atm, relative error is ~(tol/a)%. Find a way to make it independent of a. | ||
| 738 | //Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero). | ||
| 739 | Piecewise<SBasis> | ||
| 740 | divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3); | ||
| 741 | Piecewise<SBasis> | ||
| 742 | divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3); | ||
| 743 | Piecewise<SBasis> | ||
| 744 | divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3); | ||
| 745 | Piecewise<SBasis> | ||
| 746 | divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3); | ||
| 747 | |||
| 748 | //Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp. | ||
| 749 | std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g); | ||
| 750 | int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut, | ||
| 751 | std::map<double,unsigned>::iterator const &next, | ||
| 752 | std::vector<double> const &levels, | ||
| 753 | SBasis const &g); | ||
| 754 | |||
| 755 | /** | ||
| 756 | * ... | ||
| 757 | * \return ... | ||
| 758 | * \relates Piecewise | ||
| 759 | */ | ||
| 760 | template<typename T> | ||
| 761 | 24000 | Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){ | |
| 762 | /// \todo add concept check | ||
| 763 | 24000 | Piecewise<T> result; | |
| 764 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24000 times.
|
24000 | if (f.empty()) return result; |
| 765 |
1/6✗ Branch 1 not taken.
✓ Branch 2 taken 24000 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
24000 | if (g.isZero()) return Piecewise<T>(f(0)); |
| 766 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24000 times.
|
24000 | if (f.size()==1){ |
| 767 | ✗ | double t0 = f.cuts[0], width = f.cuts[1] - t0; | |
| 768 | ✗ | return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g)); | |
| 769 | } | ||
| 770 | |||
| 771 | //first check bounds... | ||
| 772 |
1/2✓ Branch 3 taken 24000 times.
✗ Branch 4 not taken.
|
24000 | Interval bs = *bounds_fast(g); |
| 773 |
3/6✓ Branch 2 taken 24000 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 24000 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 24000 times.
|
24000 | if (f.cuts.front() > bs.max() || bs.min() > f.cuts.back()){ |
| 774 | ✗ | int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2; | |
| 775 | ✗ | double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0; | |
| 776 | ✗ | return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g)); | |
| 777 | } | ||
| 778 | |||
| 779 | 24000 | std::vector<double> levels;//we can forget first and last cuts... | |
| 780 |
1/2✓ Branch 11 taken 24000 times.
✗ Branch 12 not taken.
|
24000 | levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1); |
| 781 | //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>. | ||
| 782 |
1/2✓ Branch 2 taken 24000 times.
✗ Branch 3 not taken.
|
24000 | std::map<double,unsigned> cuts_pb = compose_pullback(levels,g); |
| 783 | |||
| 784 | //-- Compose each piece of g with the relevant seg of f. | ||
| 785 |
1/2✓ Branch 2 taken 24000 times.
✗ Branch 3 not taken.
|
24000 | result.cuts.push_back(0.); |
| 786 | 24000 | std::map<double,unsigned>::iterator cut=cuts_pb.begin(); | |
| 787 | 24000 | std::map<double,unsigned>::iterator next=cut; next++; | |
| 788 |
2/2✓ Branch 4 taken 42000 times.
✓ Branch 5 taken 24000 times.
|
66000 | while(next!=cuts_pb.end()){ |
| 789 | //assert(std::abs(int((*cut).second-(*next).second))<1); | ||
| 790 | //TODO: find a way to recover from this error? the root finder missed some root; | ||
| 791 | // the levels/variations of f might be too close/fast... | ||
| 792 |
1/2✓ Branch 1 taken 42000 times.
✗ Branch 2 not taken.
|
42000 | int idx = compose_findSegIdx(cut,next,levels,g); |
| 793 | 42000 | double t0=(*cut).first; | |
| 794 | 42000 | double t1=(*next).first; | |
| 795 | |||
| 796 |
1/2✓ Branch 1 taken 42000 times.
✗ Branch 2 not taken.
|
42000 | if (!are_near(t0,t1,EPSILON*EPSILON)) { // prevent adding cuts that are extremely close together and that may cause trouble with rounding e.g. when reversing the path |
| 797 |
2/4✓ Branch 5 taken 42000 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 42000 times.
✗ Branch 9 not taken.
|
42000 | SBasis sub_g=compose(g, Linear(t0,t1)); |
| 798 |
3/6✓ Branch 7 taken 42000 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42000 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 42000 times.
✗ Branch 14 not taken.
|
84000 | sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]), |
| 799 | 42000 | (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g); | |
| 800 |
2/4✓ Branch 3 taken 42000 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 42000 times.
✗ Branch 7 not taken.
|
42000 | result.push(compose(f[idx],sub_g),t1); |
| 801 | 42000 | } | |
| 802 | |||
| 803 | 42000 | cut++; | |
| 804 | 42000 | next++; | |
| 805 | } | ||
| 806 | 24000 | return(result); | |
| 807 | 24000 | } | |
| 808 | |||
| 809 | /** | ||
| 810 | * ... | ||
| 811 | * \return ... | ||
| 812 | * \relates Piecewise | ||
| 813 | */ | ||
| 814 | template<typename T> | ||
| 815 | 600 | Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){ | |
| 816 | /// \todo add concept check | ||
| 817 | 600 | Piecewise<T> result; | |
| 818 |
2/2✓ Branch 2 taken 24000 times.
✓ Branch 3 taken 600 times.
|
48600 | for(unsigned i = 0; i < g.segs.size(); i++){ |
| 819 |
1/2✓ Branch 3 taken 24000 times.
✗ Branch 4 not taken.
|
24000 | Piecewise<T> fgi=compose(f, g.segs[i]); |
| 820 |
1/4✓ Branch 4 taken 24000 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
24000 | fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1])); |
| 821 |
1/2✓ Branch 1 taken 24000 times.
✗ Branch 2 not taken.
|
24000 | result.concat(fgi); |
| 822 | } | ||
| 823 | 600 | return result; | |
| 824 | ✗ | } | |
| 825 | |||
| 826 | /* | ||
| 827 | Piecewise<D2<SBasis> > compose(D2<SBasis2d> const &sb2d, Piecewise<D2<SBasis> > const &pwd2sb){ | ||
| 828 | /// \todo add concept check | ||
| 829 | Piecewise<D2<SBasis> > result; | ||
| 830 | result.push_cut(0.); | ||
| 831 | for(unsigned i = 0; i < pwd2sb.size(); i++){ | ||
| 832 | result.push(compose_each(sb2d,pwd2sb[i]),i+1); | ||
| 833 | } | ||
| 834 | return result; | ||
| 835 | }*/ | ||
| 836 | |||
| 837 | /** Compose an SBasis with the inverse of another. | ||
| 838 | * WARNING: It's up to the user to check that the second SBasis is indeed | ||
| 839 | * invertible (i.e. strictly increasing or decreasing). | ||
| 840 | * \return \f$ f \cdot g^{-1} \f$ | ||
| 841 | * \relates Piecewise | ||
| 842 | */ | ||
| 843 | Piecewise<SBasis> pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero); | ||
| 844 | |||
| 845 | |||
| 846 | |||
| 847 | template <typename T> | ||
| 848 | Piecewise<T> Piecewise<T>::operator()(SBasis f){return compose((*this),f);} | ||
| 849 | template <typename T> | ||
| 850 | Piecewise<T> Piecewise<T>::operator()(Piecewise<SBasis>f){return compose((*this),f);} | ||
| 851 | |||
| 852 | /** | ||
| 853 | * ... | ||
| 854 | * \return ... | ||
| 855 | * \relates Piecewise | ||
| 856 | */ | ||
| 857 | template<typename T> | ||
| 858 | 9600 | Piecewise<T> integral(Piecewise<T> const &a) { | |
| 859 | 9600 | Piecewise<T> result; | |
| 860 |
1/2✓ Branch 2 taken 9600 times.
✗ Branch 3 not taken.
|
9600 | result.segs.resize(a.segs.size()); |
| 861 |
1/2✓ Branch 1 taken 9600 times.
✗ Branch 2 not taken.
|
9600 | result.cuts = a.cuts; |
| 862 | 9600 | typename T::output_type c = a.segs[0].at0(); | |
| 863 |
2/2✓ Branch 1 taken 11700 times.
✓ Branch 2 taken 9600 times.
|
21300 | for(unsigned i = 0; i < a.segs.size(); i++){ |
| 864 |
3/6✓ Branch 6 taken 11700 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11700 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 11700 times.
✗ Branch 14 not taken.
|
11700 | result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]); |
| 865 |
2/4✓ Branch 2 taken 11700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11700 times.
✗ Branch 7 not taken.
|
11700 | result.segs[i]+= c-result.segs[i].at0(); |
| 866 |
1/2✓ Branch 2 taken 11700 times.
✗ Branch 3 not taken.
|
11700 | c = result.segs[i].at1(); |
| 867 | } | ||
| 868 | 9600 | return result; | |
| 869 | ✗ | } | |
| 870 | |||
| 871 | /** | ||
| 872 | * ... | ||
| 873 | * \return ... | ||
| 874 | * \relates Piecewise | ||
| 875 | */ | ||
| 876 | template<typename T> | ||
| 877 | 9900 | Piecewise<T> derivative(Piecewise<T> const &a) { | |
| 878 | 9900 | Piecewise<T> result; | |
| 879 |
1/2✓ Branch 2 taken 9900 times.
✗ Branch 3 not taken.
|
9900 | result.segs.resize(a.segs.size()); |
| 880 |
1/2✓ Branch 1 taken 9900 times.
✗ Branch 2 not taken.
|
9900 | result.cuts = a.cuts; |
| 881 |
2/2✓ Branch 1 taken 21300 times.
✓ Branch 2 taken 9900 times.
|
31200 | for(unsigned i = 0; i < a.segs.size(); i++){ |
| 882 |
3/6✓ Branch 6 taken 21300 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 21300 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 21300 times.
✗ Branch 14 not taken.
|
21300 | result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]); |
| 883 | } | ||
| 884 | 9900 | return result; | |
| 885 | ✗ | } | |
| 886 | |||
| 887 | std::vector<double> roots(Piecewise<SBasis> const &f); | ||
| 888 | |||
| 889 | std::vector<std::vector<double> >multi_roots(Piecewise<SBasis> const &f, std::vector<double> const &values); | ||
| 890 | |||
| 891 | //TODO: implement level_sets directly for pwsb instead of sb (and derive it fo sb). | ||
| 892 | //It should be faster than the reverse as the algorithm may jump over full cut intervals. | ||
| 893 | std::vector<Interval> level_set(Piecewise<SBasis> const &f, Interval const &level, double tol=1e-5); | ||
| 894 | std::vector<Interval> level_set(Piecewise<SBasis> const &f, double v, double vtol, double tol=1e-5); | ||
| 895 | //std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<Interval> const &levels, double tol=1e-5); | ||
| 896 | //std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<double> &v, double vtol, double tol=1e-5); | ||
| 897 | |||
| 898 | |||
| 899 | /** | ||
| 900 | * ... | ||
| 901 | * \return ... | ||
| 902 | * \relates Piecewise | ||
| 903 | */ | ||
| 904 | template<typename T> | ||
| 905 | ✗ | Piecewise<T> reverse(Piecewise<T> const &f) { | |
| 906 | ✗ | Piecewise<T> ret = Piecewise<T>(); | |
| 907 | ✗ | ret.reserve(f.size()); | |
| 908 | ✗ | double start = f.cuts[0]; | |
| 909 | ✗ | double end = f.cuts.back(); | |
| 910 | ✗ | for (unsigned i = 0; i < f.cuts.size(); i++) { | |
| 911 | ✗ | double x = f.cuts[f.cuts.size() - 1 - i]; | |
| 912 | ✗ | ret.push_cut(end - (x - start)); | |
| 913 | } | ||
| 914 | ✗ | for (unsigned i = 0; i < f.segs.size(); i++) | |
| 915 | ✗ | ret.push_seg(reverse(f[f.segs.size() - i - 1])); | |
| 916 | ✗ | return ret; | |
| 917 | ✗ | } | |
| 918 | |||
| 919 | /** | ||
| 920 | * Interpolates between a and b. | ||
| 921 | * \return a if t = 0, b if t = 1, or an interpolation between a and b for t in [0,1] | ||
| 922 | * \relates Piecewise | ||
| 923 | */ | ||
| 924 | template<typename T> | ||
| 925 | Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) { | ||
| 926 | // Make sure both paths have the same number of segments and cuts at the same locations | ||
| 927 | b.setDomain(a.domain()); | ||
| 928 | Piecewise<T> pA = partition(a, b.cuts); | ||
| 929 | Piecewise<T> pB = partition(b, a.cuts); | ||
| 930 | |||
| 931 | return (pA*(1-t) + pB*t); | ||
| 932 | } | ||
| 933 | |||
| 934 | } | ||
| 935 | #endif //LIB2GEOM_SEEN_PIECEWISE_H | ||
| 936 | /* | ||
| 937 | Local Variables: | ||
| 938 | mode:c++ | ||
| 939 | c-file-style:"stroustrup" | ||
| 940 | c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) | ||
| 941 | indent-tabs-mode:nil | ||
| 942 | fill-column:99 | ||
| 943 | End: | ||
| 944 | */ | ||
| 945 | // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : | ||
| 946 |