///|
fn scalar_numeric_integer(
value : ScalarNumber,
) -> @bigint.BigInt raise @core.DataError {
match value {
Integer(value) => value
Boolean(value) | NumpyBool(value) =>
@bigint.BigInt::from_int(if value { 1 } else { 0 })
NumpyInt64(value) => @bigint.BigInt::from_int64(value)
Number(value) | NumpyFloat64(value) => {
if value.is_nan() {
raise @core.DataError::InvalidTicks(
message="ValueError: cannot convert float NaN to integer",
)
}
if value.is_inf() {
raise @core.DataError::InvalidTicks(
message="OverflowError: cannot convert float infinity to integer",
)
}
let (n, d) = decimal_ratio(value)
if value < 0.0 {
(n / d).neg()
} else {
n / d
}
}
}
}
///|
fn scalar_numeric_double(value : ScalarNumber) -> Double raise @core.DataError {
match value {
Number(value) | NumpyFloat64(value) => value
Integer(value) => percent_python_integer_double(value)
NumpyInt64(value) => value.to_double()
Boolean(value) | NumpyBool(value) => if value { 1.0 } else { 0.0 }
}
}
///|
fn scalar_numeric_numpy(value : ScalarNumber) -> Bool {
value is (NumpyFloat64(_) | NumpyInt64(_) | NumpyBool(_))
}
///|
fn scalar_numeric_integral(value : ScalarNumber) -> Bool {
!(value is (Number(_) | NumpyFloat64(_)))
}
///|
fn scalar_numeric_truth(value : ScalarNumber?) -> Bool {
match value {
None => false
Some(Integer(value)) => !value.is_zero()
Some(Boolean(value) | NumpyBool(value)) => value
Some(NumpyInt64(value)) => value != 0L
Some(Number(value) | NumpyFloat64(value)) => value != 0.0
}
}
///|
fn scalar_numeric_int64(value : ScalarNumber) -> Int64 raise @core.DataError {
let value = scalar_numeric_integer(value)
if value < @bigint.BigInt::from_int64(-9223372036854775808L) ||
value > @bigint.BigInt::from_int64(9223372036854775807L) {
raise @core.DataError::InvalidTicks(
message="OverflowError: Python int too large to convert to C long",
)
}
value.to_int64()
}
///|
// Operation order and scalar origin follow Python/NumPy, not a universal cast.
// op0 addition, op1 subtraction, op2 multiplication, op3 true division.
fn scalar_numeric_op(
left : ScalarNumber,
right : ScalarNumber,
op : Int32,
) -> ScalarNumber raise @core.DataError {
let numpy = scalar_numeric_numpy(left) || scalar_numeric_numpy(right)
let integral = scalar_numeric_integral(left) && scalar_numeric_integral(right)
if op == 3 && integral && !numpy {
let a = scalar_numeric_integer(left)
let b = scalar_numeric_integer(right)
if b.is_zero() {
raise @core.DataError::InvalidTicks(
message="ZeroDivisionError: division by zero",
)
}
let result = log_grid_integer_ratio(a, b) catch {
@core.DataError::InvalidTicks(
message="log grid integer ratio overflows float"
) =>
raise @core.DataError::InvalidTicks(
message="OverflowError: integer division result too large for a float",
)
error => raise error
}
return ScalarNumber::Number(result)
}
if op != 3 && integral {
if numpy {
let boolean = left is (Boolean(_) | NumpyBool(_)) &&
right is (Boolean(_) | NumpyBool(_))
if boolean {
if op == 1 {
raise @core.DataError::InvalidTicks(
message="TypeError: numpy boolean subtract, the `-` operator, is not supported, use the bitwise_xor, the `^` operator, or the logical_xor function instead.",
)
}
let a = scalar_numeric_truth(Some(left))
let b = scalar_numeric_truth(Some(right))
return ScalarNumber::NumpyBool(if op == 0 { a || b } else { a && b })
}
let a = scalar_numeric_int64(left).reinterpret_as_uint64()
let b = scalar_numeric_int64(right).reinterpret_as_uint64()
return ScalarNumber::NumpyInt64(
(if op == 0 { a + b } else if op == 1 { a - b } else { a * b }).reinterpret_as_int64(),
)
}
let a = scalar_numeric_integer(left)
let b = scalar_numeric_integer(right)
return ScalarNumber::Integer(
if op == 0 {
a + b
} else if op == 1 {
a - b
} else {
a * b
},
)
}
let a = scalar_numeric_double(left)
let b = scalar_numeric_double(right)
if op == 3 && b == 0.0 && !numpy {
raise @core.DataError::InvalidTicks(
message="ZeroDivisionError: float division by zero",
)
}
let result = if op == 0 {
a + b
} else if op == 1 {
a - b
} else if op == 2 {
a * b
} else {
a / b
}
if numpy {
ScalarNumber::NumpyFloat64(result)
} else {
ScalarNumber::Number(result)
}
}
///|
fn scalar_numeric_sub_offset(
left : ScalarNumber,
offset : ScalarNumber?,
) -> ScalarNumber raise @core.DataError {
match offset {
Some(offset) => scalar_numeric_op(left, offset, 1)
None => {
let name = match left {
Number(_) | NumpyFloat64(_) => "float"
Boolean(_) | NumpyBool(_) => "bool"
_ => "int"
}
raise @core.DataError::InvalidTicks(
message="TypeError: unsupported operand type(s) for -: '" +
name +
"' and 'NoneType'",
)
}
}
}
///|
fn scalar_numeric_abs(value : ScalarNumber) -> ScalarNumber {
match value {
Number(value) => ScalarNumber::Number(Double::abs(value))
NumpyFloat64(value) => ScalarNumber::NumpyFloat64(Double::abs(value))
Integer(value) =>
ScalarNumber::Integer(
if value < @bigint.BigInt::from_int(0) {
value.neg()
} else {
value
},
)
Boolean(value) =>
ScalarNumber::Integer(@bigint.BigInt::from_int(if value { 1 } else { 0 }))
NumpyBool(value) => ScalarNumber::NumpyBool(value)
NumpyInt64(value) =>
ScalarNumber::NumpyInt64(
if value < 0L {
(0UL - value.reinterpret_as_uint64()).reinterpret_as_int64()
} else {
value
},
)
}
}
///|
fn scalar_numeric_compare(
left : ScalarNumber,
right : ScalarNumber,
) -> Int32? raise @core.DataError {
let a_integral = scalar_numeric_integral(left)
let b_integral = scalar_numeric_integral(right)
if a_integral && b_integral {
let a = scalar_numeric_integer(left)
let b = scalar_numeric_integer(right)
return Some(if a < b { -1 } else if a > b { 1 } else { 0 })
}
if !scalar_numeric_numpy(left) && !scalar_numeric_numpy(right) {
if a_integral {
return log_grid_compare_integer_float(
scalar_numeric_integer(left),
scalar_numeric_double(right),
)
}
if b_integral {
return log_grid_compare_integer_float(
scalar_numeric_integer(right),
scalar_numeric_double(left),
).map(value => -value)
}
}
let a = scalar_numeric_double(left)
let b = scalar_numeric_double(right)
if a.is_nan() || b.is_nan() {
None
} else {
Some(if a < b { -1 } else if a > b { 1 } else { 0 })
}
}
///|
fn scalar_numeric_less(
left : ScalarNumber,
right : ScalarNumber,
) -> Bool raise @core.DataError {
scalar_numeric_compare(left, right) == Some(-1)
}
///|
fn scalar_numeric_equal(
left : ScalarNumber,
right : ScalarNumber,
) -> Bool raise @core.DataError {
scalar_numeric_compare(left, right) == Some(0)
}
///|
fn scalar_numeric_floor(value : Double) -> @bigint.BigInt raise @core.DataError {
if value.is_nan() {
raise @core.DataError::InvalidTicks(
message="ValueError: cannot convert float NaN to integer",
)
}
if value.is_inf() {
raise @core.DataError::InvalidTicks(
message="OverflowError: cannot convert float infinity to integer",
)
}
auto_minor_round(value.floor())
}
///|
fn scalar_numeric_float_floor_divide(left : Double, right : Double) -> Double {
if right == 0.0 {
left / right
} else {
log_grid_float_divmod(left, right).0
}
}
///|
fn scalar_numeric_location(
locations : FormatterLocations,
index : Int32,
) -> ScalarNumber raise @core.DataError {
match locations {
Sequence(values) => ScalarNumber::NumpyFloat64(values[index])
Integers(values) =>
LogitInput::from_keys(
values.map(value => LogitKey::new(ScalarNumber::Integer(value))),
)
.snapshot()
.get_value(index)
NdArray(values) =>
if values.is_int64() {
ScalarNumber::NumpyInt64(values.get_int64(index))
} else {
ScalarNumber::NumpyFloat64(values.get(index))
}
Typed(values) => values.get_value(index)
}
}
///|
fn scalar_numeric_view(
base : BaseFormatter,
sort~ : Bool = false,
) -> (ScalarNumber, ScalarNumber) raise @core.DataError {
let view = match base.axis {
Some(axis) => axis.view_interval()
None =>
raise @core.DataError::InvalidTicks(
message="AttributeError: 'NoneType' object has no attribute 'get_view_interval'",
)
}
let length = view.length()
if length < 2 {
raise @core.DataError::InvalidTicks(
message="ValueError: not enough values to unpack (expected 2, got " +
length.to_string() +
")",
)
}
if length > 2 {
raise @core.DataError::InvalidTicks(
message="ValueError: too many values to unpack (expected 2)",
)
}
let (a, b) = match view {
Sequence(values) =>
(ScalarNumber::Number(values[0]), ScalarNumber::Number(values[1]))
Integers(values) =>
(ScalarNumber::Integer(values[0]), ScalarNumber::Integer(values[1]))
NdArray(values) =>
if values.is_int64() {
(
ScalarNumber::NumpyInt64(values.get_int64(0)),
ScalarNumber::NumpyInt64(values.get_int64(1)),
)
} else {
(
ScalarNumber::NumpyFloat64(values.get(0)),
ScalarNumber::NumpyFloat64(values.get(1)),
)
}
}
if sort && scalar_numeric_less(b, a) {
(b, a)
} else {
(a, b)
}
}