Ignore zero in std::min and calculate minimum of other numbers - c++

Minimum of non-zero numbers:
#include <iostream>
#include <algorithm>
int main() {
double s1 = 1000;
double s2 = 400;
double s3 = 300;
double s4 = 10;
double minimum = std::min({s1, s2, s3, s4});
std::cout<<minimum<<"\n";
double var = 1/minimum;
std::cout<<var;
}
This works fine and returns:
10
0.1
Problem is when one of the numbers is zero:
#include <iostream>
#include <algorithm>
int main() {
double s1 = 1000;
double s2 = 400;
double s3 = 300;
double s4 = 0;
double minimum = std::min({s1, s2, s3, s4});
std::cout<<minimum<<"\n";
double var = 1/minimum;
std::cout<<var;
}
It returns:
0
inf
Expected results:
300
0.00333333
How can I ignore zero from the calculation?

Or like this
#include <algorithm>
#include <limits>
#include <vector>
template<std::size_t N>
constexpr double min_no_zero(const double (&values)[N])
{
static_assert(N>0,"There must be at least one number");
double min = std::numeric_limits<double>::max();
for (const auto value : values)
{
if ( value != 0.0 )
{
min = std::min(min, value);
}
}
return min;
}
int main()
{
constexpr double s1 = 1000.0;
constexpr double s2 = 400.0;
constexpr double s3 = 0.0;
constexpr double s4 = 10.0;
static_assert( min_no_zero({ s1, s2, s3, s4 }) == 10.0);
}

The std::min works as it should work if you have special requirements, you could pass a compare function with the following signature:
bool cmp(const Type1 &a, const Type2 &b);
as the second std::min function argument (the Compare template parameter):
#include <iostream>
#include <algorithm>
int main()
{
double s1 = 1000;
double s2 = 400;
double s3 = 300;
double s4 = 0;
auto const ignore_zero = [](auto const& a, auto const& b) -> bool {
if(0 == a)
{
return false;
}
else if(0 == b)
{
return true;
}
return a < b;
};
double minimum = std::min({s1, s2, s3, s4}, ignore_zero);
std::cout << minimum << "\n";
}
But it's strange, I suggest you if you have such special requirement with your code:
Try to redesign your program.
Use a function/functor helper:
#include <algorithm>
#include <iostream>
#include <vector>
namespace core::helper
{
template<typename T>
struct special_find final
{
std::vector<T> nums;
explicit special_find(std::initializer_list<T> nums_)
: nums(std::move(nums_))
{
}
auto min() const
{
return *std::min_element(nums.cbegin(), nums.cend());
}
special_find& ignore(T const& value)
{
nums.erase(std::remove_if(nums.begin(), nums.end(), [&value](auto const & item)
{
return item == value;
}), nums.end());
return *this;
}
};
} // namespace core::helper
int main()
{
double s1 = 1000;
double s2 = 400;
double s3 = 300;
double s4 = 0;
auto const min = core::helper::special_find({s1, s2, s3, s4}).ignore(0).min();
std::cout << min << "\n";
}

Make sure that you don't pass the 0s to min. I guess that's the only general answer without knowing how your input looks like.

Related

Numerical integration gives very different result from analytical expression

