diff --git a/rak/algorithm.h b/rak/algorithm.h index ad8e7158..98613820 100644 --- a/rak/algorithm.h +++ b/rak/algorithm.h @@ -176,6 +176,26 @@ inline int popcount_wrapper(T t) { #endif } +// Get the median of an unordered set of numbers of arbitrary +// type by modifing the underlying dataset +template +T median(_InputIter __first, _InputIter __last) { + T __med; + + unsigned int __size = __last - __first; + unsigned int __middle = __size / 2; + _InputIter __target1 = __first + __middle; + std::nth_element(__first, __target1, __last); + __med = *__target1; + + if (__size % 2 == 0) { + _InputIter __target2 = std::max_element(__first, __target1); + __med = (__med + *__target2) / 2.0; + } + + return __med; +} + } #endif diff --git a/src/command_ui.cc b/src/command_ui.cc index 69ee2f54..c98f9427 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -519,6 +520,128 @@ apply_elapsed_greater(const torrent::Object::list_type& args) { return (int64_t)(start_time != 0 && rak::timer::current_seconds() - start_time > rpc::convert_to_value(args.back())); } +inline std::vector +as_vector(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in as_vector."); + + std::vector result; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + result.push_back(itr->as_value()); + } else if (itr->is_string()) { + result.push_back(rpc::convert_to_value(itr->as_string())); + } else if (itr->is_list()) { + std::vector subResult = as_vector(itr->as_list()); + result.insert(result.end(), subResult.begin(), subResult.end()); + } else { + throw torrent::input_error("Wrong type supplied to as_vector."); + } + + } + + return result; +} + +int64_t +apply_math_basic(const char* name, const std::function op, const torrent::Object::list_type& args) { + int64_t val = 0, rhs = 0; + bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod"); + + if (args.size() == 0) + throw torrent::input_error(std::string(name) + ": No arguments provided!"); + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + rhs = itr->as_value(); + } else if (itr->is_string()) { + rhs = rpc::convert_to_value(itr->as_string()); + } else if (itr->is_list()) { + rhs = apply_math_basic(name, op, itr->as_list()); + } else { + throw torrent::input_error(std::string(name) + ": Wrong argument type"); + } + + if (divides && !rhs && itr != args.begin()) + throw torrent::input_error(std::string(name) + ": Division by zero!"); + + val = itr == args.begin() ? rhs : op(val, rhs); + + } + + return val; +} + +int64_t +apply_arith_basic(const std::function op, const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_basic."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + val = itr == args.begin() ? itr->as_value() : (op(val, itr->as_value()) ? val : itr->as_value()); + } else if (itr->is_string()) { + int64_t cval = rpc::convert_to_value(itr->as_string()); + val = itr == args.begin() ? cval : (op(val, cval) ? val : cval); + } else if (itr->is_list()) { + int64_t fval = apply_arith_basic(op, itr->as_list()); + val = itr == args.begin() ? fval : (op(val, fval) ? val : fval); + } else { + throw torrent::input_error("Wrong type supplied to apply_arith_basic."); + } + + } + + return val; +} + +int64_t +apply_arith_count(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_count."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + switch (itr->type()) { + case torrent::Object::TYPE_VALUE: + case torrent::Object::TYPE_STRING: + val++; + break; + case torrent::Object::TYPE_LIST: + val += apply_arith_count(itr->as_list()); + break; + default: + throw torrent::input_error("Wrong type supplied to apply_arith_count."); + } + + } + + return val; +} + +int64_t +apply_arith_other(const char* op, const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_other."); + + if (strcmp(op, "average") == 0) { + return (int64_t)(apply_math_basic(op, std::plus(), args) / apply_arith_count(args)); + } else if (strcmp(op, "median") == 0) { + std::vector result = as_vector(args); + return (int64_t)rak::median(result.begin(), result.end()); + } else { + throw torrent::input_error("Wrong operation supplied to apply_arith_other."); + } +} + void initialize_command_ui() { CMD2_VAR_STRING("keys.layout", "qwerty"); @@ -585,6 +708,17 @@ initialize_command_ui() { CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2)); CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2)); + CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies(), std::placeholders::_2)); + CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less(), std::placeholders::_2)); + CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater(), std::placeholders::_2)); + CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2)); + CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2)); + CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2)); + CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2)); CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2)); }