Access individual elements of ChunkedArray by its index within column - c++

What is the best method to randomly access individual elements ("Scalars") of arrow::ChunkedArray e.g. for testing and display purposes? Is there some equivalent method to Array::GetScalar which takes into account that the ChunkedArray consists of multiple chunks?
The way I found so far is an own helper function which "searches" the appropriate chunk like this (not tested):
std::shared_ptr<arrow::Scalar>
get_scalar(const std::shared_ptr<arrow::ChunkedArray>& chunked_array, int64_t index) {
auto it = chunked_array->chunks().begin();
while (index >= (*it)->length()) {
index -= (*it)->length();
++it;
}
auto result = (*it)->GetScalar(index);
if (!result.ok()) {
return nullptr;
}
return result.MoveValueUnsafe();
}
But I'm wondering if there is something like this already provided or there is a better practice to do so?
Similar applies to the arrow::stl::ArrayIterator - is there an equivalent iterator for Chunked::Array?

Related

Correct way to write only the first element of a c++ stl vector

I have a c++ code that at one part it stored some values of a measurement in a vector and this vector is a part of set of data schema which is serialized and then sent to a streamer.
There is new requirement that for a specific case I need just one value of the measurement which is always rewritten with the latest one, but I don't want to change the vector variable in order to keep the same schema. So I thought that for that case to rewrite each time the first element of the vector, something like this
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.pushback(measurement);
}
else
{
store_measurements.at(0) = measurement ;
}
It seems to work fine when the vector is not cleared, but I'd like to ask if this is the correct way to do that or there is a more preferable way to do it?
You can use the front() function.
vector<int> store_measurements;
int measurement = 10;
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.resize(1);
store_measurements.front() = measurement ;
}
Edit:
Based on the comments I added store_measurements.resize(1); before the assignment
I would probably use assign() which replaces all the values in the vector like this:
if (condition == "several_values")
{
store_measurements.push_back(measurement);
}
else
{
store_measurements.assign(1, measurement);
}

RemoveAt from StructArray Ue4

I struggle a bit with deleting struct from my TArray of structs.My struct contains AudioComponent and float.I was using Array.RemoveAt(index), but what i got from this was only removing half of my struct, which is AudioComponent.
Why is that? My function Removing elements looks like this:
void RemoveArrayElement( UAudioComponent AudioComponent )
{
for( int i=0; i<Array.Num(); i++ )
{
if( AudioComponent == Array[i].AudioComponent )
{
Array.RemoveAt( i );
}
}
}
What i want to achieve is completely deleting index, AudioComponent with it's float.
There are few issues with your code. As others mentioned in comments, you should use pointers. And if I'm not mistaken, you aren't allowed to use construction like this:
UPROPERTY()
TArray<UAudioComponent> invalidArray;
You should use UPROPERTY macro, otherwise your properties could and probably will be garbage collected. UPROPERTY wiki.
Next thing is that you are changing array over which you are iterating. I wrote few approaches, let's look at them:
void RemoveArrayElement(UAudioComponent* AudioComponent)
{
TArray<UAudioComponent*> audioArray; // array will be initialized somewhere else, this is for demo purpose.
// you always should check your pointers for validity
if (!AudioComponent || !AudioComponent->IsValidLowLevel() || AudioComponent->IsPendingKill())
return;
// Correct approach 1 (multiple):
TQueue<UAudioComponent*> toDelete;
for (int i = 0; i < audioArray.Num(); i++)
{
auto item = audioArray[i];
if (AudioComponent == item || true) // we simulate another condition for multiselect
{
toDelete.Enqueue(item);
}
}
// better approach for iteration:
for (auto item : audioArray)
if (item == AudioComponent || true) // we simulate another condition for multiselect
toDelete.Enqueue(item);
// finalize deletion in approach 1
UAudioComponent* deleteItem;
while (toDelete.Dequeue(deleteItem))
audioArray.Remove(deleteItem);
// almost correct approach 2 (single) :
UAudioComponent* foundItem;
for (auto item : audioArray)
if (item == AudioComponent)
{
foundItem = item;
break; // we can skip rest - but we must be sure, that items were added to collection using AddUnique(...)
}
if (foundItem)
audioArray.Remove(foundItem);
// correct and the best - approach 3 (single)
audioArray.Remove(AudioComponent);
}
First keep in mind that comparing two objects does not necessarily lead to the expected result of equality. Using the == operator means executing a function (bool operator==(L, R);) that specifies what should happen. So if you did not overload the == operator then you don't know what using it would result to unless you look at the source code where it's defined. Since you want to remove the exact audio component and not an instance of it that looks the same, you want to use pointers in your array. That also helps performance since your are not copying the whole component when calling RemoveArrayElement(...); but a single pointer. Also when there are two identical audio components stored in the array and they are at index a and a+1, then removing the audio component at index a the next iteration would skip your second audio component since all upper indexes are decremented by one.

