Path B: precompute one exp() per unique exponent, then do a small linear-search lookup per term to fetch the cached value. Realistic for the typical case where unique <= 16 — linear search beats a hash map at that size.
| 147 | // lookup per term to fetch the cached value. Realistic for the typical case where |
| 148 | // unique <= 16 — linear search beats a hash map at that size. |
| 149 | double path_B(const std::vector<double>& ls, const std::vector<double>& ms, const std::vector<double>& uniq_l, const std::vector<double>& uniq_m, |
| 150 | double log_delta, double log_tau) { |
| 151 | // Precompute exp(u * log_x) once per unique exponent. |
| 152 | double cache_l[64], cache_m[64]; |
| 153 | const std::size_t Ul = std::min<std::size_t>(uniq_l.size(), 64), Um = std::min<std::size_t>(uniq_m.size(), 64); |
| 154 | for (std::size_t k = 0; k < Ul; ++k) |
| 155 | cache_l[k] = std::exp(uniq_l[k] * log_delta); |
| 156 | for (std::size_t k = 0; k < Um; ++k) |
| 157 | cache_m[k] = std::exp(uniq_m[k] * log_tau); |
| 158 | |
| 159 | double acc = 0.0; |
| 160 | const std::size_t Nl = ls.size(), Nm = ms.size(); |
| 161 | for (std::size_t i = 0; i < Nl; ++i) { |
| 162 | const double li = ls[i]; |
| 163 | for (std::size_t k = 0; k < Ul; ++k) { |
| 164 | if (uniq_l[k] == li) { // exponents loaded from JSON are bit-exact within the table |
| 165 | acc += cache_l[k]; |
| 166 | break; |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | for (std::size_t i = 0; i < Nm; ++i) { |
| 171 | const double mi = ms[i]; |
| 172 | for (std::size_t k = 0; k < Um; ++k) { |
| 173 | if (uniq_m[k] == mi) { |
| 174 | acc += cache_m[k]; |
| 175 | break; |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | return acc; |
| 180 | } |
| 181 | |
| 182 | struct TrialStats |
| 183 | { |
no test coverage detected