Boost size of largest connected component - boost

I know how to calculate the total number of connected components in Boost, but is there an efficient way to compute the size of the largest connected component using the boost's graph library.

I think the most efficient way is to replace the component map with a custom type.
I created a small WritePropertyMap to do that:
template <typename V>
struct Mapper {
using Id = int; // component id
using Cardinality = int;
using Map = boost::container::flat_map<Id, Cardinality>;
using Value = Map::value_type;
Map& storage;
friend void put(Mapper& m, V const& /*v*/, Id id) { m.storage[id] += 1; }
Value largest() const {
return not storage.empty()
? *max_element(begin(storage), end(storage),
[](Value const& a, Value const& b) {
return a.second < b.second;
})
: Value{};
}
};
We need to tell Boost about our property map:
template <typename V> struct boost::property_traits<Mapper<V>> {
using category = boost::writable_property_map_tag;
using key_type = V;
using value_type = int;
};
Note
The separation between storage and property map is because property maps are passed by value - and should be cheap to copy.
Now we can use it, adapting the library example slightly:
Live On Coliru
Mapper<V>::Map result;
Mapper<V> mapper{result};
int num = connected_components(g, mapper);
auto [id, cardinality] = mapper.largest();
std::cout << "Largest component #" << id << " (out of " << num
<< " components) has " << cardinality << " vertices\n";
Prints
Largest component #0 (out of 3 components) has 3 vertices
This matches the expected output.
BONUS
If you have an expected number of components, you may be able to optimize storage by using small_vector/static_vector, e.g.
using Value = std::pair<Id, Cardinality>;
using Map = boost::container::flat_map<
Id, Cardinality, std::less<>,
boost::container::small_vector<Value, 10>>;
This way, unless you have more than 10 components, you will never see a dynamic allocation for the mapper storage.

Related

c++ : unordered map with pair of string_viewes

Here is a code snippet I have :
struct PairHasher {
size_t operator()(const std::pair<std::string_view, std::string_view>& stop_stop) const {
return hasher(stop_stop.first) + 37*hasher(stop_stop.second);
}
std::hash<std::string_view> hasher;
};
BOOST_FIXTURE_TEST_CASE(unordered_map_string_view_pair_must_be_ok, TestCaseStartStopMessager)
{
const std::vector<std::string> from_stops = {"from_0", "from_1", "from_2"};
const std::vector<std::string> to_stops = {"to_0", "to_1", "to_2"};
std::unordered_map<std::pair<std::string_view, std::string_view>, std::int32_t, TransportCatalogue::PairHasher> distance_between_stops;
for ( std::size_t idx = 0; idx < from_stops.size(); ++idx) {
std::cout << from_stops[idx] << " : " << to_stops[idx] << std::endl;
distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
}
std::cout << "MAP CONTENT :" << std::endl;
for (auto const& x : distance_between_stops)
{
std::cout << x.first.first << " : " << x.first.second << std::endl;
}
}
I expect to see 3 pairs inside the container, but there is only 1 concerning to the output :
MAP CONTENT :
from_2 : to_2
So, where are two more pair lost? What am I doing wrong?
Moving my comment to an answer.
This is pretty sneaky. I noticed in Compiler Explorer that changing:
distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
to
distance_between_stops[std::pair(std::string_view{from_stops[idx]}, std::string_view{to_stops[idx]})] = idx;
fixes the bug. This hints that the problem lies in some implicit string -> string_view conversion. And indeed that is the case, but it is hidden behind one extra layer.
std::pair(from_stops[idx], to_stops[idx]) creates a std::pair<std::string, std::string>, but distance_between_stops requires a std::pair<std::string_view, std::string_view>. When we insert values into the map, this conversion happens implicitly via overload #5 here:
template <class U1, class U2>
constexpr pair(pair<U1, U2>&& p);
Initializes first with std::forward<U1>(p.first) and second with std::forward<U2>(p.second).
This constructor participates in overload resolution if and only if std::is_constructible_v<first_type, U1&&> and std::is_constructible_v<second_type, U2&&> are both true.
This constructor is explicit if and only if std::is_convertible_v<U1&&, first_type> is false or std::is_convertible_v<U2&&, second_type> is false.
(For reference, std::is_constructible_v<std::string_view, std::string&&> and std::is_convertible_v<std::string&&, std::string_view> are both true, so we know this overload is viable and implicit.)
See the problem yet? When we use the map's operator[], it has to do an implicit conversion to create a key with the proper type. This implicit conversion constructs a pair of string_views that are viewing the temporary memory from the local pair of strings, not the underlying strings in the vector. In other words, it is conceptually similar to:
std::string_view foo(const std::string& s) {
std::string temp = s + " foo";
return temp;
}
int main() {
std::string_view sv = foo("hello");
std::cout << sv << "\n";
}
Clang emits a warning for this small example, but not OP's full example, which is unfortunate:
warning: address of stack memory associated with local variable 'temp' returned [-Wreturn-stack-address]
return temp;
^~~~