How do I delete the least recently accessed key in an associative array?

I have an associative array limited to x amount of keys and want to remove the least recently accessed key in order to add another. I found HashAA in mintl which would be able to do the job in D1 but I've found nothing for D2. Is there anything that supports this now or would I need to maintain a second array to get the job done?
I don't have a real answer but I thought it would be fun to try and implement it in a few minutes (it's probably very inefficient and maybe buggy):
import std.stdio;
import std.traits;
struct MyHash(AA, size_t Limit)
if (isAssociativeArray!AA)
{
alias KeyType!AA Key;
alias ValueType!AA Value;
void opIndexAssign(Value value, Key key)
{
if (hash.length >= Limit)
{
Key leastUsed = leastUsedKey;
hash.remove(leastUsed);
counts.remove(leastUsed);
}
hash[key] = value;
}
Value opIndex(Key key)
{
counts[key]++;
return hash[key];
}
Value[Key] hash;
alias hash this;
private:
#property Key leastUsedKey()
{
Key result;
size_t maxCount = size_t.max;
foreach (key; hash.byKey)
{
if (auto count = key in counts)
{
if (*count < maxCount)
{
maxCount = *count;
result = key;
}
}
else
{
return key;
}
}
return result;
}
size_t[Key] counts;
}
// just to avoid declaring variables in main()
#property void consume(int key) { }
void main()
{
MyHash!(int[int], 3) hash;
hash[0] = 0;
hash[1] = 0;
hash[2] = 0;
writeln(hash.keys);
hash[2].consume;
hash[5] = 0;
writeln(hash.keys); // 2 stays, 5 added
hash.clear();
hash[0] = 0;
hash[1] = 0;
hash[2] = 0;
hash[0].consume;
hash[1].consume;
hash[1].consume;
hash[2].consume;
hash[2].consume;
hash[2].consume;
hash[5] = 0;
writeln(hash); // (0 removed)
}
The built in AAs will rehash when they need to in order to fit more elements, are not fixed in size, and will not keep track of how you've accessed or added elements, and because the language has AAs built in, alternate hash table implementations are going to be relatively rare (most everyone just uses the built in AAs).
So, I'm pretty darn sure that you would need to do this yourself - probably by creating a wrapper around the built in AA type and having the wrapper keep track of all of the accesses to it so that it knows which key was accessed least recently.
You could always check out dcollections for some alternate container implemenations (and IIRC it does have a hash table in it, but I doubt that it does what you want). Personally, I've never even heard of a hash table that acted the way that you want it to, so I expect that what you're looking for is at least somewhat abnormal. But it should be simple enough to create a wrapper around AAs which acts the way that you want.

Fastest container or algorithm for unique reusable ids in C++