I'm trying to compute some thermal-averaged integral as defined in this reference. For the sake of the discussion, let's assume that the average of a quantity X looks like:
where M and T are parameters. Using Cubature, a simple C-package for adaptive multidimensional integration, I was able to implement these integrals.
The simplest case, X(k)=1, has the following analytical approximation I want to cross-check with my numerical integration:
where K2 is a modified Bessel function. Mathematica corroborated the approximation. The implementation of these numerical integrals (see below) seem to work well for dummy examples:
./main_nNeq 30 100
0.3 | 1.77268e+06 | 1.95712e+06
but my actual code would require very extreme values, where both values are quite different:
/main_nNeq 1e12 7.11258e17
1.40596e-06 | 4.92814e+46 | 7.19634e+53
Question: What could be the underlying issue here? Thanks!
My code (written in C++ for no particula reason) looks like this:
//
// COMPILING INSTRUCTIONS:
// g++ -o main_nNeq nNeq.cpp cubature-master/hcubature.c -lgsl -lm -lgslcblas -lgmp -std=c++11
//
// MORE INFO: http://ab-initio.mit.edu/wiki/index.php/Cubature_(Multi-dimensional_integration)
//
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <chrono>
#include <cmath>
#include <complex>
#include "cubature-master/cubature.h"
#include <gsl/gsl_sf_dilog.h>
#include <gsl/gsl_math.h>
#include <vector>
#include <algorithm>
#include <iterator>
#include <boost/math/special_functions/bessel.hpp>
using namespace std;
#define SQR(x) ((x)*(x)) //x^2
#define CUB(x) ((x)*(x)*(x)) //x^3
#define K1(x) (boost::math::cyl_bessel_k(1.0, x)) //BesselK(1, x)
#define K2(x) (boost::math::cyl_bessel_k(2.0, x)) //BesselK(2, x)
//Momentum grid
const double log_kmin = -2;
const double log_kmax = 25;
const int Ngrid = 2000;
//Numerical constants
const double gw = 2.0;
const double PI = M_PI;
const double a_int = 0.0;
//Cosmological parameters
const double g_star = 106.75;
const double Mpl = 1.22e19; //GeV
const double aR = Mpl/2.0*sqrt(45.0/CUB(PI)/g_star); //as in Eq. (83), arXiv:1812.02651
const double Tcom = aR;
#define aa(eta) (aR*eta) //as in Eq. (82), arXiv:1812.02651
//f_F
double f_F(long double k, long double T, long double M){
long double root = SQR(k) + SQR(M);
root = isinf(root) ? k : sqrt(root);
long double expo = exp(root/T);
return isinf(expo) ? 0.0 : 1.0/(expo+1.0);
}
//n_N_eq
long double n_N_eq(double T, double M){
return SQR(M)*T*K2(M/T);
}
//integrand
double integrand__n_N_eq(double k, double T, double M){
return SQR(k)*f_F(k, T, M);
}
//integrator
struct fparams {
double M;
double T;
};
//function to be integrated
int inf_n_N_eq(unsigned ndim, const double *x, void *fdata, unsigned fdim, double *fval){
struct fparams * fp = (struct fparams *)fdata;
//(void)(dim); /* avoid unused parameter warnings */
//(void)(params);
double M = fp->M;
double T = fp->T;
double t = x[0];
double aux = integrand__n_N_eq(a_int+t*pow(1.0-t, -1.0), T, M)*pow(1.0-t, -2.0);
if (!isnan(aux) && !isinf(aux))
{
fval[0] = aux;
}
else
{
fval[0] = 0.0;
}
return 0;
}
int main (int argc, char* argv[])
{
//Defining variables (M, T)
double M = stof(argv[1]); //command line argument
double T = stof(argv[2]); //command line argument
//range integration 1D
double xl[1] = { 0 };
double xu[1] = { 1 };
double nNeq, nNeq_ERR;
struct fparams params_nNeq = {M, T};
hcubature(1, inf_n_N_eq, &params_nNeq, 1, xl, xu, 0, 0, 1e-4, ERROR_INDIVIDUAL, &nNeq, &nNeq_ERR);
cout << M/T << " | " << nNeq << " | " << n_N_eq(T, M) << '\n';
return 0;
}

C++ - Convert vector<uint64> to a single number