Using .sum() and += on std::valarray<T>

I am using the type std::valarray<std::valarray<double>> and wish to sum each of the contained valarrays element wise, to leave a std::valarray<double>.
The C++ documentation states that the operator .sum() can be applied to std::valarray<T> so long as the operator += is defined for type T. My code below (method1) tries to apply this to std::valarray<std::valarray<double>>, but the result appears to be nonsense.
However if I perform this manually, using the += operator (method2), I get the result I want. But the fact that method2 works seems to imply that the operator += is defined for the type std::valarray<double>, and hence that method1, using .sum(). should work. I really can't understand what is happening here...
My code:
#include <iostream>
#include <valarray>
// Attempt to use .sum() operator
std::valarray<double> method1(const std::valarray<std::valarray<double>>& data) {
return data.sum();
}
// Manual summation using += operator
std::valarray<double> method2(const std::valarray<std::valarray<double>>& data) {
std::valarray<double> sum(data[0].size());
for (size_t i{0}; i < data.size(); i++) {
sum += data[i];
}
return sum;
}
// Display size and elements
void showData(const std::valarray<double> data) {
std::cout << "Size = " << data.size() << "\n";
std::cout << "Data = ";
for (size_t i{0}; i < data.size(); i++) {
std::cout << data[i] << " ";
}
std::cout << "\n\n";
}
int main() {
std::valarray<std::valarray<double>> data{{1,2},{3,4}};
showData(method1(data));
showData(method2(data));
}
My output:
Size = 0
Data =
Size = 2
Data = 4 6
The sum method of std::valarray requires operator+= to be defined for its value type (in your case, std::valarray), but std::valarray also requires it to be default-constructible (from the "Numeric" concept requirement).
This allows the sum method to work without operator+, by first default-constructing an element, and then adding each contained element with operator+=.
Although it isn't defined anywhere, as far as I know, it probably works something like this.
T sum() const {
T result;
for (auto& it : elements) {
result += it;
}
return result;
}
The problem with a valarray of valarrays (std::valarray<std::valarray>) is that a default-constructed valarray is empty. And when operator+= is applied with an empty valarray and a non-empty one, it results in undefined behavior ("The behavior is undefined if size() != v.size()"). What you are likely to get is an empty valarray as a result (but you could potentially get anything).
What you could use instead is std::accumulate. It requires an initial value as third parameter, which takes care of the problem.
std::accumulate(std::begin(data), std::end(data), std::valarray<double>(data[0].size()))
Live on Coliru.
PS: don't ask me why std::valarray has no method begin and end.

Insert a map into other map with a unique type key (2)

