use std::ops::Range;
use crate::Axis;
pub trait Points {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn x(&self, index: usize) -> f64;
fn y(&self, index: usize) -> f64;
}
impl Points for [(f64, f64)] {
fn len(&self) -> usize {
<[_]>::len(self)
}
fn x(&self, index: usize) -> f64 {
self[index].0
}
fn y(&self, index: usize) -> f64 {
self[index].1
}
}
impl Points for (&[f64], &[f64]) {
fn len(&self) -> usize {
self.0.len().min(self.1.len())
}
fn x(&self, index: usize) -> f64 {
self.0[index]
}
fn y(&self, index: usize) -> f64 {
self.1[index]
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Candle {
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
}
impl Candle {
pub const fn new(open: f64, high: f64, low: f64, close: f64) -> Self {
Self {
open,
high,
low,
close,
}
}
pub fn is_finite(&self) -> bool {
self.open.is_finite()
&& self.high.is_finite()
&& self.low.is_finite()
&& self.close.is_finite()
}
}
pub trait Candles {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn x(&self, index: usize) -> f64;
fn candle(&self, index: usize) -> Candle;
}
impl Candles for [(f64, Candle)] {
fn len(&self) -> usize {
<[_]>::len(self)
}
fn x(&self, index: usize) -> f64 {
self[index].0
}
fn candle(&self, index: usize) -> Candle {
self[index].1
}
}
pub fn points_within<P: Points + ?Sized>(points: &P, x: &Axis) -> Range<usize> {
index_range(points.len(), |index| points.x(index), x)
}
pub fn candles_within<C: Candles + ?Sized>(candles: &C, x: &Axis) -> Range<usize> {
index_range(candles.len(), |index| candles.x(index), x)
}
pub fn y_extent<P: Points + ?Sized>(points: &P, range: Range<usize>) -> Option<(f64, f64)> {
let range = range.start.min(points.len())..range.end.min(points.len());
extent(range.map(|index| points.y(index)).map(|y| (y, y)))
}
pub fn candle_extent<C: Candles + ?Sized>(candles: &C, range: Range<usize>) -> Option<(f64, f64)> {
let range = range.start.min(candles.len())..range.end.min(candles.len());
extent(
range
.map(|index| candles.candle(index))
.filter(Candle::is_finite)
.map(|candle| (candle.low, candle.high)),
)
}
fn extent(values: impl Iterator<Item = (f64, f64)>) -> Option<(f64, f64)> {
values
.filter(|(low, high)| low.is_finite() && high.is_finite())
.fold(None, |seen, (low, high)| match seen {
None => Some((low, high)),
Some((min, max)) => Some((low.min(min), high.max(max))),
})
}
fn index_range(len: usize, x: impl Fn(usize) -> f64, axis: &Axis) -> Range<usize> {
let start = partition_point(len, |index| x(index) < axis.start());
let end = partition_point(len, |index| x(index) <= axis.end());
start..end.max(start)
}
fn partition_point(len: usize, before: impl Fn(usize) -> bool) -> usize {
let (mut low, mut high) = (0, len);
while low < high {
let middle = low + (high - low) / 2;
if before(middle) {
low = middle + 1;
} else {
high = middle;
}
}
low
}
pub(crate) fn with_neighbours(range: Range<usize>, len: usize) -> Range<usize> {
range.start.saturating_sub(1)..range.end.saturating_add(1).min(len)
}