There is a method to convert a std::vector<uint64> to a single number?
I have a vector like this:
v[0] = 0;
v[1] = 6796890219657246832;
Or like this:
v[0] = 16377;
v[1] = 2631694347470643681;
v[2] = 11730294873282192384;
The result I like to get is, in the first case 6796890219657246832 and in the second: 16377263169434747064368111730294873282192384. My main problem is choosing the data type for the value as it is possible that the vector size is not always 2 but it could be 5 or higher.
No C++ provided types will support that many digits. So obviously, you need BIG-INT for that. Ethier implemented by yourself or using a tested library like GMP.
For example, using GMP will be like:
static mpz_class convert_to_i(std::vector<std::size_t> const& vec)
{
std::string sum;
for (auto const number : vec) {
sum += std::to_string(number);
}
return mpz_class(sum);
}
Let the vec be:
std::vector<std::size_t> const vec = {
16377,
2631694347470643681,
1173029487328219238
};
Result of convert_to_i(vec) will be:
1637726316943474706436811173029487328219238
If your original numbers are also big-int:
static mpz_class convert_to_i(std::vector<mpz_class> const& vec)
{
std::string sum;
for (auto const& number : vec) {
sum += number.get_str();
}
return mpz_class(sum);
}
Let the vec_2 be:
std::vector<mpz_class> const vec_2 = {
mpz_class("26316943474706436812631694347470643681"),
mpz_class("263169434747064368126316943474706436812631694347470643681")
};
Result of convert_to_i(vec_2) will be:
26316943474706436812631694347470643681263169434747064368126316943474706436812631694347470643681
Just for copy/paste and test: the code.
The Boost.Multiprecision library can be used for computations with very large numbers. This will do what you want using that library:
#include <boost/multiprecision/cpp_int.hpp>
#include <vector>
#include <cstdint>
#include <iostream>
namespace mp = boost::multiprecision;
mp::cpp_int add_numbers(const std::vector<uint64_t> &numbers)
{
mp::cpp_int sum = 0;
size_t digits = 0;
for (std::vector<uint64_t>::const_reverse_iterator it = numbers.crbegin(); it != numbers.crend(); ++it)
{
mp::cpp_int num(*it);
sum += num * mp::pow(mp::cpp_int(10), digits);
digits += num.str().size();
}
return sum;
}
int main() {
std::vector<uint64_t> numbers = {16377U, 2631694347470643681U, 11730294873282192384U};
mp::cpp_int sum = add_numbers(numbers);
std::cout << sum << std::endl;
}
Output:
16377263169434747064368111730294873282192384
Live demo
use std::accumulate and generate a string.
std::string result = std::accumulate (
v.begin(),
v.end(),
std::string(),
[](const std::string &result, const uint64 &val) {
return result + std::to_string(val); }
);

C++: piecewise function with loop initialization into a map

Taking inspiration from: Defining a piecewise function (e.g. polynomial)
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
struct Point {
double x;
double y;
};
class LinearFunction {
public:
// Pre-calculate slope `m` and intercept `c`
LinearFunction(const Point &A, const Point &B){
double den = A.x-B.x;
m = (A.y-B.y)/den;
c = (A.x*B.y-A.y*B.x)/den;
}
// Evaluate at x
double operator()(double x){
return m*x+c;
}
private:
double m = 0;
double c = 0;
};
class PiecewiseFunction {
public:
// Initialize (m and c) for all segments of the piecewise
// function as a map of <lb,ub> -> function pointer
explicit PiecewiseFunction(std::vector<Point> &points){
for (int i = 0; i < points.size()-1; i++){
auto f = LinearFunction(points[i], points[i+1]);
fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), &f));
}
}
double operator()( double x ) {
// Match segment lb <= x < ub and evaluate
auto iter = std::find_if(fns.cbegin(), fns.cend(),
[=](const auto &fn) {return x>=fn.first.first && x<fn.first.second;});
if (iter == fns.end()){
return 0;
} else {
return iter->second->operator()(x);
}
}
private:
std::map< std::pair<double,double>, LinearFunction*> fns;
};
int main() {
std::vector<Point> points {{0,0},{0.5,1},{1,0}};
PiecewiseFunction f{points};
std::cout << "x = 0.5; f(x) = " << f(0.5) << std::endl;
return 0;
}
which produces undefined behaviour:
x = 0.5; f(x) = 1.04297e-309
as the auto f = LinearFunction(points[i], points[i+1]); created in the loop goes out of scope.
I have been trying to find a solution that would let me create a piecewise function with e.g. 20 points
As suggested in the comments, I should store a copy not a dangling reference (as I am initializing in a loop).
The line in the inner loop goes from:
fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), &f)); to
fns.insert(std::make_pair(std::make_pair(points[i].x, points[i+1].x), f));
and the variable declaration of fns, from:
std::map< std::pair<double,double>, LinearFunction *> fns; to:
std::map< std::pair<double,double>, LinearFunction> fns;
Finally,
return iter->second->operator()(x); to
return iter->second(x);

Determine conversion factors of strings describing units

