@@ -9,7 +9,9 @@ import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregators.ValueType
9
9
import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregators.aggregate
10
10
import org.jetbrains.kotlinx.dataframe.impl.aggregation.aggregators.toValueType
11
11
import org.jetbrains.kotlinx.dataframe.impl.convertToUnifiedNumberType
12
+ import org.jetbrains.kotlinx.dataframe.impl.isMixedNumber
12
13
import org.jetbrains.kotlinx.dataframe.impl.isNothing
14
+ import org.jetbrains.kotlinx.dataframe.impl.isPrimitiveOrMixedNumber
13
15
import org.jetbrains.kotlinx.dataframe.impl.nothingType
14
16
import org.jetbrains.kotlinx.dataframe.impl.primitiveNumberTypes
15
17
import org.jetbrains.kotlinx.dataframe.impl.renderType
@@ -47,14 +49,23 @@ internal class NumberInputHandler<out Return : Any?> : AggregatorInputHandler<Nu
47
49
valueType : ValueType ,
48
50
): Pair <Sequence <Number ?>, KType> {
49
51
require(valueType.kType.isSubtypeOf(typeOf<Number ?>())) {
50
- " ${ NumberInputHandler :: class .simpleName} : Type $valueType is not a subtype of Number?, only primitive numbers are supported in statistics "
52
+ " Type $valueType is not a subtype of Number?, only primitive numbers are supported in ${aggregator !! .name} . "
51
53
}
52
54
return when (valueType.kType.withNullability(false )) {
53
55
// If the type is not a specific number, but rather a mixed Number, we unify the types first.
54
56
// This is heavy and could be avoided by calling aggregate with a specific number type
55
57
// or calling aggregateCalculatingType with all known number types
56
58
typeOf<Number >() -> {
57
59
val unifiedType = calculateValueType(values).kType
60
+
61
+ // If calculateValueType returns Number(?),
62
+ // it means the values cannot be unified to a primitive number type
63
+ require(! unifiedType.isMixedNumber()) {
64
+ " Types ${
65
+ values.asIterable().types().toSet()
66
+ } are not all primitive numbers, only those are supported in ${aggregator!! .name} ."
67
+ }
68
+
58
69
val unifiedValues = values.convertToUnifiedNumberType(
59
70
UnifiedNumberTypeOptions .PRIMITIVES_ONLY ,
60
71
unifiedType,
@@ -90,23 +101,21 @@ internal class NumberInputHandler<out Return : Any?> : AggregatorInputHandler<Nu
90
101
* this function can be called to calculate it in terms of [number unification][UnifyingNumbers]
91
102
*
92
103
* @throws IllegalArgumentException if the input type is not [Number]`(?)` or a primitive number type.
104
+ * @return The (primitive) unified number type of the input values.
105
+ * If no valid unification can be found or the input is solely [Number]`(?)`, the type [Number]`(?)` is returned.
93
106
*/
94
107
override fun calculateValueType (valueTypes : Set <KType >): ValueType {
95
108
val unifiedType = valueTypes.unifiedNumberTypeOrNull(UnifiedNumberTypeOptions .Companion .PRIMITIVES_ONLY )
96
- ? : throw IllegalArgumentException (
97
- " Cannot calculate the ${aggregator!! .name} of the number types: ${
98
- valueTypes.joinToString { renderType(it) }
99
- } . Note, only primitive number types are supported in statistics." ,
100
- )
109
+ ? : typeOf<Number >().withNullability(valueTypes.any { it.isMarkedNullable })
101
110
102
111
if (unifiedType.isSubtypeOf(typeOf<Double ?>()) &&
103
112
(typeOf<ULong >() in valueTypes || typeOf<Long >() in valueTypes)
104
113
) {
105
114
logger.warn {
106
- " Number unification of Long -> Double happened during aggregation. Loss of precision may have occurred."
115
+ " Number unification of Long -> Double happened during ${aggregator !! .name} aggregation. Loss of precision may have occurred."
107
116
}
108
117
}
109
- if (unifiedType.withNullability( false ) !in primitiveNumberTypes && ! unifiedType.isNothing) {
118
+ if (! unifiedType.isPrimitiveOrMixedNumber() && ! unifiedType.isNothing) {
110
119
throw IllegalArgumentException (
111
120
" Cannot calculate ${aggregator!! .name} of ${
112
121
renderType(unifiedType)
@@ -124,7 +133,9 @@ internal class NumberInputHandler<out Return : Any?> : AggregatorInputHandler<Nu
124
133
* this function can be called to calculate it in terms of [number unification][UnifyingNumbers]
125
134
* by getting the types of [values] at runtime.
126
135
*
127
- * @throws IllegalArgumentException if the input type contains a non-primitive number type.
136
+ * @throws IllegalArgumentException if the input type is not [Number]`(?)` or a primitive number type.
137
+ * @return The (primitive) unified number type of the input values.
138
+ * If no valid unification can be found or the input is solely [Number]`(?)`, the type [Number]`(?)` is returned.
128
139
*/
129
140
override fun calculateValueType (values : Sequence <Number ?>): ValueType =
130
141
calculateValueType(values.asIterable().types().toSet())
0 commit comments