Build & bench
This commit is contained in:
parent
a0df593342
commit
a07038a61d
|
@ -6,3 +6,12 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
blurhash = { version = "0.2.0", path = "../blurhash-rs" }
|
||||
criterion = "0.5.1"
|
||||
image = "0.24.8"
|
||||
|
||||
[[bench]]
|
||||
name = "benchy"
|
||||
harness = false
|
||||
|
|
63
benches/benchy.rs
Normal file
63
benches/benchy.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use blurhash_update::{Components, ImageBounds};
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use image::EncodableLayout;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("blurhash");
|
||||
|
||||
let inputs = [
|
||||
"data/19dd1c444d1c7939.png",
|
||||
"data/f73d2ee39133d871.jpg",
|
||||
"data/shenzi.png",
|
||||
];
|
||||
|
||||
for input in inputs {
|
||||
group.bench_with_input(BenchmarkId::from_parameter(input), input, |b, i| {
|
||||
let img = image::open(i).unwrap();
|
||||
let width = img.width();
|
||||
let height = img.height();
|
||||
let rgba = img.to_rgba8();
|
||||
let bytes = rgba.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
let _bhash =
|
||||
black_box(blurhash::encode(4, 3, width, height, black_box(bytes)).unwrap());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
|
||||
let mut group = c.benchmark_group("blurhash-update");
|
||||
|
||||
let inputs = [
|
||||
"data/19dd1c444d1c7939.png",
|
||||
"data/f73d2ee39133d871.jpg",
|
||||
"data/shenzi.png",
|
||||
];
|
||||
|
||||
for input in inputs {
|
||||
group.bench_with_input(BenchmarkId::from_parameter(input), input, |b, i| {
|
||||
let img = image::open(i).unwrap();
|
||||
let width = img.width();
|
||||
let height = img.height();
|
||||
let rgba = img.to_rgba8();
|
||||
let bytes = rgba.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
let mut encoder = blurhash_update::encoder(
|
||||
Components { x: 4, y: 3 },
|
||||
ImageBounds { width, height },
|
||||
)
|
||||
.unwrap();
|
||||
encoder.update(bytes);
|
||||
let _bhash = black_box(encoder.finalize());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
BIN
data/19dd1c444d1c7939.png
Normal file
BIN
data/19dd1c444d1c7939.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 637 KiB |
BIN
data/f73d2ee39133d871.jpg
Normal file
BIN
data/f73d2ee39133d871.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 277 KiB |
BIN
data/shenzi.png
Normal file
BIN
data/shenzi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 169 KiB |
15
src/base83.rs
Normal file
15
src/base83.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
static CHARACTERS: [u8; 83] = [
|
||||
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F',
|
||||
b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
|
||||
b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l',
|
||||
b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'#', b'$',
|
||||
b'%', b'*', b'+', b',', b'-', b'.', b':', b';', b'=', b'?', b'@', b'[', b']', b'^', b'_', b'{',
|
||||
b'|', b'}', b'~',
|
||||
];
|
||||
|
||||
pub(crate) fn encode(value: u32, length: u32, s: &mut String) {
|
||||
for i in 1..=length {
|
||||
let digit: u32 = (value / u32::pow(83, length - i)) % 83;
|
||||
s.push(CHARACTERS[digit as usize] as char);
|
||||
}
|
||||
}
|
319
src/lib.rs
319
src/lib.rs
|
@ -1,14 +1,321 @@
|
|||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
mod base83;
|
||||
mod srgb_lookup;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use srgb_lookup::SRGB_LOOKUP;
|
||||
|
||||
pub struct Components {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ImageBounds {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
struct ComponentState {
|
||||
x: u32,
|
||||
y: u32,
|
||||
basis: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentError;
|
||||
|
||||
impl std::fmt::Display for ComponentError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Components out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Encoder {
|
||||
index: usize,
|
||||
components: Components,
|
||||
factors: Vec<(ComponentState, [f32; 3])>,
|
||||
bounds: ImageBounds,
|
||||
}
|
||||
|
||||
pub fn encoder(components: Components, bounds: ImageBounds) -> Result<Encoder, ComponentError> {
|
||||
Encoder::new(components, bounds)
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
fn new(Components { x, y }: Components, bounds: ImageBounds) -> Result<Self, ComponentError> {
|
||||
if !(1..=9).contains(&x) || !(1..=9).contains(&y) {
|
||||
return Err(ComponentError);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
index: 0,
|
||||
components: Components { x, y },
|
||||
factors: (0..y)
|
||||
.flat_map(|y| {
|
||||
(0..x).map(move |x| (ComponentState { x, y, basis: 0. }, [0., 0., 0.]))
|
||||
})
|
||||
.collect(),
|
||||
bounds,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, buf: &[u8]) {
|
||||
const BYTES_PER_PIXEL: usize = 4;
|
||||
|
||||
// get offset in terms of already-processed bytes
|
||||
let offset = self.index % BYTES_PER_PIXEL;
|
||||
// get offset in terms of remaining bytes on head of buf
|
||||
let offset = (BYTES_PER_PIXEL - offset) % BYTES_PER_PIXEL;
|
||||
|
||||
for (ComponentState { basis, .. }, [_, g, b]) in self.factors.iter_mut() {
|
||||
for (byte, value) in buf[..offset].iter().zip(
|
||||
[&mut *b, &mut *g][0..offset.saturating_sub(2)]
|
||||
.iter_mut()
|
||||
.rev(),
|
||||
) {
|
||||
**value += *basis * SRGB_LOOKUP[*byte as usize]
|
||||
}
|
||||
}
|
||||
|
||||
let pixels = ((self.index + offset) / BYTES_PER_PIXEL) as u32;
|
||||
|
||||
let mut chunks = buf[offset..].chunks_exact(BYTES_PER_PIXEL);
|
||||
|
||||
for (i, chunk) in (&mut chunks).enumerate() {
|
||||
let px = pixels + i as u32;
|
||||
let px_x = px % self.bounds.width;
|
||||
let px_y = px / self.bounds.width;
|
||||
|
||||
for (ComponentState { x, y, .. }, [r, g, b]) in self.factors.iter_mut() {
|
||||
let basis = compute_basis(
|
||||
*x as _,
|
||||
*y as _,
|
||||
px_x as _,
|
||||
px_y as _,
|
||||
self.bounds.width as _,
|
||||
self.bounds.height as _,
|
||||
);
|
||||
|
||||
*r += basis * SRGB_LOOKUP[chunk[0] as usize];
|
||||
*g += basis * SRGB_LOOKUP[chunk[1] as usize];
|
||||
*b += basis * SRGB_LOOKUP[chunk[2] as usize];
|
||||
}
|
||||
}
|
||||
|
||||
if !chunks.remainder().is_empty() {
|
||||
let px = pixels + (buf[offset..].len() / BYTES_PER_PIXEL) as u32;
|
||||
let px_x = px % self.bounds.width;
|
||||
let px_y = px / self.bounds.width;
|
||||
|
||||
for (ComponentState { x, y, basis }, [r, g, b]) in self.factors.iter_mut() {
|
||||
*basis = compute_basis(
|
||||
*x as _,
|
||||
*y as _,
|
||||
px_x as _,
|
||||
px_y as _,
|
||||
self.bounds.width as _,
|
||||
self.bounds.height as _,
|
||||
);
|
||||
|
||||
for (byte, value) in chunks.remainder().iter().zip([&mut *r, &mut *g, &mut *b]) {
|
||||
*value += *basis * SRGB_LOOKUP[*byte as usize]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.index += buf.len();
|
||||
}
|
||||
|
||||
pub fn finalize(mut self) -> String {
|
||||
for (ComponentState { x, y, .. }, [r, g, b]) in &mut self.factors {
|
||||
let normalisation = if *x == 0 && *y == 0 { 1. } else { 2. };
|
||||
|
||||
let scale = normalisation / (self.bounds.width * self.bounds.height) as f32;
|
||||
|
||||
*r *= scale;
|
||||
*g *= scale;
|
||||
*b *= scale;
|
||||
}
|
||||
|
||||
let mut blurhash = String::new();
|
||||
|
||||
let (_, dc) = self.factors[0];
|
||||
let ac = &self.factors[1..];
|
||||
|
||||
let size_flag = self.components.x - 1 + (self.components.y - 1) * 9;
|
||||
base83::encode(size_flag, 1, &mut blurhash);
|
||||
|
||||
let maximum_value = if !ac.is_empty() {
|
||||
let maximum = ac.iter().fold(0.0_f32, |maximum, (_, [r, g, b])| {
|
||||
maximum.max(r.abs()).max(g.abs()).max(b.abs())
|
||||
});
|
||||
|
||||
let quantized_maximum = (maximum * 166. - 0.5).floor().max(0.) as u32;
|
||||
|
||||
base83::encode(quantized_maximum, 1, &mut blurhash);
|
||||
|
||||
(quantized_maximum + 1) as f32 / 166.
|
||||
} else {
|
||||
base83::encode(0, 1, &mut blurhash);
|
||||
|
||||
1.
|
||||
};
|
||||
|
||||
base83::encode(encode_dc(dc), 4, &mut blurhash);
|
||||
|
||||
for (_, rgb) in ac {
|
||||
base83::encode(encode_ac(*rgb, maximum_value), 2, &mut blurhash);
|
||||
}
|
||||
|
||||
blurhash
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_basis(
|
||||
component_x: f32,
|
||||
component_y: f32,
|
||||
px_x: f32,
|
||||
px_y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
) -> f32 {
|
||||
f32::cos(PI * component_x * px_x / width) * f32::cos(PI * component_y * px_y / height)
|
||||
}
|
||||
|
||||
fn encode_dc([r, g, b]: [f32; 3]) -> u32 {
|
||||
let r = linear_to_srgb(r);
|
||||
let g = linear_to_srgb(g);
|
||||
let b = linear_to_srgb(b);
|
||||
|
||||
(r << 16) + (g << 8) + b
|
||||
}
|
||||
|
||||
fn encode_ac([r, g, b]: [f32; 3], maximum_value: f32) -> u32 {
|
||||
let r = encode_ac_digit(r, maximum_value);
|
||||
let g = encode_ac_digit(g, maximum_value);
|
||||
let b = encode_ac_digit(b, maximum_value);
|
||||
|
||||
r * 19 * 19 + g * 19 + b
|
||||
}
|
||||
|
||||
fn encode_ac_digit(d: f32, maximum_value: f32) -> u32 {
|
||||
((sign_pow(d / maximum_value, 0.5) * 9. + 9.5) as i32).clamp(0, 18) as u32
|
||||
}
|
||||
|
||||
pub fn linear_to_srgb(value: f32) -> u32 {
|
||||
let v = f32::max(0., f32::min(1., value));
|
||||
if v <= 0.003_130_8 {
|
||||
(v * 12.92 * 255. + 0.5).round() as u32
|
||||
} else {
|
||||
((1.055 * f32::powf(v, 1. / 2.4) - 0.055) * 255. + 0.5).round() as u32
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(n: f32) -> f32 {
|
||||
if n < 0. {
|
||||
-1.
|
||||
} else {
|
||||
1.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign_pow(val: f32, exp: f32) -> f32 {
|
||||
sign(val) * val.abs().powf(exp)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use image::{EncodableLayout, GenericImageView};
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
fn contrived() {
|
||||
let input = [
|
||||
0, 60, 120, 0, 0, 60, 120, 0, 0, 60, 120, 0, 0, 60, 120, 0, 0, 60, 120, 0, 0, 60, 120,
|
||||
0, 0, 60, 120, 0, 0, 60, 120, 0, 120, 60, 0, 0, 120, 60, 0, 0, 120, 60, 0, 0, 120, 60,
|
||||
0, 0, 120, 60, 0, 0, 120, 60, 0, 0, 120, 60, 0, 0, 120, 60, 0, 0,
|
||||
];
|
||||
let width = 4;
|
||||
let height = 4;
|
||||
|
||||
let mut encoder = super::encoder(
|
||||
crate::Components { x: 4, y: 3 },
|
||||
crate::ImageBounds { width, height },
|
||||
)
|
||||
.unwrap();
|
||||
encoder.update(&input);
|
||||
let b1 = encoder.finalize();
|
||||
|
||||
let b2 = blurhash::encode(4, 3, width, height, &input).unwrap();
|
||||
|
||||
assert_eq!(b1, b2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_blurhash() {
|
||||
let inputs = [
|
||||
"data/19dd1c444d1c7939.png",
|
||||
"data/f73d2ee39133d871.jpg",
|
||||
"data/shenzi.png",
|
||||
];
|
||||
|
||||
for input in inputs {
|
||||
let img = image::open(input).unwrap();
|
||||
let (width, height) = img.dimensions();
|
||||
|
||||
let mut encoder = super::encoder(
|
||||
crate::Components { x: 4, y: 3 },
|
||||
crate::ImageBounds { width, height },
|
||||
)
|
||||
.unwrap();
|
||||
encoder.update(img.to_rgba8().as_bytes());
|
||||
let b1 = encoder.finalize();
|
||||
|
||||
let b2 = blurhash::encode(4, 3, width, height, img.to_rgba8().as_bytes()).unwrap();
|
||||
|
||||
assert_eq!(b1, b2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_self_when_split() {
|
||||
let inputs = [
|
||||
"data/19dd1c444d1c7939.png",
|
||||
"data/f73d2ee39133d871.jpg",
|
||||
"data/shenzi.png",
|
||||
];
|
||||
|
||||
for input in inputs {
|
||||
let img = image::open(input).unwrap();
|
||||
let (width, height) = img.dimensions();
|
||||
let rgba8_img = img.to_rgba8();
|
||||
let bytes = rgba8_img.as_bytes();
|
||||
|
||||
let mut encoder = super::encoder(
|
||||
crate::Components { x: 4, y: 3 },
|
||||
crate::ImageBounds { width, height },
|
||||
)
|
||||
.unwrap();
|
||||
encoder.update(bytes);
|
||||
let b1 = encoder.finalize();
|
||||
|
||||
for chunk_count in 2..20 {
|
||||
encoder = super::encoder(
|
||||
crate::Components { x: 4, y: 3 },
|
||||
crate::ImageBounds { width, height },
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let chunk_size = bytes.len() / chunk_count;
|
||||
|
||||
for chunk in bytes.chunks(chunk_size) {
|
||||
encoder.update(chunk);
|
||||
}
|
||||
|
||||
let b2 = encoder.finalize();
|
||||
|
||||
assert_eq!(b1, b2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
258
src/srgb_lookup.rs
Normal file
258
src/srgb_lookup.rs
Normal file
|
@ -0,0 +1,258 @@
|
|||
pub(crate) static SRGB_LOOKUP: [f32; 256] = [
|
||||
0.0,
|
||||
0.000303527,
|
||||
0.000607054,
|
||||
0.000910581,
|
||||
0.001214108,
|
||||
0.001517635,
|
||||
0.001821162,
|
||||
0.0021246888,
|
||||
0.002428216,
|
||||
0.002731743,
|
||||
0.00303527,
|
||||
0.0033465356,
|
||||
0.003676507,
|
||||
0.004024717,
|
||||
0.004391442,
|
||||
0.0047769533,
|
||||
0.005181517,
|
||||
0.0056053917,
|
||||
0.0060488326,
|
||||
0.006512091,
|
||||
0.00699541,
|
||||
0.0074990317,
|
||||
0.008023192,
|
||||
0.008568125,
|
||||
0.009134057,
|
||||
0.009721218,
|
||||
0.010329823,
|
||||
0.010960094,
|
||||
0.011612245,
|
||||
0.012286487,
|
||||
0.012983031,
|
||||
0.013702081,
|
||||
0.014443844,
|
||||
0.015208514,
|
||||
0.015996292,
|
||||
0.016807375,
|
||||
0.017641952,
|
||||
0.018500218,
|
||||
0.019382361,
|
||||
0.020288562,
|
||||
0.02121901,
|
||||
0.022173883,
|
||||
0.023153365,
|
||||
0.02415763,
|
||||
0.025186857,
|
||||
0.026241222,
|
||||
0.027320892,
|
||||
0.028426038,
|
||||
0.029556833,
|
||||
0.03071344,
|
||||
0.03189603,
|
||||
0.033104762,
|
||||
0.034339808,
|
||||
0.035601314,
|
||||
0.036889445,
|
||||
0.038204364,
|
||||
0.039546236,
|
||||
0.0409152,
|
||||
0.04231141,
|
||||
0.043735027,
|
||||
0.045186203,
|
||||
0.046665084,
|
||||
0.048171822,
|
||||
0.049706563,
|
||||
0.051269468,
|
||||
0.052860655,
|
||||
0.05448028,
|
||||
0.056128494,
|
||||
0.057805434,
|
||||
0.05951124,
|
||||
0.06124607,
|
||||
0.06301003,
|
||||
0.06480328,
|
||||
0.06662595,
|
||||
0.06847818,
|
||||
0.07036011,
|
||||
0.07227186,
|
||||
0.07421358,
|
||||
0.07618539,
|
||||
0.07818743,
|
||||
0.08021983,
|
||||
0.082282715,
|
||||
0.084376216,
|
||||
0.086500466,
|
||||
0.088655606,
|
||||
0.09084173,
|
||||
0.09305898,
|
||||
0.095307484,
|
||||
0.09758736,
|
||||
0.09989874,
|
||||
0.10224175,
|
||||
0.10461649,
|
||||
0.10702311,
|
||||
0.10946172,
|
||||
0.111932434,
|
||||
0.11443538,
|
||||
0.11697067,
|
||||
0.119538434,
|
||||
0.1221388,
|
||||
0.12477184,
|
||||
0.1274377,
|
||||
0.13013649,
|
||||
0.13286833,
|
||||
0.13563335,
|
||||
0.13843162,
|
||||
0.1412633,
|
||||
0.14412849,
|
||||
0.14702728,
|
||||
0.1499598,
|
||||
0.15292616,
|
||||
0.15592647,
|
||||
0.15896086,
|
||||
0.1620294,
|
||||
0.16513222,
|
||||
0.1682694,
|
||||
0.1714411,
|
||||
0.17464739,
|
||||
0.17788841,
|
||||
0.18116423,
|
||||
0.18447499,
|
||||
0.18782076,
|
||||
0.19120167,
|
||||
0.19461781,
|
||||
0.1980693,
|
||||
0.20155624,
|
||||
0.2050787,
|
||||
0.20863685,
|
||||
0.21223073,
|
||||
0.21586053,
|
||||
0.21952623,
|
||||
0.22322798,
|
||||
0.22696589,
|
||||
0.23074007,
|
||||
0.23455065,
|
||||
0.23839766,
|
||||
0.2422812,
|
||||
0.2462014,
|
||||
0.25015837,
|
||||
0.25415218,
|
||||
0.2581829,
|
||||
0.26225072,
|
||||
0.26635566,
|
||||
0.27049786,
|
||||
0.27467737,
|
||||
0.27889434,
|
||||
0.2831488,
|
||||
0.2874409,
|
||||
0.2917707,
|
||||
0.29613832,
|
||||
0.30054384,
|
||||
0.30498737,
|
||||
0.30946895,
|
||||
0.31398875,
|
||||
0.31854683,
|
||||
0.32314324,
|
||||
0.32777813,
|
||||
0.33245158,
|
||||
0.33716366,
|
||||
0.34191445,
|
||||
0.3467041,
|
||||
0.3515327,
|
||||
0.35640025,
|
||||
0.36130688,
|
||||
0.3662527,
|
||||
0.37123778,
|
||||
0.37626222,
|
||||
0.3813261,
|
||||
0.38642952,
|
||||
0.39157256,
|
||||
0.3967553,
|
||||
0.40197787,
|
||||
0.4072403,
|
||||
0.4125427,
|
||||
0.41788515,
|
||||
0.42326775,
|
||||
0.42869055,
|
||||
0.4341537,
|
||||
0.43965724,
|
||||
0.44520125,
|
||||
0.45078585,
|
||||
0.45641106,
|
||||
0.46207705,
|
||||
0.46778384,
|
||||
0.47353154,
|
||||
0.47932023,
|
||||
0.48514998,
|
||||
0.4910209,
|
||||
0.49693304,
|
||||
0.5028866,
|
||||
0.50888145,
|
||||
0.5149178,
|
||||
0.5209957,
|
||||
0.5271152,
|
||||
0.5332765,
|
||||
0.5394796,
|
||||
0.5457246,
|
||||
0.5520115,
|
||||
0.5583405,
|
||||
0.56471163,
|
||||
0.5711249,
|
||||
0.5775805,
|
||||
0.5840785,
|
||||
0.5906189,
|
||||
0.5972019,
|
||||
0.6038274,
|
||||
0.6104956,
|
||||
0.61720663,
|
||||
0.62396044,
|
||||
0.6307572,
|
||||
0.63759696,
|
||||
0.64447975,
|
||||
0.6514057,
|
||||
0.65837485,
|
||||
0.66538733,
|
||||
0.6724432,
|
||||
0.67954254,
|
||||
0.68668544,
|
||||
0.6938719,
|
||||
0.701102,
|
||||
0.70837593,
|
||||
0.71569365,
|
||||
0.72305524,
|
||||
0.7304609,
|
||||
0.73791057,
|
||||
0.74540436,
|
||||
0.7529423,
|
||||
0.76052463,
|
||||
0.7681513,
|
||||
0.77582234,
|
||||
0.7835379,
|
||||
0.79129803,
|
||||
0.79910284,
|
||||
0.80695236,
|
||||
0.8148467,
|
||||
0.82278585,
|
||||
0.83076996,
|
||||
0.8387991,
|
||||
0.8468733,
|
||||
0.8549927,
|
||||
0.8631573,
|
||||
0.8713672,
|
||||
0.87962234,
|
||||
0.8879232,
|
||||
0.8962694,
|
||||
0.90466136,
|
||||
0.9130987,
|
||||
0.92158204,
|
||||
0.9301109,
|
||||
0.9386859,
|
||||
0.9473066,
|
||||
0.9559735,
|
||||
0.9646863,
|
||||
0.9734455,
|
||||
0.9822506,
|
||||
0.9911022,
|
||||
1.0,
|
||||
];
|
Loading…
Reference in a new issue