24 private readonly
int _capacity;
26 private readonly
double[] _slope;
27 private readonly
double[] _intercept;
28 private readonly
double[] _startX;
30 private readonly
int[] _left;
31 private readonly
int[] _right;
32 private readonly
int[] _parent;
33 private readonly
int[] _height;
34 private readonly
int[] _previous;
35 private readonly
int[] _next;
37 private int _root = -1;
38 private int _first = -1;
39 private bool _disposed;
45 throw new ArgumentOutOfRangeException(
48 "Candidate-tree capacity must be positive.");
53 _slope = ArrayPool<double>.Shared.Rent(capacity);
54 _intercept = ArrayPool<double>.Shared.Rent(capacity);
55 _startX = ArrayPool<double>.Shared.Rent(capacity);
57 _left = ArrayPool<int>.Shared.Rent(capacity);
58 _right = ArrayPool<int>.Shared.Rent(capacity);
59 _parent = ArrayPool<int>.Shared.Rent(capacity);
60 _height = ArrayPool<int>.Shared.Rent(capacity);
61 _previous = ArrayPool<int>.Shared.Rent(capacity);
62 _next = ArrayPool<int>.Shared.Rent(capacity);
68 return _slope[period];
74 return _intercept[period];
92 if ((uint)period >= (uint)_capacity)
94 throw new ArgumentOutOfRangeException(nameof(period));
97 if (!
double.IsFinite(slope) || !
double.IsFinite(intercept))
99 throw new ArithmeticException(
100 "Federgruen-Tzur candidate coefficients must be finite.");
103 var equalSlope = FindBySlope(slope);
110 if (_intercept[equalSlope] <= intercept)
118 InitializeNode(period, slope, intercept);
120 var higherSlope = -1;
127 treeParent = current;
129 if (slope < _slope[current])
131 higherSlope = current;
132 current = _left[current];
136 lowerSlope = current;
137 current = _right[current];
141 _parent[period] = treeParent;
147 else if (slope < _slope[treeParent])
149 _left[treeParent] = period;
153 _right[treeParent] = period;
158 _previous[period] = higherSlope;
159 _next[period] = lowerSlope;
161 if (higherSlope >= 0)
163 _next[higherSlope] = period;
172 _previous[lowerSlope] = period;
175 Rebalance(treeParent);
177 if (higherSlope >= 0 &&
179 IntersectionX(higherSlope, period) >=
180 IntersectionX(period, lowerSlope))
186 var previous = _previous[period];
188 while (previous >= 0)
190 var previousPrevious = _previous[previous];
192 if (previousPrevious < 0 ||
193 IntersectionX(previousPrevious, previous) <
194 IntersectionX(previous, period))
200 previous = _previous[period];
203 var next = _next[period];
207 var nextNext = _next[next];
210 IntersectionX(period, next) <
211 IntersectionX(next, nextNext))
217 next = _next[period];
220 previous = _previous[period];
221 next = _next[period];
225 ? double.NegativeInfinity
226 : IntersectionX(previous, period);
230 _startX[next] = IntersectionX(period, next);
244 if (!
double.IsFinite(cumulativeDemand))
246 throw new ArithmeticException(
247 "Cumulative demand must be finite.");
252 throw new InvalidOperationException(
253 "The Federgruen-Tzur candidate tree is empty.");
256 while (_next[_first] >= 0)
258 var current = _first;
259 var next = _next[current];
260 var threshold = _startX[next];
262 var nextIsPreferred =
263 threshold < cumulativeDemand ||
264 (threshold == cumulativeDemand && next < current);
266 if (!nextIsPreferred)
286 ArrayPool<double>.Shared.Return(_slope, clearArray:
false);
287 ArrayPool<double>.Shared.Return(_intercept, clearArray:
false);
288 ArrayPool<double>.Shared.Return(_startX, clearArray:
false);
290 ArrayPool<int>.Shared.Return(_left, clearArray:
false);
291 ArrayPool<int>.Shared.Return(_right, clearArray:
false);
292 ArrayPool<int>.Shared.Return(_parent, clearArray:
false);
293 ArrayPool<int>.Shared.Return(_height, clearArray:
false);
294 ArrayPool<int>.Shared.Return(_previous, clearArray:
false);
295 ArrayPool<int>.Shared.Return(_next, clearArray:
false);
298 private void InitializeNode(
303 _slope[period] = slope;
304 _intercept[period] = intercept;
305 _startX[period] =
double.NegativeInfinity;
309 _parent[period] = -1;
311 _previous[period] = -1;
315 private int FindBySlope(
double slope)
321 if (slope < _slope[current])
323 current = _left[current];
325 else if (slope > _slope[current])
327 current = _right[current];
338 private double IntersectionX(
339 int higherSlopePeriod,
340 int lowerSlopePeriod)
343 _slope[higherSlopePeriod] -
344 _slope[lowerSlopePeriod];
346 if (!(denominator > 0.0) ||
347 !
double.IsFinite(denominator))
349 throw new ArithmeticException(
350 "A Federgruen-Tzur candidate intersection has an invalid denominator.");
354 _intercept[lowerSlopePeriod] -
355 _intercept[higherSlopePeriod];
357 var intersection = numerator / denominator;
359 if (!
double.IsFinite(intersection))
361 throw new ArithmeticException(
362 "A Federgruen-Tzur candidate intersection is not finite.");
368 private void Remove(
int node)
370 var previous = _previous[node];
371 var next = _next[node];
375 _next[previous] = next;
384 _previous[next] = previous;
388 _startX[next] =
double.NegativeInfinity;
392 DeleteTreeNode(node);
394 _previous[node] = -1;
398 private void DeleteTreeNode(
int node)
404 rebalanceStart = _parent[node];
405 Transplant(node, _right[node]);
407 else if (_right[node] < 0)
409 rebalanceStart = _parent[node];
410 Transplant(node, _left[node]);
414 var successor = Minimum(_right[node]);
416 if (_parent[successor] == node)
418 Transplant(node, successor);
420 _left[successor] = _left[node];
421 _parent[_left[successor]] = successor;
423 UpdateHeight(successor);
424 rebalanceStart = successor;
428 var successorOldParent = _parent[successor];
430 Transplant(successor, _right[successor]);
432 _right[successor] = _right[node];
433 _parent[_right[successor]] = successor;
435 Transplant(node, successor);
437 _left[successor] = _left[node];
438 _parent[_left[successor]] = successor;
440 UpdateHeight(successor);
441 rebalanceStart = successorOldParent;
450 Rebalance(rebalanceStart);
453 private int Minimum(
int node)
457 while (_left[current] >= 0)
459 current = _left[current];
465 private void Transplant(
int oldNode,
int replacement)
467 var oldParent = _parent[oldNode];
473 else if (_left[oldParent] == oldNode)
475 _left[oldParent] = replacement;
479 _right[oldParent] = replacement;
482 if (replacement >= 0)
484 _parent[replacement] = oldParent;
488 private void Rebalance(
int node)
494 UpdateHeight(current);
496 var balance = Balance(current);
497 var subtreeRoot = current;
501 var left = _left[current];
503 if (Balance(left) < 0)
508 subtreeRoot = RotateRight(current);
510 else if (balance < -1)
512 var right = _right[current];
514 if (Balance(right) > 0)
519 subtreeRoot = RotateLeft(current);
522 current = _parent[subtreeRoot];
526 private int RotateLeft(
int node)
528 var pivot = _right[node];
532 throw new InvalidOperationException(
533 "Invalid AVL left rotation.");
536 var middle = _left[pivot];
537 var oldParent = _parent[node];
539 _parent[pivot] = oldParent;
545 else if (_left[oldParent] == node)
547 _left[oldParent] = pivot;
551 _right[oldParent] = pivot;
555 _parent[node] = pivot;
557 _right[node] = middle;
561 _parent[middle] = node;
570 private int RotateRight(
int node)
572 var pivot = _left[node];
576 throw new InvalidOperationException(
577 "Invalid AVL right rotation.");
580 var middle = _right[pivot];
581 var oldParent = _parent[node];
583 _parent[pivot] = oldParent;
589 else if (_left[oldParent] == node)
591 _left[oldParent] = pivot;
595 _right[oldParent] = pivot;
598 _right[pivot] = node;
599 _parent[node] = pivot;
601 _left[node] = middle;
605 _parent[middle] = node;
614 private int Balance(
int node)
621 return Height(_left[node]) - Height(_right[node]);
624 private int Height(
int node)
626 return node < 0 ? 0 : _height[node];
629 private void UpdateHeight(
int node)
639 Height(_right[node]));
642 private void ValidateNode(
int period)
646 if ((uint)period >= (uint)_capacity ||
647 _height[period] <= 0)
649 throw new ArgumentOutOfRangeException(
652 "The candidate period is not active in the tree.");
656 private void ThrowIfDisposed()
658 ObjectDisposedException.ThrowIf(_disposed,
this);