fc1fcae0b0
We now specify intrins as separate DAPS lines. This syntax is much nicer and well-grouped than the previous negtrin-*/postrin-* param names. Alas, we're about to replace it in the next few commits already though.
347 lines
8.8 KiB
C++
347 lines
8.8 KiB
C++
#ifndef SMO_INTRIN_THRESHOLD_PARAMS_H
|
|
#define SMO_INTRIN_THRESHOLD_PARAMS_H
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace smo::intrin {
|
|
|
|
enum class ThresholdUnit
|
|
{
|
|
Percentage,
|
|
Absolute,
|
|
};
|
|
|
|
struct ParsedThresholdParam
|
|
{
|
|
int value;
|
|
ThresholdUnit unit;
|
|
std::string_view matchedName;
|
|
bool wasSpecified;
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kPosIntPcParamNames = {
|
|
"postrin-interest-percentage",
|
|
"postrin-interest-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kPosIntThrParamNames = {
|
|
"postrin-interest-threshold",
|
|
"postrin-interest-thresh",
|
|
"postrin-interest-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kNegIntPcParamNames = {
|
|
"negtrin-interest-percentage",
|
|
"negtrin-interest-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kNegIntThrParamNames = {
|
|
"negtrin-interest-threshold",
|
|
"negtrin-interest-thresh",
|
|
"negtrin-interest-thr",
|
|
};
|
|
|
|
// Short interest-* names: only valid on dedicated postrin/negtrin qualeIfaceApi
|
|
// lines (docs/design/intrin-thresholds.md); never on sensory qualeIfaceApi specs.
|
|
inline constexpr std::array<std::string_view, 2> kIntrinInterestPcUnprefixed = {
|
|
"interest-percentage",
|
|
"interest-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kIntrinInterestThrUnprefixed = {
|
|
"interest-threshold",
|
|
"interest-thresh",
|
|
"interest-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kPosDistPcParamNames = {
|
|
"postrin-distraction-percentage",
|
|
"postrin-distraction-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kPosDistThrParamNames = {
|
|
"postrin-distraction-threshold",
|
|
"postrin-distraction-thresh",
|
|
"postrin-distraction-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 2> kNegDistPcParamNames = {
|
|
"negtrin-distraction-percentage",
|
|
"negtrin-distraction-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 3> kNegDistThrParamNames = {
|
|
"negtrin-distraction-threshold",
|
|
"negtrin-distraction-thresh",
|
|
"negtrin-distraction-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 8> kStupefactionPcParamNames = {
|
|
"postrin-stupefaction-percentage",
|
|
"postrin-stupefaction-pc",
|
|
"postrin-stupefying-percentage",
|
|
"postrin-stupefying-pc",
|
|
"stupefaction-percentage",
|
|
"stupefaction-pc",
|
|
"stupefying-percentage",
|
|
"stupefying-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 12> kStupefactionThrParamNames = {
|
|
"postrin-stupefaction-threshold",
|
|
"postrin-stupefaction-thresh",
|
|
"postrin-stupefaction-thr",
|
|
"postrin-stupefying-threshold",
|
|
"postrin-stupefying-thresh",
|
|
"postrin-stupefying-thr",
|
|
"stupefaction-threshold",
|
|
"stupefaction-thresh",
|
|
"stupefaction-thr",
|
|
"stupefying-threshold",
|
|
"stupefying-thresh",
|
|
"stupefying-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 4> kIntolerablePcParamNames = {
|
|
"negtrin-intolerable-percentage",
|
|
"negtrin-intolerable-pc",
|
|
"intolerable-percentage",
|
|
"intolerable-pc",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 6> kIntolerableThrParamNames = {
|
|
"negtrin-intolerable-threshold",
|
|
"negtrin-intolerable-thresh",
|
|
"negtrin-intolerable-thr",
|
|
"intolerable-threshold",
|
|
"intolerable-thresh",
|
|
"intolerable-thr",
|
|
};
|
|
|
|
inline constexpr std::array<std::string_view, 10> kForbiddenUnitlessIntrinParamNames = {
|
|
"postrin-interest",
|
|
"negtrin-interest",
|
|
"postrin-distraction",
|
|
"negtrin-distraction",
|
|
"postrin-stupefaction",
|
|
"postrin-stupefying",
|
|
"stupefaction",
|
|
"stupefying",
|
|
"negtrin-intolerable",
|
|
"intolerable",
|
|
};
|
|
|
|
template <std::size_t N>
|
|
bool arrayContains(
|
|
const std::array<std::string_view, N>& names,
|
|
std::string_view candidate)
|
|
{
|
|
for (const auto& name : names) {
|
|
if (name == candidate) { return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename NameCollectionT>
|
|
bool namesContain(
|
|
const NameCollectionT& names,
|
|
std::string_view candidate)
|
|
{
|
|
for (const auto& name : names) {
|
|
if (name == candidate) { return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
inline bool isDedicatedIntrinsQualeIfaceApi(std::string_view qualeIfaceApi)
|
|
{
|
|
return qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin";
|
|
}
|
|
|
|
inline bool isKnownIntrinsPipelineParamName(std::string_view name)
|
|
{
|
|
return namesContain(kPosIntPcParamNames, name)
|
|
|| namesContain(kPosIntThrParamNames, name)
|
|
|| namesContain(kNegIntPcParamNames, name)
|
|
|| namesContain(kNegIntThrParamNames, name)
|
|
|| namesContain(kIntrinInterestPcUnprefixed, name)
|
|
|| namesContain(kIntrinInterestThrUnprefixed, name)
|
|
|| namesContain(kPosDistPcParamNames, name)
|
|
|| namesContain(kPosDistThrParamNames, name)
|
|
|| namesContain(kNegDistPcParamNames, name)
|
|
|| namesContain(kNegDistThrParamNames, name)
|
|
|| namesContain(kStupefactionPcParamNames, name)
|
|
|| namesContain(kStupefactionThrParamNames, name)
|
|
|| namesContain(kIntolerablePcParamNames, name)
|
|
|| namesContain(kIntolerableThrParamNames, name)
|
|
|| name == "passband-count-gt-val"
|
|
|| name == "passband-count-lt-val";
|
|
}
|
|
|
|
inline bool hasNonEmptyFromStimbuffParam(
|
|
const std::vector<std::pair<std::string, std::string>>& params)
|
|
{
|
|
for (const auto& [key, value] : params)
|
|
{
|
|
if (key == "from-stimbuff" && !value.empty())
|
|
{ return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Enforces dedicated-only intrin specs: intrinsic thresholds and passband
|
|
* comparators appear only on negtrin/postrin qualeIfaceApi specs with
|
|
* from-stimbuff. Embedding intrins in other qualeIfaceApi parameter lists is
|
|
* rejected (docs/design/intrin-thresholds.md).
|
|
*/
|
|
inline void validateIntrinsQualeApiPolicy(
|
|
const std::string& qualeIfaceApi,
|
|
const std::vector<std::pair<std::string, std::string>>& params)
|
|
{
|
|
if (isDedicatedIntrinsQualeIfaceApi(qualeIfaceApi))
|
|
{
|
|
if (!hasNonEmptyFromStimbuffParam(params))
|
|
{
|
|
throw std::runtime_error(
|
|
"qualeIfaceApi '" + qualeIfaceApi + "' requires a non-empty "
|
|
"'from-stimbuff=<stimbuffQualeIfaceApi>' parameter naming the "
|
|
"sensory stimbuff that feeds this intrinsic pipeline.");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for (const auto& [name, value] : params)
|
|
{
|
|
(void)value;
|
|
|
|
if (isKnownIntrinsPipelineParamName(name))
|
|
{
|
|
throw std::runtime_error(
|
|
"Intrinsic threshold and passband comparator params must not "
|
|
"appear on qualeIfaceApi '" + qualeIfaceApi + "'. Use dedicated "
|
|
"negtrin(...) or postrin(...) lines with from-stimbuff=... "
|
|
"(offending param: '" + name + "').");
|
|
}
|
|
|
|
if (name == "from-stimbuff")
|
|
{
|
|
throw std::runtime_error(
|
|
"'from-stimbuff' is only valid on negtrin(...) or postrin(...) "
|
|
"qualeIfaceApi specs.");
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void validateNoForbiddenUnitlessIntrinParams(
|
|
const std::vector<std::pair<std::string, std::string>>& params)
|
|
{
|
|
for (const auto& [name, value] : params)
|
|
{
|
|
(void)value;
|
|
|
|
if (arrayContains(kForbiddenUnitlessIntrinParamNames, name))
|
|
{
|
|
throw std::runtime_error(
|
|
"Intrinsic threshold param '" + name
|
|
+ "' is invalid without a unit suffix. Use a "
|
|
"'-percentage'/'-pc' or '-threshold'/'-thresh'/'-thr' variant.");
|
|
}
|
|
}
|
|
}
|
|
|
|
inline std::optional<std::pair<std::string_view, int>> findLastMatchingIntParam(
|
|
const std::vector<std::pair<std::string, std::string>>& params,
|
|
const auto& synonymNames)
|
|
{
|
|
for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt)
|
|
{
|
|
const auto& [name, value] = *paramIt;
|
|
if (!namesContain(synonymNames, name))
|
|
{ continue; }
|
|
|
|
try {
|
|
return std::pair<std::string_view, int>(name, std::stoi(value));
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error(
|
|
"Failed to parse '" + name + "' param value '" + value
|
|
+ "' as integer: " + e.what());
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
inline ParsedThresholdParam parseOptionalThresholdParam(
|
|
const std::vector<std::pair<std::string, std::string>>& params,
|
|
const auto& percentageNames,
|
|
const auto& absoluteNames,
|
|
int defaultValue,
|
|
ThresholdUnit defaultUnit)
|
|
{
|
|
for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt)
|
|
{
|
|
const auto& [name, value] = *paramIt;
|
|
ThresholdUnit unit;
|
|
|
|
if (namesContain(percentageNames, name))
|
|
{ unit = ThresholdUnit::Percentage; }
|
|
else if (namesContain(absoluteNames, name))
|
|
{ unit = ThresholdUnit::Absolute; }
|
|
else
|
|
{ continue; }
|
|
|
|
try
|
|
{
|
|
return ParsedThresholdParam{
|
|
.value = std::stoi(value),
|
|
.unit = unit,
|
|
.matchedName = name,
|
|
.wasSpecified = true,
|
|
};
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error(
|
|
"Failed to parse '" + name + "' param value '" + value
|
|
+ "' as integer: " + e.what());
|
|
}
|
|
}
|
|
|
|
return ParsedThresholdParam{
|
|
.value = defaultValue,
|
|
.unit = defaultUnit,
|
|
.matchedName = {},
|
|
.wasSpecified = false,
|
|
};
|
|
}
|
|
|
|
inline uint32_t resolveThresholdValue(
|
|
const ParsedThresholdParam& param,
|
|
size_t percentageBase)
|
|
{
|
|
if (param.unit == ThresholdUnit::Percentage) {
|
|
return static_cast<uint32_t>((percentageBase * param.value) / 100);
|
|
}
|
|
|
|
return static_cast<uint32_t>(param.value);
|
|
}
|
|
|
|
} // namespace smo::intrin
|
|
|
|
#endif // SMO_INTRIN_THRESHOLD_PARAMS_H
|