It's been a while since I used C++. I was asked for job interview to create a C++ struct for a downsampling routine which would meet the following interface:
struct deterministic_sample
{
deterministic_rate( double rate );
bool operator()();
};
-- with the following behaviour:
We have an object of that class: deterministic_sample s;
We call s() N times, and it returns true, M times. M / N is roughly equal to the rate
The sequence is deterministic, not random and should be the same each time
The class should be "industrial strength", for use on a busy stream.
My solution, version 2:
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
struct deterministic_sample
{
double sampRate;
int index;
deterministic_sample() {
sampRate = 0.1;
index = 0;
}
void deterministic_rate( double rate ) {
this->sampRate = rate; // Set the ivar. Not so necessary to hide data, but just complying with the interface, as given...
this->index = 0; // Reset the incrementer
};
bool operator()() {
if (this->index == INT_MAX) {
this->index = 0;
}
double multiple = this->index * this->sampRate;
this->index++; // Increment the index
if (fmod(multiple, 1) < this->sampRate) {
return true;
} else {
return false;
}
};
};
int main()
{
deterministic_sample s; // Create a sampler
s.deterministic_rate(0.253); // Set the rate
int tcnt = 0; // Count of True
int fcnt = 0; // Count of False
for (int i = 0; i < 10000; i++) {
bool o = s();
if (o) {
tcnt++;
} else {
fcnt++;
}
}
cout << "Trues: " << tcnt << endl;
cout << "Falses: " << fcnt << endl;
cout << "Ratio: " << ((float)tcnt / (float)(tcnt + fcnt)) << endl; // Show M / N
return 0;
}
The interviewer said this v2 code "partly" addressed the requirements. v1 didn't have the constructor (my error), and didn't deal with overflow of the int ivar.
What have I missed here to make this class robust/correct? I think it is some aspect of "industrial strength" that I've missed.
ps. for any ethical types, I've already submitted my second-chance attempt... It's just bothering me to know why this was "partly"...
What you have is far more complex than necessary. All you need to do is keep track of the current position, and return true when it goes past the threshold.
struct deterministic_sample
{
double sampRate;
double position;
deterministic_sample() : sampRate(0.1), position(0.0) {
}
void deterministic_rate( double rate ) {
assert(rate <= 1.0); // Only one output is allowed per input
sampRate = rate; // Set the ivar. Not so necessary to hide data, but just complying with the interface, as given...
// No need to reset the position, it will work with changing rates
};
bool operator()() {
position += sampRate;
if (position < 1.0)
return false;
position -= 1.0;
return true;
}
};
Use unsigned and integer overflow is a well-defined wraparound. This is very fast on normal CPU's.
The second problem I see is the mix of floating-point and integer math. That's not really efficient. It may be more efficient to store multiple as a member and just do multiple += rate. This saves you one integer to double conversion.
However, the fmod is still quite expensive. You can avoid that by keeping int trueSoFar instead. Now the rate so far is double(trueSoFar)/double(index) and you can check double(trueSoFar)/double(index) > rate or more efficiently trueSoFar> int(index * rate). As we already saw, rate*index can be replaced by multiple += rate.
This means we have one double addition (multiple +=), one FP to int conversion int(multiple) and one integer comparison.
[edit]
You can also avoid FP math altogether by keeping a 32/32 rational approximation of rate, and comparing that to the realised rate (again stored as a 32/32 ratio). Since a/b > c/d when a*d > b*c you can use a 64 bit multiply here. Even better, for the target ratio you can choose 2^32 as a fixed denominator (i.e. unsigned long targetRate = rate*pow(2^32), b=2^32 implicit) so that you now have unsigned long(unsigned long long(a)*index) >> 32) > trueSoFar. Even on a 32 bit CPU, this is fairly quick. >>32 is a no-op there.
OK, so it seems there are some improvements to the efficiency which could be made (certainly), that "industrial strength" has some implications though nothing concrete (possibly the problem...), or that the constructor was incorrectly named in the question (also possible).
In any case, no one has jumped on some glaring omission that I made to my constructor (like, I see there are two ways to do a C++ constructor; you should do both to be really bullet-proof, etc.)
I guess I'll just cross my fingers and hope I still progress to the soft-skills interview!
Thanks all.
Related
At present I am working on piece of C++ code in which I need to read data from a database, and if database value is non-zero, then I need to apply some further logic.
But in the database there are values which are being calculated and can come out as -0.0. And this negative zero is being treated as Garbage value in C++ double variable. I have already initialized the value as 0.0 in constructor.
Sample Code:
for(Sample::List<BalanceSheet>::Iterator i((Sample::List<BalanceSheet> &) Balance.Entries()); i.HaveItem(); ++i) // This list is being populated from Database
{
if (SomeCondition != "")
{
if (i->GetBalance() != 0) // This is where am getting Garbage values since GetBalance() returns -0.0
{
DoOperation();
}
}
}
-0.0 is perfectly valid value for a double. The problem you are having is that you are comparing doubles for inequality.
What you should do is something like this:
i->GetBalance() > std::numeric_limits<double>::epsilon()
First off, you should never be using == or != with floating point variables. They are essentially meaningless operations, as the limitations of floating point types mean that even seemingly innocuous values might not compare identically. It is completely possible that 2 + 2 isn't 4, at least as far as == would identify it.
The real issue here is that you are making use of the sign of a "zero" value, which as per above, might not actually be exactly zero in the first place, but more importantly, is difficult to test for using standard comparison operators. See this related question for some discussion.
The best solution for this, if you have access to C++11 or a compiler supporting it, is to use copysign as per Vlad's answer on that question. This function takes 2 parameters. The first represents the magnitude of the return value, and the second the sign. Here is an example:
#include "iostream"
#include <math.h>
using namespace std;
int main()
{
double posZero = +0.0d;
double negZero = -0.0d;
if( copysign( 1, posZero ) < 0 )
{
cout << "posZero is negative\n";
}
else
{
cout << "posZero is positive\n";
}
if( copysign( 1, negZero ) < 0 )
{
cout << "negZero is negative\n";
}
else
{
cout << "negZero is positive\n";
}
}
posZero is positive
negZero is negative
In this example, copysign creates a value of +/- 1, according to the sign on the second argument. The first argument for your purposes could be any non-zero value, but might as well be 1.
Alternatively, you could use signbit, which is honestly probably more direct. A version of the above using this function:
#include "iostream"
#include <math.h>
using namespace std;
int main()
{
double posZero = +0.0d;
double negZero = -0.0d;
if( signbit( posZero ) )
{
cout << "posZero is negative\n";
}
else
{
cout << "posZero is positive\n";
}
if( signbit( negZero ) )
{
cout << "negZero is negative\n";
}
else
{
cout << "negZero is positive\n";
}
}
With the same output.
A problem set for people learning C++ is
Write a short program to simulate a ball being dropped off of a tower. To start, the user should be asked for the initial height of the tower in meters. Assume normal gravity (9.8 m/s2), and that the ball has no initial velocity. Have the program output the height of the ball above the ground after 0, 1, 2, 3, 4, and 5 seconds. The ball should not go underneath the ground (height 0).
Before starting C++ I had a reasonable, but primarily self taught, knowledge of Java. So looking at the problem it seems like it ought to be split into
input class
output class
calculations class
Physical constants class (recommended by the question setter)
controller ('main') class
The input class would ask the user for a starting height, which would be passed to the controller. The controller would give this and a number of seconds (5) to the calculations class, which would create an array of results and return this to the controller. The controller would hand the array of results to the output class that would print them to the console.
I will put the actual code at the bottom, but it's possibly not needed.
You can probably already see the problem, attempting to return an array. I'm not asking how to get round that problem, there is a workaround here and here. I'm asking, is the problem a result of bad design? Should my program be structured differently, for performance, maintenance or style reasons, such that I would not be attempting to return an array like object?
Here is the code (which works apart from trying to return arrays);
main.cpp
/*
* Just the main class, call other classes and passes variables around
*/
#include <iostream>
#include "dropSim.h"
using namespace std;
int main()
{
double height = getHeight();
int seconds = 5;
double* results = calculateResults(height, seconds);
outputResults(results);
return 0;
}
getHeight.cpp
/*
* Asks the user for a height from which to start the experiment
* SI units
*/
#include <iostream>
using namespace std;
double getHeight()
{
cout << "What height should the experiment start at; ";
double height;
cin >> height;
return height;
}
calculateResults.cpp
/*
* given the initial height and the physical constants, the position of the ball
* is calculated at integer number seconds, beginning at 0
*/
#include "constants.h"
#include <cmath>
#include <iostream>
using namespace std;
double getPosition(double height, double time);
double* calculateResults(double height, int seconds)
{
double positions[seconds + 1];
for(int t = 0; t < seconds + 1; t++)
{
positions[t] = getPosition(height, t);
}
return positions;
}
double getPosition(double height, double time)
{
double position = height - 0.5*constants::gravity*pow(static_cast<double>(time), 2);
if( position < 0) position = 0;
//Commented code is for testing
//cout << position << endl;
return position;
}
outputResults.cpp
/*
* Takes the array of results and prints them in an appropriate format
*/
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
void outputResults(double* results){
string outputText = "";
//The commented code is to test the output method
//Which is working
//double results1[] = {1,2,3,4,5};
//int numResults = sizeof(results1)/sizeof(results1[0]);
int numResults = sizeof(results)/sizeof(results[0]);
//cout << numResults; //= 0 ... Oh
for(int t = 0; t < numResults; t++)
{
ostringstream line;
line << "After " << t << " seconds the height of the object is " << results[t] << "\r";
outputText.append(line.str());
}
cout << outputText;
}
And finally a couple of headers;
dropSim.h
/*
* dropSim.h
*/
#ifndef DROPSIM_H_
#define DROPSIM_H_
double getHeight();
double* calculateResults(double height, int seconds);
void outputResults(double* results);
#endif /* DROPSIM_H_ */
constants.h
/*
* Contains physical constants relevant to simulation.
* SI units
*/
#ifndef CONSTANTS_H_
#define CONSTANTS_H_
namespace constants
{
const double gravity(9.81);
}
#endif /* CONSTANTS_H_ */
I would say that you're over-engineering a big solution to a little problem, but to answer your specific question:
Should my program be structured differently, for performance, maintenance or style reasons, such that I would not be attempting to return an array like object?
Returning an array-like object is fine. But that doesn't mean returning an array, nor does it mean allocating raw memory with new.
And it's not restricted to return values either. When you're starting out with C++, it's probably best to just forget that it has built-in arrays at all. Most of the time, you should be using either std::vector or std::array (or another linear collection such as std::deque).
Built-in arrays should normally be viewed as a special-purpose item, included primarily for compatibility with C, not for everyday use.
It may, however, be worth considering writing your computation in the same style as the algorithms in the standard library. This would mean writing the code to receive an iterator to a destination, and writing its output to wherever that iterator designates.
I'd probably package the height and time together as a set of input parameters, and have a function that generates output based on those:
struct params {
double height;
int seconds;
};
template <class OutIt>
void calc_pos(params const &p, OutIt output) {
for (int i=0; i<p.seconds; i++) {
*output = get_position(p.height, i);
++output;
}
}
This works somewhat more clearly along with the rest of the standard library:
std::vector<double> results;
calc_pos(inputs, std::back_inserter(results));
You can go a few steps further if you like--the standard library has quite a bit to help with a great deal of this. Your calc_pos does little more than invoke another function repeatedly with successive values for the time. You could (for example) use std::iota to generate the successive times, then use std::transform to generate outputs:
std::vector<int> times(6);
std::iota(times.begin(), times.end(), 0);
std::vector<double> distances;
std::transform(times.begin(), times.end(), compute_distance);
This computes the distances as the distance dropped after a given period of time rather than the height above the ground, but given an initial height, computing the difference between the two is quite trivial:
double initial_height = 5;
std::vector<double> heights;
std::transform(distances.begin(), distances.end(),
std::back_inserter(heights),
[=](double v) { return max(initial_height-v, 0); });
At least for now, this doesn't attempt to calculate the ball bouncing when it hits the ground--it just assumes the ball immediately stops when it hits the ground.
You should get rid of self-allocated double * and use std::vector<double> instead. It's not difficult to learn and a basic step in modern C++
This is how I would solve the problem:
#include <cmath>
#include <iostream>
#include <iomanip>
using std::cin;
using std::cout;
using std::endl;
using std::sqrt;
using std::fixed;
using std::setprecision;
using std::max;
using std::setw;
static const double g = 9.81;
class Calculator {
public:
Calculator(double inh) : h(inh)
{
}
void DoWork() const {
double tmax = sqrt(h / ( g / 2));
for (double t=0.0; t<tmax; t+=1.0) {
GenerateOutput(t);
}
GenerateOutput(tmax);
}
private:
void GenerateOutput(double t) const {
double x = g * t * t / 2;
double hremaining = max(h - x, 0.0);
cout << fixed << setprecision(2) << setw(10) << t;
cout << setw(10) << hremaining << endl;
}
double h;
};
int main() {
double h(0.0);
cout << "Enter height in meters: ";
cin >> h;
if (h > 0.0) {
const Calculator calc(h);
calc.DoWork();
} else {
return 1;
}
return 0;
}
I want to create code that will help me get numbers bigger than MAXINT. I heard about that I can use Binary Code Decimal to do this, and then every two of decimal numbers(converted to BCD) of the bigger number keep in char. But how to do this? I should give string as input, then convert somehow to BCD every single decimal number? And how can I put two converted decimal numbers to one char? I'm new in C++ and don't know how can i do it.
P.S. I don't want to use libraries which are "special" for that kind of problems.
As it turns out, this is actually quite simple. How about we try to take it to the next level?
Below is an implementation of a BCD number with infinite(or as much as memory can hold) size. It only supports positive integer numbers. I'll leave extending this to support negative numbers(or real numbers) as an exercise.
First things first: Yes, we want to get our number as a string and then build it up from that. Since it's only an integer, this is actually quite easy to do. We primarily create a helper function to aid us in identifying all the digits.
int char_to_int(const char c) {
int ret = c - '0';
if(ret > 9 || ret < 0) throw 1; // for simplicity. Use a class derived from std::exception instead.
return ret;
}
We can now try to implement input and output for our big number.
First Try
Having that helper guy, turning a string to a BCD-encoded buffer is easy. A common implementation may look like this:
int main() {
unsigned char bignum[10]; // stores at most 20 BCD digits.
std::memset(bignum, 0, sizeof(bignum));
std::string input;
std::cin >> input;
try {
if (input.size() > 20) throw 1; // Avoid problems with buffer overflow.
for (int i=1;i<=input.size();i++) {
int n = char_to_int(input[input.size()-i]);
bignum[sizeof(bignum) - (i+1)/2] |= n << (i%2)*4; // These are bitwise operations. Google them!
}
}
catch(int) {
std::cout << "ERROR: Invalid input.\n";
return 0; // Exit cleanly.
}
// bignum is now filled. Let's print it to prove.
for (int i=0;i<sizeof(bignum);i++) {
int first_digit = bignum[i] & '\x0F'; // Right side, doesn't need to shift.
int second_digit = (bignum[i] & '\xF0')>>4; // Left side, shifted.
std::cout << first_digit << second_digit;
}
}
This is not very space-efficient, however. Note that we have to store all the 20 digits, even if our number is small! What if we needed 1000 digits? What if we need 1000 numbers that may or may not have these 1000 digits? It is also error-prone: Look that we had to remmember to initialize the array, and do a bounds check before conversion to avoid a buffer overflow.
Second Try
We can improve our implementation using a std::vector:
int main() {
std::vector<unsigned char> bignum; // stores any quantity of digits.
std::string input;
std::cin >> input;
try {
// For an odd number of digits we want a trailling zero at the end.
if(input.size()%2) n.num_vec.push_back(char_to_int(input[0]));
for (unsigned i=input.size()%2;i<input.size();i+=2) {
int left = char_to_int(input[i]);
int right = char_to_int(input[i+1]);
n.num_vec.push_back(0);
n.num_vec.back() = left << 4;
n.num_vec.back() |= right;
}
}
catch(int) {
std::cout << "ERROR: Invalid input.\n";
exit(0); // Exit cleanly.
}
// bignum is now filled. Let's print it to prove.
for (unsigned i=0;i<bignum.size();++i) {
// Notice that we inverted this from the previous one! Try to think why.
int first_digit = (bignum[i] & '\xF0')>>4; // Left side, shifted.
int second_digit = bignum[i] & '\x0F'; // Right side, doesn't need to shift.
if(i || first_digit) std::cout << first_digit; // avoid printing trailling 0.
std::cout << second_digit;
}
}
Lookin' good, but that is too cumbersome. Ideally, the bignumber user shouldn't have to deal with the vector positions and all that mumbo-jumbo. We want to write code that behaves like:
int main() {
int a;
cin >> a;
cout << a;
}
And it should just work.
Third Try
Turns out this is possible! Just wrap bignum into a class, with some helpful operators:
class bignum {
std::vector<unsigned char> num_vec;
template<typename T>
friend T& operator<<(T& is, bignum& n);
template<typename T>
friend T& operator>>(T& os, bignum& n);
};
// Get input from any object that behaves like an std::istream (i.e.: std::cin)
template<typename T>
T& operator>>(T& is, bignum& n) {
std::string input;
is >> input;
n.num_vec.reserve(input.size());
if(input.size()%2) n.num_vec.push_back(char_to_int(input[0]));
for (unsigned i=input.size()%2;i<input.size();i+=2) {
int left = char_to_int(input[i]);
int right = (i+1) != input.size()?char_to_int(input[i+1]):0; // If odd number of digits, avoid getting garbage.
n.num_vec.push_back(0);
n.num_vec.back() = left << 4;
n.num_vec.back() |= right;
}
return is;
}
// Output to any object that behaves like an std::ostream (i.e.: std::cout)
template<typename T>
T& operator<<(T& os, bignum& n) {
for (unsigned i=0;i<n.num_vec.size();++i) {
int first_digit = (n.num_vec[i] & '\xF0')>>4; // Left side, shifted.
int second_digit = n.num_vec[i] & '\x0F'; // Right side, doesn't need to shift.
if(i || first_digit) os << first_digit; // avoid printing trailling 0.
os << second_digit;
}
return os;
}
Then our main function looks much more readable:
int main() {
bignum a;
try {
std::cin >> a;
}
catch(int) {
std::cout << "ERROR: Invalid input.\n";
return 0; // Exit cleanly.
}
std::cout << a;
}
Epilogue
And here we have it. Of course with no addition, multiplication, etc. operators, it isn't very useful. I'll leave them as an exercise. Code, code and code some more, and soon this will look like a piece of cake to you.
Please feel free to ask any questions. Good coding!
I am trying to make a farey seq program with C++ list library
My program works fine when I use the first level, however, it crashes after that with all other levels for some reason.
I am using visual studio as a compiler. I tried the debugger mode as well as the resource.
The program doesn't crash in the resource mode, however, it doesn't give me the even numbers levels output. In addition, it gives me half of the odds levels output for some reason.
I want to fix this problem and I want it to work in the dubgger mode as well.
Any suggestions?
Here is what I've done so far
class RULLZ: public list<Fraction>
{
private:
list<Fraction>::iterator head,tail,buf,buf1;
public :
Farey2()
{
this->push_front( Fraction(1,1));
this->push_front( Fraction(0,1));
}
void Add(int level)
{
Fraction *tmp,*tmp2;
buf=this->first();
for(int i=0;i<level-1;i++)
{
head=this->first();
tail=this->last();
while(head!=tail)
{
tmp=new Fraction(head->num,head->den);
head++;
if(tmp->den+head->den<=level)
{
tmp2=new Fraction(tmp->num+head->num,tmp->den+head->den);
this->insert(head,*tmp2);
head--;
}
}
this->pop_back();
}
}
friend ostream& operator<<(ostream& out, RULLZ& f)
{
for(list<Fraction>::iterator it=f.first();it !=f.last();it++)
out <<*it;
return out;
}
};
class RULLZ: public list<Fraction>
Before even looking at your question, the above code is a problem. The C++ standard containers are deliberately not designed to be base classes (none of them have a virtual destructor), so this will cause problems. For reasons why you should not publicly derive from a standard container, see the following:
When is it "okay"?
The risks
Why it is a bad design decision
Coding Guidelines (Page ~60)
Why inheritance is usually not the right approach
It appears you want the Add function to add the next X number of fractions together (if I understand your intent correctly). A better way to do that is to use a std::stack:
std::stack<Fraction, std::list<Fraction>> expression;
// add fractions to your stack here
Fraction Add(unsigned int count)
{
// assume count is greater than 1 (as adding 0 fractions makes no sense, and adding 1 is trivial)
Fraction result(0, 1);
for (int i = 0; i < count; ++i)
{
Fraction a = expression.top();
expression.pop();
Fraction b = expression.top();
expression.pop();
result += a + b; // assume operators + and += have been implemented for Fraction
}
expression.push(result);
return result;
}
Another problem you appear to have is a logic problem (again, assuming I understand your intent correctly):
for(int i=0;i<level-1;i++)
If level is the number of fractions you want to add, and you pass in 2, this loop will only include the first one. That is, it will not add fractions 0 and 1, but rather just grab fraction 0 and return it. I think you meant for this to be
for(int i=0; i < level; i++)
Which will grab both fractions 0 and 1 to add together.
I'm not sure where, but your logic for generating the series appears to be off. A more simple approach can be found here:
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <iostream>
#include <vector>
struct Fraction
{
std::uint32_t Numerator;
std::uint32_t Denominator;
Fraction(std::uint32_t n, std::uint32_t d) : Numerator(n), Denominator(d) { }
};
std::ostream& operator<<(std::ostream& os, const Fraction& f)
{
os << "(" << f.Numerator << " / " << f.Denominator << ")";
return os;
}
typedef std::vector<Fraction> FareySeries;
FareySeries generate_series(std::uint32_t depth)
{
std::uint32_t a = 0;
std::uint32_t b = 1;
std::uint32_t c = 1;
std::uint32_t d = depth;
FareySeries results;
results.emplace_back(a, b);
while (c <= depth)
{
std::uint32_t k = (depth + b) / d;
std::uint32_t nc = (k * c) - a;
std::uint32_t nd = (k * d) - b;
a = c;
b = d;
c = nc;
d = nd;
results.emplace_back(a, b);
}
return results;
}
int main()
{
const std::uint32_t DEPTH = 4;
FareySeries series = generate_series(DEPTH);
std::copy(series.begin(), series.end(), std::ostream_iterator<Fraction>(std::cout, "\n"));
return 0;
}
Consider that I have a symmetrical relationship matrix, similar to this:
Except that each "outcome" is a small piece of code.
My scenario: I have a bunch of Entity objects that "collide" with eachother. Each entity has a CollisionType value (an enum). In the design, a relationship matrix exists which describes how the entities behave when different CollisionTypes meet each other.
I'm wondering: How would I represent the relationships, and also implement logic on top of it, in an clean and high-performance manner which is easy to add new CollisionTypes to? In my mind it looks something like a 2D Switch statement.
Example (poor) solution:
void CollideEntities( Entity e1, Entity e2 ) {
CollisionType t1 = e1.GetCollisionType();
CollisionType t2 = e2.GetCollisionType();
// perform basic logic based on t1 & t2
if ( (t1 == COL_SOLID && t2 == COL_SQUISHY) || (t1 == COL_SQUISHY && t2 == COL_SOLID) ) {
// do stuff..
} else if ( (t1 == COL_SOLID && t2 == COL_DAMAGE) || (t1 == COL_DAMAGE && t2 == COL_SOLID) ) {
// do other stuff..
} // and so on...
}
Many potential solutions are apparent to me, but none of them strike me as particularly clean or efficient or easy to add new types to...
I wouldn't do it that way. I'd have a Map where the key would look up a Command object containing the desired behavior.
Another possibility would be a Visitor pattern (aka "double dispatch").
Try this:
#include <vector>
#include <iostream>
class Symmetric_matrix {
private:
size_t size1;
// The next should be <bool> rather than <int>,
// but the writer's compiler objects.
std::vector<int> outcomes;
public:
size_t size() const { return size1; }
int &operator()(const size_t i, const size_t j) {
const size_t a = i <= j ? i : j;
const size_t b = i <= j ? j : i;
return outcomes[(b*(b-1))/2 + a];
}
Symmetric_matrix(const size_t size0)
: size1(size0), outcomes((size()*(size()-1))/2, false) {}
};
// Here is a test driver.
int main() {
Symmetric_matrix sm(5);
sm(0, 1) = true;
sm(0, 3) = true;
sm(1, 3) = true;
sm(2, 3) = true;
sm(3, 4) = true;
std::cout << "buyer-approver : " << sm(0, 2) << "\n";
std::cout << "approver-buyer : " << sm(2, 0) << "\n";
std::cout << "approver-requisition: " << sm(2, 3) << "\n";
std::cout << "requisition-approver: " << sm(3, 2) << "\n";
return 0;
}
Your question is an interesting one. As you have observed, one need only store the upper or the lower triangle of the matrix, not both.
But what's the (b*(b-1))/2 about, you ask? Answer: it comes of the curious arithmetical fact that 0 + 1 + 2 + ... + (b-1) == (b*(b-1))/2 (try it).
Of course, my sample code could stand some improvement. For one thing, for some reason (advice is requested), my code fails when it uses a std::vector<bool>, so I have used a std::vector<int> as a workaround. For another, it does not include proper handling for the case i == j. What it does do however is to convey the essential technique. You can fill out details at your discretion.
(Update: It has later occurred to my why the std::vector<bool> fails. It fails because std::vector<bool> is implemented as an array of bits, whereas a single bit cannot be an lvalue because it has no address of its own. With clever coding, by having the operator()() return a manipulator of some specially defined type, one could probably finesse the problem without altering main(), but it is probably easiest just to define and use a set() member function if the <bool> is what we want to use.)