///|
// NumPy's integer-array _clip first discards out-of-range *Python int*
// endpoints. This precedes ufunc promotion; converting all bounds eagerly
// would reject valid wide integer prefix tables.
fn eng_math_clip(
phase : @bigint.BigInt,
lower : ScalarNumber,
upper : ScalarNumber,
) -> ScalarNumber raise @core.DataError {
let no_lower = match lower {
Integer(value) => value <= @bigint.BigInt::from_int64(-9223372036854775808L)
_ => false
}
let no_upper = match upper {
Integer(value) => value >= @bigint.BigInt::from_int64(9223372036854775807L)
_ => false
}
let floating = (!no_lower && lower is (Number(_) | NumpyFloat64(_))) ||
(!no_upper && upper is (Number(_) | NumpyFloat64(_)))
if floating {
let value = scalar_numeric_double(ScalarNumber::Integer(phase))
let a = if no_lower { None } else { Some(scalar_numeric_double(lower)) }
let b = if no_upper { None } else { Some(scalar_numeric_double(upper)) }
let result = match (a, b) {
(Some(a), Some(b)) =>
if a.is_nan() {
a
} else if b.is_nan() {
b
} else if value < a {
a
} else if value > b {
b
} else {
value
}
// One-bound paths are NumPy maximum/minimum, not the clip loop.
// phase0 is positive zero: maximum with lower -0 keeps +0, whereas
// minimum with upper -0 returns -0. Keep these tie rules separate.
(Some(a), None) => if a.is_nan() || value < a { a } else { value }
(None, Some(b)) => if b.is_nan() || value >= b { b } else { value }
(None, None) => value
}
ScalarNumber::NumpyFloat64(result)
} else {
let value = scalar_numeric_int64(ScalarNumber::Integer(phase))
let a = if no_lower { None } else { Some(scalar_numeric_int64(lower)) }
let b = if no_upper { None } else { Some(scalar_numeric_int64(upper)) }
let result = match (a, b) {
(Some(a), Some(b)) =>
if value < a {
a
} else if value > b {
b
} else {
value
}
(Some(a), None) => if value < a { a } else { value }
(None, Some(b)) => if value > b { b } else { value }
(None, None) => value
}
ScalarNumber::NumpyInt64(result)
}
}
///|
// Preserve math.log's PyLong path without first overflowing a Double cast.
// Round each natural logarithm independently before the base division.
fn eng_math_natural_log(value : ScalarNumber) -> Double raise @core.DataError {
match value {
Integer(integer) => {
if integer <= @bigint.BigInt::from_int(0) {
raise @core.DataError::InvalidTicks(
message="ValueError: math domain error",
)
}
for precision = 192 {
let (lower, upper) = asinh_math_log_bounds(integer, 0, precision)
let a = index_round_binary(lower, -precision)
let b = index_round_binary(upper, -precision)
if a.reinterpret_as_uint64() == b.reinterpret_as_uint64() {
return a
}
continue precision * 2
}
}
_ => {
let value = scalar_numeric_double(value)
if value <= 0.0 {
raise @core.DataError::InvalidTicks(
message="ValueError: math domain error",
)
}
log_grid_natural_log(value)
}
}
}
///|
fn eng_math_order(span : ScalarNumber) -> ScalarNumber raise @core.DataError {
let numerator = eng_math_natural_log(span)
let denominator = log_grid_natural_log(1000.0)
let ratio = numerator / denominator
ScalarNumber::Integer(
scalar_numeric_floor(ratio) * @bigint.BigInt::from_int(3),
)
}