Wrong behaviour of set<Vec3b> - c++

I have a set of Vec3b to hold posible RGB pixel values.
std::set<cv::Vec3b> used_colors;
But behaves weird:
used_colors.insert(cv::Vec3b(100, 255, 255));
// this returns 1 although (100, 0, 0) is NOT in the set
used_colors.count(cv::Vec3b(100, 0, 0));
The value (100, 0, 0) is found because other value starting with 100 is already inserted in the set. Other values like (80, 0, 0) can not be found. This is obviously the wrong weird behaviour.
I implemented the < comparision operator like this:
bool operator <(const cv::Vec3b &a, const cv::Vec3b &b) {
if(a[0] < b[0])
return true;
if(a[0] > b[0]);
return false;
if(a[1] < b[1])
return true;
if(a[1] > b[1]);
return false;
if(a[2] < b[2])
return true;
if(a[2] > b[2]);
return false;
return false;
}

Your operator< is broken due to errant semicolons after several of the if statements.
Consider the inputs a = Vec3b(100, 255, 255) and b = Vec3b(100, 0, 0). Because the R value of both is 100 the test makes it to
if(a[0] > b[0]); // <-- notice the semicolon?
Because of that trailing semicolon the function unconditionally returns false. Due to the same reason, the comparison b < a also returns false; and set::count thinks the element already exists.
Get rid of the trailing semicolons and your comparison operator works as expected.
Instead of manually writing all those comparisons to do lexographical ordering, an easier, less error prone way is to use std::tie
bool operator<(const cv::Vec3b &a, const cv::Vec3b &b)
{
return std::tie(a[0], a[1], a[2]) < std::tie(b[0], b[1], b[2]);
}

Your comparison function is stuffed, probably because you have ; after a number of your if statements.
Even then, this is far more complex than it needs to be. std::tie makes this into a one-liner:
bool operator <(const cv::Vec3b &a, const cv::Vec3b &b)
{
return std::tie(a[0], a[1], a[2]) < std::tie(b[0], b[1], b[2]);
}

Related

Binary Division using Templated Big Integers

I am trying to implement a Division Binary Algorithm.
The code has some Logical Errors which I am still trying to figure out how to fix them.
myuint operator/(const myuint<T>& x)
{
myuint<T> temp;
myuint<T> result;
int count = 0;
for(int i = bits.size() - 1 ; i >= 0; --i)
{
temp.bits.push_back(bits[i]);
if(temp >= x)
{
count++;
*this = *this - x;
temp = 0;
}
}
result = count;
return result;
}
I am also overloading the >=, >, and == operators for the division.
The logical problem most probably is in the for loop . What should I do? Thanks
Full code can be accessible from here
== EDIT
What I am trying to achieve is this.
*this is 10100 (20 in decimal)
x is 100 (4 in decimal)
Get the first Bit (1).
Compare it to x
If the bit is greater than the value of x, count++, subtract x from *this. And then Start the loop again which a different *this size.
If the bit is small, then we move to the bit next to it so, now we have 2 bits (10) and we compare it to x.
Then I return the value of count which represents this number of divisions to reach 0.
Not a full answer, but here is the algorithm that you need to implement:
myuint div(const myuint& x, const myuint& y)
{
if (y == 0)
throw "division by zero";
myuint res = 0;
myuint one = 1;
unsigned int xLength = x.bitLength();
unsigned int yLength = y.bitLength();
while (xLength > yLength)
{
res += one << (xLength - yLength - 1);
x -= y << (xLength - yLength - 1);
xLength = x.bitLength();
}
if (x >= y)
return res+1;
return res;
}
So, I found a simple implementation of how to go around Binary Division.
The idea is that you subtract the LSH from the RHS until LHS is smaller RHS and keep a hold for the many times you subtracted RHS from LSH.
myuint operator/(const myuint<T>& x)
{
myuint<T> LHS = *this;
myuint<T> RHS = x;
myuint<T> result;
int count = 0;
bool flag = true;
if(LHS == RHS)
{
return 1;
}
else
{
do
{
if(LHS >= RHS)
{
LHS = LHS - RHS;
count++;
}
else if(LHS < RHS)
{
flag = false;
}
}while(flag);
}
result = count;
return result;
}
This may not be the most efficient way. But it gets the job done.

Is it safe to use zero as second index in accessing Eigen::VectorXd?

