1
//! Implementation of the Tor Vegas congestion control algorithm.
2
//!
3
//! This is used by the circuit reactor in order to decide when to send data and SENDMEs.
4
//!
5
//! Spec: prop324 section 3.3 (TOR_VEGAS)
6

            
7
use super::{
8
    params::{Algorithm, VegasParams},
9
    rtt::RoundtripTimeEstimator,
10
    CongestionControlAlgorithm, CongestionSignals, CongestionWindow, State,
11
};
12
use crate::Result;
13

            
14
use tor_error::{error_report, internal};
15

            
16
/// Bandwidth-Delay Product (BDP) estimator.
17
///
18
/// Spec: prop324 section 3.1 (BDP_ESTIMATION).
19
#[derive(Clone, Debug, Default)]
20
pub(crate) struct BdpEstimator {
21
    /// The BDP value of this estimator.
22
    bdp: u32,
23
}
24

            
25
impl BdpEstimator {
26
    /// Return the current BDP value.
27
138
    fn get(&self) -> u32 {
28
138
        self.bdp
29
138
    }
30

            
31
    /// Update the estimator with the given congestion window, RTT estimator and any condition
32
    /// signals that we are currently experiencing.
33
    ///
34
    /// C-tor: congestion_control_update_circuit_bdp() in congestion_control_common.c
35
124
    fn update(
36
124
        &mut self,
37
124
        cwnd: &CongestionWindow,
38
124
        rtt: &RoundtripTimeEstimator,
39
124
        signals: &CongestionSignals,
40
124
    ) {
41
124
        // Stalled clock means our RTT value is invalid so set the BDP to the cwnd.
42
124
        if rtt.clock_stalled() {
43
            self.bdp = if signals.channel_blocked {
44
                // Set the BDP to the cwnd minus the outbound queue size, capping it to the minimum
45
                // cwnd.
46
                cwnd.get()
47
                    .saturating_sub(signals.channel_outbound_size)
48
                    .max(cwnd.min())
49
            } else {
50
                cwnd.get()
51
            };
52
124
        } else {
53
124
            // Congestion window based BDP will respond to changes in RTT only, and is relative to
54
124
            // cwnd growth. It is useful for correcting for BDP overestimation, but if BDP is
55
124
            // higher than the current cwnd, it will underestimate it.
56
124
            //
57
124
            // To clarify this is equivalent to: cwnd * min_rtt / ewma_rtt.
58
124
            self.bdp = cwnd
59
124
                .get()
60
124
                .saturating_mul(rtt.min_rtt_usec())
61
124
                .saturating_div(rtt.ewma_rtt_usec());
62
124
        }
63
124
    }
64
}
65

            
66
/// Congestion control Vegas algorithm.
67
///
68
/// TCP Vegas control algorithm estimates the queue lengths at relays by subtracting the current
69
/// BDP estimate from the current congestion window.
70
///
71
/// This object implements CongestionControlAlgorithm trait used by the ['CongestionControl'].
72
///
73
/// Spec: prop324 section 3.3 (TOR_VEGAS)
74
/// C-tor: Split between congestion_control_vegas.c and the congestion_control_t struct.
75
#[derive(Clone, Debug)]
76
pub(crate) struct Vegas {
77
    /// Congestion control parameters.
78
    params: VegasParams,
79
    /// Bandwidth delay product.
80
    /// C-tor: "bdp"
81
    bdp: BdpEstimator,
82
    /// Congestion window.
83
    /// C-tor: "cwnd", "cwnd_inc_pct_ss", "cwnd_inc", "cwnd_min", "cwnd_inc_rate", "cwnd_full",
84
    cwnd: CongestionWindow,
85
    /// Number of cells expected before we send a SENDME resulting in more data.
86
    num_cell_until_sendme: u32,
87
    /// The number of SENDME until we will acknowledge a congestion event again.
88
    /// C-tor: "next_cc_event"
89
    num_sendme_until_cwnd_update: u32,
90
    /// Counts down until we process a cwnd worth of SENDME acks. Used to track full cwnd status.
91
    /// C-tor: "next_cwnd_event"
92
    num_sendme_per_cwnd: u32,
93
    /// Number of cells in-flight (sent but awaiting SENDME ack).
94
    /// C-tor: "inflight"
95
    num_inflight: u32,
96
    /// Indicate if we noticed we were blocked on channel during an algorithm run. This is used to
97
    /// notice a change from blocked to non-blocked in order to reset the num_sendme_per_cwnd.
98
    /// C-tor: "blocked_chan"
99
    is_blocked_on_chan: bool,
100
}
101

            
102
impl Vegas {
103
    /// Create a new [`Vegas`] from the specified parameters, state, and cwnd.
104
232
    pub(crate) fn new(params: VegasParams, state: &State, cwnd: CongestionWindow) -> Self {
105
232
        Self {
106
232
            params,
107
232
            bdp: BdpEstimator::default(),
108
232
            num_cell_until_sendme: cwnd.sendme_inc(),
109
232
            num_inflight: 0,
110
232
            num_sendme_per_cwnd: 0,
111
232
            num_sendme_until_cwnd_update: cwnd.update_rate(state),
112
232
            cwnd,
113
232
            is_blocked_on_chan: false,
114
232
        }
115
232
    }
116
}
117

            
118
impl CongestionControlAlgorithm for Vegas {
119
4
    fn uses_stream_sendme(&self) -> bool {
120
4
        // Not allowed as in Vegas doesn't need them.
121
4
        false
122
4
    }
123
1200
    fn is_next_cell_sendme(&self) -> bool {
124
1200
        // Matching inflight number to the SENDME increment, time to send a SENDME. Contrary to
125
1200
        // C-tor, this is called after num_inflight is incremented.
126
1200
        self.num_inflight % self.cwnd.sendme_inc() == 0
127
1200
    }
128

            
129
8144
    fn can_send(&self) -> bool {
130
8144
        self.num_inflight < self.cwnd.get()
131
8144
    }
132

            
133
416
    fn cwnd(&self) -> Option<&CongestionWindow> {
134
416
        Some(&self.cwnd)
135
416
    }
136

            
137
    /// Called when a SENDME cell is received.
138
    ///
139
    /// This is where the Vegas algorithm magic happens entirely. For every SENDME we get, the
140
    /// entire state is updated which usually result in the congestion window being changed.
141
    ///
142
    /// An error is returned if there is a protocol violation with regards to flow or congestion
143
    /// control.
144
    ///
145
    /// Spec: prop324 section 3.3 (TOR_VEGAS)
146
    /// C-tor: congestion_control_vegas_process_sendme() in congestion_control_vegas.c
147
124
    fn sendme_received(
148
124
        &mut self,
149
124
        state: &mut State,
150
124
        rtt: &mut RoundtripTimeEstimator,
151
124
        signals: CongestionSignals,
152
124
    ) -> Result<()> {
153
124
        // Update the countdown until we need to update the congestion window.
154
124
        self.num_sendme_until_cwnd_update = self.num_sendme_until_cwnd_update.saturating_sub(1);
155
124
        // We just got a SENDME so decrement the amount of expected SENDMEs for a cwnd.
156
124
        self.num_sendme_per_cwnd = self.num_sendme_per_cwnd.saturating_sub(1);
157
124

            
158
124
        // From here, C-tor proceeds to update the RTT and BDP (circuit estimates). The RTT is
159
124
        // updated before this is called and so the "rtt" object is up to date with the latest. As
160
124
        // for the BDP, we update it now. See C-tor congestion_control_update_circuit_estimates().
161
124

            
162
124
        // Update the BDP estimator even if the RTT estimator is not ready. If that is the case,
163
124
        // we'll estimate a BDP value to bootstrap.
164
124
        self.bdp.update(&self.cwnd, rtt, &signals);
165
124

            
166
124
        // Evaluate if we changed state on the blocked chan. This is done in the BDP update function
167
124
        // in C-tor. Instead, we do it now after the update of the BDP value.
168
124
        if rtt.is_ready() {
169
124
            if signals.channel_blocked {
170
                // Going from non blocked to block, it is an immediate congestion signal so reset the
171
                // number of sendme per cwnd because we are about to reevaluate it.
172
10
                if !self.is_blocked_on_chan {
173
4
                    self.num_sendme_until_cwnd_update = 0;
174
6
                }
175
            } else {
176
                // Going from blocked to non block, need to reevaluate the cwnd and so reset num
177
                // sendme.
178
114
                if self.is_blocked_on_chan {
179
                    self.num_sendme_until_cwnd_update = 0;
180
114
                }
181
            }
182
        }
183
124
        self.is_blocked_on_chan = signals.channel_blocked;
184
124

            
185
124
        // Only run the algorithm if the RTT estimator is ready or we have a blocked channel.
186
124
        if !rtt.is_ready() && !self.is_blocked_on_chan {
187
            // The inflight value can never be below a sendme_inc because every time a cell is sent,
188
            // inflight is incremented and we only end up decrementing if we receive a valid
189
            // authenticated SENDME which is always after the sendme_inc value that we get that.
190
            debug_assert!(self.num_inflight >= self.cwnd.sendme_inc());
191
            self.num_inflight = self.num_inflight.saturating_sub(self.cwnd.sendme_inc());
192
            return Ok(());
193
124
        }
194
124

            
195
124
        // The queue use is the amount in which our cwnd is above BDP;
196
124
        // if it is below, then 0 queue use.
197
124
        let queue_use = self.cwnd.get().saturating_sub(self.bdp.get());
198
124

            
199
124
        // Evaluate if the congestion window has became full or not.
200
124
        self.cwnd.eval_fullness(
201
124
            self.num_inflight,
202
124
            self.params.cwnd_full_gap(),
203
124
            self.params.cwnd_full_min_pct().as_percent(),
204
124
        );
205
124

            
206
124
        // Spec: See the pseudocode of TOR_VEGAS with RFC3742
207
124
        if state.in_slow_start() {
208
96
            if queue_use < self.params.cell_in_queue_params().gamma() && !self.is_blocked_on_chan {
209
                // If the congestion window is not fully in use, skip any increment in slow start.
210
86
                if self.cwnd.is_full() {
211
                    // This is the "Limited Slow Start" increment.
212
80
                    let inc = self
213
80
                        .cwnd
214
80
                        .rfc3742_ss_inc(self.params.cell_in_queue_params().ss_cwnd_cap());
215
80

            
216
80
                    // Check if inc is less than what we would do in steady-state avoidance. Note
217
80
                    // that this is likely never to happen in practice. If so, exit slow start.
218
80
                    if (inc * self.cwnd.sendme_per_cwnd())
219
80
                        <= (self.cwnd.increment() * self.cwnd.increment_rate())
220
                    {
221
                        *state = State::Steady;
222
80
                    }
223
6
                }
224
10
            } else {
225
10
                // Congestion signal: Set cwnd to gamma threshold
226
10
                self.cwnd
227
10
                    .set(self.bdp.get() + self.params.cell_in_queue_params().gamma());
228
10
                // Exit slow start due to congestion signal.
229
10
                *state = State::Steady;
230
10
            }
231

            
232
            // Max the window and exit slow start.
233
96
            if self.cwnd.get() >= self.params.ss_cwnd_max() {
234
                self.cwnd.set(self.params.ss_cwnd_max());
235
                *state = State::Steady;
236
96
            }
237
28
        } else if self.num_sendme_until_cwnd_update == 0 {
238
            // Once in steady state, we only update once per window.
239
28
            if queue_use > self.params.cell_in_queue_params().delta() {
240
4
                // Above delta threshold, drop cwnd down to the delta.
241
4
                self.cwnd.set(
242
4
                    self.bdp.get() + self.params.cell_in_queue_params().delta()
243
4
                        - self.cwnd.increment(),
244
4
                );
245
24
            } else if queue_use > self.params.cell_in_queue_params().beta()
246
20
                || self.is_blocked_on_chan
247
8
            {
248
8
                // Congestion signal: Above beta or if channel is blocked, decrement window.
249
8
                self.cwnd.dec();
250
16
            } else if self.cwnd.is_full() && queue_use < self.params.cell_in_queue_params().alpha()
251
10
            {
252
10
                // Congestion window is full and the queue usage is below alpha, increment.
253
10
                self.cwnd.inc();
254
10
            }
255
        }
256

            
257
        // Reset our counters if they reached their bottom.
258
124
        if self.num_sendme_until_cwnd_update == 0 {
259
124
            self.num_sendme_until_cwnd_update = self.cwnd.update_rate(state);
260
124
        }
261
124
        if self.num_sendme_per_cwnd == 0 {
262
22
            self.num_sendme_per_cwnd = self.cwnd.sendme_per_cwnd();
263
102
        }
264

            
265
        // Decide if enough time has passed to reset the cwnd.
266
124
        if self.params.cwnd_full_per_cwnd() != 0 {
267
124
            if self.num_sendme_per_cwnd == self.cwnd.sendme_per_cwnd() {
268
22
                self.cwnd.reset_full();
269
102
            }
270
        } else if self.num_sendme_until_cwnd_update == self.cwnd.update_rate(state) {
271
            self.cwnd.reset_full();
272
        }
273

            
274
        // Finally, update the inflight now that we have a SENDME.
275
124
        self.num_inflight = self.num_inflight.saturating_sub(self.cwnd.sendme_inc());
276
124
        Ok(())
277
124
    }
278

            
279
    fn sendme_sent(&mut self) -> Result<()> {
280
        // SENDME is on the wire, set our counter until next one.
281
        self.num_cell_until_sendme = self.cwnd.sendme_inc();
282
        Ok(())
283
    }
284

            
285
    fn data_received(&mut self) -> Result<bool> {
286
        if self.num_cell_until_sendme == 0 {
287
            // This is not a protocol violation, it is a code flow error and so don't close the
288
            // circuit by sending back an Error. Catching this prevents from sending two SENDMEs
289
            // back to back. We recover from this but scream very loudly.
290
            error_report!(internal!("Congestion control unexptected data cell"), "");
291
            return Ok(false);
292
        }
293

            
294
        // Decrement the expected window.
295
        self.num_cell_until_sendme = self.num_cell_until_sendme.saturating_sub(1);
296

            
297
        // Reaching zero, lets inform the caller a SENDME needs to be sent. This counter is reset
298
        // when the SENDME is actually sent.
299
        Ok(self.num_cell_until_sendme == 0)
300
    }
301

            
302
1200
    fn data_sent(&mut self) -> Result<()> {
303
1200
        // This can be above cwnd because that cwnd can shrink while we are still sending data.
304
1200
        self.num_inflight = self.num_inflight.saturating_add(1);
305
1200
        Ok(())
306
1200
    }
307

            
308
    #[cfg(feature = "conflux")]
309
16
    fn inflight(&self) -> Option<u32> {
310
16
        Some(self.num_inflight)
311
16
    }
312

            
313
    #[cfg(test)]
314
    fn send_window(&self) -> u32 {
315
        self.cwnd.get()
316
    }
317

            
318
36
    fn algorithm(&self) -> Algorithm {
319
36
        Algorithm::Vegas(self.params)
320
36
    }
321
}
322

            
323
#[cfg(test)]
324
#[allow(clippy::print_stderr)]
325
pub(crate) mod test {
326
    // @@ begin test lint list maintained by maint/add_warning @@
327
    #![allow(clippy::bool_assert_comparison)]
328
    #![allow(clippy::clone_on_copy)]
329
    #![allow(clippy::dbg_macro)]
330
    #![allow(clippy::mixed_attributes_style)]
331
    #![allow(clippy::print_stderr)]
332
    #![allow(clippy::print_stdout)]
333
    #![allow(clippy::single_char_pattern)]
334
    #![allow(clippy::unwrap_used)]
335
    #![allow(clippy::unchecked_duration_subtraction)]
336
    #![allow(clippy::useless_vec)]
337
    #![allow(clippy::needless_pass_by_value)]
338
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
339

            
340
    use std::{
341
        collections::VecDeque,
342
        time::{Duration, Instant},
343
    };
344
    use tor_units::Percentage;
345

            
346
    use super::*;
347
    use crate::congestion::{
348
        params::VegasParamsBuilder,
349
        test_utils::{new_cwnd, new_rtt_estimator},
350
    };
351

            
352
    impl Vegas {
353
        /// Set the number of inflight cell.
354
        pub(crate) fn set_inflight(&mut self, v: u32) {
355
            self.num_inflight = v;
356
        }
357
        /// Return the state of the blocked on chan flag.
358
        fn is_blocked_on_chan(&self) -> bool {
359
            self.is_blocked_on_chan
360
        }
361
        /// Set the state of the blocked on chan flag.
362
        fn set_is_blocked_on_chan(&mut self, v: bool) {
363
            self.is_blocked_on_chan = v;
364
        }
365
    }
366

            
367
    /// The test vector parameters. They have the exact same name as in C-tor in order to help
368
    /// matching them and avoid confusion.
369
    #[derive(Debug)]
370
    struct TestVectorParams {
371
        // Inbound parameters.
372
        sent_usec_in: u64,
373
        got_sendme_usec_in: u64,
374
        or_conn_blocked_in: bool,
375
        inflight_in: u32,
376
        // Expected outbound parameters.
377
        ewma_rtt_usec_out: u32,
378
        min_rtt_usec_out: u32,
379
        cwnd_out: u32,
380
        in_slow_start_out: bool,
381
        cwnd_full_out: bool,
382
        blocked_chan_out: bool,
383
    }
384

            
385
    impl From<[u32; 10]> for TestVectorParams {
386
        fn from(arr: [u32; 10]) -> Self {
387
            Self {
388
                sent_usec_in: u64::from(arr[0]),
389
                got_sendme_usec_in: u64::from(arr[1]),
390
                or_conn_blocked_in: arr[2] == 1,
391
                inflight_in: arr[3],
392
                ewma_rtt_usec_out: arr[4],
393
                min_rtt_usec_out: arr[5],
394
                cwnd_out: arr[6],
395
                in_slow_start_out: arr[7] == 1,
396
                cwnd_full_out: arr[8] == 1,
397
                blocked_chan_out: arr[9] == 1,
398
            }
399
        }
400
    }
401

            
402
    struct VegasTest {
403
        params: VecDeque<TestVectorParams>,
404
        rtt: RoundtripTimeEstimator,
405
        state: State,
406
        vegas: Vegas,
407
    }
408

            
409
    impl VegasTest {
410
        fn new(vec: Vec<[u32; 10]>) -> Self {
411
            let mut params = VecDeque::new();
412
            for values in vec {
413
                params.push_back(values.into());
414
            }
415
            let state = State::default();
416
            Self {
417
                params,
418
                rtt: new_rtt_estimator(),
419
                vegas: Vegas::new(build_vegas_params(), &state, new_cwnd()),
420
                state,
421
            }
422
        }
423

            
424
        fn run_once(&mut self, p: &TestVectorParams) {
425
            eprintln!("Testing vector: {:?}", p);
426
            // Set the inflight and channel blocked value from the test vector.
427
            self.vegas.set_inflight(p.inflight_in);
428
            self.vegas.set_is_blocked_on_chan(p.or_conn_blocked_in);
429

            
430
            let now = Instant::now();
431
            self.rtt
432
                .expect_sendme(now + Duration::from_micros(p.sent_usec_in));
433
            let ret = self.rtt.update(
434
                now + Duration::from_micros(p.got_sendme_usec_in),
435
                &self.state,
436
                self.vegas.cwnd().expect("No CWND"),
437
            );
438
            assert!(ret.is_ok());
439

            
440
            let signals = CongestionSignals::new(p.or_conn_blocked_in, 0);
441
            let ret = self
442
                .vegas
443
                .sendme_received(&mut self.state, &mut self.rtt, signals);
444
            assert!(ret.is_ok());
445

            
446
            assert_eq!(self.rtt.ewma_rtt_usec(), p.ewma_rtt_usec_out);
447
            assert_eq!(self.rtt.min_rtt_usec(), p.min_rtt_usec_out);
448
            assert_eq!(self.vegas.cwnd().expect("No CWND").get(), p.cwnd_out);
449
            assert_eq!(
450
                self.vegas.cwnd().expect("No CWND").is_full(),
451
                p.cwnd_full_out
452
            );
453
            assert_eq!(self.state.in_slow_start(), p.in_slow_start_out);
454
            assert_eq!(self.vegas.is_blocked_on_chan(), p.blocked_chan_out);
455
        }
456

            
457
        fn run(&mut self) {
458
            while let Some(param) = self.params.pop_front() {
459
                self.run_once(&param);
460
            }
461
        }
462
    }
463

            
464
    pub(crate) fn build_vegas_params() -> VegasParams {
465
        const OUTBUF_CELLS: u32 = 62;
466
        VegasParamsBuilder::default()
467
            .cell_in_queue_params(
468
                (
469
                    3 * OUTBUF_CELLS, // alpha
470
                    4 * OUTBUF_CELLS, // beta
471
                    5 * OUTBUF_CELLS, // delta
472
                    3 * OUTBUF_CELLS, // gamma
473
                    600,              // ss_cap
474
                )
475
                    .into(),
476
            )
477
            .ss_cwnd_max(5_000)
478
            .cwnd_full_gap(4)
479
            .cwnd_full_min_pct(Percentage::new(25))
480
            .cwnd_full_per_cwnd(1)
481
            .build()
482
            .expect("Unable to build Vegas parameters")
483
    }
484

            
485
    #[test]
486
    fn test_vectors() {
487
        let vec1 = vec![
488
            [100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0],
489
            [200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0],
490
            [350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0],
491
            [500000, 550000, 0, 217, 77777, 77777, 248, 1, 1, 0],
492
            [600000, 700000, 0, 248, 92592, 77777, 279, 1, 1, 0],
493
            [700000, 750000, 0, 279, 64197, 64197, 310, 1, 0, 0], // Fullness expiry
494
            [750000, 875000, 0, 310, 104732, 64197, 341, 1, 1, 0],
495
            [875000, 900000, 0, 341, 51577, 51577, 372, 1, 1, 0],
496
            [900000, 950000, 0, 279, 50525, 50525, 403, 1, 1, 0],
497
            [950000, 1000000, 0, 279, 50175, 50175, 434, 1, 1, 0],
498
            [1000000, 1050000, 0, 279, 50058, 50058, 465, 1, 1, 0],
499
            [1050000, 1100000, 0, 279, 50019, 50019, 496, 1, 1, 0],
500
            [1100000, 1150000, 0, 279, 50006, 50006, 527, 1, 1, 0],
501
            [1150000, 1200000, 0, 279, 50002, 50002, 558, 1, 1, 0],
502
            [1200000, 1250000, 0, 550, 50000, 50000, 589, 1, 1, 0],
503
            [1250000, 1300000, 0, 550, 50000, 50000, 620, 1, 0, 0], // Fullness expiry
504
            [1300000, 1350000, 0, 550, 50000, 50000, 635, 1, 1, 0],
505
            [1350000, 1400000, 0, 550, 50000, 50000, 650, 1, 1, 0],
506
            [1400000, 1450000, 0, 150, 50000, 50000, 650, 1, 0, 0], // cwnd not full
507
            [1450000, 1500000, 0, 150, 50000, 50000, 650, 1, 0, 0], // cwnd not full
508
            [1500000, 1550000, 0, 550, 50000, 50000, 664, 1, 1, 0], // cwnd full
509
            [1500000, 1600000, 0, 550, 83333, 50000, 584, 0, 1, 0], // gamma exit
510
            [1600000, 1650000, 0, 550, 61111, 50000, 585, 0, 1, 0], // alpha
511
            [1650000, 1700000, 0, 550, 53703, 50000, 586, 0, 1, 0],
512
            [1700000, 1750000, 0, 100, 51234, 50000, 586, 0, 0, 0], // alpha, not full
513
            [1750000, 1900000, 0, 100, 117078, 50000, 559, 0, 0, 0], // delta, not full
514
            [1900000, 2000000, 0, 100, 105692, 50000, 558, 0, 0, 0], // beta, not full
515
            [2000000, 2075000, 0, 500, 85230, 50000, 558, 0, 1, 0], // no change
516
            [2075000, 2125000, 1, 500, 61743, 50000, 557, 0, 1, 1], // beta, blocked
517
            [2125000, 2150000, 0, 500, 37247, 37247, 558, 0, 1, 0], // alpha
518
            [2150000, 2350000, 0, 500, 145749, 37247, 451, 0, 1, 0], // delta
519
        ];
520
        VegasTest::new(vec1).run();
521

            
522
        let vec2 = vec![
523
            [100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0],
524
            [200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0],
525
            [350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0],
526
            [500000, 550000, 1, 217, 77777, 77777, 403, 0, 1, 1], // ss exit, blocked
527
            [600000, 700000, 0, 248, 92592, 77777, 404, 0, 1, 0], // alpha
528
            [700000, 750000, 1, 404, 64197, 64197, 403, 0, 0, 1], // blocked beta
529
            [750000, 875000, 0, 403, 104732, 64197, 404, 0, 1, 0],
530
        ];
531
        VegasTest::new(vec2).run();
532

            
533
        let vec3 = vec![
534
            [18258527, 19002938, 0, 83, 744411, 744411, 155, 1, 0, 0],
535
            [18258580, 19254257, 0, 52, 911921, 744411, 186, 1, 1, 0],
536
            [20003224, 20645298, 0, 164, 732023, 732023, 217, 1, 1, 0],
537
            [20003367, 21021444, 0, 133, 922725, 732023, 248, 1, 1, 0],
538
            [20003845, 21265508, 0, 102, 1148683, 732023, 279, 1, 1, 0],
539
            [20003975, 21429157, 0, 71, 1333015, 732023, 310, 1, 0, 0],
540
            [20004309, 21707677, 0, 40, 1579917, 732023, 310, 1, 0, 0],
541
        ];
542
        VegasTest::new(vec3).run();
543

            
544
        let vec4 = vec![
545
            [358297091, 358854163, 0, 83, 557072, 557072, 155, 1, 0, 0],
546
            [358297649, 359123845, 0, 52, 736488, 557072, 186, 1, 1, 0],
547
            [359492879, 359995330, 0, 186, 580463, 557072, 217, 1, 1, 0],
548
            [359493043, 360489243, 0, 217, 857621, 557072, 248, 1, 1, 0],
549
            [359493232, 360489673, 0, 248, 950167, 557072, 279, 1, 1, 0],
550
            [359493795, 360489971, 0, 279, 980839, 557072, 310, 1, 0, 0],
551
            [359493918, 360490248, 0, 310, 991166, 557072, 341, 1, 1, 0],
552
            [359494029, 360716465, 0, 341, 1145346, 557072, 372, 1, 1, 0],
553
            [359996888, 360948867, 0, 372, 1016434, 557072, 403, 1, 1, 0],
554
            [359996979, 360949330, 0, 403, 973712, 557072, 434, 1, 1, 0],
555
            [360489528, 361113615, 0, 434, 740628, 557072, 465, 1, 1, 0],
556
            [360489656, 361281604, 0, 465, 774841, 557072, 496, 1, 1, 0],
557
            [360489837, 361500461, 0, 496, 932029, 557072, 482, 0, 1, 0],
558
            [360489963, 361500631, 0, 482, 984455, 557072, 482, 0, 1, 0],
559
            [360490117, 361842481, 0, 482, 1229727, 557072, 481, 0, 1, 0],
560
        ];
561
        VegasTest::new(vec4).run();
562
    }
563
}