2using System.Collections.Generic;
28 throw new ArgumentNullException(
29 nameof(supplyChain))))
42 throw new ArgumentNullException(nameof(index));
87 return FindFastestPathCore(
90 allowedTransportResourceIds:
null);
117 Index.GetRequiredItem(itemId);
120 HashSet<int> allowedTransportResourceIds =
124 characteristic.ItemId == itemId)
127 characteristic.TransportResourceId)
130 return FindFastestPathCore(
133 allowedTransportResourceIds);
149 throw new KeyNotFoundException(
150 "No transport path exists between " +
151 $
"{FormatWarehouse(origin)} and " +
152 $
"{FormatWarehouse(destination)}.");
171 throw new KeyNotFoundException(
172 $
"No transport path exists for item {itemId} " +
173 $
"between {FormatWarehouse(origin)} and " +
174 $
"{FormatWarehouse(destination)}.");
187 destination) is not
null;
202 destination) is not
null;
205 private TransportPath? FindFastestPathCore(
208 IReadOnlySet<int>? allowedTransportResourceIds)
210 ArgumentNullException.ThrowIfNull(origin);
211 ArgumentNullException.ThrowIfNull(destination);
220 WarehouseKey originKey =
221 WarehouseKey.FromReference(origin);
223 WarehouseKey destinationKey =
224 WarehouseKey.FromReference(destination);
227 if (originKey == destinationKey)
229 return new TransportPath(
232 Array.Empty<TransportLeg>());
235 Dictionary<WarehouseKey, List<TransportLeg>>
236 adjacency = BuildAdjacency(
237 allowedTransportResourceIds);
241 new Dictionary<WarehouseKey, int>
247 new Dictionary<WarehouseKey, TransportLeg>();
250 new PriorityQueue<WarehouseKey, int>();
256 while (queue.TryDequeue(
257 out WarehouseKey current,
258 out
int currentDistance))
260 if (!distances.TryGetValue(
262 out
int bestKnownDistance) ||
263 currentDistance != bestKnownDistance)
273 if (current == destinationKey)
278 if (!adjacency.TryGetValue(
280 out List<TransportLeg>? outgoingLegs))
288 WarehouseKey.FromReference(
291 int candidateDistance;
296 candidateDistance = checked(
300 catch (OverflowException exception)
302 throw new InvalidOperationException(
303 "The accumulated transport lead time " +
304 "exceeds the supported integer range.",
308 if (distances.TryGetValue(
310 out
int existingDistance) &&
311 candidateDistance >= existingDistance)
316 distances[next] = candidateDistance;
317 predecessors[next] = leg;
325 if (!distances.ContainsKey(destinationKey))
331 List<TransportLeg> reversedLegs =
337 reversedLegs.Reverse();
345 private Dictionary<WarehouseKey, List<TransportLeg>>
347 IReadOnlySet<int>? allowedTransportResourceIds)
350 new Dictionary<WarehouseKey, List<TransportLeg>>();
352 foreach (TransportResource resource
356 if (allowedTransportResourceIds is not
null &&
357 !allowedTransportResourceIds.Contains(
363 foreach (AssignedTransportLane lane
in SupplyChain.GetTransportLanes(resource.
Id))
365 if (lane.
Origin is
null ||
375 if (!
Index.TryGetWarehouse(
378 !
Index.TryGetWarehouse(
387 throw new InvalidOperationException(
388 "A transport path cannot be calculated " +
389 "because a lane has a negative lead time.");
392 WarehouseKey originKey =
393 WarehouseKey.FromReference(
396 if (!adjacency.TryGetValue(
398 out List<TransportLeg>? legs))
400 legs =
new List<TransportLeg>();
401 adjacency.Add(originKey, legs);
414 private static List<TransportLeg> ReconstructPath(
416 WarehouseKey destination,
422 new List<TransportLeg>();
424 WarehouseKey current = destination;
426 while (current != origin)
428 if (!predecessors.TryGetValue(
432 throw new InvalidOperationException(
433 "The transport path cannot be reconstructed.");
436 reversedLegs.Add(leg);
439 WarehouseKey.FromReference(
446 private static string FormatWarehouse(
447 WarehouseReference reference)
449 ArgumentNullException.ThrowIfNull(reference);
452 $
"{reference.Kind}:{reference.ReferenceId}";
459 private readonly record
struct WarehouseKey(
463 public static WarehouseKey FromReference(
464 WarehouseReference reference)
466 ArgumentNullException.ThrowIfNull(reference);
468 return new WarehouseKey(
493 throw new ArgumentNullException(
494 nameof(transportResource));
497 throw new ArgumentNullException(nameof(lane));
530 $
"{TransportResource.Name}: " +
531 $
"{FormatWarehouse(Origin)} -> " +
532 $
"{FormatWarehouse(Destination)} " +
533 $
"({LeadTime} period(s))";
561 IEnumerable<TransportLeg> legs)
564 throw new ArgumentNullException(nameof(origin));
567 throw new ArgumentNullException(
568 nameof(destination));
570 ArgumentNullException.ThrowIfNull(legs);
572 _legs = legs.ToArray();
575 leg => leg.LeadTime);
591 public IReadOnlyList<TransportLeg>
Legs =>
616 $
"{FormatWarehouse(Origin)} " +
617 "(same origin and destination)";
620 string route =
string.Join(
623 leg => leg.ToString()));
626 $
"{route} — total lead time: " +
627 $
"{TotalLeadTime} period(s)";
int Id
Gets or sets the numerical identifier of the entity.
Provides fast access to the entities contained in a supply chain.
SupplyChain SupplyChain
Gets the indexed supply chain.
Warehouse GetRequiredWarehouse(WarehouseReference reference)
Resolves a warehouse from a warehouse reference.
A runtime join of the central lane and assignment; stores no copied business data.
WarehouseReference Origin
WarehouseReference Destination
Represents a serializable reference to a warehouse.
int ReferenceId
Gets or sets the referenced identifier.
WarehouseReferenceKind Kind
Gets or sets the kind of warehouse being referenced.
Represents one leg of a transport path.
TransportResource TransportResource
Gets the transport resource used for this leg.
AssignedTransportLane Lane
Gets the transport lane used for this leg.
WarehouseReference Origin
Gets the origin warehouse reference.
int LeadTime
Gets the transport lead time of this leg.
WarehouseReference Destination
Gets the destination warehouse reference.
TransportLeg(TransportResource transportResource, AssignedTransportLane lane)
Initializes one leg of a transport path.
override string ToString()
Represents a complete transport path between two warehouses.
int TotalLeadTime
Gets the sum of the lead times of all transport legs.
WarehouseReference Origin
Gets the path origin.
TransportPath(WarehouseReference origin, WarehouseReference destination, IEnumerable< TransportLeg > legs)
Initializes a complete transport path between two warehouses.
override string ToString()
bool IsEmpty
Gets a value indicating whether the origin and destination are the same warehouse.
WarehouseReference Destination
Gets the path destination.
IReadOnlyList< TransportLeg > Legs
Gets the ordered transport legs.
int LegCount
Gets the number of transport legs.
SupplyChain SupplyChain
Gets the explored supply chain.
TransportPathFinder(SupplyChain supplyChain)
Initializes a path finder and creates a new entity index.
TransportPath? FindFastestPathForItem(int itemId, WarehouseReference origin, WarehouseReference destination)
Finds the fastest transport path compatible with a specified item.
SupplyChainIndex Index
Gets the entity index used by the path finder.
void RebuildIndex()
Rebuilds the underlying entity index.
TransportPath? FindFastestPath(WarehouseReference origin, WarehouseReference destination)
Finds the fastest transport path between two warehouses.
bool HasPath(WarehouseReference origin, WarehouseReference destination)
Determines whether a transport path exists between two warehouses.
TransportPath GetRequiredFastestPathForItem(int itemId, WarehouseReference origin, WarehouseReference destination)
Gets the fastest item-compatible transport path between two warehouses.
TransportPath GetRequiredFastestPath(WarehouseReference origin, WarehouseReference destination)
Gets the fastest transport path between two warehouses.
bool HasPathForItem(int itemId, WarehouseReference origin, WarehouseReference destination)
Determines whether an item-compatible transport path exists between two warehouses.
TransportPathFinder(SupplyChainIndex index)
Initializes a path finder using an existing entity index.
List< TransportCharacteristic > TransportCharacteristics
Gets the item-transport-resource characteristics.
WarehouseReferenceKind
Identifies the type of warehouse referenced by a relationship.