OpenFPGA/libs/libvtrutil/src/vtr_math.cpp

88 lines
3.1 KiB
C++

#include <map>
#include "vtr_assert.h"
#include "vtr_error.h"
#include "vtr_math.h"
namespace vtr {
int ipow(int base, int exp) {
int result = 1;
VTR_ASSERT(exp >= 0);
while (exp) {
if (exp & 1)
result *= base;
exp >>= 1;
base *= base;
}
return result;
}
/* Performs linear interpolation or extrapolation on the set of (x,y) values specified by the xy_map.
* A requested x value is passed in, and we return the interpolated/extrapolated y value at this requested value of x.
* Meant for maps where both key and element are numbers.
* This is specifically enforced by the explicit instantiations below this function. i.e. only templates
* using those types listed in the explicit instantiations below are allowed */
template<typename X, typename Y>
Y linear_interpolate_or_extrapolate(const std::map<X, Y>* xy_map, X requested_x) {
Y result;
/* the intention of this function is to interpolate/extrapolate. we can't do so with less than 2 values in the xy_map */
if (xy_map->size() < 2) {
throw VtrError("linear_interpolate_or_extrapolate: cannot interpolate/extrapolate based on less than 2 (x,y) pairs", __FILE__, __LINE__);
}
auto itr = xy_map->find(requested_x);
if (itr != xy_map->end()) {
/* requested x already exists in the x,y map */
result = itr->second;
} else {
/* requested x does not exist in the x,y map. need to interpolate/extrapolate */
typename std::map<X, Y>::const_iterator it;
double x_low, x_high, y_low, y_high;
double slope, reference_y, delta_x;
/* get first x greater than the one requested */
it = xy_map->upper_bound(requested_x);
if (it == xy_map->end()) {
/* need to extrapolate to higher x. based on the y values at the two largest x values */
it--;
x_high = (double)it->first;
y_high = (double)it->second;
it--;
x_low = (double)it->first;
y_low = (double)it->second;
} else if (it == xy_map->begin()) {
/* need to extrapolate to lower x. based on the y values at the two smallest x */
x_low = (double)it->first;
y_low = (double)it->second;
it++;
x_high = (double)it->first;
y_high = (double)it->second;
} else {
/* need to interpolate. based on y values at x just above/below
* the one we want */
x_high = (double)it->first;
y_high = (double)it->second;
it--;
x_low = (double)it->first;
y_low = (double)it->second;
}
slope = (y_high - y_low) / (x_high - x_low);
reference_y = y_low;
delta_x = (double)requested_x - x_low;
result = (Y)(reference_y + (slope * delta_x));
}
return result;
}
template double linear_interpolate_or_extrapolate(const std::map<int, double>* xy_map, int requested_x); /* (int,double) */
template double linear_interpolate_or_extrapolate(const std::map<double, double>* xy_map, double requested_x); /* (double,double) */
} // namespace vtr