I have a need for unique reusable ids. The user can choose his own ids or he can ask for a free one. The API is basically
class IdManager {
public:
int AllocateId(); // Allocates an id
void FreeId(int id); // Frees an id so it can be used again
bool MarkAsUsed(int id); // Let's the user register an id.
// returns false if the id was already used.
bool IsUsed(int id); // Returns true if id is used.
};
Assume ids happen to start at 1 and progress, 2, 3, etc. This is not a requirement, just to help illustrate.
IdManager mgr;
mgr.MarkAsUsed(3);
printf ("%d\n", mgr.AllocateId());
printf ("%d\n", mgr.AllocateId());
printf ("%d\n", mgr.AllocateId());
Would print
1
2
4
Because id 3 has already been declared used.
What's the best container / algorithm to both remember which ids are used AND find a free id?
If you want to know the a specific use case, OpenGL's glGenTextures, glBindTexture and glDeleteTextures are equivalent to AllocateId, MarkAsUsed and FreeId
My idea is to use std::set and Boost.interval so IdManager will hold a set of non-overlapping intervals of free IDs.
AllocateId() is very simple and very quick and just returns the left boundary of the first free interval. Other two methods are slightly more difficult because it might be necessary to split an existing interval or to merge two adjacent intervals. However they are also quite fast.
So this is an illustration of the idea of using intervals:
IdManager mgr; // Now there is one interval of free IDs: [1..MAX_INT]
mgr.MarkAsUsed(3);// Now there are two interval of free IDs: [1..2], [4..MAX_INT]
mgr.AllocateId(); // two intervals: [2..2], [4..MAX_INT]
mgr.AllocateId(); // Now there is one interval: [4..MAX_INT]
mgr.AllocateId(); // Now there is one interval: [5..MAX_INT]
This is code itself:
#include <boost/numeric/interval.hpp>
#include <limits>
#include <set>
#include <iostream>
class id_interval
{
public:
id_interval(int ll, int uu) : value_(ll,uu) {}
bool operator < (const id_interval& ) const;
int left() const { return value_.lower(); }
int right() const { return value_.upper(); }
private:
boost::numeric::interval<int> value_;
};
class IdManager {
public:
IdManager();
int AllocateId(); // Allocates an id
void FreeId(int id); // Frees an id so it can be used again
bool MarkAsUsed(int id); // Let's the user register an id.
private:
typedef std::set<id_interval> id_intervals_t;
id_intervals_t free_;
};
IdManager::IdManager()
{
free_.insert(id_interval(1, std::numeric_limits<int>::max()));
}
int IdManager::AllocateId()
{
id_interval first = *(free_.begin());
int free_id = first.left();
free_.erase(free_.begin());
if (first.left() + 1 <= first.right()) {
free_.insert(id_interval(first.left() + 1 , first.right()));
}
return free_id;
}
bool IdManager::MarkAsUsed(int id)
{
id_intervals_t::iterator it = free_.find(id_interval(id,id));
if (it == free_.end()) {
return false;
} else {
id_interval free_interval = *(it);
free_.erase (it);
if (free_interval.left() < id) {
free_.insert(id_interval(free_interval.left(), id-1));
}
if (id +1 <= free_interval.right() ) {
free_.insert(id_interval(id+1, free_interval.right()));
}
return true;
}
}
void IdManager::FreeId(int id)
{
id_intervals_t::iterator it = free_.find(id_interval(id,id));
if (it != free_.end() && it->left() <= id && it->right() > id) {
return ;
}
it = free_.upper_bound(id_interval(id,id));
if (it == free_.end()) {
return ;
} else {
id_interval free_interval = *(it);
if (id + 1 != free_interval.left()) {
free_.insert(id_interval(id, id));
} else {
if (it != free_.begin()) {
id_intervals_t::iterator it_2 = it;
--it_2;
if (it_2->right() + 1 == id ) {
id_interval free_interval_2 = *(it_2);
free_.erase(it);
free_.erase(it_2);
free_.insert(
id_interval(free_interval_2.left(),
free_interval.right()));
} else {
free_.erase(it);
free_.insert(id_interval(id, free_interval.right()));
}
} else {
free_.erase(it);
free_.insert(id_interval(id, free_interval.right()));
}
}
}
}
bool id_interval::operator < (const id_interval& s) const
{
return
(value_.lower() < s.value_.lower()) &&
(value_.upper() < s.value_.lower());
}
int main()
{
IdManager mgr;
mgr.MarkAsUsed(3);
printf ("%d\n", mgr.AllocateId());
printf ("%d\n", mgr.AllocateId());
printf ("%d\n", mgr.AllocateId());
return 0;
}
It would be good to know how many ids you're supposed to keep track of. If there's only a hundred or so, a simple set would do, with linear traversal to get a new id. If it's more like a few thousands, then of course the linear traversal will become a performance killer, especially considering the cache unfriendliness of the set.
Personally, I would go for the following:
set, which helps keeping track of the ids easily O(log N)
proposing the new id as the current maximum + 1... O(1)
If you don't allocate (in the lifetime of the application) more than max<int>() ids, it should be fine, otherwise... use a larger type (make it unsigned, use a long or long long) that's the easiest to begin with.
And if it does not suffice, leave me a comment and I'll edit and search for more complicated solutions. But the more complicated the book-keeping, the longer it'll take to execute in practice and the higher the chances of making a mistake.
But I don't think you have to guarantee the id must starts from 1. You can just make sure the available id must be larger than all allocated ids.
Like if the 3 is registered first, then the next available id can just be 4. I don't think it is necessary to use 1.
I'm assuming that you want to be able to use all available values for the Id type and that you want to reuse freed Ids? I'm also assuming that you'll lock the collection if you're using it from more than one thread...
I'd create a class with a set to store the allocated ids, a list to store the free ids and a max allocated value to prevent me having to preload the free id list with every available id.
So you start off with an empty set of allocated ids and empty list of free ids and the max allocated as 0. You allocate, take the head of the free list if there is one, else take max, check it's not in your set of allocated ids as it might be if someone reserved it, if it is, increment max and try again, if not add it to the set and return it.
When you free an id you simply check it's in your set and if so push it on your free list.
To reserve an id you simply check the set and if not present add it.
This recycles ids quickly, which may or may not be good for you, that is, allocate(), free(), allocate() will give you the same id back if no other threads are accessing the collection.
Compressed vector. But I don't think any container would make noticeable difference.
Normally, i'd say stick to an simple implementation until you have an idea of which methods are used most. Premature tuning might prove wrong. Use the simple implementation, and log its use, then you can optimize from the functions that are used the most. No use in optimizing for quick removal or quick allocation if you only need a couple of hundred ids and a simple vector would be enough.
Similar to skwllsp, I'd keep track of the ranges that have not been allocated, but my methods are slightly different. The base container would be a map, with the key being the upper bound of the range and the value being the lower bound.
IdManager::IdManager()
{
m_map.insert(std::make_pair(std::numeric_limits<int>::max(), 1);
}
int IdManager::AllocateId()
{
assert(!m_map.empty());
MyMap::iterator p = m_map.begin();
int id = p->second;
++p->second;
if (p->second > p->first)
m_map.erase(p);
return id;
}
void IdManager::FreeId(int id)
{
// I'll fill this in later
}
bool IdManager::MarkAsUsed(int id)
{
MyMap::iterator p = m_map.lower_bound(id);
// return false if the ID is already allocated
if (p == m_map.end() || id < p->second || id > p->first)))
return false;
// first thunderstorm of the season, I'll leave this for now before the power glitches
}
bool IdManager::IsUsed(int id)
{
MyMap::iterator p = m_map.lower_bound(id);
return (p != m_map.end() && id >= p->second && id <= p->first);
}
So a friend pointed out that in this case a hash might be better. Most OpenGL programs don't use more than a few thousand ids so a hash with say 4096 slots is almost guaranteed to have only 1 or 2 entries per slot. There is some degenerate case where lots of ids might go in 1 slot but that's seriously unlikely. Using a hash would make AllocateID much slower but a set could be used for that. Allocating being slower is less important than InUse being fast for my use case.