Eigen::VectorXd has an Scalar operator()(Index i) which returns the coefficient at the index i in the vector. However, since Eigen::VectorXd is a special type of an Eigen::Matrix, i.e. of type Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;, there is also an Scalar operator()(Index i, Index j).
Question:
Can I assume that it is safe (i.e. no undefined behaviour) to use the second version if I set j to zero? In other words, is the code below OK?
Eigen::VectorXd v(4);
v << 1, 2, 3, 4;
std::cout << v(2, 0); // displays 3
It looks like it's OK, there are no failed assertions or warnings when compiled in debug mode with all warnings on, but I am not 100% sure.
It is safe as long as v is a column vector, whereas using v(i) works for both column and row vectors, e.g.:
template<typename T>
void foo(const T &v) {
v(2); // OK
v(2,0); // -> out of bounds runtime assertion
}
MatrixXd mat(10,10);
foo(mat.row(5));
I'll expound upon #ggaels answer. If you look at the operator() definitions in DenseCoeffsBase.h (I'm quoting 3.2.10) you'll see that they both call coeff (or coeffRef)
EIGEN_STRONG_INLINE CoeffReturnType operator()(Index row, Index col) const
{
eigen_assert(row >= 0 && row < rows()
&& col >= 0 && col < cols());
return derived().coeff(row, col);
}
EIGEN_STRONG_INLINE CoeffReturnType
operator()(Index index) const
{
eigen_assert(index >= 0 && index < size());
return derived().coeff(index);
}
Looking at the definitions of coeffRef in PlainObjectBase.h we see that the offset is calculated simply:
EIGEN_STRONG_INLINE Scalar& coeffRef(Index rowId, Index colId)
{
if(Flags & RowMajorBit)
return m_storage.data()[colId + rowId * m_storage.cols()];
else // column-major
return m_storage.data()[rowId + colId * m_storage.rows()];
}
EIGEN_STRONG_INLINE Scalar& coeffRef(Index index)
{
return m_storage.data()[index];
}
So in the case of a row vector, you would have to write v(0,2) to avoid possible assertions failures/out of bounds errors.

Best way to to average duplicate properties in C++ vector

I have a std::vector<PLY> that holds a number of structs:
struct PLY {
int x;
int y;
int greyscale;
}
Some of the PLY's could be duplicates in terms of their position x and y but not necessarily in terms of their greyscale value. What is the best way to find those (position-) duplicates and replace them with a single PLY instace which has a greyscale value that represents the average greyscale of all duplicates?
E.g: PLY a{1,1,188} is a duplicate of PLY b{1,1,255}. Same (x,y) position possibly different greyscale.
Based on your description of Ply you need these operators:
auto operator==(const Ply& a, const Ply& b)
{
return a.x == b.x && a.y == b.y;
}
auto operator<(const Ply& a, const Ply& b)
{
// whenever you can be lazy!
return std::make_pair(a.x, a.y) < std::make_pair(b.x, b.y);
}
Very important: if the definition "Two Ply are identical if their x and y are identical" is not general valid, then defining comparator operators that ignore greyscale is a bad ideea. In that case you should define separate function objects or non-operator functions and pass them around to function.
There is a nice rule of thumb that a function should not have more than a loop. So instead of a nested 2 for loops, we define this helper function which computes the average of consecutive duplicates and also returns the end of the consecutive duplicates range:
// prereq: [begin, end) has at least one element
// i.e. begin != end
template <class It>
auto compute_average_duplicates(It begin, It end) -> std::pair<int, It>
// (sadly not C++17) concepts:
//requires requires(It i) { {*i} -> Ply; }
{
auto it = begin + 1;
int sum = begin->greyscale;
for (; it != end && *begin == *it; ++it) {
sum += it->greyscale;
}
// you might need rounding instead of truncation:
return std::make_pair(sum / std::distance(begin, it), it);
}
With this we can have our algorithm:
auto foo()
{
std::vector<Ply> v = {{1, 5, 10}, {2, 4, 6}, {1, 5, 2}};
std::sort(std::begin(v), std::end(v));
for (auto i = std::begin(v); i != std::end(v); ++i) {
decltype(i) j;
int average;
std::tie(average, j) = compute_average_duplicates(i, std::end(v));
// C++17 (coming soon in a compiler near you):
// auto [average, j] = compute_average_duplicates(i, std::end(v));
if (i + 1 == j)
continue;
i->greyscale = average;
v.erase(i + 1, j);
// std::vector::erase Invalidates iterators and references
// at or after the point of the erase
// which means i remains valid, and `++i` (from the for) is correct
}
}
You can apply lexicographical sorting first. During sorting you should take care of overflowing greyscale. With current approach you will have some roundoff error, but it will be small as i first sum and only then average.
In the second part you need to remove duplicates from the array. I used additional array of indices to copy every element not more than once. If you have some forbidden value for x, y or greyscale you can use it and thus get along without additional array.
struct PLY {
int x;
int y;
int greyscale;
};
int main()
{
struct comp
{
bool operator()(const PLY &a, const PLY &b) { return a.x != b.x ? a.x < b.x : a.y < b.y; }
};
vector<PLY> v{ {1,1,1}, {1,2,2}, {1,1,2}, {1,3,5}, {1,2,7} };
sort(begin(v), end(v), comp());
vector<bool> ind(v.size(), true);
int s = 0;
for (int i = 1; i < v.size(); ++i)
{
if (v[i].x == v[i - 1].x &&v[i].y == v[i - 1].y)
{
v[s].greyscale += v[i].greyscale;
ind[i] = false;
}
else
{
int d = i - s;
if (d != 1)
{
v[s].greyscale /= d;
}
s = i;
}
}
s = 0;
for (int i = 0; i < v.size(); ++i)
{
if (ind[i])
{
if (s != i)
{
v[s] = v[i];
}
++s;
}
}
v.resize(s);
}
So you need to check, is PLY a1 { 1,1,1 }; duplicates PLY a2 {2,2,1};
So simple method is to override operator == to check a1.x == a2.x and a1.y == a2.y. After you can write own function removeDuplicates(std::vector<PLU>& mPLY); which will use iterators of this vector, compare and remove. But better to use std::list if you want to remove from middle of array too frequently.

