mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-02 11:13:39 -07:00
global: snapshot
This commit is contained in:
@@ -160,35 +160,15 @@ impl ComputedFromHeightStdDevExtended {
|
||||
.height
|
||||
.collect_range_at(start, self.base.sd.height.len());
|
||||
|
||||
for (offset, _ratio) in source_data.into_iter().enumerate() {
|
||||
let index = start + offset;
|
||||
let average = sma_data[offset];
|
||||
let sd = sd_data[offset];
|
||||
|
||||
self.p0_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average + StoredF32::from(0.5 * *sd))?;
|
||||
self.p1sd.height.truncate_push_at(index, average + sd)?;
|
||||
self.p1_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average + StoredF32::from(1.5 * *sd))?;
|
||||
self.p2sd.height.truncate_push_at(index, average + 2 * sd)?;
|
||||
self.p2_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average + StoredF32::from(2.5 * *sd))?;
|
||||
self.p3sd.height.truncate_push_at(index, average + 3 * sd)?;
|
||||
self.m0_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average - StoredF32::from(0.5 * *sd))?;
|
||||
self.m1sd.height.truncate_push_at(index, average - sd)?;
|
||||
self.m1_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average - StoredF32::from(1.5 * *sd))?;
|
||||
self.m2sd.height.truncate_push_at(index, average - 2 * sd)?;
|
||||
self.m2_5sd
|
||||
.height
|
||||
.truncate_push_at(index, average - StoredF32::from(2.5 * *sd))?;
|
||||
self.m3sd.height.truncate_push_at(index, average - 3 * sd)?;
|
||||
const MULTIPLIERS: [f32; 12] = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, -0.5, -1.0, -1.5, -2.0, -2.5, -3.0];
|
||||
let band_vecs: Vec<_> = self.mut_band_height_vecs().collect();
|
||||
for (vec, mult) in band_vecs.into_iter().zip(MULTIPLIERS) {
|
||||
for (offset, _) in source_data.iter().enumerate() {
|
||||
let index = start + offset;
|
||||
let average = sma_data[offset];
|
||||
let sd = sd_data[offset];
|
||||
vec.truncate_push_at(index, average + StoredF32::from(mult * *sd))?;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -56,26 +56,28 @@ impl SortedBlocks {
|
||||
|
||||
/// Remove one occurrence of value. O(sqrt(n)).
|
||||
fn remove(&mut self, value: f64) -> bool {
|
||||
for (bi, block) in self.blocks.iter_mut().enumerate() {
|
||||
if block.is_empty() {
|
||||
continue;
|
||||
}
|
||||
// If value > block max, it's not in this block
|
||||
if *block.last().unwrap() < value {
|
||||
continue;
|
||||
}
|
||||
let pos = block.partition_point(|a| *a < value);
|
||||
if pos < block.len() && block[pos] == value {
|
||||
block.remove(pos);
|
||||
self.total_len -= 1;
|
||||
if block.is_empty() {
|
||||
self.blocks.remove(bi);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Value not found (would be in this block range but isn't)
|
||||
if self.blocks.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Binary search for first block whose max >= value
|
||||
let bi = self
|
||||
.blocks
|
||||
.partition_point(|b| b.last().is_some_and(|&last| last < value));
|
||||
if bi >= self.blocks.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let block = &mut self.blocks[bi];
|
||||
let pos = block.partition_point(|a| *a < value);
|
||||
if pos < block.len() && block[pos] == value {
|
||||
block.remove(pos);
|
||||
self.total_len -= 1;
|
||||
if block.is_empty() {
|
||||
self.blocks.remove(bi);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
@@ -66,20 +66,25 @@ impl TDigest {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find nearest centroid by mean
|
||||
let pos = self
|
||||
// Single binary search: unclamped position doubles as insert point
|
||||
let search = self
|
||||
.centroids
|
||||
.binary_search_by(|c| c.mean.partial_cmp(&value).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or_else(|i| i.min(self.centroids.len() - 1));
|
||||
.binary_search_by(|c| c.mean.partial_cmp(&value).unwrap_or(std::cmp::Ordering::Equal));
|
||||
let insert_pos = match search {
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
|
||||
// Check neighbors for the actual nearest
|
||||
let nearest = if pos > 0
|
||||
&& (value - self.centroids[pos - 1].mean).abs()
|
||||
< (value - self.centroids[pos].mean).abs()
|
||||
// Find nearest centroid from insert_pos
|
||||
let nearest = if insert_pos >= self.centroids.len() {
|
||||
self.centroids.len() - 1
|
||||
} else if insert_pos == 0 {
|
||||
0
|
||||
} else if (value - self.centroids[insert_pos - 1].mean).abs()
|
||||
< (value - self.centroids[insert_pos].mean).abs()
|
||||
{
|
||||
pos - 1
|
||||
insert_pos - 1
|
||||
} else {
|
||||
pos
|
||||
insert_pos
|
||||
};
|
||||
|
||||
// Compute quantile of nearest centroid
|
||||
@@ -97,15 +102,7 @@ impl TDigest {
|
||||
c.mean = (c.mean * c.weight + value) / (c.weight + 1.0);
|
||||
c.weight += 1.0;
|
||||
} else {
|
||||
// Insert new centroid at sorted position
|
||||
let insert_pos = self
|
||||
.centroids
|
||||
.binary_search_by(|c| {
|
||||
c.mean
|
||||
.partial_cmp(&value)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap_or_else(|i| i);
|
||||
// Insert new centroid at sorted position (reuse insert_pos)
|
||||
self.centroids.insert(
|
||||
insert_pos,
|
||||
Centroid {
|
||||
@@ -148,65 +145,84 @@ impl TDigest {
|
||||
self.centroids = merged;
|
||||
}
|
||||
|
||||
pub fn quantile(&self, q: f64) -> f64 {
|
||||
/// Batch quantile query in a single pass. `qs` must be sorted ascending.
|
||||
pub fn quantiles(&self, qs: &[f64], out: &mut [f64]) {
|
||||
if self.centroids.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
if q <= 0.0 {
|
||||
return self.min;
|
||||
}
|
||||
if q >= 1.0 {
|
||||
return self.max;
|
||||
out.iter_mut().for_each(|o| *o = 0.0);
|
||||
return;
|
||||
}
|
||||
if self.centroids.len() == 1 {
|
||||
return self.centroids[0].mean;
|
||||
let mean = self.centroids[0].mean;
|
||||
for (i, &q) in qs.iter().enumerate() {
|
||||
out[i] = if q <= 0.0 {
|
||||
self.min
|
||||
} else if q >= 1.0 {
|
||||
self.max
|
||||
} else {
|
||||
mean
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let total: f64 = self.centroids.iter().map(|c| c.weight).sum();
|
||||
let target = q * total;
|
||||
let mut cum = 0.0;
|
||||
let mut ci = 0;
|
||||
|
||||
for i in 0..self.centroids.len() {
|
||||
let c = &self.centroids[i];
|
||||
let mid = cum + c.weight / 2.0;
|
||||
for (qi, &q) in qs.iter().enumerate() {
|
||||
if q <= 0.0 {
|
||||
out[qi] = self.min;
|
||||
continue;
|
||||
}
|
||||
if q >= 1.0 {
|
||||
out[qi] = self.max;
|
||||
continue;
|
||||
}
|
||||
|
||||
if target < mid {
|
||||
// Interpolate between previous centroid (or min) and this one
|
||||
if i == 0 {
|
||||
// Between min and first centroid center
|
||||
let first_mid = c.weight / 2.0;
|
||||
if first_mid == 0.0 {
|
||||
return self.min;
|
||||
}
|
||||
return self.min + (c.mean - self.min) * (target / first_mid);
|
||||
let target = q * total;
|
||||
|
||||
// Advance centroids until the current centroid's midpoint exceeds target
|
||||
while ci < self.centroids.len() {
|
||||
let mid = cum + self.centroids[ci].weight / 2.0;
|
||||
if target < mid {
|
||||
break;
|
||||
}
|
||||
let prev = &self.centroids[i - 1];
|
||||
cum += self.centroids[ci].weight;
|
||||
ci += 1;
|
||||
}
|
||||
|
||||
if ci >= self.centroids.len() {
|
||||
// Past all centroids — interpolate between last centroid and max
|
||||
let last = self.centroids.last().unwrap();
|
||||
let last_mid = total - last.weight / 2.0;
|
||||
let remaining = total - last_mid;
|
||||
out[qi] = if remaining == 0.0 {
|
||||
self.max
|
||||
} else {
|
||||
last.mean + (self.max - last.mean) * ((target - last_mid) / remaining)
|
||||
};
|
||||
} else if ci == 0 {
|
||||
// Before first centroid — interpolate between min and first centroid
|
||||
let c = &self.centroids[0];
|
||||
let first_mid = c.weight / 2.0;
|
||||
out[qi] = if first_mid == 0.0 {
|
||||
self.min
|
||||
} else {
|
||||
self.min + (c.mean - self.min) * (target / first_mid)
|
||||
};
|
||||
} else {
|
||||
// Between centroid ci-1 and ci
|
||||
let c = &self.centroids[ci];
|
||||
let prev = &self.centroids[ci - 1];
|
||||
let mid = cum + c.weight / 2.0;
|
||||
let prev_center = cum - prev.weight / 2.0;
|
||||
let frac = if mid == prev_center {
|
||||
0.5
|
||||
} else {
|
||||
(target - prev_center) / (mid - prev_center)
|
||||
};
|
||||
return prev.mean + (c.mean - prev.mean) * frac;
|
||||
out[qi] = prev.mean + (c.mean - prev.mean) * frac;
|
||||
}
|
||||
|
||||
cum += c.weight;
|
||||
}
|
||||
|
||||
// Between last centroid center and max
|
||||
let last = self.centroids.last().unwrap();
|
||||
let last_mid = total - last.weight / 2.0;
|
||||
let remaining = total - last_mid;
|
||||
if remaining == 0.0 {
|
||||
return self.max;
|
||||
}
|
||||
last.mean + (self.max - last.mean) * ((target - last_mid) / remaining)
|
||||
}
|
||||
|
||||
/// Batch quantile query. `qs` must be sorted ascending.
|
||||
pub fn quantiles(&self, qs: &[f64], out: &mut [f64]) {
|
||||
for (i, &q) in qs.iter().enumerate() {
|
||||
out[i] = self.quantile(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,6 +231,12 @@ impl TDigest {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn quantile(td: &TDigest, q: f64) -> f64 {
|
||||
let mut out = [0.0];
|
||||
td.quantiles(&[q], &mut out);
|
||||
out[0]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_quantiles() {
|
||||
let mut td = TDigest::default();
|
||||
@@ -223,13 +245,13 @@ mod tests {
|
||||
}
|
||||
assert_eq!(td.count(), 1000);
|
||||
|
||||
let median = td.quantile(0.5);
|
||||
let median = quantile(&td, 0.5);
|
||||
assert!((median - 500.0).abs() < 10.0, "median was {median}");
|
||||
|
||||
let p99 = td.quantile(0.99);
|
||||
let p99 = quantile(&td, 0.99);
|
||||
assert!((p99 - 990.0).abs() < 15.0, "p99 was {p99}");
|
||||
|
||||
let p01 = td.quantile(0.01);
|
||||
let p01 = quantile(&td, 0.01);
|
||||
assert!((p01 - 10.0).abs() < 15.0, "p01 was {p01}");
|
||||
}
|
||||
|
||||
@@ -237,16 +259,16 @@ mod tests {
|
||||
fn empty_digest() {
|
||||
let td = TDigest::default();
|
||||
assert_eq!(td.count(), 0);
|
||||
assert_eq!(td.quantile(0.5), 0.0);
|
||||
assert_eq!(quantile(&td, 0.5), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_value() {
|
||||
let mut td = TDigest::default();
|
||||
td.add(42.0);
|
||||
assert_eq!(td.quantile(0.0), 42.0);
|
||||
assert_eq!(td.quantile(0.5), 42.0);
|
||||
assert_eq!(td.quantile(1.0), 42.0);
|
||||
assert_eq!(quantile(&td, 0.0), 42.0);
|
||||
assert_eq!(quantile(&td, 0.5), 42.0);
|
||||
assert_eq!(quantile(&td, 1.0), 42.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -258,6 +280,6 @@ mod tests {
|
||||
assert_eq!(td.count(), 100);
|
||||
td.reset();
|
||||
assert_eq!(td.count(), 0);
|
||||
assert_eq!(td.quantile(0.5), 0.0);
|
||||
assert_eq!(quantile(&td, 0.5), 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::Cents;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// Cents -> Cents (identity transform for lazy references)
|
||||
pub struct CentsIdentity;
|
||||
|
||||
impl UnaryTransform<Cents, Cents> for CentsIdentity {
|
||||
#[inline(always)]
|
||||
fn apply(cents: Cents) -> Cents {
|
||||
cents
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::Dollars;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// Dollars -> Dollars (identity transform for lazy references)
|
||||
pub struct DollarsIdentity;
|
||||
|
||||
impl UnaryTransform<Dollars, Dollars> for DollarsIdentity {
|
||||
#[inline(always)]
|
||||
fn apply(dollars: Dollars) -> Dollars {
|
||||
dollars
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// StoredF32 -> StoredF32 (identity transform for lazy references/proxies)
|
||||
pub struct StoredF32Identity;
|
||||
|
||||
impl UnaryTransform<StoredF32, StoredF32> for StoredF32Identity {
|
||||
#[inline(always)]
|
||||
fn apply(v: StoredF32) -> StoredF32 {
|
||||
v
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use vecdb::{UnaryTransform, VecValue};
|
||||
|
||||
/// T -> T (identity transform for lazy references)
|
||||
pub struct Identity<T>(PhantomData<T>);
|
||||
|
||||
impl<T: VecValue> UnaryTransform<T, T> for Identity<T> {
|
||||
#[inline(always)]
|
||||
fn apply(v: T) -> T {
|
||||
v
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ mod bps16_to_float;
|
||||
mod bps32_to_float;
|
||||
mod block_count_target;
|
||||
mod cents_halve;
|
||||
mod cents_identity;
|
||||
mod identity;
|
||||
mod cents_plus;
|
||||
mod cents_signed_to_dollars;
|
||||
mod cents_subtract_to_cents_signed;
|
||||
@@ -12,9 +12,7 @@ mod cents_times_tenths;
|
||||
mod cents_to_dollars;
|
||||
mod cents_to_sats;
|
||||
mod dollar_halve;
|
||||
mod dollar_identity;
|
||||
mod dollars_to_sats_fract;
|
||||
mod f32_identity;
|
||||
mod neg_cents_to_dollars;
|
||||
mod ohlc_cents_to_dollars;
|
||||
mod ohlc_cents_to_sats;
|
||||
@@ -30,6 +28,7 @@ mod percentage_u32_f32;
|
||||
mod price_times_ratio_cents;
|
||||
mod ratio32;
|
||||
mod ratio_cents64;
|
||||
mod per_sec;
|
||||
mod ratio_u64_f32;
|
||||
mod return_f32_tenths;
|
||||
mod return_i8;
|
||||
@@ -38,13 +37,10 @@ mod sats_to_cents;
|
||||
|
||||
mod sat_halve;
|
||||
mod sat_halve_to_bitcoin;
|
||||
mod sat_identity;
|
||||
mod sat_mask;
|
||||
mod sat_to_bitcoin;
|
||||
mod days_to_years;
|
||||
mod volatility_sqrt30;
|
||||
mod volatility_sqrt365;
|
||||
mod volatility_sqrt7;
|
||||
mod volatility;
|
||||
|
||||
pub use bp16_to_float::*;
|
||||
pub use bp32_to_float::*;
|
||||
@@ -52,7 +48,7 @@ pub use bps16_to_float::*;
|
||||
pub use bps32_to_float::*;
|
||||
pub use block_count_target::*;
|
||||
pub use cents_halve::*;
|
||||
pub use cents_identity::*;
|
||||
pub use identity::*;
|
||||
pub use cents_plus::*;
|
||||
pub use cents_signed_to_dollars::*;
|
||||
pub use cents_subtract_to_cents_signed::*;
|
||||
@@ -67,9 +63,7 @@ pub use percentage_cents_signed_dollars_f32::*;
|
||||
pub use percentage_cents_signed_f32::*;
|
||||
|
||||
pub use dollar_halve::*;
|
||||
pub use dollar_identity::*;
|
||||
pub use dollars_to_sats_fract::*;
|
||||
pub use f32_identity::*;
|
||||
pub use percentage_diff_close_cents::*;
|
||||
pub use percentage_diff_close_dollars::*;
|
||||
pub use percentage_dollars_f32::*;
|
||||
@@ -79,6 +73,7 @@ pub use percentage_u32_f32::*;
|
||||
pub use price_times_ratio_cents::*;
|
||||
pub use ratio32::*;
|
||||
pub use ratio_cents64::*;
|
||||
pub use per_sec::*;
|
||||
pub use ratio_u64_f32::*;
|
||||
pub use return_f32_tenths::*;
|
||||
pub use return_i8::*;
|
||||
@@ -86,10 +81,7 @@ pub use return_u16::*;
|
||||
pub use sats_to_cents::*;
|
||||
pub use sat_halve::*;
|
||||
pub use sat_halve_to_bitcoin::*;
|
||||
pub use sat_identity::*;
|
||||
pub use sat_mask::*;
|
||||
pub use sat_to_bitcoin::*;
|
||||
pub use days_to_years::*;
|
||||
pub use volatility_sqrt7::*;
|
||||
pub use volatility_sqrt30::*;
|
||||
pub use volatility_sqrt365::*;
|
||||
pub use volatility::*;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use brk_types::{StoredF32, StoredU64, Timestamp};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredU64, Timestamp) -> StoredF32 rate (count / interval_seconds)
|
||||
pub struct PerSec;
|
||||
|
||||
impl BinaryTransform<StoredU64, Timestamp, StoredF32> for PerSec {
|
||||
#[inline(always)]
|
||||
fn apply(count: StoredU64, interval: Timestamp) -> StoredF32 {
|
||||
let interval_f64 = f64::from(*interval);
|
||||
if interval_f64 > 0.0 {
|
||||
StoredF32::from(*count as f64 / interval_f64)
|
||||
} else {
|
||||
StoredF32::NAN
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::Sats;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// Sats -> Sats (identity transform for lazy references)
|
||||
pub struct SatsIdentity;
|
||||
|
||||
impl UnaryTransform<Sats, Sats> for SatsIdentity {
|
||||
#[inline(always)]
|
||||
fn apply(sats: Sats) -> Sats {
|
||||
sats
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
pub trait SqrtDays {
|
||||
const FACTOR: f32;
|
||||
}
|
||||
|
||||
pub struct Days7;
|
||||
impl SqrtDays for Days7 {
|
||||
const FACTOR: f32 = 2.6457513; // 7.0_f32.sqrt()
|
||||
}
|
||||
|
||||
pub struct Days30;
|
||||
impl SqrtDays for Days30 {
|
||||
const FACTOR: f32 = 5.477226; // 30.0_f32.sqrt()
|
||||
}
|
||||
|
||||
pub struct Days365;
|
||||
impl SqrtDays for Days365 {
|
||||
const FACTOR: f32 = 19.104973; // 365.0_f32.sqrt()
|
||||
}
|
||||
|
||||
/// StoredF32 × sqrt(D) -> StoredF32 (annualize daily volatility to D-day period)
|
||||
pub struct TimesSqrt<D: SqrtDays>(PhantomData<D>);
|
||||
|
||||
impl<D: SqrtDays> UnaryTransform<StoredF32, StoredF32> for TimesSqrt<D> {
|
||||
#[inline(always)]
|
||||
fn apply(v: StoredF32) -> StoredF32 {
|
||||
(*v * D::FACTOR).into()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// StoredF32 × sqrt(30) -> StoredF32 (1-month volatility from daily SD)
|
||||
pub struct StoredF32TimesSqrt30;
|
||||
|
||||
impl UnaryTransform<StoredF32, StoredF32> for StoredF32TimesSqrt30 {
|
||||
#[inline(always)]
|
||||
fn apply(v: StoredF32) -> StoredF32 {
|
||||
// 30.0_f32.sqrt() = 5.477226
|
||||
(*v * 5.477226_f32).into()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// StoredF32 × sqrt(365) -> StoredF32 (1-year volatility from daily SD)
|
||||
pub struct StoredF32TimesSqrt365;
|
||||
|
||||
impl UnaryTransform<StoredF32, StoredF32> for StoredF32TimesSqrt365 {
|
||||
#[inline(always)]
|
||||
fn apply(v: StoredF32) -> StoredF32 {
|
||||
// 365.0_f32.sqrt() = 19.104973
|
||||
(*v * 19.104973_f32).into()
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
/// StoredF32 × sqrt(7) -> StoredF32 (1-week volatility from daily SD)
|
||||
pub struct StoredF32TimesSqrt7;
|
||||
|
||||
impl UnaryTransform<StoredF32, StoredF32> for StoredF32TimesSqrt7 {
|
||||
#[inline(always)]
|
||||
fn apply(v: StoredF32) -> StoredF32 {
|
||||
// 7.0_f32.sqrt() = 2.6457513
|
||||
(*v * 2.6457513_f32).into()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user