hello guys i am new to maps in C++ i am having a question regarding copying a particular type map to another map of same kind the details are shown below
I initially declared a map like this
map<string,int> objmap,obj_porcess ;
for(int i = 0; i < 10; i++) {
objmap ["process"+to_string(i)]=i+10//some processing the to_string is just in case but i have strings with names for all 10 values
}
like
objmap["process_today"]=1;
objmap["process_yesterday"]=-1;
objmap["process_tommorow"]=2;
now i want to define some thing like this just my key word should be added with the process and remaining all can be same for all the keys from obj_process
obj_process["today"]=objmap["process_today"] ;
instead of defining all 10 can i have a simple code cause in here i took an example of 10 but i have like 200 set of different strings in the key of map
i already asked a qn for exact opposite one this was my previous qn now when i try its vice versa i got an issue hope i find some help
If you can initialize both at the same time, the solution is straightforward:
const std::vector<std::string> days = {"today", "yesterday", /*...*/};
for(const auto& d : days)
{
objmap["process_" + d] = foo();
obj_process[d] = foo();
}
If you cannot, you should be able to iterate over objmap and get rid of the "process_" prefix with some basic string manipulation:
constexpr auto prefix_length = 8; // length of "process_"
for (const auto& p : objmap)
{
const auto& key = p.first;
const auto& processed_key = key.substr(prefix_length);
obj_process[processed_key] = objmap[key];
}

C++11 set range based for using structs as elements

Let's say I have a struct like this:
struct Something{
string name;
int code;
};
And a set of Something type:
set<Something> myset;
myset.insert({"aaa",123,});
myset.insert({"bbb",321});
myset.insert({"ccc",213});
What's wrong with this?
for (auto sth : myset){
cout << sth.name;
cout << sth.code;
}
Along the same lines... why can't I modify an element (even when the set contains plain int items) using something like this?
for (auto &sth : myset){
sth=[some value];
}
I know I can do this with vectors and maps. Why not sets?
Thanks!
Modifying an element of a set implies its position in the set's order can change. Because your compiler cannot know what exactly a particular set uses to determine its element's orders. Well, it could, theoretically, but even then it would be nearly impossible to keep track of the rearrangements while iterating through the container. It would make no sense.
What you can do, if you want to modify the elements of a set in such a way that you know will not change their order in a set, you can make the non-ordering members of your struct mutable. Note that if you make a mistake and the set's order is disturbed, any other operations on the set (like a binary search) will give incorrect results after that faulty modification. If you don't want to make members mutable, const_cast is an option, with the same caveats.
To elaborate on my answer above, an example:
#include <iostream>
#include <set>
struct bla
{
std::string name;
int index;
};
bool operator<(const bla& left, const bla& right) { return left.index < right.index; }
int main()
{
std::set<bla> example{{"har", 1}, {"diehar", 2}};
// perfectly fine
for(auto b : example)
std::cout << b.index << ' ' << b.name << '\n';
// perfectly fine - name doesn't influence set order
for(auto& b : example) // decltype(b) == const bla&
const_cast<std::string&>(b.name) = "something";
// better than first loop: no temporary copies
for(const auto& b : example)
std::cout << b.index << ' ' << b.name << '\n';
// using a "universal reference auto&&", mostly useful in template contexts
for(auto&& b : example) // decltype(b) == const bla&
std::cout << b.index << ' ' << b.name << '\n';
// destroying order of the set here:
for(auto& b : example)
const_cast<int&>(b.index) = -b.index;
// anything here relying on an ordered collection will fail
// This includes std::set::find, all the algorithms that depend on uniqueness and/or ordering
// This is pretty much all that will still work, although it may not even be guaranteed
for(auto&& b : example)
std::cout << b.index << ' ' << b.name << '\n';
}
Live code on Coliru.
Note the first const_cast is only ok because the underlying example isn't const in the first place.

Is there an easier way to set/get values in a gSOAP request/response?

