///|
/// Description of a constant segment for generated numeric traces.
pub struct SegmentSpec {
length : Int
level : Double
}
///|
pub fn SegmentSpec::new(
length : Int,
level : Double,
) -> Result[SegmentSpec, DetectError] {
if length <= 0 {
return Err(InvalidRunLengthLimit)
}
if !finite(level) {
return Err(NonFiniteObservation)
}
Ok({ length, level, })
}
///|
pub fn segment_boundaries(specs : Array[SegmentSpec]) -> Array[Boundary] {
let result = []
let mut accumulated = 0
for index in 0.. Result[Array[Double], DetectError] {
if length <= 0 || !finite(lower) || !finite(upper) || lower >= upper {
return Err(InvalidVariance)
}
let rng = ParkMiller::new(seed).unwrap()
let result = []
for _ in 0.. Result[Array[Double], DetectError] {
if length <= 0 || !finite(center) || !finite(scale) || scale < 0.0 {
return Err(InvalidVariance)
}
let rng = ParkMiller::new(seed).unwrap()
let result = []
while result.length() < length {
let mut uniform = rng.next_unit()
if uniform < 0.000000001 {
uniform = 0.000000001
}
let radius = @math.exp(-@math.ln(uniform) * 0.5 * 2.0)
let angle = 6.283185307179586 * rng.next_unit()
result.push(center + scale * radius * @math.cos(angle))
if result.length() < length {
result.push(center + scale * radius * @math.sin(angle))
}
}
Ok(result)
}
///|
pub fn gaussian_segments(
specs : Array[SegmentSpec],
scale : Double,
seed : Int,
) -> Result[NumericSeries, DetectError] {
if specs.length() == 0 || scale < 0.0 || !finite(scale) {
return Err(InvalidVariance)
}
let values = []
let mut offset = 0
for spec in specs {
let noise = match
normal_noise(spec.length, spec.level, scale, seed + offset + 1) {
Ok(result) => result
Err(error) => return Err(error)
}
for value in noise {
values.push(value)
}
offset = offset + spec.length
}
NumericSeries::new(values)
}
///|
pub fn linear_ramp(
length : Int,
start : Double,
finish : Double,
) -> Result[NumericSeries, DetectError] {
if length < 2 || !finite(start) || !finite(finish) {
return Err(InvalidRunLengthLimit)
}
let values = []
for index in 0.. Result[NumericSeries, DetectError] {
if length <= 0 ||
!finite(center) ||
!finite(amplitude) ||
!finite(period) ||
period <= 0.0 {
return Err(InvalidRunLengthLimit)
}
let values = []
for index in 0.. Result[NumericSeries, DetectError] {
if left.length() != right.length() {
return Err(InvalidRunLengthLimit)
}
let values = []
let left_values = left.values()
let right_values = right.values()
for index in 0.. Result[NumericSeries, DetectError] {
let noise = match normal_noise(series.length(), 0.0, scale, seed) {
Ok(result) => result
Err(error) => return Err(error)
}
add_series(series, NumericSeries::new(noise).unwrap())
}
///|
pub fn inject_spikes(
series : NumericSeries,
positions : Array[Int],
amount : Double,
) -> Result[NumericSeries, DetectError] {
if !finite(amount) {
return Err(NonFiniteObservation)
}
let values = series.values()
for position in positions {
if position < 0 || position >= values.length() {
return Err(InvalidRunLengthLimit)
}
values[position] = values[position] + amount
}
NumericSeries::new(values)
}
///|
pub fn clip_series(
series : NumericSeries,
lower : Double,
upper : Double,
) -> Result[NumericSeries, DetectError] {
if !finite(lower) || !finite(upper) || lower > upper {
return Err(InvalidVariance)
}
let values = []
for value in series.values() {
if value < lower {
values.push(lower)
} else if value > upper {
values.push(upper)
} else {
values.push(value)
}
}
NumericSeries::new(values)
}
///|
pub fn poisson_sample(
rate : Double,
rng : ParkMiller,
) -> Result[Int, DetectError] {
if !finite(rate) || rate < 0.0 || rate > 100.0 {
return Err(InvalidVariance)
}
let cutoff = @math.exp(-rate)
let mut product = 1.0
let mut count = 0
while product > cutoff {
count = count + 1
product = product * rng.next_unit()
}
Ok(count - 1)
}
///|
pub fn poisson_segments(
lengths : Array[Int],
rates : Array[Double],
seed : Int,
) -> Result[Array[Int], DetectError] {
if lengths.length() == 0 || lengths.length() != rates.length() {
return Err(InvalidRunLengthLimit)
}
let rng = ParkMiller::new(seed).unwrap()
let values = []
for index in 0.. value
Err(error) => return Err(error)
}
values.push(sample)
}
}
Ok(values)
}
///|
pub fn contamination_mask(
length : Int,
probability : Double,
seed : Int,
) -> Result[Array[Bool], DetectError] {
if length <= 0 ||
!finite(probability) ||
probability < 0.0 ||
probability > 1.0 {
return Err(InvalidHazard)
}
let rng = ParkMiller::new(seed).unwrap()
let result = []
for _ in 0..