Finding a nonexisting key in a std::map

Is there a way to find a nonexisting key in a map?
I am using std::map<int,myclass>, and I want to automatically generate a key for new items. Items may be deleted from the map in different order from their insertion.
The myclass items may, or may not be identical, so they can not serve as a key by themself.
During the run time of the program, there is no limit to the number of items that are generated and deleted, so I can not use a counter as a key.
An alternative data structure that have the same functionality and performance will do.
Edit
I trying to build a container for my items - such that I can delete/modify items according to their keys, and I can iterate over the items. The key value itself means nothing to me, however, other objects will store those keys for their internal usage.
The reason I can not use incremental counter, is that during the life-span of the program they may be more than 2^32 (or theoretically 2^64) items, however item 0 may theoretically still exist even after all other items are deleted.
It would be nice to ask std::map for the lowest-value non-used key, so i can use it for new items, instead of using a vector or some other extrnal storage for non-used keys.
I'd suggest a combination of counter and queue. When you delete an item from the map, add its key to the queue. The queue then keeps track of the keys that have been deleted from the map so that they can be used again. To get a new key, you first check if the queue is empty. If it isn't, pop the top index off and use it, otherwise use the counter to get the next available key.
Let me see if I understand. What you want to do is
look for a key.
If not present, insert an element.
Items may be deleted.
Keep a counter (wait wait) and a vector. The vector will keep the ids of the deleted items.
When you are about to insert the new element,look for a key in the vector. If vector is not empty, remove the key and use it. If its empty, take one from the counter (counter++).
However, if you neveer remove items from the map, you are just stuck with a counter.
Alternative:
How about using the memory address of the element as a key ?
I would say that for general case, when key can have any type allowed by map, this is not possible. Even ability to say whether some unused key exists requires some knowledge about type.
If we consider situation with int, you can store std::set of contiguous segments of unused keys (since these segments do not overlap, natural ordering can be used - simply compare their starting points). When a new key is needed, you take the first segment, cut off first index and place the rest in the set (if the rest is not empty). When some key is released, you find whether there are neighbour segments in the set (due to set nature it's possible with O(log n) complexity) and perform merging if needed, otherwise simply put [n,n] segment into the set.
in this way you will definitely have the same order of time complexity and order of memory consumption as map has independently on requests history (because number of segments cannot be more than map.size()+1)
something like this:
class TKeyManager
{
public:
TKeyManager()
{
FreeKeys.insert(
std::make_pair(
std::numeric_limits<int>::min(),
std::numeric_limits<int>::max());
}
int AlocateKey()
{
if(FreeKeys.empty())
throw something bad;
const std::pair<int,int> freeSegment=*FreeKeys.begin();
if(freeSegment.second>freeSegment.first)
FreeKeys.insert(std::make_pair(freeSegment.first+1,freeSegment.second));
return freeSegment.first;
}
void ReleaseKey(int key)
{
std:set<std::pair<int,int>>::iterator position=FreeKeys.insert(std::make_pair(key,key)).first;
if(position!=FreeKeys.begin())
{//try to merge with left neighbour
std::set<std::pair<int,int>>::iterator left=position;
--left;
if(left->second+1==key)
{
left->second=key;
FreeKeys.erase(position);
position=left;
}
}
if(position!=--FreeKeys.end())
{//try to merge with right neighbour
std::set<std::pair<int,int>>::iterator right=position;
++right;
if(right->first==key+1)
{
position->second=right->second;
FreeKeys.erase(right);
}
}
}
private:
std::set<std::pair<int,int>> FreeKeys;
};
Is there a way to find a nonexisting
key in a map?
I'm not sure what you mean here. How can you find something that doesn't exist? Do you mean, is there a way to tell if a map does not contain a key?
If that's what you mean, you simply use the find function, and if the key doesn't exist it will return an iterator pointing to end().
if (my_map.find(555) == my_map.end()) { /* do something */ }
You go on to say...
I am using std::map, and
I want to automatically generate a key
for new items. Items may be deleted
from the map in different order from
their insertion. The myclass items may, or may not be identical, so they can not serve as a key by themself.
It's a bit unclear to me what you're trying to accomplish here. It seems your problem is that you want to store instances of myclass in a map, but since you may have duplicate values of myclass, you need some way to generate a unique key. Rather than doing that, why not just use std::multiset<myclass> and just store duplicates? When you look up a particular value of myclass, the multiset will return an iterator to all the instances of myclass which have that value. You'll just need to implement a comparison functor for myclass.
Could you please clarify why you can not use a simple incremental counter as auto-generated key? (increment on insert)? It seems that there's no problem doing that.
Consider, that you decided how to generate non-counter based keys and found that generating them in a bulk is much more effective than generating them one-by-one.
Having this generator proved to be "infinite" and "statefull" (it is your requirement), you can create a second fixed sized container with say 1000 unused keys.
Supply you new entries in map with keys from this container, and return keys back for recycling.
Set some low "threshold" to react on key container reaching low level and refill keys in bulk using "infinite" generator.
The actual posted problem still exists "how to make efficient generator based on non-counter". You may want to have a second look at the "infinity" requirement and check if say 64-bit or 128-bit counter still can satisfy your algorithms for some limited period of time like 1000 years.
use uint64_t as a key type of sequence or even if you think that it will be not enough
struct sequence_key_t {
uint64_t upper;
uint64_t lower;
operator++();
bool operator<()
};
Like:
sequence_key_t global_counter;
std::map<sequence_key_t,myclass> my_map;
my_map.insert(std::make_pair(++global_counter, myclass()));
and you will not have any problems.
Like others I am having difficulty figuring out exactly what you want. It sounds like you want to create an item if it is not found. sdt::map::operator[] ( const key_type& x ) will do this for you.
std::map<int, myclass> Map;
myclass instance1, instance2;
Map[instance1] = 5;
Map[instance2] = 6;
Is this what you are thinking of?
Going along with other answers, I'd suggest a simple counter for generating the ids. If you're worried about being perfectly correct, you could use an arbitrary precision integer for the counter, rather than a built in type. Or something like the following, which will iterate through all possible strings.
void string_increment(std::string& counter)
{
bool carry=true;
for (size_t i=0;i<counter.size();++i)
{
unsigned char original=static_cast<unsigned char>(counter[i]);
if (carry)
{
++counter[i];
}
if (original>static_cast<unsigned char>(counter[i]))
{
carry=true;
}
else
{
carry=false;
}
}
if (carry)
{
counter.push_back(0);
}
}
e.g. so that you have:
std::string counter; // empty string
string_increment(counter); // now counter=="\x00"
string_increment(counter); // now counter=="\x01"
...
string_increment(counter); // now counter=="\xFF"
string_increment(counter); // now counter=="\x00\x00"
string_increment(counter); // now counter=="\x01\x00"
...
string_increment(counter); // now counter=="\xFF\x00"
string_increment(counter); // now counter=="\x00\x01"
string_increment(counter); // now counter=="\x01\x01"
...
string_increment(counter); // now counter=="\xFF\xFF"
string_increment(counter); // now counter=="\x00\x00\x00"
string_increment(counter); // now counter=="\x01\x00\x00"
// etc..
Another option, if the working set actually in the map is small enough would be to use an incrementing key, then re-generate the keys when the counter is about to wrap. This solution would only require temporary extra storage. The hash table performance would be unchanged, and the key generation would just be an if and an increment.
The number of items in the current working set would really determine if this approach is viable or not.
I loved Jon Benedicto's and Tom's answer very much. To be fair, the other answers that only used counters may have been the starting point.
Problem with only using counters
You always have to increment higher and higher; never trying to fill the empty gaps.
Once you run out of numbers and wrap around, you have to do log(n) iterations to find unused keys.
Problem with the queue for holding used keys
It is easy to imagine lots and lots of used keys being stored in this queue.
My Improvement to queues!
Rather than storing single used keys in the queue; we store ranges of unused keys.
Interface
using Key = wchar_t; //In my case
struct Range
{
Key first;
Key last;
size_t size() { return last - first + 1; }
};
bool operator< (const Range&,const Range&);
bool operator< (const Range&,Key);
bool operator< (Key,const Range&);
struct KeyQueue__
{
public:
virtual void addKey(Key)=0;
virtual Key getUniqueKey()=0;
virtual bool shouldMorph()=0;
protected:
Key counter = 0;
friend class Morph;
};
struct KeyQueue : KeyQueue__
{
public:
void addKey(Key)override;
Key getUniqueKey()override;
bool shouldMorph()override;
private:
std::vector<Key> pool;
friend class Morph;
};
struct RangeKeyQueue : KeyQueue__
{
public:
void addKey(Key)override;
Key getUniqueKey()override;
bool shouldMorph()override;
private:
boost::container::flat_set<Range,std::less<>> pool;
friend class Morph;
};
void morph(KeyQueue__*);
struct Morph
{
static void morph(const KeyQueue &from,RangeKeyQueue &to);
static void morph(const RangeKeyQueue &from,KeyQueue &to);
};
Implementation
Note: Keys being added are assumed to be key not found in queue
// Assumes that Range is valid. first <= last
// Assumes that Ranges do not overlap
bool operator< (const Range &l,const Range &r)
{
return l.first < r.first;
}
// Assumes that Range is valid. first <= last
bool operator< (const Range &l,Key r)
{
int diff_1 = l.first - r;
int diff_2 = l.last - r;
return diff_1 < -1 && diff_2 < -1;
}
// Assumes that Range is valid. first <= last
bool operator< (Key l,const Range &r)
{
int diff = l - r.first;
return diff < -1;
}
void KeyQueue::addKey(Key key)
{
if(counter - 1 == key) counter = key;
else pool.push_back(key);
}
Key KeyQueue::getUniqueKey()
{
if(pool.empty()) return counter++;
else
{
Key key = pool.back();
pool.pop_back();
return key;
}
}
bool KeyQueue::shouldMorph()
{
return pool.size() > 10;
}
void RangeKeyQueue::addKey(Key key)
{
if(counter - 1 == key) counter = key;
else
{
auto elem = pool.find(key);
if(elem == pool.end()) pool.insert({key,key});
else // Expand existing range
{
Range &range = (Range&)*elem;
// Note at this point, key is 1 value less or greater than range
if(range.first > key) range.first = key;
else range.last = key;
}
}
}
Key RangeKeyQueue::getUniqueKey()
{
if(pool.empty()) return counter++;
else
{
Range &range = (Range&)*pool.begin();
Key key = range.first++;
if(range.first > range.last) // exhausted all keys in range
pool.erase(pool.begin());
return key;
}
}
bool RangeKeyQueue::shouldMorph()
{
return pool.size() == 0 || pool.size() == 1 && pool.begin()->size() < 4;
}
void morph(KeyQueue__ *obj)
{
if(KeyQueue *queue = dynamic_cast<KeyQueue*>(obj))
{
RangeKeyQueue *new_queue = new RangeKeyQueue();
Morph::morph(*queue,*new_queue);
obj = new_queue;
}
else if(RangeKeyQueue *queue = dynamic_cast<RangeKeyQueue*>(obj))
{
KeyQueue *new_queue = new KeyQueue();
Morph::morph(*queue,*new_queue);
obj = new_queue;
}
}
void Morph::morph(const KeyQueue &from,RangeKeyQueue &to)
{
to.counter = from.counter;
for(Key key : from.pool) to.addKey(key);
}
void Morph::morph(const RangeKeyQueue &from,KeyQueue &to)
{
to.counter = from.counter;
for(Range range : from.pool)
while(range.first <= range.last)
to.addKey(range.first++);
}
Usage:
int main()
{
std::vector<Key> keys;
KeyQueue__ *keyQueue = new KeyQueue();
srand(time(NULL));
bool insertKey = true;
for(int i=0; i < 1000; ++i)
{
if(insertKey)
{
Key key = keyQueue->getUniqueKey();
keys.push_back(key);
}
else
{
int index = rand() % keys.size();
Key key = keys[index];
keys.erase(keys.begin()+index);
keyQueue->addKey(key);
}
if(keyQueue->shouldMorph())
{
morph(keyQueue);
}
insertKey = rand() % 3; // more chances of insert
}
}