how to compare structs

I am having difficulties to set up the comparison correctly.
Here is an example of my problem, where my code wrongly assumes {1,2}={2,1}: http://ideone.com/i7huL
#include <iostream>
#include <map>
using namespace std;
struct myStruct {
int a;
int b;
bool operator<(const myStruct& rhs) const {
return rhs.a < this->a && rhs.b < this->b;
}
};
int main() {
std::map <myStruct, int> mymap ;
myStruct m1={1,2};
myStruct m2={2,1};
mymap.insert(make_pair(m1,3));
std::map<myStruct, int>::iterator it1 = mymap.find(m1);
std::map<myStruct, int>::iterator it2 = mymap.find(m2);
cout << it1->second << it2->second;
// here it1->second=it2->second=3, although I would have expected it2 to be equal to map.end().
}
I could use || instead of &&, but I'm not sure this is the correct way either. I just want to have operator< implemented in such a way that I am able to find objects in my map, without making any errors, as is the case in the code I linked to.
Thanks.
Yes, this operator implementation doesn't make much sense. I'd recommend:
bool operator<(const myStruct& rhs) const {
return rhs.a < this->a || (rhs.a == this->a && rhs.b < this->b);
}
bool operator<(const myStruct& rhs) const {
if (a < rhs.a) return true;
if (a == rhs.a) return b < rhs.b;
return false;
}
If you are looking for a generalization to many data members, there is a great example using C++11 std::tie:
struct S {
int n;
std::string s;
float d;
bool operator<(const S& rhs) const {
return std::tie(n, s, d) < std::tie(rhs.n, rhs.s, rhs.d);
}
};
The problem is that your operator does not define a strict weak ordering. Think through your how your example of {1,2} and {2,1} would go down in your operator. Assume X = {1,2}, and Y = {2,1}.
Is X < Y? Is 1 < 2 AND 2 < 1? No, therefore X is not less than Y.
Is Y < X? Is 2 < 1 AND 1 < 2? No, therefore Y is not less than X.
So, if X is not less than Y, and Y is not less than X, what's left? They're equal.
You need to pick one of the members of your struct, either a or b to be the primary comparison. If the primary comparison results in equality, only then do you check the secondary comparison. Just like when you alphabetize something. First you check the first letter, and only if they are equal do you go on to the next. Hans Passant has provided an example of this.
Here's a more serious problem example for your operator. The one I gave above is not necessarily bad, because maybe you want {1,2} to be considered equal to {2,1}. The fundamental problem crops with a set of values like this: consider X = {1,1}, Y = {1,2}, Z = {2,2}
With your operator, X is definitively less than Z, because 1 is less than 2. But X comes out equal to Y, and Y comes out equal to Z. In order to adhere to strict weak ordering, if X = Y, and Y = Z, then X should equal Z. But here that is not the case.
You asked about generalising to four int members, here's how I would structure such code for maximum clarity.
bool operator<(const myStruct& rhs) const
{
if (a < rhs.a)
return true;
if (a > rhs.a)
return false;
if (b < rhs.b)
return true;
if (b > rhs.b)
return false;
if (c < rhs.c)
return true;
if (c > rhs.c)
return false;
if (d < rhs.d)
return true;
if (d > rhs.d)
return false;
return false;
}
You can easily extend such code for as many data members as you wish.
The simplest solution uses std::tie to compare the tuples.
return std::tie(rhs.a, rhs.b) < std::tie(a, b);
This generalizes very quickly and simply to more data members.
I prefer to write this by comparing elements for equality until two are found that are different:
bool operator<(const myStruct& rhs) const {
if (a != rhs.a)
return a < rhs.a;
if (b != rhs.b)
return b < rhs.b;
return false; // this and rhs are equal.
}
I find this clearer and more extensible than writing a single expression with a mix of || and && (as per #HansPassant), and more compact than #jahhaj's approach of having each passing test lead to a return true; or return false;. Performance is about the same, unless you know something about the distribution of values. There is an argument for avoiding operator==() and just using operator<(), but that only applies if you are trying to write maximally generic template code.
Problem is that you need to know what your structure represents. Otherwise defining a < operator would just become arbitrary. Others won't be able to give you a fitting answer. Take an example where when your structure represents a cartisian coordinate of a point in 2D. In this case you could define a meaningful ordering operator such as the distance from the origin for the structure.
i.e, distance d1 = this->a*this->a + this->b*this->b
distance d2 = rhs.a*rhs.a + rhs.b*rhs.b
if(d1 < d2)
return true;
else
return false;

Neatest / Fastest Algorithm for Smallest Positive Number

Simple question - In c++, what's the neatest way of getting which of two numbers (u0 and u1) is the smallest positive number? (that's still efficient)
Every way I try it involves big if statements or complicated conditional statements.
Thanks,
Dan
Here's a simple example:
bool lowestPositive(int a, int b, int& result)
{
//checking code
result = b;
return true;
}
lowestPositive(5, 6, result);
If the values are represented in twos complement, then
result = ((unsigned )a < (unsigned )b) ? a : b;
will work since negative values in twos complement are larger, when treated as unsigned, than positive values. As with Jeff's answer, this assumes at least one of the values is positive.
return result >= 0;
I prefer clarity over compactness:
bool lowestPositive( int a, int b, int& result )
{
if (a > 0 && a <= b) // a is positive and smaller than or equal to b
result = a;
else if (b > 0) // b is positive and either smaller than a or a is negative
result = b;
else
result = a; // at least b is negative, we might not have an answer
return result > 0; // zero is not positive
}
Might get me modded down, but just for kicks, here is the result without any comparisons, because comparisons are for whimps. :-)
bool lowestPositive(int u, int v, int& result)
{
result = (u + v - abs(u - v))/2;
return (bool) result - (u + v + abs(u - v)) / 2;
}
Note: Fails if (u + v) > max_int. At least one number must be positive for the return code to be correct. Also kudos to polythinker's solution :)
unsigned int mask = 1 << 31;
unsigned int m = mask;
while ((a & m) == (b & m)) {
m >>= 1;
}
result = (a & m) ? b : a;
return ! ((a & mask) && (b & mask));
EDIT: Thought this is not so interesting so I deleted it. But on the second thought, just leave it here for fun :) This can be considered as a dump version of Doug's answer :)
Here's a fast solution in C using bit twiddling to find min(x, y). It is a modified version of #Doug Currie's answer and inspired by the answer to the Find the Minimum Positive Value question:
bool lowestPositive(int a, int b, int* pout)
{
/* exclude zero, make a negative number to be larger any positive number */
unsigned x = (a - 1), y = (b - 1);
/* min(x, y) + 1 */
*pout = y + ((x - y) & -(x < y)) + 1;
return *pout > 0;
}
Example:
/** gcc -std=c99 *.c && a */
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
void T(int a, int b)
{
int result = 0;
printf("%d %d ", a, b);
if (lowestPositive(a, b, &result))
printf(": %d\n", result);
else
printf(" are not positive\n");
}
int main(int argc, char *argv[])
{
T(5, 6);
T(6, 5);
T(6, -1);
T(-1, -2);
T(INT_MIN, INT_MAX);
T(INT_MIN, INT_MIN);
T(INT_MAX, INT_MIN);
T(0, -1);
T(0, INT_MIN);
T(-1, 0);
T(INT_MIN, 0);
T(INT_MAX, 0);
T(0, INT_MAX);
T(0, 0);
return 0;
}
Output:
5 6 : 5
6 5 : 5
6 -1 : 6
-1 -2 are not positive
-2147483648 2147483647 : 2147483647
-2147483648 -2147483648 are not positive
2147483647 -2147483648 : 2147483647
0 -1 are not positive
0 -2147483648 are not positive
-1 0 are not positive
-2147483648 0 are not positive
2147483647 0 : 2147483647
0 2147483647 : 2147483647
0 0 are not positive
This will handle all possible inputs as you request.
bool lowestPositive(int a, int b, int& result)
{
if ( a < 0 and b < 0 )
return false
result = std::min<unsigned int>( a, b );
return true;
}
That being said, the signature you supply allows sneaky bugs to appear, as it is easy to ignore the return value of this function or not even remember that there is a return value that has to be checked to know if the result is correct.
You may prefer one of these alternatives that makes it harder to overlook that a success result has to be checked:
boost::optional<int> lowestPositive(int a, int b)
{
boost::optional<int> result;
if ( a >= 0 or b >= 0 )
result = std::min<unsigned int>( a, b );
return result;
}
or
void lowestPositive(int a, int b, int& result, bool &success)
{
success = ( a >= 0 or b >= 0 )
if ( success )
result = std::min<unsigned int>( a, b );
}
tons of the answers here are ignoring the fact that zero isn't positive :)
with tricky casting and tern:
bool leastPositive(int a, int b, int& result) {
result = ((unsigned) a < (unsigned) b) ? a : b;
return result > 0;
}
less cute:
bool leastPositive(int a, int b, int& result) {
if(a > 0 && b > 0)
result = a < b ? a : b;
else
result = a > b ? a : b:
return result > 0;
}
I suggest you refactor the function into simpler functions. Furthermore, this allows your compiler to better enforce expected input data.
unsigned int minUnsigned( unsigned int a, unsigned int b )
{
return ( a < b ) ? a : b;
}
bool lowestPositive( int a, int b, int& result )
{
if ( a < 0 && b < 0 ) // SO comments refer to the previous version that had || here
{
return false;
}
result = minUnsigned( (unsigned)a, (unsigned)b ); // negative signed integers become large unsigned values
return true;
}
This works on all three signed-integer representations allowed by ISO C:
two's complement, one's complement, and even sign/magnitude. All we care about is that any positive signed integer (MSB cleared) compares below anything with the MSB set.
This actually compiles to really nice code with clang for x86, as you can see on the Godbolt Compiler Explorer. gcc 5.3 unfortunately does a much worse job.
Hack using "magic constant" -1:
enum
{
INVALID_POSITIVE = -1
};
int lowestPositive(int a, int b)
{
return (a>=0 ? ( b>=0 ? (b > a ? a : b ) : INVALID_POSITIVE ) : INVALID_POSITIVE );
}
This makes no assumptions about the numbers being positive.
Pseudocode because I have no compiler on hand:
////0 if both negative, 1 if u0 positive, 2 if u1 positive, 3 if both positive
switch((u0 > 0 ? 1 : 0) + (u1 > 0 ? 2 : 0)) {
case 0:
return false; //Note that this leaves the result value undef.
case 1:
result = u0;
return true;
case 2:
result = u1;
return true;
case 3:
result = (u0 < u1 ? u0 : u1);
return true;
default: //undefined and probably impossible condition
return false;
}
This is compact without a lot of if statements, but relies on the ternary " ? : " operator, which is just a compact if, then, else statement. "(true ? "yes" : "no")" returns "yes", "(false ? "yes" : "no") returns "no".
In a normal switch statement after every case you should have a break;, to exit the switch. In this case we have a return statement, so we're exiting the entire function.
With all due respect, your problem may be that the English phrase used to describe the problem really does hide some complexity (or at least some unresolved questions). In my experience, this is a common source of bugs and/or unfulfilled expectations in the "real world" as well. Here are some of the issues I observed:
Some programmers use a naming
convention in which a leading u
implies unsigned, but you didn't
state explicitly whether your
"numbers" are unsigned or signed
(or, for that matter, whether they
are even supposed to be integral!)
I suspect that all of us who read it
assumed that if one argument is
positive and the other is not, then
the (only) positive argument value
is the correct response, but that is
not explicitly stated.
The description also doesn't define
the required behavior if both values
are non-positive.
Finally, some of the responses
offered prior to this post seem to
imply that the responder thought
(mistakenly) that 0 is positive! A
more specific requirements statement
might help prevent any
misunderstanding (or make it clear
that the issue of zero hadn't been
thought out completely when the
requirement was written).
I'm not trying to be overly critical; I'm just suggesting that a more precisely-written requirement will probably help, and will probably also make it clear whether some of the complexity you're concerned about in the implementation is really implicit in the nature of the problem.
Three lines with the use (abuse?) of the ternary operator
int *smallest_positive(int *u1, int *u2) {
if (*u1 < 0) return *u2 >= 0 ? u2 : NULL;
if (*u2 < 0) return u1;
return *u1 < *u2 ? u1 : u2;
}
Don't know about efficiency or what to do if both u1 and u2 are negative. I opted to return NULL (which has to be checked in the caller); a return of a pointer to a static -1 might be more useful.
Edited to reflect the changes in the original question :)
bool smallest_positive(int u1, int u2, int& result) {
if (u1 < 0) {
if (u2 < 0) return false; /* result unchanged */
result = u2;
} else {
if (u2 < 0) result = u1;
else result = u1 < u2 ? u1 : u2;
}
return true;
}
uint lowestPos(uint a, uint b) { return (a < b ? a : b); }
You are looking for the smallest positive, it is be wise to accept positive values only in that case. You don't have to catch the negative values problem in your function, you should solve it at an earlier point in the caller function. For the same reason I left the boolean oit.
A precondition is that they are not equal, you would use it like this in that way:
if (a == b)
cout << "equal";
else
{
uint lowest = lowestPos(a, b);
cout << (lowest == a ? "a is lowest" : "b is lowest");
}
You can introduce const when you want to prevent changes or references if you want to change the result. Under normal conditions the computer will optimize and even inline the function.
No cleverness, reasonable clarity, works for ints and floats:
template<class T>
inline
bool LowestPositive( const T a, const T b, T* result ) {
const bool b_is_pos = b > 0;
if( a > 0 && ( !b_is_pos || a < b ) ) {
*result = a;
return true;
}
if( b_is_pos ) {
*result = b;
return true;
}
return false;
}
Note that 0 (zero) is not a positive number.
OP asks for dealing with numbers (I interpret this as ints and floats).
Only dereference result pointer if there is a positive result (performance)
Only test a and b for positiveness once (performance -- not sure if such a test is expensive?)
Note also that the accepted answer (by tvanfosson) is wrong. It fails if a is positive and b is negative (saying that "neither is positive"). (This is the only reason I add a separate answer -- I don't have reputation enough to add comments.)
My idea is based on using min and max. And categorized the result into three cases, where
min <= 0 and max <= 0
min <= 0 and max > 0
min > 0 and max > 0
The best thing is that it's not look too complicated.
Code:
bool lowestPositive(int a, int b, int& result)
{
int min = (a < b) ? a : b;
int max = (a > b) ? a : b;
bool smin = min > 0;
bool smax = max > 0;
if(!smax) return false;
if(smin) result = min;
else result = max;
return true;
}
After my first post was rejected, allow me to suggest that you are prematurely optimizing the problem and you shouldn't worry about having lots of if statements. The code you're writing naturally requires multiple 'if' statements, and whether they are expressed with the ternary if operator (A ? B : C) or classic if blocks, the execution time is the same, the compiler is going to optimize almost all of the code posted into very nearly the same logic.
Concern yourself with the readability and reliability of your code rather than trying to outwit your future self or anyone else who reads the code. Every solution posted is O(1) from what I can tell, that is, every single solution will contribute insignificantly to the performance of your code.
I would like to suggest that this post be tagged "premature optimization," the poster is not looking for elegant code.