Change LinkGraph::EdgeMatrix to a sparse storage format
Use a btree_map instead of a SmallMatrix. This avoids excessive storage requirements for large graphs, and overly expensive edge matrix resize operations. Remove next_edge iteration mechanism.
This commit is contained in:
@@ -427,10 +427,9 @@ DemandCalculator::DemandCalculator(LinkGraphJob &job) :
|
|||||||
};
|
};
|
||||||
std::vector<bool> symmetric_edges(se_index(0, size));
|
std::vector<bool> symmetric_edges(se_index(0, size));
|
||||||
|
|
||||||
for (NodeID node_id = 0; node_id < size; ++node_id) {
|
for (auto &it : job.Graph().GetEdges()) {
|
||||||
Node from = job[node_id];
|
if (it.first.first != it.first.second) {
|
||||||
for (EdgeIterator it(from.Begin()); it != from.End(); ++it) {
|
symmetric_edges[se_index(it.first.first, it.first.second)] = true;
|
||||||
symmetric_edges[se_index(node_id, it->first)] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint first_unseen = 0;
|
uint first_unseen = 0;
|
||||||
|
@@ -32,20 +32,6 @@ inline void LinkGraph::BaseNode::Init(TileIndex xy, StationID st, uint demand)
|
|||||||
this->last_update = INVALID_DATE;
|
this->last_update = INVALID_DATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an edge.
|
|
||||||
*/
|
|
||||||
inline void LinkGraph::BaseEdge::Init()
|
|
||||||
{
|
|
||||||
this->capacity = 0;
|
|
||||||
this->usage = 0;
|
|
||||||
this->travel_time_sum = 0;
|
|
||||||
this->last_unrestricted_update = INVALID_DATE;
|
|
||||||
this->last_restricted_update = INVALID_DATE;
|
|
||||||
this->last_aircraft_update = INVALID_DATE;
|
|
||||||
this->next_edge = INVALID_NODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shift all dates by given interval.
|
* Shift all dates by given interval.
|
||||||
* This is useful if the date has been modified with the cheat menu.
|
* This is useful if the date has been modified with the cheat menu.
|
||||||
@@ -57,12 +43,12 @@ void LinkGraph::ShiftDates(int interval)
|
|||||||
for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
|
for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
|
||||||
BaseNode &source = this->nodes[node1];
|
BaseNode &source = this->nodes[node1];
|
||||||
if (source.last_update != INVALID_DATE) source.last_update += interval;
|
if (source.last_update != INVALID_DATE) source.last_update += interval;
|
||||||
for (NodeID node2 = 0; node2 < this->Size(); ++node2) {
|
}
|
||||||
BaseEdge &edge = this->edges[node1][node2];
|
for (auto &it : this->edges) {
|
||||||
if (edge.last_unrestricted_update != INVALID_DATE) edge.last_unrestricted_update += interval;
|
BaseEdge &edge = it.second;
|
||||||
if (edge.last_restricted_update != INVALID_DATE) edge.last_restricted_update += interval;
|
if (edge.last_unrestricted_update != INVALID_DATE) edge.last_unrestricted_update += interval;
|
||||||
if (edge.last_aircraft_update != INVALID_DATE) edge.last_aircraft_update += interval;
|
if (edge.last_restricted_update != INVALID_DATE) edge.last_restricted_update += interval;
|
||||||
}
|
if (edge.last_aircraft_update != INVALID_DATE) edge.last_aircraft_update += interval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,18 +57,18 @@ void LinkGraph::Compress()
|
|||||||
this->last_compression = (_date + this->last_compression) / 2;
|
this->last_compression = (_date + this->last_compression) / 2;
|
||||||
for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
|
for (NodeID node1 = 0; node1 < this->Size(); ++node1) {
|
||||||
this->nodes[node1].supply /= 2;
|
this->nodes[node1].supply /= 2;
|
||||||
for (NodeID node2 = 0; node2 < this->Size(); ++node2) {
|
}
|
||||||
BaseEdge &edge = this->edges[node1][node2];
|
for (auto &it : this->edges) {
|
||||||
if (edge.capacity > 0) {
|
BaseEdge &edge = it.second;
|
||||||
uint new_capacity = std::max(1U, edge.capacity / 2);
|
if (edge.capacity > 0) {
|
||||||
if (edge.capacity < (1 << 16)) {
|
uint new_capacity = std::max(1U, edge.capacity / 2);
|
||||||
edge.travel_time_sum = edge.travel_time_sum * new_capacity / edge.capacity;
|
if (edge.capacity < (1 << 16)) {
|
||||||
} else if (edge.travel_time_sum != 0) {
|
edge.travel_time_sum = edge.travel_time_sum * new_capacity / edge.capacity;
|
||||||
edge.travel_time_sum = std::max(1ULL, edge.travel_time_sum / 2);
|
} else if (edge.travel_time_sum != 0) {
|
||||||
}
|
edge.travel_time_sum = std::max(1ULL, edge.travel_time_sum / 2);
|
||||||
edge.capacity = new_capacity;
|
|
||||||
edge.usage /= 2;
|
|
||||||
}
|
}
|
||||||
|
edge.capacity = new_capacity;
|
||||||
|
edge.usage /= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,29 +82,23 @@ void LinkGraph::Merge(LinkGraph *other)
|
|||||||
Date age = _date - this->last_compression + 1;
|
Date age = _date - this->last_compression + 1;
|
||||||
Date other_age = _date - other->last_compression + 1;
|
Date other_age = _date - other->last_compression + 1;
|
||||||
NodeID first = this->Size();
|
NodeID first = this->Size();
|
||||||
|
this->nodes.reserve(first + other->Size());
|
||||||
for (NodeID node1 = 0; node1 < other->Size(); ++node1) {
|
for (NodeID node1 = 0; node1 < other->Size(); ++node1) {
|
||||||
Station *st = Station::Get(other->nodes[node1].station);
|
Station *st = Station::Get(other->nodes[node1].station);
|
||||||
NodeID new_node = this->AddNode(st);
|
NodeID new_node = this->AddNode(st);
|
||||||
this->nodes[new_node].supply = LinkGraph::Scale(other->nodes[node1].supply, age, other_age);
|
this->nodes[new_node].supply = LinkGraph::Scale(other->nodes[node1].supply, age, other_age);
|
||||||
st->goods[this->cargo].link_graph = this->index;
|
st->goods[this->cargo].link_graph = this->index;
|
||||||
st->goods[this->cargo].node = new_node;
|
st->goods[this->cargo].node = new_node;
|
||||||
for (NodeID node2 = 0; node2 < node1; ++node2) {
|
}
|
||||||
BaseEdge &forward = this->edges[new_node][first + node2];
|
for (const auto &iter : other->edges) {
|
||||||
BaseEdge &backward = this->edges[first + node2][new_node];
|
std::pair<NodeID, NodeID> key = std::make_pair(iter.first.first + first, iter.first.second + first);
|
||||||
forward = other->edges[node1][node2];
|
BaseEdge edge = iter.second;
|
||||||
backward = other->edges[node2][node1];
|
if (key.first != key.second) {
|
||||||
forward.capacity = LinkGraph::Scale(forward.capacity, age, other_age);
|
edge.capacity = LinkGraph::Scale(edge.capacity, age, other_age);
|
||||||
forward.usage = LinkGraph::Scale(forward.usage, age, other_age);
|
edge.usage = LinkGraph::Scale(edge.usage, age, other_age);
|
||||||
forward.travel_time_sum = LinkGraph::Scale(forward.travel_time_sum, age, other_age);
|
edge.travel_time_sum = LinkGraph::Scale(edge.travel_time_sum, age, other_age);
|
||||||
if (forward.next_edge != INVALID_NODE) forward.next_edge += first;
|
|
||||||
backward.capacity = LinkGraph::Scale(backward.capacity, age, other_age);
|
|
||||||
backward.usage = LinkGraph::Scale(backward.usage, age, other_age);
|
|
||||||
backward.travel_time_sum = LinkGraph::Scale(backward.travel_time_sum, age, other_age);
|
|
||||||
if (backward.next_edge != INVALID_NODE) backward.next_edge += first;
|
|
||||||
}
|
}
|
||||||
BaseEdge &new_start = this->edges[new_node][new_node];
|
this->edges[key] = edge;
|
||||||
new_start = other->edges[node1][node1];
|
|
||||||
if (new_start.next_edge != INVALID_NODE) new_start.next_edge += first;
|
|
||||||
}
|
}
|
||||||
delete other;
|
delete other;
|
||||||
}
|
}
|
||||||
@@ -131,31 +111,31 @@ void LinkGraph::RemoveNode(NodeID id)
|
|||||||
{
|
{
|
||||||
assert(id < this->Size());
|
assert(id < this->Size());
|
||||||
|
|
||||||
|
std::vector<std::pair<std::pair<NodeID, NodeID>, BaseEdge>> saved_nodes;
|
||||||
|
|
||||||
NodeID last_node = this->Size() - 1;
|
NodeID last_node = this->Size() - 1;
|
||||||
for (NodeID i = 0; i <= last_node; ++i) {
|
|
||||||
(*this)[i].RemoveEdge(id);
|
for (auto iter = this->edges.begin(); iter != this->edges.end();) {
|
||||||
BaseEdge *node_edges = this->edges[i];
|
if (iter->first.first == id || iter->first.second == id) {
|
||||||
NodeID prev = i;
|
/* Erase this node */
|
||||||
NodeID next = node_edges[i].next_edge;
|
iter = this->edges.erase(iter);
|
||||||
while (next != INVALID_NODE) {
|
} else if (iter->first.first == last_node || iter->first.second == last_node) {
|
||||||
if (next == last_node) {
|
/* The edge refers to the last node, remove and save to be re-added later with the updated id */
|
||||||
node_edges[prev].next_edge = id;
|
saved_nodes.push_back(std::make_pair(std::make_pair(iter->first.first == last_node ? id : iter->first.first, iter->first.second == last_node ? id : iter->first.second), iter->second));
|
||||||
break;
|
iter = this->edges.erase(iter);
|
||||||
}
|
} else {
|
||||||
prev = next;
|
++iter;
|
||||||
next = node_edges[prev].next_edge;
|
|
||||||
}
|
}
|
||||||
node_edges[id] = node_edges[last_node];
|
|
||||||
}
|
}
|
||||||
|
for (const auto &it : saved_nodes) {
|
||||||
|
this->edges.insert(it);
|
||||||
|
}
|
||||||
|
|
||||||
Station::Get(this->nodes[last_node].station)->goods[this->cargo].node = id;
|
Station::Get(this->nodes[last_node].station)->goods[this->cargo].node = id;
|
||||||
/* Erase node by swapping with the last element. Node index is referenced
|
/* Erase node by swapping with the last element. Node index is referenced
|
||||||
* directly from station goods entries so the order and position must remain. */
|
* directly from station goods entries so the order and position must remain. */
|
||||||
this->nodes[id] = this->nodes.back();
|
this->nodes[id] = this->nodes.back();
|
||||||
this->nodes.pop_back();
|
this->nodes.pop_back();
|
||||||
this->edges.EraseColumn(id);
|
|
||||||
/* Not doing EraseRow here, as having the extra invalid row doesn't hurt
|
|
||||||
* and removing it would trigger a lot of memmove. The data has already
|
|
||||||
* been copied around in the loop above. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,43 +152,26 @@ NodeID LinkGraph::AddNode(const Station *st)
|
|||||||
|
|
||||||
NodeID new_node = this->Size();
|
NodeID new_node = this->Size();
|
||||||
this->nodes.emplace_back();
|
this->nodes.emplace_back();
|
||||||
/* Avoid reducing the height of the matrix as that is expensive and we
|
|
||||||
* most likely will increase it again later which is again expensive. */
|
|
||||||
this->edges.Resize(new_node + 1U, std::max(new_node + 1U, this->edges.Height()));
|
|
||||||
|
|
||||||
this->nodes[new_node].Init(st->xy, st->index,
|
this->nodes[new_node].Init(st->xy, st->index,
|
||||||
HasBit(good.status, GoodsEntry::GES_ACCEPTANCE));
|
HasBit(good.status, GoodsEntry::GES_ACCEPTANCE));
|
||||||
|
|
||||||
BaseEdge *new_edges = this->edges[new_node];
|
|
||||||
|
|
||||||
/* Reset the first edge starting at the new node */
|
|
||||||
new_edges[new_node].next_edge = INVALID_NODE;
|
|
||||||
|
|
||||||
for (NodeID i = 0; i <= new_node; ++i) {
|
|
||||||
new_edges[i].Init();
|
|
||||||
this->edges[i][new_node].Init();
|
|
||||||
}
|
|
||||||
return new_node;
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill an edge with values from a link. Set the restricted or unrestricted
|
* Fill an edge with values from a link. Set the restricted or unrestricted
|
||||||
* update timestamp according to the given update mode.
|
* update timestamp according to the given update mode.
|
||||||
* @param to Destination node of the link.
|
* @param edge Edge to fill.
|
||||||
* @param capacity Capacity of the link.
|
* @param capacity Capacity of the link.
|
||||||
* @param usage Usage to be added.
|
* @param usage Usage to be added.
|
||||||
* @param mode Update mode to be used.
|
* @param mode Update mode to be used.
|
||||||
*/
|
*/
|
||||||
void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
static void AddEdge(LinkGraph::BaseEdge &edge, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
||||||
{
|
{
|
||||||
assert(this->index != to);
|
|
||||||
BaseEdge &edge = this->edges[to];
|
|
||||||
BaseEdge &first = this->edges[this->index];
|
|
||||||
edge.capacity = capacity;
|
edge.capacity = capacity;
|
||||||
edge.usage = usage;
|
edge.usage = usage;
|
||||||
edge.travel_time_sum = travel_time * capacity;
|
edge.travel_time_sum = travel_time * capacity;
|
||||||
edge.next_edge = first.next_edge;
|
|
||||||
first.next_edge = to;
|
|
||||||
if (mode & EUM_UNRESTRICTED) edge.last_unrestricted_update = _date;
|
if (mode & EUM_UNRESTRICTED) edge.last_unrestricted_update = _date;
|
||||||
if (mode & EUM_RESTRICTED) edge.last_restricted_update = _date;
|
if (mode & EUM_RESTRICTED) edge.last_restricted_update = _date;
|
||||||
if (mode & EUM_AIRCRAFT) edge.last_aircraft_update = _date;
|
if (mode & EUM_AIRCRAFT) edge.last_aircraft_update = _date;
|
||||||
@@ -216,50 +179,34 @@ void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 trave
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an edge if none exists yet or updates an existing edge.
|
* Creates an edge if none exists yet or updates an existing edge.
|
||||||
|
* @param from Source node.
|
||||||
* @param to Target node.
|
* @param to Target node.
|
||||||
* @param capacity Capacity of the link.
|
* @param capacity Capacity of the link.
|
||||||
* @param usage Usage to be added.
|
* @param usage Usage to be added.
|
||||||
* @param mode Update mode to be used.
|
* @param mode Update mode to be used.
|
||||||
*/
|
*/
|
||||||
void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
void LinkGraph::UpdateEdge(NodeID from, NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
||||||
{
|
{
|
||||||
assert(capacity > 0);
|
assert(capacity > 0);
|
||||||
assert(usage <= capacity);
|
assert(usage <= capacity);
|
||||||
if (this->edges[to].capacity == 0) {
|
BaseEdge &edge = this->edges[std::make_pair(from, to)];
|
||||||
this->AddEdge(to, capacity, usage, travel_time, mode);
|
if (edge.capacity == 0) {
|
||||||
|
assert(from != to);
|
||||||
|
AddEdge(edge, capacity, usage, travel_time, mode);
|
||||||
} else {
|
} else {
|
||||||
(*this)[to].Update(capacity, usage, travel_time, mode);
|
Edge(edge).Update(capacity, usage, travel_time, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an outgoing edge from this node.
|
* Remove an outgoing edge from this node.
|
||||||
|
* @param from ID of source node.
|
||||||
* @param to ID of destination node.
|
* @param to ID of destination node.
|
||||||
*/
|
*/
|
||||||
void LinkGraph::Node::RemoveEdge(NodeID to)
|
void LinkGraph::RemoveEdge(NodeID from, NodeID to)
|
||||||
{
|
{
|
||||||
if (this->index == to) return;
|
if (from == to) return;
|
||||||
BaseEdge &edge = this->edges[to];
|
this->edges.erase(std::make_pair(from, to));
|
||||||
edge.capacity = 0;
|
|
||||||
edge.last_unrestricted_update = INVALID_DATE;
|
|
||||||
edge.last_restricted_update = INVALID_DATE;
|
|
||||||
edge.last_aircraft_update = INVALID_DATE;
|
|
||||||
edge.usage = 0;
|
|
||||||
edge.travel_time_sum = 0;
|
|
||||||
|
|
||||||
NodeID prev = this->index;
|
|
||||||
NodeID next = this->edges[this->index].next_edge;
|
|
||||||
while (next != INVALID_NODE) {
|
|
||||||
if (next == to) {
|
|
||||||
/* Will be removed, skip it. */
|
|
||||||
this->edges[prev].next_edge = edge.next_edge;
|
|
||||||
edge.next_edge = INVALID_NODE;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
prev = next;
|
|
||||||
next = this->edges[next].next_edge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,34 +221,35 @@ void LinkGraph::Node::RemoveEdge(NodeID to)
|
|||||||
*/
|
*/
|
||||||
void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
|
||||||
{
|
{
|
||||||
assert(this->edge.capacity > 0);
|
BaseEdge &edge = *(this->edge);
|
||||||
|
assert(edge.capacity > 0);
|
||||||
assert(capacity >= usage);
|
assert(capacity >= usage);
|
||||||
|
|
||||||
if (mode & EUM_INCREASE) {
|
if (mode & EUM_INCREASE) {
|
||||||
if (this->edge.travel_time_sum == 0) {
|
if (edge.travel_time_sum == 0) {
|
||||||
this->edge.travel_time_sum = (this->edge.capacity + capacity) * travel_time;
|
edge.travel_time_sum = (edge.capacity + capacity) * travel_time;
|
||||||
} else if (travel_time == 0) {
|
} else if (travel_time == 0) {
|
||||||
this->edge.travel_time_sum += this->edge.travel_time_sum / this->edge.capacity * capacity;
|
edge.travel_time_sum += edge.travel_time_sum / edge.capacity * capacity;
|
||||||
} else {
|
} else {
|
||||||
this->edge.travel_time_sum += travel_time * capacity;
|
edge.travel_time_sum += travel_time * capacity;
|
||||||
}
|
}
|
||||||
this->edge.capacity += capacity;
|
edge.capacity += capacity;
|
||||||
this->edge.usage += usage;
|
edge.usage += usage;
|
||||||
} else if (mode & EUM_REFRESH) {
|
} else if (mode & EUM_REFRESH) {
|
||||||
/* If travel time is not provided, we scale the stored time based on
|
/* If travel time is not provided, we scale the stored time based on
|
||||||
* the capacity increase. */
|
* the capacity increase. */
|
||||||
if (capacity > this->edge.capacity && travel_time == 0) {
|
if (capacity > edge.capacity && travel_time == 0) {
|
||||||
this->edge.travel_time_sum = this->edge.travel_time_sum / this->edge.capacity * capacity;
|
edge.travel_time_sum = edge.travel_time_sum / edge.capacity * capacity;
|
||||||
this->edge.capacity = capacity;
|
edge.capacity = capacity;
|
||||||
} else {
|
} else {
|
||||||
this->edge.capacity = std::max(this->edge.capacity, capacity);
|
edge.capacity = std::max(edge.capacity, capacity);
|
||||||
this->edge.travel_time_sum = std::max<uint64>(this->edge.travel_time_sum, travel_time * capacity);
|
edge.travel_time_sum = std::max<uint64>(edge.travel_time_sum, travel_time * capacity);
|
||||||
}
|
}
|
||||||
this->edge.usage = std::max(this->edge.usage, usage);
|
edge.usage = std::max(edge.usage, usage);
|
||||||
}
|
}
|
||||||
if (mode & EUM_UNRESTRICTED) this->edge.last_unrestricted_update = _date;
|
if (mode & EUM_UNRESTRICTED) edge.last_unrestricted_update = _date;
|
||||||
if (mode & EUM_RESTRICTED) this->edge.last_restricted_update = _date;
|
if (mode & EUM_RESTRICTED) edge.last_restricted_update = _date;
|
||||||
if (mode & EUM_AIRCRAFT) this->edge.last_aircraft_update = _date;
|
if (mode & EUM_AIRCRAFT) edge.last_aircraft_update = _date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -312,12 +260,5 @@ void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, Edge
|
|||||||
void LinkGraph::Init(uint size)
|
void LinkGraph::Init(uint size)
|
||||||
{
|
{
|
||||||
assert(this->Size() == 0);
|
assert(this->Size() == 0);
|
||||||
this->edges.Resize(size, size);
|
|
||||||
this->nodes.resize(size);
|
this->nodes.resize(size);
|
||||||
|
|
||||||
for (uint i = 0; i < size; ++i) {
|
|
||||||
this->nodes[i].Init();
|
|
||||||
BaseEdge *column = this->edges[i];
|
|
||||||
for (uint j = 0; j < size; ++j) column[j].Init();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include "../date_func.h"
|
#include "../date_func.h"
|
||||||
#include "../saveload/saveload_common.h"
|
#include "../saveload/saveload_common.h"
|
||||||
#include "linkgraph_type.h"
|
#include "linkgraph_type.h"
|
||||||
|
#include "../3rdparty/cpp-btree/btree_map.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
class LinkGraph;
|
class LinkGraph;
|
||||||
@@ -74,10 +75,23 @@ public:
|
|||||||
Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
|
Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
|
||||||
Date last_restricted_update; ///< When the restricted part of the link was last updated.
|
Date last_restricted_update; ///< When the restricted part of the link was last updated.
|
||||||
Date last_aircraft_update; ///< When aircraft capacity of the link was last updated.
|
Date last_aircraft_update; ///< When aircraft capacity of the link was last updated.
|
||||||
NodeID next_edge; ///< Destination of next valid edge starting at the same source node.
|
|
||||||
void Init();
|
void Init()
|
||||||
|
{
|
||||||
|
this->capacity = 0;
|
||||||
|
this->usage = 0;
|
||||||
|
this->travel_time_sum = 0;
|
||||||
|
this->last_unrestricted_update = INVALID_DATE;
|
||||||
|
this->last_restricted_update = INVALID_DATE;
|
||||||
|
this->last_aircraft_update = INVALID_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseEdge() { this->Init(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::vector<BaseNode> NodeVector;
|
||||||
|
typedef btree::btree_map<std::pair<NodeID, NodeID>, BaseEdge> EdgeMatrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for an edge (const or not) allowing retrieval, but no modification.
|
* Wrapper for an edge (const or not) allowing retrieval, but no modification.
|
||||||
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
|
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
|
||||||
@@ -85,7 +99,7 @@ public:
|
|||||||
template<typename Tedge>
|
template<typename Tedge>
|
||||||
class EdgeWrapper {
|
class EdgeWrapper {
|
||||||
protected:
|
protected:
|
||||||
Tedge &edge; ///< Actual edge to be used.
|
Tedge *edge; ///< Actual edge to be used.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -93,73 +107,69 @@ public:
|
|||||||
* Wrap a an edge.
|
* Wrap a an edge.
|
||||||
* @param edge Edge to be wrapped.
|
* @param edge Edge to be wrapped.
|
||||||
*/
|
*/
|
||||||
EdgeWrapper (Tedge &edge) : edge(edge) {}
|
EdgeWrapper (Tedge &edge) : edge(&edge) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get edge's capacity.
|
* Get edge's capacity.
|
||||||
* @return Capacity.
|
* @return Capacity.
|
||||||
*/
|
*/
|
||||||
uint Capacity() const { return this->edge.capacity; }
|
uint Capacity() const { return this->edge->capacity; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get edge's usage.
|
* Get edge's usage.
|
||||||
* @return Usage.
|
* @return Usage.
|
||||||
*/
|
*/
|
||||||
uint Usage() const { return this->edge.usage; }
|
uint Usage() const { return this->edge->usage; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get edge's average travel time.
|
* Get edge's average travel time.
|
||||||
* @return Travel time, in ticks.
|
* @return Travel time, in ticks.
|
||||||
*/
|
*/
|
||||||
uint32 TravelTime() const { return this->edge.travel_time_sum / this->edge.capacity; }
|
uint32 TravelTime() const { return this->edge->travel_time_sum / this->edge->capacity; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the date of the last update to the edge's unrestricted capacity.
|
* Get the date of the last update to the edge's unrestricted capacity.
|
||||||
* @return Last update.
|
* @return Last update.
|
||||||
*/
|
*/
|
||||||
Date LastUnrestrictedUpdate() const { return this->edge.last_unrestricted_update; }
|
Date LastUnrestrictedUpdate() const { return this->edge->last_unrestricted_update; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the date of the last update to the edge's restricted capacity.
|
* Get the date of the last update to the edge's restricted capacity.
|
||||||
* @return Last update.
|
* @return Last update.
|
||||||
*/
|
*/
|
||||||
Date LastRestrictedUpdate() const { return this->edge.last_restricted_update; }
|
Date LastRestrictedUpdate() const { return this->edge->last_restricted_update; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the date of the last update to the edge's aircraft capacity.
|
* Get the date of the last update to the edge's aircraft capacity.
|
||||||
* @return Last update.
|
* @return Last update.
|
||||||
*/
|
*/
|
||||||
Date LastAircraftUpdate() const { return this->edge.last_aircraft_update; }
|
Date LastAircraftUpdate() const { return this->edge->last_aircraft_update; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the date of the last update to any part of the edge's capacity.
|
* Get the date of the last update to any part of the edge's capacity.
|
||||||
* @return Last update.
|
* @return Last update.
|
||||||
*/
|
*/
|
||||||
Date LastUpdate() const { return std::max(this->edge.last_unrestricted_update, this->edge.last_restricted_update); }
|
Date LastUpdate() const { return std::max(this->edge->last_unrestricted_update, this->edge->last_restricted_update); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for a node (const or not) allowing retrieval, but no modification.
|
* Wrapper for a node (const or not) allowing retrieval, but no modification.
|
||||||
* @tparam Tedge Actual node class, may be "const BaseNode" or just "BaseNode".
|
* @tparam Tedge Actual node class, may be "const BaseNode" or just "BaseNode".
|
||||||
* @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge".
|
|
||||||
*/
|
*/
|
||||||
template<typename Tnode, typename Tedge>
|
template<typename Tnode>
|
||||||
class NodeWrapper {
|
class NodeWrapper {
|
||||||
protected:
|
protected:
|
||||||
Tnode &node; ///< Node being wrapped.
|
Tnode &node; ///< Node being wrapped.
|
||||||
Tedge *edges; ///< Outgoing edges for wrapped node.
|
NodeID index; ///< ID of wrapped node.
|
||||||
NodeID index; ///< ID of wrapped node.
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap a node.
|
* Wrap a node.
|
||||||
* @param node Node to be wrapped.
|
* @param node Node to be wrapped.
|
||||||
* @param edges Outgoing edges for node to be wrapped.
|
|
||||||
* @param index ID of node to be wrapped.
|
* @param index ID of node to be wrapped.
|
||||||
*/
|
*/
|
||||||
NodeWrapper(Tnode &node, Tedge *edges, NodeID index) : node(node),
|
NodeWrapper(Tnode &node, NodeID index) : node(node), index(index) {}
|
||||||
edges(edges), index(index) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get supply of wrapped node.
|
* Get supply of wrapped node.
|
||||||
@@ -190,117 +200,8 @@ public:
|
|||||||
* @return Location of the station.
|
* @return Location of the station.
|
||||||
*/
|
*/
|
||||||
TileIndex XY() const { return this->node.xy; }
|
TileIndex XY() const { return this->node.xy; }
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
NodeID GetNodeID() const { return this->index; }
|
||||||
* Base class for iterating across outgoing edges of a node. Only the real
|
|
||||||
* edges (those with capacity) are iterated. The ones with only distance
|
|
||||||
* information are skipped.
|
|
||||||
* @tparam Tedge Actual edge class. May be "BaseEdge" or "const BaseEdge".
|
|
||||||
* @tparam Titer Actual iterator class.
|
|
||||||
*/
|
|
||||||
template <class Tedge, class Tedge_wrapper, class Titer>
|
|
||||||
class BaseEdgeIterator {
|
|
||||||
protected:
|
|
||||||
Tedge *base; ///< Array of edges being iterated.
|
|
||||||
NodeID current; ///< Current offset in edges array.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "fake" pointer to enable operator-> on temporaries. As the objects
|
|
||||||
* returned from operator* aren't references but real objects, we have
|
|
||||||
* to return something that implements operator->, but isn't a pointer
|
|
||||||
* from operator->. A fake pointer.
|
|
||||||
*/
|
|
||||||
class FakePointer : public std::pair<NodeID, Tedge_wrapper> {
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a fake pointer from a pair of NodeID and edge.
|
|
||||||
* @param pair Pair to be "pointed" to (in fact shallow-copied).
|
|
||||||
*/
|
|
||||||
FakePointer(const std::pair<NodeID, Tedge_wrapper> &pair) : std::pair<NodeID, Tedge_wrapper>(pair) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the pair by operator->.
|
|
||||||
* @return Pair being "pointed" to.
|
|
||||||
*/
|
|
||||||
std::pair<NodeID, Tedge_wrapper> *operator->() { return this; }
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
* @param base Array of edges to be iterated.
|
|
||||||
* @param current ID of current node (to locate the first edge).
|
|
||||||
*/
|
|
||||||
BaseEdgeIterator (Tedge *base, NodeID current) :
|
|
||||||
base(base),
|
|
||||||
current(current == INVALID_NODE ? current : base[current].next_edge)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prefix-increment.
|
|
||||||
* @return This.
|
|
||||||
*/
|
|
||||||
Titer &operator++()
|
|
||||||
{
|
|
||||||
this->current = this->base[this->current].next_edge;
|
|
||||||
return static_cast<Titer &>(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Postfix-increment.
|
|
||||||
* @return Version of this before increment.
|
|
||||||
*/
|
|
||||||
Titer operator++(int)
|
|
||||||
{
|
|
||||||
Titer ret(static_cast<Titer &>(*this));
|
|
||||||
this->current = this->base[this->current].next_edge;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare with some other edge iterator. The other one may be of a
|
|
||||||
* child class.
|
|
||||||
* @tparam Tother Class of other iterator.
|
|
||||||
* @param other Instance of other iterator.
|
|
||||||
* @return If the iterators have the same edge array and current node.
|
|
||||||
*/
|
|
||||||
template<class Tother>
|
|
||||||
bool operator==(const Tother &other)
|
|
||||||
{
|
|
||||||
return this->base == other.base && this->current == other.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare for inequality with some other edge iterator. The other one
|
|
||||||
* may be of a child class.
|
|
||||||
* @tparam Tother Class of other iterator.
|
|
||||||
* @param other Instance of other iterator.
|
|
||||||
* @return If either the edge arrays or the current nodes differ.
|
|
||||||
*/
|
|
||||||
template<class Tother>
|
|
||||||
bool operator!=(const Tother &other)
|
|
||||||
{
|
|
||||||
return this->base != other.base || this->current != other.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dereference with operator*.
|
|
||||||
* @return Pair of current target NodeID and edge object.
|
|
||||||
*/
|
|
||||||
std::pair<NodeID, Tedge_wrapper> operator*() const
|
|
||||||
{
|
|
||||||
return std::pair<NodeID, Tedge_wrapper>(this->current, Tedge_wrapper(this->base[this->current]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dereference with operator->.
|
|
||||||
* @return Fake pointer to Pair of current target NodeID and edge object.
|
|
||||||
*/
|
|
||||||
FakePointer operator->() const {
|
|
||||||
return FakePointer(this->operator*());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,46 +220,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
Edge(BaseEdge &edge) : EdgeWrapper<BaseEdge>(edge) {}
|
Edge(BaseEdge &edge) : EdgeWrapper<BaseEdge>(edge) {}
|
||||||
void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||||
void Restrict() { this->edge.last_unrestricted_update = INVALID_DATE; }
|
void Restrict() { this->edge->last_unrestricted_update = INVALID_DATE; }
|
||||||
void Release() { this->edge.last_restricted_update = INVALID_DATE; }
|
void Release() { this->edge->last_restricted_update = INVALID_DATE; }
|
||||||
void ClearAircraft() { this->edge.last_aircraft_update = INVALID_DATE; }
|
void ClearAircraft() { this->edge->last_aircraft_update = INVALID_DATE; }
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An iterator for const edges. Cannot be typedef'ed because of
|
|
||||||
* template-reference to ConstEdgeIterator itself.
|
|
||||||
*/
|
|
||||||
class ConstEdgeIterator : public BaseEdgeIterator<const BaseEdge, ConstEdge, ConstEdgeIterator> {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
* @param edges Array of edges to be iterated over.
|
|
||||||
* @param current ID of current edge's end node.
|
|
||||||
*/
|
|
||||||
ConstEdgeIterator(const BaseEdge *edges, NodeID current) :
|
|
||||||
BaseEdgeIterator<const BaseEdge, ConstEdge, ConstEdgeIterator>(edges, current) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An iterator for non-const edges. Cannot be typedef'ed because of
|
|
||||||
* template-reference to EdgeIterator itself.
|
|
||||||
*/
|
|
||||||
class EdgeIterator : public BaseEdgeIterator<BaseEdge, Edge, EdgeIterator> {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
* @param edges Array of edges to be iterated over.
|
|
||||||
* @param current ID of current edge's end node.
|
|
||||||
*/
|
|
||||||
EdgeIterator(BaseEdge *edges, NodeID current) :
|
|
||||||
BaseEdgeIterator<BaseEdge, Edge, EdgeIterator>(edges, current) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant node class. Only retrieval operations are allowed on both the
|
* Constant node class. Only retrieval operations are allowed on both the
|
||||||
* node itself and its edges.
|
* node itself and its edges.
|
||||||
*/
|
*/
|
||||||
class ConstNode : public NodeWrapper<const BaseNode, const BaseEdge> {
|
class ConstNode : public NodeWrapper<const BaseNode> {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -366,34 +237,14 @@ public:
|
|||||||
* @param node ID of the node.
|
* @param node ID of the node.
|
||||||
*/
|
*/
|
||||||
ConstNode(const LinkGraph *lg, NodeID node) :
|
ConstNode(const LinkGraph *lg, NodeID node) :
|
||||||
NodeWrapper<const BaseNode, const BaseEdge>(lg->nodes[node], lg->edges[node], node)
|
NodeWrapper<const BaseNode>(lg->nodes[node], node)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a ConstEdge. This is not a reference as the wrapper objects are
|
|
||||||
* not actually persistent.
|
|
||||||
* @param to ID of end node of edge.
|
|
||||||
* @return Constant edge wrapper.
|
|
||||||
*/
|
|
||||||
ConstEdge operator[](NodeID to) const { return ConstEdge(this->edges[to]); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator pointing to the start of the edges array.
|
|
||||||
* @return Constant edge iterator.
|
|
||||||
*/
|
|
||||||
ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->edges, this->index); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator pointing beyond the end of the edges array.
|
|
||||||
* @return Constant edge iterator.
|
|
||||||
*/
|
|
||||||
ConstEdgeIterator End() const { return ConstEdgeIterator(this->edges, INVALID_NODE); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updatable node class. The node itself as well as its edges can be modified.
|
* Updatable node class. The node itself as well as its edges can be modified.
|
||||||
*/
|
*/
|
||||||
class Node : public NodeWrapper<BaseNode, BaseEdge> {
|
class Node : public NodeWrapper<BaseNode> {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -401,29 +252,9 @@ public:
|
|||||||
* @param node ID of the node.
|
* @param node ID of the node.
|
||||||
*/
|
*/
|
||||||
Node(LinkGraph *lg, NodeID node) :
|
Node(LinkGraph *lg, NodeID node) :
|
||||||
NodeWrapper<BaseNode, BaseEdge>(lg->nodes[node], lg->edges[node], node)
|
NodeWrapper<BaseNode>(lg->nodes[node], node)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an Edge. This is not a reference as the wrapper objects are not
|
|
||||||
* actually persistent.
|
|
||||||
* @param to ID of end node of edge.
|
|
||||||
* @return Edge wrapper.
|
|
||||||
*/
|
|
||||||
Edge operator[](NodeID to) { return Edge(this->edges[to]); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator pointing to the start of the edges array.
|
|
||||||
* @return Edge iterator.
|
|
||||||
*/
|
|
||||||
EdgeIterator Begin() { return EdgeIterator(this->edges, this->index); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an iterator pointing beyond the end of the edges array.
|
|
||||||
* @return Constant edge iterator.
|
|
||||||
*/
|
|
||||||
EdgeIterator End() { return EdgeIterator(this->edges, INVALID_NODE); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the node's supply and set last_update to the current date.
|
* Update the node's supply and set last_update to the current date.
|
||||||
* @param supply Supply to be added.
|
* @param supply Supply to be added.
|
||||||
@@ -451,15 +282,8 @@ public:
|
|||||||
{
|
{
|
||||||
this->node.demand = demand;
|
this->node.demand = demand;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
|
||||||
void UpdateEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
|
||||||
void RemoveEdge(NodeID to);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<BaseNode> NodeVector;
|
|
||||||
typedef SmallMatrix<BaseEdge> EdgeMatrix;
|
|
||||||
|
|
||||||
/** Minimum effective distance for timeout calculation. */
|
/** Minimum effective distance for timeout calculation. */
|
||||||
static const uint MIN_TIMEOUT_DISTANCE = 32;
|
static const uint MIN_TIMEOUT_DISTANCE = 32;
|
||||||
|
|
||||||
@@ -548,6 +372,9 @@ public:
|
|||||||
NodeID AddNode(const Station *st);
|
NodeID AddNode(const Station *st);
|
||||||
void RemoveNode(NodeID id);
|
void RemoveNode(NodeID id);
|
||||||
|
|
||||||
|
void UpdateEdge(NodeID from, NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
|
||||||
|
void RemoveEdge(NodeID from, NodeID to);
|
||||||
|
|
||||||
inline uint64 CalculateCostEstimate() const {
|
inline uint64 CalculateCostEstimate() const {
|
||||||
uint64 size_squared = this->Size() * this->Size();
|
uint64 size_squared = this->Size() * this->Size();
|
||||||
return size_squared * FindLastBit(size_squared * size_squared); // N^2 * 4log_2(N)
|
return size_squared * FindLastBit(size_squared * size_squared); // N^2 * 4log_2(N)
|
||||||
@@ -570,6 +397,89 @@ protected:
|
|||||||
Date last_compression; ///< Last time the capacities and supplies were compressed.
|
Date last_compression; ///< Last time the capacities and supplies were compressed.
|
||||||
NodeVector nodes; ///< Nodes in the component.
|
NodeVector nodes; ///< Nodes in the component.
|
||||||
EdgeMatrix edges; ///< Edges in the component.
|
EdgeMatrix edges; ///< Edges in the component.
|
||||||
|
|
||||||
|
public:
|
||||||
|
const EdgeMatrix &GetEdges() const { return this->edges; }
|
||||||
|
|
||||||
|
const BaseEdge &GetBaseEdge(NodeID from, NodeID to) const
|
||||||
|
{
|
||||||
|
auto iter = this->edges.find(std::make_pair(from, to));
|
||||||
|
if (iter != this->edges.end()) return iter->second;
|
||||||
|
|
||||||
|
static LinkGraph::BaseEdge empty_edge = {};
|
||||||
|
return empty_edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstEdge GetConstEdge(NodeID from, NodeID to) const { return ConstEdge(this->GetBaseEdge(from, to)); }
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void IterateEdgesFromNode(NodeID from_id, F proc) const
|
||||||
|
{
|
||||||
|
auto iter = this->edges.lower_bound(std::make_pair(from_id, (NodeID)0));
|
||||||
|
while (iter != this->edges.end()) {
|
||||||
|
NodeID from = iter->first.first;
|
||||||
|
NodeID to = iter->first.second;
|
||||||
|
if (from != from_id) return;
|
||||||
|
if (from != to) {
|
||||||
|
proc(from, to, ConstEdge(iter->second));
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class EdgeIterationResult {
|
||||||
|
None,
|
||||||
|
EraseEdge,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdgeIterationHelper {
|
||||||
|
EdgeMatrix &edges;
|
||||||
|
EdgeMatrix::iterator &iter;
|
||||||
|
const NodeID from_id;
|
||||||
|
const NodeID to_id;
|
||||||
|
size_t expected_size;
|
||||||
|
|
||||||
|
EdgeIterationHelper(EdgeMatrix &edges, EdgeMatrix::iterator &iter, NodeID from_id, NodeID to_id) :
|
||||||
|
edges(edges), iter(iter), from_id(from_id), to_id(to_id), expected_size(0) {}
|
||||||
|
|
||||||
|
Edge GetEdge() { return Edge(this->iter->second); }
|
||||||
|
|
||||||
|
void RecordSize() { this->expected_size = this->edges.size(); }
|
||||||
|
|
||||||
|
bool RefreshIterationIfSizeChanged()
|
||||||
|
{
|
||||||
|
if (this->expected_size != this->edges.size()) {
|
||||||
|
/* Edges container has resized, our iterator is now invalid, so find it again */
|
||||||
|
this->iter = this->edges.find(std::make_pair(this->from_id, this->to_id));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void MutableIterateEdgesFromNode(NodeID from_id, F proc)
|
||||||
|
{
|
||||||
|
EdgeMatrix::iterator iter = this->edges.lower_bound(std::make_pair(from_id, (NodeID)0));
|
||||||
|
while (iter != this->edges.end()) {
|
||||||
|
NodeID from = iter->first.first;
|
||||||
|
NodeID to = iter->first.second;
|
||||||
|
if (from != from_id) return;
|
||||||
|
EdgeIterationResult result = EdgeIterationResult::None;
|
||||||
|
if (from != to) {
|
||||||
|
result = proc(EdgeIterationHelper(this->edges, iter, from, to));
|
||||||
|
}
|
||||||
|
switch (result) {
|
||||||
|
case EdgeIterationResult::None:
|
||||||
|
++iter;
|
||||||
|
break;
|
||||||
|
case EdgeIterationResult::EraseEdge:
|
||||||
|
iter = this->edges.erase(iter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LINKGRAPH_H */
|
#endif /* LINKGRAPH_H */
|
||||||
|
@@ -15,11 +15,9 @@
|
|||||||
|
|
||||||
typedef LinkGraph::Node Node;
|
typedef LinkGraph::Node Node;
|
||||||
typedef LinkGraph::Edge Edge;
|
typedef LinkGraph::Edge Edge;
|
||||||
typedef LinkGraph::EdgeIterator EdgeIterator;
|
|
||||||
|
|
||||||
typedef LinkGraph::ConstNode ConstNode;
|
typedef LinkGraph::ConstNode ConstNode;
|
||||||
typedef LinkGraph::ConstEdge ConstEdge;
|
typedef LinkGraph::ConstEdge ConstEdge;
|
||||||
typedef LinkGraph::ConstEdgeIterator ConstEdgeIterator;
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* LINKGRAPH_BASE_H */
|
#endif /* LINKGRAPH_BASE_H */
|
||||||
|
@@ -163,7 +163,7 @@ void LinkGraphOverlay::RebuildCache(bool incremental)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const LinkGraph &lg = *LinkGraph::Get(ge.link_graph);
|
const LinkGraph &lg = *LinkGraph::Get(ge.link_graph);
|
||||||
ConstEdge edge = lg[ge.node][to->goods[c].node];
|
ConstEdge edge = lg.GetConstEdge(ge.node, to->goods[c].node);
|
||||||
if (edge.Capacity() > 0) {
|
if (edge.Capacity() > 0) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
auto iter = link_cache_map.insert(insert_iter, std::make_pair(std::make_pair(from->index, to->index), LinkCacheItem()));
|
auto iter = link_cache_map.insert(insert_iter, std::make_pair(std::make_pair(from->index, to->index), LinkCacheItem()));
|
||||||
@@ -198,33 +198,33 @@ void LinkGraphOverlay::RebuildCache(bool incremental)
|
|||||||
|
|
||||||
ConstNode from_node = lg[sta->goods[c].node];
|
ConstNode from_node = lg[sta->goods[c].node];
|
||||||
supply += lg.Monthly(from_node.Supply());
|
supply += lg.Monthly(from_node.Supply());
|
||||||
for (ConstEdgeIterator i = from_node.Begin(); i != from_node.End(); ++i) {
|
lg.IterateEdgesFromNode(from_node.GetNodeID(), [&](NodeID from_id, NodeID to_id, ConstEdge edge) {
|
||||||
StationID to = lg[i->first].Station();
|
StationID to = lg[to_id].Station();
|
||||||
assert(from != to);
|
assert(from != to);
|
||||||
if (!Station::IsValidID(to)) continue;
|
if (!Station::IsValidID(to)) return;
|
||||||
|
|
||||||
const Station *stb = Station::Get(to);
|
const Station *stb = Station::Get(to);
|
||||||
assert(sta != stb);
|
assert(sta != stb);
|
||||||
|
|
||||||
/* Show links between stations of selected companies or "neutral" ones like oilrigs. */
|
/* Show links between stations of selected companies or "neutral" ones like oilrigs. */
|
||||||
if (stb->owner != OWNER_NONE && sta->owner != OWNER_NONE && !HasBit(this->company_mask, stb->owner)) continue;
|
if (stb->owner != OWNER_NONE && sta->owner != OWNER_NONE && !HasBit(this->company_mask, stb->owner)) return;
|
||||||
if (stb->rect.IsEmpty()) continue;
|
if (stb->rect.IsEmpty()) return;
|
||||||
|
|
||||||
if (incremental && std::binary_search(incremental_station_exclude.begin(), incremental_station_exclude.end(), to)) continue;
|
if (incremental && std::binary_search(incremental_station_exclude.begin(), incremental_station_exclude.end(), to)) return;
|
||||||
if (incremental && std::binary_search(incremental_link_exclude.begin(), incremental_link_exclude.end(), std::make_pair(from, to))) continue;
|
if (incremental && std::binary_search(incremental_link_exclude.begin(), incremental_link_exclude.end(), std::make_pair(from, to))) return;
|
||||||
|
|
||||||
auto key = std::make_pair(from, to);
|
auto key = std::make_pair(from, to);
|
||||||
auto iter = link_cache_map.lower_bound(key);
|
auto iter = link_cache_map.lower_bound(key);
|
||||||
if (iter != link_cache_map.end() && !(link_cache_map.key_comp()(key, iter->first))) {
|
if (iter != link_cache_map.end() && !(link_cache_map.key_comp()(key, iter->first))) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point ptb = this->GetStationMiddle(stb);
|
Point ptb = this->GetStationMiddle(stb);
|
||||||
|
|
||||||
if (!cache_all && !this->IsLinkVisible(pta, ptb, &dpi)) continue;
|
if (!cache_all && !this->IsLinkVisible(pta, ptb, &dpi)) return;
|
||||||
|
|
||||||
AddLinks(sta, stb, pta, ptb, iter);
|
AddLinks(sta, stb, pta, ptb, iter);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
if (cache_all || this->IsPointVisible(pta, &dpi)) {
|
if (cache_all || this->IsPointVisible(pta, &dpi)) {
|
||||||
this->cached_stations.push_back({ from, supply, pta });
|
this->cached_stations.push_back({ from, supply, pta });
|
||||||
|
@@ -125,24 +125,25 @@ void LinkGraphJob::FinaliseJob()
|
|||||||
LinkGraph *lg = LinkGraph::Get(ge.link_graph);
|
LinkGraph *lg = LinkGraph::Get(ge.link_graph);
|
||||||
FlowStatMap &flows = from.Flows();
|
FlowStatMap &flows = from.Flows();
|
||||||
|
|
||||||
for (EdgeIterator it(from.Begin()); it != from.End(); ++it) {
|
this->IterateEdgesFromNode(from, [&](NodeID from_id, NodeID to_id, Edge edge) {
|
||||||
if (from[it->first].Flow() == 0) continue;
|
if (edge.Flow() == 0) return;
|
||||||
StationID to = (*this)[it->first].Station();
|
StationID to = (*this)[to_id].Station();
|
||||||
Station *st2 = Station::GetIfValid(to);
|
Station *st2 = Station::GetIfValid(to);
|
||||||
|
LinkGraph::ConstEdge lg_edge = lg->GetConstEdge(from_id, to_id);
|
||||||
if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index ||
|
if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index ||
|
||||||
st2->goods[this->Cargo()].node != it->first ||
|
st2->goods[this->Cargo()].node != to_id ||
|
||||||
(*lg)[node_id][it->first].LastUpdate() == INVALID_DATE) {
|
lg_edge.LastUpdate() == INVALID_DATE) {
|
||||||
/* Edge has been removed. Delete flows. */
|
/* Edge has been removed. Delete flows. */
|
||||||
StationIDStack erased = flows.DeleteFlows(to);
|
StationIDStack erased = flows.DeleteFlows(to);
|
||||||
/* Delete old flows for source stations which have been deleted
|
/* Delete old flows for source stations which have been deleted
|
||||||
* from the new flows. This avoids flow cycles between old and
|
* from the new flows. This avoids flow cycles between old and
|
||||||
* new flows. */
|
* new flows. */
|
||||||
while (!erased.IsEmpty()) ge.flows.erase(erased.Pop());
|
while (!erased.IsEmpty()) ge.flows.erase(erased.Pop());
|
||||||
} else if ((*lg)[node_id][it->first].LastUnrestrictedUpdate() == INVALID_DATE) {
|
} else if (lg_edge.LastUnrestrictedUpdate() == INVALID_DATE) {
|
||||||
/* Edge is fully restricted. */
|
/* Edge is fully restricted. */
|
||||||
flows.RestrictFlows(to);
|
flows.RestrictFlows(to);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
/* Swap shares and invalidate ones that are completely deleted. Don't
|
/* Swap shares and invalidate ones that are completely deleted. Don't
|
||||||
* really delete them as we could then end up with unroutable cargo
|
* really delete them as we could then end up with unroutable cargo
|
||||||
@@ -260,7 +261,7 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist)
|
|||||||
uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
|
uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation)
|
||||||
{
|
{
|
||||||
if (this->GetParent() != nullptr) {
|
if (this->GetParent() != nullptr) {
|
||||||
LinkGraphJob::Edge edge = job[this->GetParent()->node][this->node];
|
LinkGraphJob::Edge edge = job[this->GetParent()->node].MakeEdge(job, this->node);
|
||||||
if (max_saturation != UINT_MAX) {
|
if (max_saturation != UINT_MAX) {
|
||||||
uint usable_cap = edge.Capacity() * max_saturation / 100;
|
uint usable_cap = edge.Capacity() * max_saturation / 100;
|
||||||
if (usable_cap > edge.Flow()) {
|
if (usable_cap > edge.Flow()) {
|
||||||
|
@@ -82,20 +82,19 @@ public:
|
|||||||
DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths
|
DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A job edge. Wraps a link graph edge and an edge annotation. The
|
* An annotation-only job edge. Wraps an edge annotation. The
|
||||||
* annotation can be modified, the edge is constant.
|
* annotation can be modified.
|
||||||
*/
|
*/
|
||||||
class Edge : public LinkGraph::ConstEdge {
|
class AnnoEdge {
|
||||||
private:
|
private:
|
||||||
EdgeAnnotation &anno; ///< Annotation being wrapped.
|
EdgeAnnotation &anno; ///< Annotation being wrapped.
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param edge Link graph edge to be wrapped.
|
|
||||||
* @param anno Annotation to be wrapped.
|
* @param anno Annotation to be wrapped.
|
||||||
*/
|
*/
|
||||||
Edge(const LinkGraph::BaseEdge &edge, EdgeAnnotation &anno) :
|
AnnoEdge(EdgeAnnotation &anno) :
|
||||||
LinkGraph::ConstEdge(edge), anno(anno) {}
|
anno(anno) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the transport demand between end the points of the edge.
|
* Get the transport demand between end the points of the edge.
|
||||||
@@ -153,39 +152,18 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator for job edges.
|
* A job edge. Wraps a link graph edge and an edge annotation. The
|
||||||
|
* annotation can be modified, the edge is constant.
|
||||||
*/
|
*/
|
||||||
class EdgeIterator : public LinkGraph::BaseEdgeIterator<const LinkGraph::BaseEdge, Edge, EdgeIterator> {
|
class Edge : public LinkGraph::ConstEdge, public AnnoEdge {
|
||||||
EdgeAnnotation *base_anno; ///< Array of annotations to be (indirectly) iterated.
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param base Array of edges to be iterated.
|
* @param edge Link graph edge to be wrapped.
|
||||||
* @param base_anno Array of annotations to be iterated.
|
* @param anno Annotation to be wrapped.
|
||||||
* @param current Start offset of iteration.
|
|
||||||
*/
|
*/
|
||||||
EdgeIterator(const LinkGraph::BaseEdge *base, EdgeAnnotation *base_anno, NodeID current) :
|
Edge(const LinkGraph::BaseEdge &edge, EdgeAnnotation &anno) :
|
||||||
LinkGraph::BaseEdgeIterator<const LinkGraph::BaseEdge, Edge, EdgeIterator>(base, current),
|
LinkGraph::ConstEdge(edge), AnnoEdge(anno) {}
|
||||||
base_anno(base_anno) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dereference.
|
|
||||||
* @return Pair of the edge currently pointed to and the ID of its
|
|
||||||
* other end.
|
|
||||||
*/
|
|
||||||
std::pair<NodeID, Edge> operator*() const
|
|
||||||
{
|
|
||||||
return std::pair<NodeID, Edge>(this->current, Edge(this->base[this->current], this->base_anno[this->current]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dereference. Has to be repeated here as operator* is different than
|
|
||||||
* in LinkGraph::EdgeWrapper.
|
|
||||||
* @return Fake pointer to pair of NodeID/Edge.
|
|
||||||
*/
|
|
||||||
FakePointer operator->() const {
|
|
||||||
return FakePointer(this->operator*());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,21 +192,11 @@ public:
|
|||||||
* @param to Remote end of the edge.
|
* @param to Remote end of the edge.
|
||||||
* @return Edge between this node and "to".
|
* @return Edge between this node and "to".
|
||||||
*/
|
*/
|
||||||
Edge operator[](NodeID to) const { return Edge(this->edges[to], this->edge_annos[to]); }
|
AnnoEdge operator[](NodeID to) const { return AnnoEdge(this->edge_annos[to]); }
|
||||||
|
|
||||||
/**
|
Edge MakeEdge(const LinkGraph::BaseEdge &base_edge, NodeID to) const { return Edge(base_edge, this->edge_annos[to]); }
|
||||||
* Iterator for the "begin" of the edge array. Only edges with capacity
|
|
||||||
* are iterated. The others are skipped.
|
|
||||||
* @return Iterator pointing to the first edge.
|
|
||||||
*/
|
|
||||||
EdgeIterator Begin() const { return EdgeIterator(this->edges, this->edge_annos, index); }
|
|
||||||
|
|
||||||
/**
|
Edge MakeEdge(const LinkGraphJob &lgj, NodeID to) const { return this->MakeEdge(lgj.GetBaseEdge(this->GetNodeID(), to), to); }
|
||||||
* Iterator for the "end" of the edge array. Only edges with capacity
|
|
||||||
* are iterated. The others are skipped.
|
|
||||||
* @return Iterator pointing beyond the last edge.
|
|
||||||
*/
|
|
||||||
EdgeIterator End() const { return EdgeIterator(this->edges, this->edge_annos, INVALID_NODE); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get amount of supply that hasn't been delivered, yet.
|
* Get amount of supply that hasn't been delivered, yet.
|
||||||
@@ -390,6 +358,26 @@ public:
|
|||||||
* @return Link graph.
|
* @return Link graph.
|
||||||
*/
|
*/
|
||||||
inline const LinkGraph &Graph() const { return this->link_graph; }
|
inline const LinkGraph &Graph() const { return this->link_graph; }
|
||||||
|
|
||||||
|
const LinkGraph::BaseEdge &GetBaseEdge(NodeID from, NodeID to) const
|
||||||
|
{
|
||||||
|
return this->link_graph.GetBaseEdge(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void IterateEdgesFromNode(const Node &from_node, F proc)
|
||||||
|
{
|
||||||
|
auto iter = this->link_graph.GetEdges().lower_bound(std::make_pair(from_node.GetNodeID(), (NodeID)0));
|
||||||
|
while (iter != this->link_graph.GetEdges().end()) {
|
||||||
|
NodeID from = iter->first.first;
|
||||||
|
NodeID to = iter->first.second;
|
||||||
|
if (from != from_node.GetNodeID()) return;
|
||||||
|
if (from != to) {
|
||||||
|
proc(from, to, from_node.MakeEdge(iter->second, to));
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,6 +16,6 @@
|
|||||||
|
|
||||||
typedef LinkGraphJob::Node Node;
|
typedef LinkGraphJob::Node Node;
|
||||||
typedef LinkGraphJob::Edge Edge;
|
typedef LinkGraphJob::Edge Edge;
|
||||||
typedef LinkGraphJob::EdgeIterator EdgeIterator;
|
typedef LinkGraphJob::AnnoEdge AnnoEdge;
|
||||||
|
|
||||||
#endif /* LINKGRAPHJOB_BASE_H */
|
#endif /* LINKGRAPHJOB_BASE_H */
|
||||||
|
@@ -112,8 +112,10 @@ public:
|
|||||||
class GraphEdgeIterator {
|
class GraphEdgeIterator {
|
||||||
private:
|
private:
|
||||||
LinkGraphJob &job; ///< Job being executed
|
LinkGraphJob &job; ///< Job being executed
|
||||||
EdgeIterator i; ///< Iterator pointing to current edge.
|
LinkGraph::EdgeMatrix::const_iterator i; ///< Iterator pointing to current edge.
|
||||||
EdgeIterator end; ///< Iterator pointing beyond last edge.
|
LinkGraph::EdgeMatrix::const_iterator end; ///< Iterator pointing beyond last edge.
|
||||||
|
NodeID node; ///< Source node
|
||||||
|
const LinkGraph::BaseEdge *saved; ///< Saved edge
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -122,7 +124,7 @@ public:
|
|||||||
* @param job Job to iterate on.
|
* @param job Job to iterate on.
|
||||||
*/
|
*/
|
||||||
GraphEdgeIterator(LinkGraphJob &job) : job(job),
|
GraphEdgeIterator(LinkGraphJob &job) : job(job),
|
||||||
i(nullptr, nullptr, INVALID_NODE), end(nullptr, nullptr, INVALID_NODE)
|
i(LinkGraph::EdgeMatrix::const_iterator()), end(LinkGraph::EdgeMatrix::const_iterator()), node(INVALID_NODE), saved(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,8 +134,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SetNode(NodeID source, NodeID node)
|
void SetNode(NodeID source, NodeID node)
|
||||||
{
|
{
|
||||||
this->i = this->job[node].Begin();
|
this->i = this->job.Graph().GetEdges().lower_bound(std::make_pair(node, (NodeID)0));
|
||||||
this->end = this->job[node].End();
|
this->end = this->job.Graph().GetEdges().end();
|
||||||
|
this->node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,8 +145,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
NodeID Next()
|
NodeID Next()
|
||||||
{
|
{
|
||||||
return this->i != this->end ? (this->i++)->first : INVALID_NODE;
|
if (this->i == this->end) return INVALID_NODE;
|
||||||
|
NodeID from = this->i->first.first;
|
||||||
|
NodeID to = this->i->first.second;
|
||||||
|
if (from != this->node) return INVALID_NODE;
|
||||||
|
this->saved = &(this->i->second);
|
||||||
|
++this->i;
|
||||||
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SavedEdge() const { return true; }
|
||||||
|
|
||||||
|
const LinkGraph::BaseEdge &GetSavedEdge() { return *(this->saved); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,6 +218,10 @@ public:
|
|||||||
if (this->it == this->end) return INVALID_NODE;
|
if (this->it == this->end) return INVALID_NODE;
|
||||||
return this->station_to_node[(this->it++)->second];
|
return this->station_to_node[(this->it++)->second];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SavedEdge() const { return false; }
|
||||||
|
|
||||||
|
const LinkGraph::BaseEdge &GetSavedEdge() { NOT_REACHED(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,7 +320,8 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
|
|||||||
iter.SetNode(source_node, from);
|
iter.SetNode(source_node, from);
|
||||||
for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
|
for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
|
||||||
if (to == from) continue; // Not a real edge but a consumption sign.
|
if (to == from) continue; // Not a real edge but a consumption sign.
|
||||||
Edge edge = this->job[from][to];
|
const LinkGraph::BaseEdge &base_edge = iter.SavedEdge() ? iter.GetSavedEdge() : this->job.GetBaseEdge(from, to);
|
||||||
|
Edge edge = this->job[from].MakeEdge(base_edge, to);
|
||||||
uint capacity = edge.Capacity();
|
uint capacity = edge.Capacity();
|
||||||
if (this->max_saturation != UINT_MAX) {
|
if (this->max_saturation != UINT_MAX) {
|
||||||
capacity *= this->max_saturation;
|
capacity *= this->max_saturation;
|
||||||
@@ -379,7 +397,7 @@ void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
|
|||||||
* @param max_saturation If < UINT_MAX only push flow up to the given
|
* @param max_saturation If < UINT_MAX only push flow up to the given
|
||||||
* saturation, otherwise the path can be "overloaded".
|
* saturation, otherwise the path can be "overloaded".
|
||||||
*/
|
*/
|
||||||
uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy,
|
uint MultiCommodityFlow::PushFlow(AnnoEdge &edge, Path *path, uint accuracy,
|
||||||
uint max_saturation)
|
uint max_saturation)
|
||||||
{
|
{
|
||||||
assert(edge.UnsatisfiedDemand() > 0);
|
assert(edge.UnsatisfiedDemand() > 0);
|
||||||
@@ -428,7 +446,7 @@ void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cycle_begin = path[prev];
|
cycle_begin = path[prev];
|
||||||
Edge edge = this->job[prev][cycle_begin->GetNode()];
|
AnnoEdge edge = this->job[prev][cycle_begin->GetNode()];
|
||||||
edge.RemoveFlow(flow);
|
edge.RemoveFlow(flow);
|
||||||
} while (cycle_begin != cycle_end);
|
} while (cycle_begin != cycle_end);
|
||||||
}
|
}
|
||||||
@@ -556,7 +574,7 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
|
|||||||
|
|
||||||
bool source_demand_left = false;
|
bool source_demand_left = false;
|
||||||
for (NodeID dest = 0; dest < size; ++dest) {
|
for (NodeID dest = 0; dest < size; ++dest) {
|
||||||
Edge edge = job[source][dest];
|
AnnoEdge edge = job[source][dest];
|
||||||
if (edge.UnsatisfiedDemand() > 0) {
|
if (edge.UnsatisfiedDemand() > 0) {
|
||||||
Path *path = paths[dest];
|
Path *path = paths[dest];
|
||||||
assert(path != nullptr);
|
assert(path != nullptr);
|
||||||
@@ -603,7 +621,7 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
|
|||||||
|
|
||||||
bool source_demand_left = false;
|
bool source_demand_left = false;
|
||||||
for (NodeID dest = 0; dest < size; ++dest) {
|
for (NodeID dest = 0; dest < size; ++dest) {
|
||||||
Edge edge = this->job[source][dest];
|
AnnoEdge edge = this->job[source][dest];
|
||||||
Path *path = paths[dest];
|
Path *path = paths[dest];
|
||||||
if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
|
if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
|
||||||
this->PushFlow(edge, path, accuracy, UINT_MAX);
|
this->PushFlow(edge, path, accuracy, UINT_MAX);
|
||||||
|
@@ -24,7 +24,7 @@ protected:
|
|||||||
template<class Tannotation, class Tedge_iterator>
|
template<class Tannotation, class Tedge_iterator>
|
||||||
void Dijkstra(NodeID from, PathVector &paths);
|
void Dijkstra(NodeID from, PathVector &paths);
|
||||||
|
|
||||||
uint PushFlow(Edge &edge, Path *path, uint accuracy, uint max_saturation);
|
uint PushFlow(AnnoEdge &edge, Path *path, uint accuracy, uint max_saturation);
|
||||||
|
|
||||||
void CleanupPaths(NodeID source, PathVector &paths);
|
void CleanupPaths(NodeID source, PathVector &paths);
|
||||||
|
|
||||||
|
@@ -176,6 +176,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
|
|||||||
{ XSLFI_NEW_SIGNAL_STYLES, XSCF_NULL, 2, 2, "new_signal_styles", nullptr, nullptr, "XBST,NSID" },
|
{ XSLFI_NEW_SIGNAL_STYLES, XSCF_NULL, 2, 2, "new_signal_styles", nullptr, nullptr, "XBST,NSID" },
|
||||||
{ XSLFI_NO_TREE_COUNTER, XSCF_IGNORABLE_ALL, 1, 1, "no_tree_counter", nullptr, nullptr, nullptr },
|
{ XSLFI_NO_TREE_COUNTER, XSCF_IGNORABLE_ALL, 1, 1, "no_tree_counter", nullptr, nullptr, nullptr },
|
||||||
{ XSLFI_TOWN_SETTING_OVERRIDE, XSCF_NULL, 1, 1, "town_setting_override", nullptr, nullptr, nullptr },
|
{ XSLFI_TOWN_SETTING_OVERRIDE, XSCF_NULL, 1, 1, "town_setting_override", nullptr, nullptr, nullptr },
|
||||||
|
{ XSLFI_LINKGRAPH_SPARSE_EDGES, XSCF_NULL, 1, 1, "linkgraph_sparse_edges", nullptr, nullptr, nullptr },
|
||||||
{ XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr },
|
{ XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr },
|
||||||
{ XSLFI_U64_TICK_COUNTER, XSCF_NULL, 1, 1, "u64_tick_counter", nullptr, nullptr, nullptr },
|
{ XSLFI_U64_TICK_COUNTER, XSCF_NULL, 1, 1, "u64_tick_counter", nullptr, nullptr, nullptr },
|
||||||
{ XSLFI_LINKGRAPH_TRAVEL_TIME, XSCF_NULL, 1, 1, "linkgraph_travel_time", nullptr, nullptr, nullptr },
|
{ XSLFI_LINKGRAPH_TRAVEL_TIME, XSCF_NULL, 1, 1, "linkgraph_travel_time", nullptr, nullptr, nullptr },
|
||||||
|
@@ -129,6 +129,7 @@ enum SlXvFeatureIndex {
|
|||||||
XSLFI_NEW_SIGNAL_STYLES, ///< New signal styles
|
XSLFI_NEW_SIGNAL_STYLES, ///< New signal styles
|
||||||
XSLFI_NO_TREE_COUNTER, ///< No tree counter
|
XSLFI_NO_TREE_COUNTER, ///< No tree counter
|
||||||
XSLFI_TOWN_SETTING_OVERRIDE, ///< Town setting overrides
|
XSLFI_TOWN_SETTING_OVERRIDE, ///< Town setting overrides
|
||||||
|
XSLFI_LINKGRAPH_SPARSE_EDGES, ///< Link graph edge matrix is stored in sparse format, and saved in order
|
||||||
|
|
||||||
XSLFI_SCRIPT_INT64, ///< See: SLV_SCRIPT_INT64
|
XSLFI_SCRIPT_INT64, ///< See: SLV_SCRIPT_INT64
|
||||||
XSLFI_U64_TICK_COUNTER, ///< See: SLV_U64_TICK_COUNTER
|
XSLFI_U64_TICK_COUNTER, ///< See: SLV_U64_TICK_COUNTER
|
||||||
|
@@ -130,7 +130,7 @@ static const SaveLoad _edge_desc[] = {
|
|||||||
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
|
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
|
||||||
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
|
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
|
||||||
SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)),
|
SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)),
|
||||||
SLE_VAR(Edge, next_edge, SLE_UINT16),
|
// SLE_VAR(Edge, next_edge, SLE_UINT16), // Removed since XSLFI_LINKGRAPH_SPARSE_EDGES
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<SaveLoad> _filtered_node_desc;
|
std::vector<SaveLoad> _filtered_node_desc;
|
||||||
@@ -151,13 +151,19 @@ static void FilterDescs()
|
|||||||
void Save_LinkGraph(LinkGraph &lg)
|
void Save_LinkGraph(LinkGraph &lg)
|
||||||
{
|
{
|
||||||
uint16 size = lg.Size();
|
uint16 size = lg.Size();
|
||||||
|
auto edge_iter = lg.edges.begin();
|
||||||
|
auto edge_end = lg.edges.end();
|
||||||
for (NodeID from = 0; from < size; ++from) {
|
for (NodeID from = 0; from < size; ++from) {
|
||||||
Node *node = &lg.nodes[from];
|
Node *node = &lg.nodes[from];
|
||||||
SlObjectSaveFiltered(node, _filtered_node_desc);
|
SlObjectSaveFiltered(node, _filtered_node_desc);
|
||||||
/* ... but as that wasted a lot of space we save a sparse matrix now. */
|
|
||||||
for (NodeID to = from; to != INVALID_NODE; to = lg.edges[from][to].next_edge) {
|
while (edge_iter != edge_end && edge_iter->first.first == from) {
|
||||||
SlObjectSaveFiltered(&lg.edges[from][to], _filtered_edge_desc);
|
SlWriteUint16(edge_iter->first.second);
|
||||||
|
Edge *edge = &edge_iter->second;
|
||||||
|
SlObjectSaveFiltered(edge, _filtered_edge_desc);
|
||||||
|
++edge_iter;
|
||||||
}
|
}
|
||||||
|
SlWriteUint16(INVALID_NODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,19 +174,42 @@ void Save_LinkGraph(LinkGraph &lg)
|
|||||||
void Load_LinkGraph(LinkGraph &lg)
|
void Load_LinkGraph(LinkGraph &lg)
|
||||||
{
|
{
|
||||||
uint size = lg.Size();
|
uint size = lg.Size();
|
||||||
for (NodeID from = 0; from < size; ++from) {
|
if (SlXvIsFeaturePresent(XSLFI_LINKGRAPH_SPARSE_EDGES)) {
|
||||||
Node *node = &lg.nodes[from];
|
for (NodeID from = 0; from < size; ++from) {
|
||||||
SlObjectLoadFiltered(node, _filtered_node_desc);
|
Node *node = &lg.nodes[from];
|
||||||
if (IsSavegameVersionBefore(SLV_191)) {
|
SlObjectLoadFiltered(node, _filtered_node_desc);
|
||||||
|
while (true) {
|
||||||
|
NodeID to = SlReadUint16();
|
||||||
|
if (to == INVALID_NODE) break;
|
||||||
|
SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (IsSavegameVersionBefore(SLV_191)) {
|
||||||
|
std::vector<Edge> temp_edges;
|
||||||
|
std::vector<NodeID> temp_next_edges;
|
||||||
|
temp_edges.resize(size);
|
||||||
|
temp_next_edges.resize(size);
|
||||||
|
for (NodeID from = 0; from < size; ++from) {
|
||||||
|
Node *node = &lg.nodes[from];
|
||||||
|
SlObjectLoadFiltered(node, _filtered_node_desc);
|
||||||
/* We used to save the full matrix ... */
|
/* We used to save the full matrix ... */
|
||||||
for (NodeID to = 0; to < size; ++to) {
|
for (NodeID to = 0; to < size; ++to) {
|
||||||
SlObjectLoadFiltered(&lg.edges[from][to], _filtered_edge_desc);
|
SlObjectLoadFiltered(&temp_edges[to], _filtered_edge_desc);
|
||||||
|
temp_next_edges[to] = SlReadUint16();
|
||||||
}
|
}
|
||||||
} else {
|
for (NodeID to = from; to != INVALID_NODE; to = temp_next_edges[to]) {
|
||||||
|
lg.edges[std::make_pair(from, to)] = temp_edges[to];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (NodeID from = 0; from < size; ++from) {
|
||||||
|
Node *node = &lg.nodes[from];
|
||||||
|
SlObjectLoadFiltered(node, _filtered_node_desc);
|
||||||
/* ... but as that wasted a lot of space we save a sparse matrix now. */
|
/* ... but as that wasted a lot of space we save a sparse matrix now. */
|
||||||
for (NodeID to = from; to != INVALID_NODE; to = lg.edges[from][to].next_edge) {
|
for (NodeID to = from; to != INVALID_NODE;) {
|
||||||
if (to >= size) SlErrorCorrupt("Link graph structure overflow");
|
if (to >= size) SlErrorCorrupt("Link graph structure overflow");
|
||||||
SlObjectLoadFiltered(&lg.edges[from][to], _filtered_edge_desc);
|
SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc);
|
||||||
|
to = SlReadUint16();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ typedef LinkGraph::BaseEdge Edge;
|
|||||||
static uint16 _num_nodes;
|
static uint16 _num_nodes;
|
||||||
static LinkGraph *_linkgraph; ///< Contains the current linkgraph being saved/loaded.
|
static LinkGraph *_linkgraph; ///< Contains the current linkgraph being saved/loaded.
|
||||||
static NodeID _linkgraph_from; ///< Contains the current "from" node being saved/loaded.
|
static NodeID _linkgraph_from; ///< Contains the current "from" node being saved/loaded.
|
||||||
|
static NodeID _edge_next_edge;
|
||||||
|
|
||||||
class SlLinkgraphEdge : public DefaultSaveLoadHandler<SlLinkgraphEdge, Node> {
|
class SlLinkgraphEdge : public DefaultSaveLoadHandler<SlLinkgraphEdge, Node> {
|
||||||
public:
|
public:
|
||||||
@@ -36,21 +37,13 @@ public:
|
|||||||
SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION),
|
SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION),
|
||||||
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
|
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
|
||||||
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
|
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
|
||||||
SLE_VAR(Edge, next_edge, SLE_UINT16),
|
SLEG_VAR("next_edge", _edge_next_edge, SLE_UINT16),
|
||||||
};
|
};
|
||||||
inline const static SaveLoadCompatTable compat_description = _linkgraph_edge_sl_compat;
|
inline const static SaveLoadCompatTable compat_description = _linkgraph_edge_sl_compat;
|
||||||
|
|
||||||
void Save(Node *bn) const override
|
void Save(Node *bn) const override
|
||||||
{
|
{
|
||||||
uint16 size = 0;
|
NOT_REACHED();
|
||||||
for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) {
|
|
||||||
size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SlSetStructListLength(size);
|
|
||||||
for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) {
|
|
||||||
SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Load(Node *bn) const override
|
void Load(Node *bn) const override
|
||||||
@@ -58,22 +51,18 @@ public:
|
|||||||
uint16 max_size = _linkgraph->Size();
|
uint16 max_size = _linkgraph->Size();
|
||||||
|
|
||||||
if (IsSavegameVersionBefore(SLV_191)) {
|
if (IsSavegameVersionBefore(SLV_191)) {
|
||||||
/* We used to save the full matrix ... */
|
NOT_REACHED();
|
||||||
for (NodeID to = 0; to < max_size; ++to) {
|
|
||||||
SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetLoadDescription());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX);
|
size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX);
|
||||||
|
|
||||||
/* ... but as that wasted a lot of space we save a sparse matrix now. */
|
/* ... but as that wasted a lot of space we save a sparse matrix now. */
|
||||||
for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) {
|
for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _edge_next_edge) {
|
||||||
if (used_size == 0) SlErrorCorrupt("Link graph structure overflow");
|
if (used_size == 0) SlErrorCorrupt("Link graph structure overflow");
|
||||||
used_size--;
|
used_size--;
|
||||||
|
|
||||||
if (to >= max_size) SlErrorCorrupt("Link graph structure overflow");
|
if (to >= max_size) SlErrorCorrupt("Link graph structure overflow");
|
||||||
SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetLoadDescription());
|
SlObject(&_linkgraph->edges[std::make_pair(_linkgraph_from, to)], this->GetLoadDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph");
|
if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph");
|
||||||
|
@@ -114,7 +114,7 @@ Station::~Station()
|
|||||||
for (NodeID node = 0; node < lg->Size(); ++node) {
|
for (NodeID node = 0; node < lg->Size(); ++node) {
|
||||||
Station *st = Station::Get((*lg)[node].Station());
|
Station *st = Station::Get((*lg)[node].Station());
|
||||||
st->goods[c].flows.erase(this->index);
|
st->goods[c].flows.erase(this->index);
|
||||||
if ((*lg)[node][this->goods[c].node].LastUpdate() != INVALID_DATE) {
|
if (lg->GetConstEdge(node, this->goods[c].node).LastUpdate() != INVALID_DATE) {
|
||||||
st->goods[c].flows.DeleteFlows(this->index);
|
st->goods[c].flows.DeleteFlows(this->index);
|
||||||
RerouteCargo(st, c, this->index, st->index);
|
RerouteCargo(st, c, this->index, st->index);
|
||||||
}
|
}
|
||||||
|
@@ -4405,12 +4405,14 @@ void DeleteStaleLinks(Station *from)
|
|||||||
GoodsEntry &ge = from->goods[c];
|
GoodsEntry &ge = from->goods[c];
|
||||||
LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph);
|
LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph);
|
||||||
if (lg == nullptr) continue;
|
if (lg == nullptr) continue;
|
||||||
Node node = (*lg)[ge.node];
|
lg->MutableIterateEdgesFromNode(ge.node, [&](LinkGraph::EdgeIterationHelper edge_helper) -> LinkGraph::EdgeIterationResult {
|
||||||
for (EdgeIterator it(node.Begin()); it != node.End();) {
|
Edge edge = edge_helper.GetEdge();
|
||||||
Edge edge = it->second;
|
NodeID to_id = edge_helper.to_id;
|
||||||
Station *to = Station::Get((*lg)[it->first].Station());
|
|
||||||
assert(to->goods[c].node == it->first);
|
LinkGraph::EdgeIterationResult result = LinkGraph::EdgeIterationResult::None;
|
||||||
++it; // Do that before removing the edge. Anything else may crash.
|
|
||||||
|
Station *to = Station::Get((*lg)[to_id].Station());
|
||||||
|
assert(to->goods[c].node == to_id);
|
||||||
assert(_date >= edge.LastUpdate());
|
assert(_date >= edge.LastUpdate());
|
||||||
uint timeout = std::max<uint>((LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3)) / _settings_game.economy.day_length_factor, 1);
|
uint timeout = std::max<uint>((LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3)) / _settings_game.economy.day_length_factor, 1);
|
||||||
if (edge.LastAircraftUpdate() != INVALID_DATE && (uint)(_date - edge.LastAircraftUpdate()) > timeout) {
|
if (edge.LastAircraftUpdate() != INVALID_DATE && (uint)(_date - edge.LastAircraftUpdate()) > timeout) {
|
||||||
@@ -4450,7 +4452,11 @@ void DeleteStaleLinks(Station *from)
|
|||||||
/* Do not refresh links of vehicles that have been stopped in depot for a long time. */
|
/* Do not refresh links of vehicles that have been stopped in depot for a long time. */
|
||||||
if (!v->IsStoppedInDepot() || static_cast<uint>(_date - v->date_of_last_service) <=
|
if (!v->IsStoppedInDepot() || static_cast<uint>(_date - v->date_of_last_service) <=
|
||||||
LinkGraph::STALE_LINK_DEPOT_TIMEOUT) {
|
LinkGraph::STALE_LINK_DEPOT_TIMEOUT) {
|
||||||
|
edge_helper.RecordSize();
|
||||||
LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
|
LinkRefresher::Run(v, false); // Don't allow merging. Otherwise lg might get deleted.
|
||||||
|
if (edge_helper.RefreshIterationIfSizeChanged()) {
|
||||||
|
edge = edge_helper.GetEdge();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (edge.LastUpdate() == _date) {
|
if (edge.LastUpdate() == _date) {
|
||||||
@@ -4472,7 +4478,7 @@ void DeleteStaleLinks(Station *from)
|
|||||||
|
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
/* If it's still considered dead remove it. */
|
/* If it's still considered dead remove it. */
|
||||||
node.RemoveEdge(to->goods[c].node);
|
result = LinkGraph::EdgeIterationResult::EraseEdge;
|
||||||
ge.flows.DeleteFlows(to->index);
|
ge.flows.DeleteFlows(to->index);
|
||||||
RerouteCargo(from, c, to->index, from->index);
|
RerouteCargo(from, c, to->index, from->index);
|
||||||
}
|
}
|
||||||
@@ -4483,7 +4489,9 @@ void DeleteStaleLinks(Station *from)
|
|||||||
} else if (edge.LastRestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastRestrictedUpdate()) > timeout) {
|
} else if (edge.LastRestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastRestrictedUpdate()) > timeout) {
|
||||||
edge.Release();
|
edge.Release();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
assert(_date >= lg->LastCompression());
|
assert(_date >= lg->LastCompression());
|
||||||
if ((uint)(_date - lg->LastCompression()) > std::max<uint>(LinkGraph::COMPRESSION_INTERVAL / _settings_game.economy.day_length_factor, 1)) {
|
if ((uint)(_date - lg->LastCompression()) > std::max<uint>(LinkGraph::COMPRESSION_INTERVAL / _settings_game.economy.day_length_factor, 1)) {
|
||||||
lg->Compress();
|
lg->Compress();
|
||||||
@@ -4542,7 +4550,7 @@ void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lg != nullptr) {
|
if (lg != nullptr) {
|
||||||
(*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, mode);
|
lg->UpdateEdge(ge1.node, ge2.node, capacity, usage, time, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user