I am using gSOAP to configure an ONVIF compatible camera.
Currently, I am manually setting all the parameters in the request by doing something like this. This is for the SetVideEncoderConfiguration
MediaBindingProxy mediaDevice (uri);
AUTHENTICATE (mediaDevice);
_trt__SetVideoEncoderConfiguration req;
_trt__SetVideoEncoderConfigurationResponse resp;
struct tt__VideoEncoderConfiguration encoderConfig;
struct tt__VideoResolution resolutionConfig;
encoderConfig.Name = strdup (name);
encoderConfig.UseCount = 1;
encoderConfig.Quality = 50;
if (strcmp (encoding, "H264") == 0)
encoderConfig.Encoding = tt__VideoEncoding__H264;
else if (strcmp (encoding, "JPEG") == 0)
encoderConfig.Encoding = tt__VideoEncoding__JPEG;
encoderConfig.token = strdup (profileToken);
encoderConfig.SessionTimeout = (LONG64)"PT0S";
resolutionConfig.Width=1280;
resolutionConfig.Height=720;
encoderConfig.Resolution = &resolutionConfig;
tt__VideoRateControl rateControl;
rateControl.FrameRateLimit = 15;
rateControl.EncodingInterval = 1;
rateControl.BitrateLimit = 4500;
encoderConfig.RateControl = &rateControl;
struct tt__H264Configuration h264;
h264.GovLength = 30;
h264.H264Profile = tt__H264Profile__Baseline;
encoderConfig.H264 = &h264;
struct tt__MulticastConfiguration multicast;
struct tt__IPAddress address;
address.IPv4Address = strdup ("0.0.0.0");
multicast.Address = &address;
encoderConfig.Multicast = &multicast;
req.Configuration = &encoderConfig;
req.ForcePersistence = true;
int ret = mediaDevice.SetVideoEncoderConfiguration (&req, resp);
qDebug () << "Set Encoder: " << ret;
Is there an easier way to do this? May be some function calls that set the request parameters? Another way I found with GetMediaUri was to use something like
soap_new_req__trt__GetStreamUri (mediaDevice.soap,soap_new_req_tt__StreamSetup (mediaDevice.soap, (enum tt__StreamType)0, soap_new_tt__Transport(mediaDevice.soap), 1, NULL), "profile1");
Are these the only two ways for client side code with gSOAP?
-Mandar Joshi
There are four variations of soap_new_T() to allocate data of type T in C++ with gSOAP:
T * soap_new_T(struct soap*) returns a new instance of T that is default
initialized and allocated on the heap managed by the soap context.
T * soap_new_T(struct soap*, int n) returns an array of n new instances of
T on the managed heap. The instances in the array are default initialized as described above.
T * soap_new_req_T(struct soap*, ...) (structs and classes only) returns a
new instance of T allocated on the managed heap and sets the required data members to the values specified in the other arguments ....
T * soap_new_set_T(struct soap*, ...) (structs and classes only) returns a
new instance of T on the managed heap and sets the public/serializable data members to the values specified in the other arguments ....
Use soap_strdup(struct soap*, const char*) instead of strdup to dup strings onto the managed heap.
All data on the managed heap is mass-deleted with soap_destroy(soap) and
soap_end(soap) (call these in that order) which must be called before soap_done(soap) or soap_free(soap).
To allocate pointers to data, use templates:
template<class T>
T * soap_make(struct soap *soap, T val)
{
T *p = (T*)soap_malloc(soap, sizeof(T));
if (p)
*p = val;
return p;
}
template<class T>
T **soap_make_array(struct soap *soap, T* array, int n)
{
T **p = (T**)soap_malloc(soap, n * sizeof(T*));
for (int i = 0; i < n; ++i)
p[i] = &array[i];
return p;
}
Then use soap_make<int>(soap, 123) to create a pointer to the value 123 on the managed heap and soap_make_array(soap, soap_new_CLASSNAME(soap, 100), 100) to create 100 pointers to 100 instances of CLASSNAME.
The gSOAP tools also generate deep copy operations for you: CLASSNAME::soap_dup(struct soap*) creates a deep copy of the object and allocates it in a another soap context that you provide as argument. Use NULL as this argument to allocate unmanaged deep copies (but these cannot have pointer cycles!). Then delete unmanaged copies with CLASSNAME::soap_del() for deep deletion of all members and then delete the object itself.
See Memory management in C++ for more details. Use gSOAP 2.8.39 and greater.

Resources