In one of my project I need to determine the conversion factors of fairly complex units. I was able to write a static conversion function in case of statically defined units using the excellent boost library Boost.Units.
In my case the user enters the type of a conversion at run-time, so that I need a dynamic conversion function. A nice solution should use the already implemented functions in Boost.Units. Is this possible?
My own final solution
After some thoughts I was able to derive the following partial solution to my problem, which is sufficient for my needs. I'm relying on boost-spirit to parse the unit string, making this task indeed very easy. Great library!
Parsing unit strings might be a common task, that others might be interested in. Hence, I'm posting my final solution here including some tests for illustration.
The most important function is here convertUnit computing the conversion factor from one unit to another, if this conversion is possible.
UnitParser.cpp
#include "UnitParser.h"
#pragma warning(push)
#pragma warning(disable: 4512 4100 4503 4127 4348 4459)
#include <map>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <vector>
#include <algorithm>
using namespace boost;
namespace {
struct modifier_ : spirit::qi::symbols<char, int> {
modifier_() { add("m", -4)("c", -3)("k", 4); }
} modifier;
struct baseUnit_ : spirit::qi::symbols<char, UnitParser::UnitType> {
baseUnit_() {
add
("g", UnitParser::UnitType::GRAM)
("m", UnitParser::UnitType::METER)
("s", UnitParser::UnitType::SECONDS)
("rad", UnitParser::UnitType::RADIANS)
("deg", UnitParser::UnitType::DEGREE)
("N", UnitParser::UnitType::NEWTON)
;
}
} baseUnit;
class UnitParserImpl : public spirit::qi::grammar<std::string::iterator, UnitParser::Units()>
{
public:
UnitParserImpl() : UnitParserImpl::base_type(unitsTop_)
{
using namespace boost::spirit::qi;
unitsTop_ = units_.alias();
units_ = (unit_ % '*');
unit_ = (-(modifier >> &baseUnit) >> baseUnit >> -(lexeme["^"] >> int_ ))[_val = boost::phoenix::construct<UnitParser::Unit>(_2, _3, _1)];
}
spirit::qi::rule<std::string::iterator, UnitParser::Units()> unitsTop_;
spirit::qi::rule<std::string::iterator, UnitParser::Units()> units_;
spirit::qi::rule<std::string::iterator, UnitParser::Unit()> unit_;
};
}
boost::optional<UnitParser::Units> UnitParser::parse(const std::string& expression, std::string&& errorMessage)
{
boost::optional<UnitParser::Units> result;
try {
Units units;
std::string formula = expression;
auto b = formula.begin();
auto e = formula.end();
UnitParserImpl parser;
bool ok = spirit::qi::phrase_parse(b, e, parser, spirit::qi::space, units);
if (!ok || b != e) {
return result;
}
result = units;
return result;
}
catch (const spirit::qi::expectation_failure<std::string::iterator>& except) {
errorMessage = except.what();
return result;
}
}
std::map<UnitParser::UnitType, UnitParser::Dimension> dimMap() {
std::map<UnitParser::UnitType, UnitParser::Dimension> ret;
ret[UnitParser::UnitType::SECONDS] = UnitParser::Dimension({ 0,1,0,0 });
ret[UnitParser::UnitType::METER] = UnitParser::Dimension({ 1,0,0,0 });
ret[UnitParser::UnitType::DEGREE] = UnitParser::Dimension({ 0,0,1,0 });
ret[UnitParser::UnitType::RADIANS] = UnitParser::Dimension({ 0,0,1,0 });
ret[UnitParser::UnitType::GRAM] = UnitParser::Dimension({ 0,0,0,1 });
ret[UnitParser::UnitType::NEWTON] = UnitParser::Dimension({ 1,-2,0,1 });
return ret;
}
UnitParser::Dimension UnitParser::getDimension(const UnitParser::Units& units)
{
auto map = dimMap();
UnitParser::Dimension ret;
for (auto unit : units) {
if (map.find(unit.unitType) != map.end()) {
auto dim=map[unit.unitType];
auto exp = unit.exponent;
ret.length += exp*dim.length;
ret.time += exp*dim.time;
ret.weigth += exp*dim.weigth;
ret.planarAngle += exp*dim.planarAngle;
}
}
return ret;
}
bool UnitParser::equalDimension(const Units& u1, const Units& u2)
{
return getDimension(u1) == getDimension(u2);
}
bool UnitParser::checkDimension(const UnitParser::Units& u1, const UnitParser::Units& u2)
{
return true;
}
// Bezogen auf die Einheiten: m,s,kg,rad
std::pair<double,int> UnitParser::getScale(const Units& units)
{
double ret = 1.;
int exp = 0;
for (auto unit : units) {
double scale = 1;
int e = 0;
if (unit.unitType==UnitType::DEGREE) {
scale = 180./boost::math::constants::pi<double>();
}
if (unit.unitType == UnitType::GRAM) {
e = unit.exponent*(unit.modifier-4);
}
else {
e = unit.exponent*unit.modifier;
}
exp += e;
ret *= scale;
}
return{ ret, exp };
}
boost::optional<double> UnitParser::convertUnit(const std::string& unitString1, const std::string& unitString2, std::string&& errorMessage)
{
boost::optional<double> ret;
auto unit1 = parse(unitString1);
auto unit2 = parse(unitString2);
if (!unit1) { errorMessage = unitString1 + " is not valid!"; return ret; }
if (!unit2) { errorMessage = unitString2 + " is not valid!"; return ret; }
if (!equalDimension(*unit1, *unit2)) {
errorMessage = "Dimensions of " + unitString1 + " and " + unitString2 + " mismatch!"; return ret;
}
auto s1 = getScale(*unit1);
auto s2 = getScale(*unit2);
int exp = s1.second - s2.second;
double scale = s1.first / s2.first;
ret = scale*std::pow(10, exp);
return ret;
}
UnitParser.h
#pragma once
#include <boost/optional.hpp>
#include <vector>
namespace UnitParser {
enum class UnitType {
SECONDS, METER, DEGREE, RADIANS, GRAM, NEWTON
};
struct Unit {
Unit() {}
Unit(const UnitType& unitType, const boost::optional<int> exponent, const boost::optional<int>& modifier) : unitType(unitType), exponent(exponent.value_or(1)), modifier(modifier.value_or(0)) {}
UnitType unitType;
int exponent;
int modifier;
};
typedef std::vector<Unit> Units;
struct Dimension {
Dimension() {};
Dimension(int length, int time, int planarAngle, int weigth) : length(length), time(time), planarAngle(planarAngle), weigth(weigth) {}
int length = 0;
int time = 0;
int planarAngle = 0;
int weigth = 0;
bool operator==(const UnitParser::Dimension& dim) {
return length == dim.length && planarAngle == dim.planarAngle && time == dim.time && weigth == dim.weigth;
}
};
boost::optional<Units> parse(const std::string& string, std::string&& errorMessage=std::string());
Dimension getDimension(const Units& units);
bool equalDimension(const Units& u1, const Units& u2);
bool checkDimension(const Units& u1, const Units& u2);
std::pair<double,int> getScale(const Units& u1);
boost::optional<double> convertUnit(const std::string& unitString1, const std::string& unitString2, std::string&& errorMessage=std::string());
}
UnitParserCatch.cpp
#define CATCH_CONFIG_MAIN
#include "catch.h"
#include "UnitParser.h"
#include <boost/math/constants/constants.hpp>
using namespace UnitParser;
TEST_CASE("ConvertUnit", "[UnitParser]") {
SECTION("Simple") {
auto s = convertUnit("mm^2", "cm^2"); // 1*mm^2 = 0.01*cm^2
REQUIRE(s);
CHECK(*s == 0.01);
}
SECTION("Newton") {
auto s = convertUnit("N", "kg*m*s^-2");
REQUIRE(s);
CHECK(*s == 1.);
}
SECTION("Wrong") {
std::string err;
auto s = convertUnit("m", "m*kg", std::move(err));
REQUIRE(!s);
CHECK(!err.empty());
}
}
TEST_CASE("Dimension", "[UnitParser]") {
SECTION("Simple") {
auto a=*parse("mm^2");
auto dim=getDimension(a);
CHECK(dim == Dimension(2, 0, 0, 0));
}
SECTION("Newton") {
auto a = *parse("mN^2");
auto dim = getDimension(a);
CHECK(dim == Dimension(2, -4, 0, 2));
}
SECTION("Fits") {
auto a = *parse("mm^2");
auto b = *parse("cm^2");
auto fits = equalDimension(a, b);
CHECK(fits);
}
SECTION("Newton") {
auto a = *parse("N");
auto b = *parse("kg*m*s^-2");
auto fits = equalDimension(a, b);
CHECK(fits);
}
SECTION("NoFit") {
auto a = *parse("mm^2*g");
auto b = *parse("cm^2");
auto fits = equalDimension(a, b);
CHECK(!fits);
}
}
TEST_CASE("Scale", "[UnitParser]") {
SECTION("Length") {
auto s = getScale(*parse("mm^2")); // 1*mm^2=1e-8*m^2
CHECK(s == std::make_pair(1., -8));
}
SECTION("Degree") {
auto s = getScale(*parse("deg"));
CHECK(s == std::make_pair(180. / boost::math::constants::pi<double>(),0));
}
SECTION("Complex") {
auto s = getScale(*parse("km^2*kg"));
CHECK(s == std::make_pair(1., 8));
}
}
TEST_CASE("Simple", "[UnitParser]") {
SECTION("Complex") {
SECTION("Full") {
auto u = parse("mm^2");
CHECK(u);
}
SECTION("Many") {
auto u = parse("mm^2*ms^-1");
CHECK(u);
}
}
SECTION("Units") {
SECTION("Newton") {
auto u = parse("N");
CHECK(u);
}
SECTION("Meter") {
auto u = parse("m");
CHECK(u);
}
SECTION("Seconds") {
auto u = parse("s");
CHECK(u);
SECTION("Exponent") {
CHECK(parse("s^2"));
CHECK(parse("ms^-2"));
CHECK(parse("ks^-3"));
}
}
SECTION("PlanarAngle") {
auto u = parse("deg");
CHECK(u);
}
}
}

