1#![allow(dead_code)]
10
11use crate::client::retries::RetryPartition;
12use std::sync::{Arc, Mutex};
13use std::time::Duration;
14use tracing::debug;
15
16#[non_exhaustive]
18#[derive(Clone, Debug, Hash, PartialEq, Eq)]
19pub struct ClientRateLimiterPartition {
20    retry_partition: RetryPartition,
21}
22
23impl ClientRateLimiterPartition {
24    pub fn new(retry_partition: RetryPartition) -> Self {
26        Self { retry_partition }
27    }
28}
29
30const RETRY_COST: f64 = 5.0;
31const RETRY_TIMEOUT_COST: f64 = RETRY_COST * 2.0;
32const INITIAL_REQUEST_COST: f64 = 1.0;
33
34const MIN_FILL_RATE: f64 = 0.5;
35const MIN_CAPACITY: f64 = 1.0;
36const SMOOTH: f64 = 0.8;
37const BETA: f64 = 0.7;
39const SCALE_CONSTANT: f64 = 0.4;
41
42#[derive(Clone, Debug)]
44pub struct ClientRateLimiter {
45    pub(crate) inner: Arc<Mutex<Inner>>,
46}
47
48#[derive(Debug)]
49pub(crate) struct Inner {
50    fill_rate: f64,
52    max_capacity: f64,
54    current_capacity: f64,
56    last_timestamp: Option<f64>,
58    enabled: bool,
62    measured_tx_rate: f64,
64    last_tx_rate_bucket: f64,
66    request_count: u64,
68    last_max_rate: f64,
70    time_of_last_throttle: f64,
72}
73
74pub(crate) enum RequestReason {
75    Retry,
76    RetryTimeout,
77    InitialRequest,
78}
79
80impl Default for ClientRateLimiter {
81    fn default() -> Self {
82        Self::builder().build()
83    }
84}
85
86impl ClientRateLimiter {
87    pub fn new(seconds_since_unix_epoch: f64) -> Self {
89        Self::builder()
90            .tokens_retrieved_per_second(MIN_FILL_RATE)
91            .time_of_last_throttle(seconds_since_unix_epoch)
92            .previous_time_bucket(seconds_since_unix_epoch.floor())
93            .build()
94    }
95
96    pub fn builder() -> ClientRateLimiterBuilder {
98        ClientRateLimiterBuilder::new()
99    }
100
101    pub(crate) fn acquire_permission_to_send_a_request(
102        &self,
103        seconds_since_unix_epoch: f64,
104        kind: RequestReason,
105    ) -> Result<(), Duration> {
106        let mut it = self.inner.lock().unwrap();
107
108        if !it.enabled {
109            return Ok(());
111        }
112        let amount = match kind {
113            RequestReason::Retry => RETRY_COST,
114            RequestReason::RetryTimeout => RETRY_TIMEOUT_COST,
115            RequestReason::InitialRequest => INITIAL_REQUEST_COST,
116        };
117
118        it.refill(seconds_since_unix_epoch);
119
120        let res = if amount > it.current_capacity {
121            let sleep_time = (amount - it.current_capacity) / it.fill_rate;
122            debug!(
123                amount,
124                it.current_capacity,
125                it.fill_rate,
126                sleep_time,
127                "client rate limiter delayed a request"
128            );
129
130            Err(Duration::from_secs_f64(sleep_time))
131        } else {
132            Ok(())
133        };
134
135        it.current_capacity -= amount;
136        res
137    }
138
139    pub(crate) fn update_rate_limiter(
140        &self,
141        seconds_since_unix_epoch: f64,
142        is_throttling_error: bool,
143    ) {
144        let mut it = self.inner.lock().unwrap();
145        it.update_tokens_retrieved_per_second(seconds_since_unix_epoch);
146
147        let calculated_rate;
148        if is_throttling_error {
149            let rate_to_use = if it.enabled {
150                f64::min(it.measured_tx_rate, it.fill_rate)
151            } else {
152                it.measured_tx_rate
153            };
154
155            it.last_max_rate = rate_to_use;
157            it.calculate_time_window();
158            it.time_of_last_throttle = seconds_since_unix_epoch;
159            calculated_rate = cubic_throttle(rate_to_use);
160            it.enable_token_bucket();
161        } else {
162            it.calculate_time_window();
163            calculated_rate = it.cubic_success(seconds_since_unix_epoch);
164        }
165
166        let new_rate = f64::min(calculated_rate, 2.0 * it.measured_tx_rate);
167        it.update_bucket_refill_rate(seconds_since_unix_epoch, new_rate);
168    }
169}
170
171impl Inner {
172    fn refill(&mut self, seconds_since_unix_epoch: f64) {
173        if let Some(last_timestamp) = self.last_timestamp {
174            let fill_amount = (seconds_since_unix_epoch - last_timestamp) * self.fill_rate;
175            self.current_capacity =
176                f64::min(self.max_capacity, self.current_capacity + fill_amount);
177            debug!(
178                fill_amount,
179                self.current_capacity, self.max_capacity, "refilling client rate limiter tokens"
180            );
181        }
182        self.last_timestamp = Some(seconds_since_unix_epoch);
183    }
184
185    fn update_bucket_refill_rate(&mut self, seconds_since_unix_epoch: f64, new_fill_rate: f64) {
186        self.refill(seconds_since_unix_epoch);
188
189        self.fill_rate = f64::max(new_fill_rate, MIN_FILL_RATE);
190        self.max_capacity = f64::max(new_fill_rate, MIN_CAPACITY);
191
192        debug!(
193            fill_rate = self.fill_rate,
194            max_capacity = self.max_capacity,
195            current_capacity = self.current_capacity,
196            measured_tx_rate = self.measured_tx_rate,
197            "client rate limiter state has been updated"
198        );
199
200        self.current_capacity = f64::min(self.current_capacity, self.max_capacity);
202    }
203
204    fn enable_token_bucket(&mut self) {
205        if !self.enabled {
207            debug!("client rate limiting has been enabled");
208        }
209        self.enabled = true;
210    }
211
212    fn update_tokens_retrieved_per_second(&mut self, seconds_since_unix_epoch: f64) {
213        let next_time_bucket = (seconds_since_unix_epoch * 2.0).floor() / 2.0;
214        self.request_count += 1;
215
216        if next_time_bucket > self.last_tx_rate_bucket {
217            let current_rate =
218                self.request_count as f64 / (next_time_bucket - self.last_tx_rate_bucket);
219            self.measured_tx_rate = current_rate * SMOOTH + self.measured_tx_rate * (1.0 - SMOOTH);
220            self.request_count = 0;
221            self.last_tx_rate_bucket = next_time_bucket;
222        }
223    }
224
225    fn calculate_time_window(&self) -> f64 {
226        let base = (self.last_max_rate * (1.0 - BETA)) / SCALE_CONSTANT;
227        base.powf(1.0 / 3.0)
228    }
229
230    fn cubic_success(&self, seconds_since_unix_epoch: f64) -> f64 {
231        let dt =
232            seconds_since_unix_epoch - self.time_of_last_throttle - self.calculate_time_window();
233        (SCALE_CONSTANT * dt.powi(3)) + self.last_max_rate
234    }
235}
236
237fn cubic_throttle(rate_to_use: f64) -> f64 {
238    rate_to_use * BETA
239}
240
241#[derive(Clone, Debug, Default)]
243pub struct ClientRateLimiterBuilder {
244    token_refill_rate: Option<f64>,
246    maximum_bucket_capacity: Option<f64>,
248    current_bucket_capacity: Option<f64>,
250    time_of_last_refill: Option<f64>,
252    tokens_retrieved_per_second: Option<f64>,
254    previous_time_bucket: Option<f64>,
256    request_count: Option<u64>,
258    enable_throttling: Option<bool>,
260    tokens_retrieved_per_second_at_time_of_last_throttle: Option<f64>,
262    time_of_last_throttle: Option<f64>,
264}
265
266impl ClientRateLimiterBuilder {
267    pub fn new() -> Self {
269        ClientRateLimiterBuilder::default()
270    }
271    pub fn token_refill_rate(mut self, token_refill_rate: f64) -> Self {
273        self.set_token_refill_rate(Some(token_refill_rate));
274        self
275    }
276    pub fn set_token_refill_rate(&mut self, token_refill_rate: Option<f64>) -> &mut Self {
278        self.token_refill_rate = token_refill_rate;
279        self
280    }
281    pub fn maximum_bucket_capacity(mut self, maximum_bucket_capacity: f64) -> Self {
285        self.set_maximum_bucket_capacity(Some(maximum_bucket_capacity));
286        self
287    }
288    pub fn set_maximum_bucket_capacity(
292        &mut self,
293        maximum_bucket_capacity: Option<f64>,
294    ) -> &mut Self {
295        self.maximum_bucket_capacity = maximum_bucket_capacity;
296        self
297    }
298    pub fn current_bucket_capacity(mut self, current_bucket_capacity: f64) -> Self {
302        self.set_current_bucket_capacity(Some(current_bucket_capacity));
303        self
304    }
305    pub fn set_current_bucket_capacity(
309        &mut self,
310        current_bucket_capacity: Option<f64>,
311    ) -> &mut Self {
312        self.current_bucket_capacity = current_bucket_capacity;
313        self
314    }
315    fn time_of_last_refill(mut self, time_of_last_refill: f64) -> Self {
317        self.set_time_of_last_refill(Some(time_of_last_refill));
318        self
319    }
320    fn set_time_of_last_refill(&mut self, time_of_last_refill: Option<f64>) -> &mut Self {
322        self.time_of_last_refill = time_of_last_refill;
323        self
324    }
325    pub fn tokens_retrieved_per_second(mut self, tokens_retrieved_per_second: f64) -> Self {
327        self.set_tokens_retrieved_per_second(Some(tokens_retrieved_per_second));
328        self
329    }
330    pub fn set_tokens_retrieved_per_second(
332        &mut self,
333        tokens_retrieved_per_second: Option<f64>,
334    ) -> &mut Self {
335        self.tokens_retrieved_per_second = tokens_retrieved_per_second;
336        self
337    }
338    fn previous_time_bucket(mut self, previous_time_bucket: f64) -> Self {
340        self.set_previous_time_bucket(Some(previous_time_bucket));
341        self
342    }
343    fn set_previous_time_bucket(&mut self, previous_time_bucket: Option<f64>) -> &mut Self {
345        self.previous_time_bucket = previous_time_bucket;
346        self
347    }
348    fn request_count(mut self, request_count: u64) -> Self {
350        self.set_request_count(Some(request_count));
351        self
352    }
353    fn set_request_count(&mut self, request_count: Option<u64>) -> &mut Self {
355        self.request_count = request_count;
356        self
357    }
358    fn enable_throttling(mut self, enable_throttling: bool) -> Self {
360        self.set_enable_throttling(Some(enable_throttling));
361        self
362    }
363    fn set_enable_throttling(&mut self, enable_throttling: Option<bool>) -> &mut Self {
365        self.enable_throttling = enable_throttling;
366        self
367    }
368    fn tokens_retrieved_per_second_at_time_of_last_throttle(
370        mut self,
371        tokens_retrieved_per_second_at_time_of_last_throttle: f64,
372    ) -> Self {
373        self.set_tokens_retrieved_per_second_at_time_of_last_throttle(Some(
374            tokens_retrieved_per_second_at_time_of_last_throttle,
375        ));
376        self
377    }
378    fn set_tokens_retrieved_per_second_at_time_of_last_throttle(
380        &mut self,
381        tokens_retrieved_per_second_at_time_of_last_throttle: Option<f64>,
382    ) -> &mut Self {
383        self.tokens_retrieved_per_second_at_time_of_last_throttle =
384            tokens_retrieved_per_second_at_time_of_last_throttle;
385        self
386    }
387    fn time_of_last_throttle(mut self, time_of_last_throttle: f64) -> Self {
389        self.set_time_of_last_throttle(Some(time_of_last_throttle));
390        self
391    }
392    fn set_time_of_last_throttle(&mut self, time_of_last_throttle: Option<f64>) -> &mut Self {
394        self.time_of_last_throttle = time_of_last_throttle;
395        self
396    }
397    pub fn build(self) -> ClientRateLimiter {
399        ClientRateLimiter {
400            inner: Arc::new(Mutex::new(Inner {
401                fill_rate: self.token_refill_rate.unwrap_or_default(),
402                max_capacity: self.maximum_bucket_capacity.unwrap_or(f64::MAX),
403                current_capacity: self.current_bucket_capacity.unwrap_or_default(),
404                last_timestamp: self.time_of_last_refill,
405                enabled: self.enable_throttling.unwrap_or_default(),
406                measured_tx_rate: self.tokens_retrieved_per_second.unwrap_or_default(),
407                last_tx_rate_bucket: self.previous_time_bucket.unwrap_or_default(),
408                request_count: self.request_count.unwrap_or_default(),
409                last_max_rate: self
410                    .tokens_retrieved_per_second_at_time_of_last_throttle
411                    .unwrap_or_default(),
412                time_of_last_throttle: self.time_of_last_throttle.unwrap_or_default(),
413            })),
414        }
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::{cubic_throttle, ClientRateLimiter};
421    use crate::client::retries::client_rate_limiter::RequestReason;
422    use approx::assert_relative_eq;
423    use aws_smithy_async::rt::sleep::AsyncSleep;
424    use aws_smithy_async::test_util::instant_time_and_sleep;
425    use std::time::{Duration, SystemTime};
426
427    const ONE_SECOND: Duration = Duration::from_secs(1);
428    const TWO_HUNDRED_MILLISECONDS: Duration = Duration::from_millis(200);
429
430    #[test]
431    fn should_match_beta_decrease() {
432        let new_rate = cubic_throttle(10.0);
433        assert_relative_eq!(new_rate, 7.0);
434
435        let rate_limiter = ClientRateLimiter::builder()
436            .tokens_retrieved_per_second_at_time_of_last_throttle(10.0)
437            .time_of_last_throttle(1.0)
438            .build();
439
440        rate_limiter.inner.lock().unwrap().calculate_time_window();
441        let new_rate = rate_limiter.inner.lock().unwrap().cubic_success(1.0);
442        assert_relative_eq!(new_rate, 7.0);
443    }
444
445    #[tokio::test]
446    async fn throttling_is_enabled_once_throttling_error_is_received() {
447        let rate_limiter = ClientRateLimiter::builder()
448            .previous_time_bucket(0.0)
449            .time_of_last_throttle(0.0)
450            .build();
451
452        assert!(
453            !rate_limiter.inner.lock().unwrap().enabled,
454            "rate_limiter should be disabled by default"
455        );
456        rate_limiter.update_rate_limiter(0.0, true);
457        assert!(
458            rate_limiter.inner.lock().unwrap().enabled,
459            "rate_limiter should be enabled after throttling error"
460        );
461    }
462
463    #[tokio::test]
464    async fn test_calculated_rate_with_successes() {
465        let rate_limiter = ClientRateLimiter::builder()
466            .time_of_last_throttle(5.0)
467            .tokens_retrieved_per_second_at_time_of_last_throttle(10.0)
468            .build();
469
470        struct Attempt {
471            seconds_since_unix_epoch: f64,
472            expected_calculated_rate: f64,
473        }
474
475        let attempts = [
476            Attempt {
477                seconds_since_unix_epoch: 5.0,
478                expected_calculated_rate: 7.0,
479            },
480            Attempt {
481                seconds_since_unix_epoch: 6.0,
482                expected_calculated_rate: 9.64893600966,
483            },
484            Attempt {
485                seconds_since_unix_epoch: 7.0,
486                expected_calculated_rate: 10.000030849917364,
487            },
488            Attempt {
489                seconds_since_unix_epoch: 8.0,
490                expected_calculated_rate: 10.453284520772092,
491            },
492            Attempt {
493                seconds_since_unix_epoch: 9.0,
494                expected_calculated_rate: 13.408697022224185,
495            },
496            Attempt {
497                seconds_since_unix_epoch: 10.0,
498                expected_calculated_rate: 21.26626835427364,
499            },
500            Attempt {
501                seconds_since_unix_epoch: 11.0,
502                expected_calculated_rate: 36.425998516920465,
503            },
504        ];
505
506        for attempt in attempts {
510            rate_limiter.inner.lock().unwrap().calculate_time_window();
511            let calculated_rate = rate_limiter
512                .inner
513                .lock()
514                .unwrap()
515                .cubic_success(attempt.seconds_since_unix_epoch);
516
517            assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate);
518        }
519    }
520
521    #[tokio::test]
522    async fn test_calculated_rate_with_throttles() {
523        let rate_limiter = ClientRateLimiter::builder()
524            .tokens_retrieved_per_second_at_time_of_last_throttle(10.0)
525            .time_of_last_throttle(5.0)
526            .build();
527
528        struct Attempt {
529            throttled: bool,
530            seconds_since_unix_epoch: f64,
531            expected_calculated_rate: f64,
532        }
533
534        let attempts = [
535            Attempt {
536                throttled: false,
537                seconds_since_unix_epoch: 5.0,
538                expected_calculated_rate: 7.0,
539            },
540            Attempt {
541                throttled: false,
542                seconds_since_unix_epoch: 6.0,
543                expected_calculated_rate: 9.64893600966,
544            },
545            Attempt {
546                throttled: true,
547                seconds_since_unix_epoch: 7.0,
548                expected_calculated_rate: 6.754255206761999,
549            },
550            Attempt {
551                throttled: true,
552                seconds_since_unix_epoch: 8.0,
553                expected_calculated_rate: 4.727978644733399,
554            },
555            Attempt {
556                throttled: false,
557                seconds_since_unix_epoch: 9.0,
558                expected_calculated_rate: 4.670125557970046,
559            },
560            Attempt {
561                throttled: false,
562                seconds_since_unix_epoch: 10.0,
563                expected_calculated_rate: 4.770870456867401,
564            },
565            Attempt {
566                throttled: false,
567                seconds_since_unix_epoch: 11.0,
568                expected_calculated_rate: 6.011819748005445,
569            },
570            Attempt {
571                throttled: false,
572                seconds_since_unix_epoch: 12.0,
573                expected_calculated_rate: 10.792973431384178,
574            },
575        ];
576
577        let mut calculated_rate = 0.0;
581        for attempt in attempts {
582            let mut inner = rate_limiter.inner.lock().unwrap();
583            inner.calculate_time_window();
584            if attempt.throttled {
585                calculated_rate = cubic_throttle(calculated_rate);
586                inner.time_of_last_throttle = attempt.seconds_since_unix_epoch;
587                inner.last_max_rate = calculated_rate;
588            } else {
589                calculated_rate = inner.cubic_success(attempt.seconds_since_unix_epoch);
590            };
591
592            assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate);
593        }
594    }
595
596    #[tokio::test]
597    async fn test_client_sending_rates() {
598        let (_, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH);
599        let rate_limiter = ClientRateLimiter::builder().build();
600
601        struct Attempt {
602            throttled: bool,
603            seconds_since_unix_epoch: f64,
604            expected_tokens_retrieved_per_second: f64,
605            expected_token_refill_rate: f64,
606        }
607
608        let attempts = [
609            Attempt {
610                throttled: false,
611                seconds_since_unix_epoch: 0.2,
612                expected_tokens_retrieved_per_second: 0.000000,
613                expected_token_refill_rate: 0.500000,
614            },
615            Attempt {
616                throttled: false,
617                seconds_since_unix_epoch: 0.4,
618                expected_tokens_retrieved_per_second: 0.000000,
619                expected_token_refill_rate: 0.500000,
620            },
621            Attempt {
622                throttled: false,
623                seconds_since_unix_epoch: 0.6,
624                expected_tokens_retrieved_per_second: 4.800000000000001,
625                expected_token_refill_rate: 0.500000,
626            },
627            Attempt {
628                throttled: false,
629                seconds_since_unix_epoch: 0.8,
630                expected_tokens_retrieved_per_second: 4.800000000000001,
631                expected_token_refill_rate: 0.500000,
632            },
633            Attempt {
634                throttled: false,
635                seconds_since_unix_epoch: 1.0,
636                expected_tokens_retrieved_per_second: 4.160000,
637                expected_token_refill_rate: 0.500000,
638            },
639            Attempt {
640                throttled: false,
641                seconds_since_unix_epoch: 1.2,
642                expected_tokens_retrieved_per_second: 4.160000,
643                expected_token_refill_rate: 0.691200,
644            },
645            Attempt {
646                throttled: false,
647                seconds_since_unix_epoch: 1.4,
648                expected_tokens_retrieved_per_second: 4.160000,
649                expected_token_refill_rate: 1.0975999999999997,
650            },
651            Attempt {
652                throttled: false,
653                seconds_since_unix_epoch: 1.6,
654                expected_tokens_retrieved_per_second: 5.632000000000001,
655                expected_token_refill_rate: 1.6384000000000005,
656            },
657            Attempt {
658                throttled: false,
659                seconds_since_unix_epoch: 1.8,
660                expected_tokens_retrieved_per_second: 5.632000000000001,
661                expected_token_refill_rate: 2.332800,
662            },
663            Attempt {
664                throttled: true,
665                seconds_since_unix_epoch: 2.0,
666                expected_tokens_retrieved_per_second: 4.326400,
667                expected_token_refill_rate: 3.0284799999999996,
668            },
669            Attempt {
670                throttled: false,
671                seconds_since_unix_epoch: 2.2,
672                expected_tokens_retrieved_per_second: 4.326400,
673                expected_token_refill_rate: 3.48663917347026,
674            },
675            Attempt {
676                throttled: false,
677                seconds_since_unix_epoch: 2.4,
678                expected_tokens_retrieved_per_second: 4.326400,
679                expected_token_refill_rate: 3.821874416040255,
680            },
681            Attempt {
682                throttled: false,
683                seconds_since_unix_epoch: 2.6,
684                expected_tokens_retrieved_per_second: 5.665280,
685                expected_token_refill_rate: 4.053385727709987,
686            },
687            Attempt {
688                throttled: false,
689                seconds_since_unix_epoch: 2.8,
690                expected_tokens_retrieved_per_second: 5.665280,
691                expected_token_refill_rate: 4.200373108479454,
692            },
693            Attempt {
694                throttled: false,
695                seconds_since_unix_epoch: 3.0,
696                expected_tokens_retrieved_per_second: 4.333056,
697                expected_token_refill_rate: 4.282036558348658,
698            },
699            Attempt {
700                throttled: true,
701                seconds_since_unix_epoch: 3.2,
702                expected_tokens_retrieved_per_second: 4.333056,
703                expected_token_refill_rate: 2.99742559084406,
704            },
705            Attempt {
706                throttled: false,
707                seconds_since_unix_epoch: 3.4,
708                expected_tokens_retrieved_per_second: 4.333056,
709                expected_token_refill_rate: 3.4522263943863463,
710            },
711        ];
712
713        for attempt in attempts {
714            sleep_impl.sleep(TWO_HUNDRED_MILLISECONDS).await;
715            assert_eq!(
716                attempt.seconds_since_unix_epoch,
717                sleep_impl.total_duration().as_secs_f64()
718            );
719
720            rate_limiter.update_rate_limiter(attempt.seconds_since_unix_epoch, attempt.throttled);
721            assert_relative_eq!(
722                attempt.expected_tokens_retrieved_per_second,
723                rate_limiter.inner.lock().unwrap().measured_tx_rate
724            );
725            assert_relative_eq!(
726                attempt.expected_token_refill_rate,
727                rate_limiter.inner.lock().unwrap().fill_rate
728            );
729        }
730    }
731
732    #[tokio::test]
738    async fn test_when_throttling_is_enabled_requests_can_still_be_sent() {
739        let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH);
740        let crl = ClientRateLimiter::builder()
741            .time_of_last_throttle(0.0)
742            .previous_time_bucket(0.0)
743            .build();
744
745        crl.update_rate_limiter(0.0, true);
747
748        for _i in 0..100 {
749            let duration = Duration::from_secs_f64(fastrand::f64());
751            sleep_impl.sleep(duration).await;
752            if let Err(delay) = crl.acquire_permission_to_send_a_request(
753                time_source.seconds_since_unix_epoch(),
754                RequestReason::InitialRequest,
755            ) {
756                sleep_impl.sleep(delay).await;
757            }
758
759            crl.update_rate_limiter(time_source.seconds_since_unix_epoch(), false);
761        }
762
763        let inner = crl.inner.lock().unwrap();
764        assert!(inner.enabled, "the rate limiter should still be enabled");
765        assert_relative_eq!(
767            inner.last_timestamp.unwrap(),
768            sleep_impl.total_duration().as_secs_f64(),
769            max_relative = 0.0001
770        );
771    }
772}