use std::mem::MaybeUninit;
use std::os::raw::c_ulong;
use crate::config::SpotConfig;
use crate::error::{SpotError, SpotResult};
use crate::ffi::{self, SpotRaw};
use crate::status::SpotStatus;
#[derive(Debug)]
pub struct SpotDetector {
raw: MaybeUninit<SpotRaw>,
initialized: bool,
}
impl SpotDetector {
pub fn new(config: SpotConfig) -> SpotResult<Self> {
unsafe {
ffi::set_allocators(libc::malloc, libc::free);
}
let mut detector = SpotDetector {
raw: MaybeUninit::uninit(),
initialized: false,
};
unsafe {
let status = ffi::spot_init(
detector.raw.as_mut_ptr(),
config.q,
if config.low_tail { 1 } else { 0 },
if config.discard_anomalies { 1 } else { 0 },
config.level,
config.max_excess as c_ulong,
);
if status < 0 {
return Err(SpotError::from_code(status));
}
}
detector.initialized = true;
Ok(detector)
}
pub fn fit(&mut self, data: &[f64]) -> SpotResult<()> {
if !self.initialized {
return Err(SpotError::NotInitialized);
}
unsafe {
let status = ffi::spot_fit(self.raw.as_mut_ptr(), data.as_ptr(), data.len() as c_ulong);
if status < 0 {
return Err(SpotError::from_code(status));
}
}
Ok(())
}
pub fn step(&mut self, value: f64) -> SpotResult<SpotStatus> {
if !self.initialized {
return Err(SpotError::NotInitialized);
}
unsafe {
let status = ffi::spot_step(self.raw.as_mut_ptr(), value);
if status < 0 {
return Err(SpotError::from_code(status));
}
Ok(SpotStatus::from(status))
}
}
pub fn quantile(&self, q: f64) -> f64 {
if !self.initialized {
return f64::NAN;
}
unsafe { ffi::spot_quantile(self.raw.as_ptr(), q) }
}
pub fn anomaly_threshold(&self) -> f64 {
if !self.initialized {
return f64::NAN;
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
spot_ref.anomaly_threshold
}
}
pub fn excess_threshold(&self) -> f64 {
if !self.initialized {
return f64::NAN;
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
spot_ref.excess_threshold
}
}
pub fn config(&self) -> Option<SpotConfig> {
if !self.initialized {
return None;
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
Some(SpotConfig {
q: spot_ref.q,
low_tail: spot_ref.low != 0,
discard_anomalies: spot_ref.discard_anomalies != 0,
level: spot_ref.level,
max_excess: spot_ref.tail.peaks.container.capacity as usize,
})
}
}
pub fn n(&self) -> usize {
if !self.initialized {
return 0;
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
spot_ref.n as usize
}
}
pub fn nt(&self) -> usize {
if !self.initialized {
return 0;
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
spot_ref.nt as usize
}
}
pub fn tail_parameters(&self) -> (f64, f64) {
if !self.initialized {
return (f64::NAN, f64::NAN);
}
unsafe {
let spot_ref = &*self.raw.as_ptr();
(spot_ref.tail.gamma, spot_ref.tail.sigma)
}
}
}
impl Drop for SpotDetector {
fn drop(&mut self) {
if self.initialized {
unsafe {
ffi::spot_free(self.raw.as_mut_ptr());
}
}
}
}
unsafe impl Send for SpotDetector {}