Custom Functor in std::set

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int order[26];
struct lexcmp
{
bool operator()(const string &s1,const string &s2)
{
int i=0;
int j=min(s1.size(),s2.size());
while(1)
{
if(order[s1[i]-'a']<order[s2[i]-'a'])
return true;
if(order[s1[i]-'a']>order[s2[i]-'a'])
return false;
if(i==j-1)
return false;
i++;
}
}
};
int main()
{
string s;
cin>>s;
for(int i=0;i<s.size();i++)
{
order[s[i]-'a']=i;
}
set<string,lexcmp> store;
int m;
cin>>m;
while(m--)
{
string q;
cin>>q;
store.insert(q);
}
for(auto i=store.begin();i!=store.end();i++)
{
cout<<*i<<endl;
}
}
return 0;
}
Problem in making the Custom Functor
The problem is, i have a new order of elements (instead of simple a-z). //Saved in order array
All i want is order the given strings on the based of new order.
for eg: Order is : bacdefghijklmnopqrstuvwxyz
So if the strings are ss , aa , bb
The new ordering will be bb,aa,ss.
The Code is working fine but it is giving me a problem while the strings are like "pas" "p" to be compared.
p should come before pas but it is coming after.
What modifications should i do in the functor?
Here's one approach:
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <numeric>
#include <array>
#include <string>
#include <locale>
struct lexcmp {
lexcmp() { std::iota(order_.begin(), order_.end(), std::int_fast8_t{}); }
explicit lexcmp(std::string const& order) {
assert(order.size() == order_.size());
for (std::size_t i{}; i != order_.size(); ++i) {
char const order_letter = order[i];
assert(std::isalpha(order_letter, std::locale::classic()));
assert(std::islower(order_letter, std::locale::classic()));
order_[i] = order_letter - 'a';
}
auto unique_order_letters = [this]{
auto order = order_;
std::sort(order.begin(), order.end());
return order.end() - std::unique(order.begin(), order.end()) == 0;
};
assert(unique_order_letters());
}
bool operator ()(std::string const& a, std::string const& b) const {
auto const a_len = a.size(), b_len = b.size();
std::size_t i{};
for (auto const len = std::min(a_len, b_len); i != len; ++i) {
if (auto const diff = order_[a[i] - 'a'] - order_[b[i] - 'a']) {
return diff < 0;
}
}
return i == a_len && i != b_len;
}
private:
std::array<std::int_fast8_t, 26> order_;
};
Online Demo