github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/utils/aggutils.go (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package utils
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  )
    23  
    24  func Reduce(e1 CValueEnclosure, e2 CValueEnclosure, fun AggregateFunctions) (CValueEnclosure, error) {
    25  
    26  	if e1.Dtype == SS_INVALID {
    27  		return e2, nil
    28  	} else if e2.Dtype == SS_INVALID {
    29  		return e1, nil
    30  	} else if e2.Dtype == SS_DT_BACKFILL {
    31  		return e1, nil
    32  	} else if e1.Dtype == SS_DT_BACKFILL {
    33  		return e2, nil
    34  	}
    35  
    36  	// cannot reduce with incoming as string
    37  	if e2.Dtype == SS_DT_STRING {
    38  		return e1, nil
    39  	}
    40  
    41  	// Convert to float if needed
    42  	if e1.Dtype == SS_DT_FLOAT && e2.Dtype != SS_DT_FLOAT {
    43  		switch e2.Dtype {
    44  		case SS_DT_UNSIGNED_NUM:
    45  			e2 = CValueEnclosure{Dtype: SS_DT_FLOAT, CVal: float64(e2.CVal.(uint64))}
    46  		case SS_DT_SIGNED_NUM:
    47  			e2 = CValueEnclosure{Dtype: SS_DT_FLOAT, CVal: float64(e2.CVal.(int64))}
    48  		}
    49  	}
    50  
    51  	if e2.Dtype == SS_DT_FLOAT && e1.Dtype != SS_DT_FLOAT {
    52  		switch e1.Dtype {
    53  		case SS_DT_UNSIGNED_NUM:
    54  			e1 = CValueEnclosure{Dtype: SS_DT_FLOAT, CVal: float64(e1.CVal.(uint64))}
    55  		case SS_DT_SIGNED_NUM:
    56  			e1 = CValueEnclosure{Dtype: SS_DT_FLOAT, CVal: float64(e1.CVal.(int64))}
    57  		}
    58  	}
    59  
    60  	// TODO: what if one is int64 and the other is uint64? Is there any way to avoid annoying conversions?
    61  
    62  	switch e1.Dtype {
    63  	case SS_DT_UNSIGNED_NUM:
    64  		switch fun {
    65  		case Sum:
    66  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(uint64) + e2.CVal.(uint64)}, nil
    67  		case Min:
    68  			return CValueEnclosure{Dtype: e1.Dtype, CVal: MinUint64(e1.CVal.(uint64), e2.CVal.(uint64))}, nil
    69  		case Max:
    70  			return CValueEnclosure{Dtype: e1.Dtype, CVal: MaxUint64(e1.CVal.(uint64), e2.CVal.(uint64))}, nil
    71  		case Count:
    72  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(uint64) + e2.CVal.(uint64)}, nil
    73  		}
    74  	case SS_DT_SIGNED_NUM:
    75  		switch fun {
    76  		case Sum:
    77  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(int64) + e2.CVal.(int64)}, nil
    78  		case Min:
    79  			return CValueEnclosure{Dtype: e1.Dtype, CVal: MinInt64(e1.CVal.(int64), e2.CVal.(int64))}, nil
    80  		case Max:
    81  			return CValueEnclosure{Dtype: e1.Dtype, CVal: MaxInt64(e1.CVal.(int64), e2.CVal.(int64))}, nil
    82  		case Count:
    83  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(int64) + e2.CVal.(int64)}, nil
    84  		}
    85  	case SS_DT_FLOAT:
    86  		switch fun {
    87  		case Sum:
    88  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(float64) + e2.CVal.(float64)}, nil
    89  		case Min:
    90  			return CValueEnclosure{Dtype: e1.Dtype, CVal: math.Min(e1.CVal.(float64), e2.CVal.(float64))}, nil
    91  		case Max:
    92  			return CValueEnclosure{Dtype: e1.Dtype, CVal: math.Max(e1.CVal.(float64), e2.CVal.(float64))}, nil
    93  		case Count:
    94  			return CValueEnclosure{Dtype: e1.Dtype, CVal: e1.CVal.(float64) + e2.CVal.(float64)}, nil
    95  		}
    96  	case SS_DT_STRING_SET:
    97  		{
    98  			switch fun {
    99  			case Cardinality:
   100  				fallthrough
   101  			case Values:
   102  				set1 := e1.CVal.(map[string]struct{})
   103  				set2 := e2.CVal.(map[string]struct{})
   104  				for str := range set2 {
   105  					set1[str] = struct{}{}
   106  				}
   107  				return CValueEnclosure{Dtype: e1.Dtype, CVal: set1}, nil
   108  			}
   109  			return e1, fmt.Errorf("Reduce: unsupported CVal Dtype: %v", e1.Dtype)
   110  		}
   111  	default:
   112  		return e1, fmt.Errorf("Reduce: unsupported CVal Dtype: %v", e1.Dtype)
   113  	}
   114  	return e1, fmt.Errorf("Reduce: unsupported reduce function: %v", fun)
   115  }
   116  
   117  func (self *NumTypeEnclosure) ReduceFast(e2Dtype SS_DTYPE, e2int64 int64,
   118  	e2float64 float64, fun AggregateFunctions) error {
   119  
   120  	if self.Ntype == SS_INVALID { // on first node we hit this, and we just use whatever is e2
   121  		self.Ntype = e2Dtype
   122  		switch e2Dtype {
   123  		case SS_DT_UNSIGNED_NUM, SS_DT_SIGNED_NUM:
   124  			self.IntgrVal = e2int64
   125  		case SS_DT_FLOAT:
   126  			self.FloatVal = e2float64
   127  		}
   128  		return nil
   129  	} else if e2Dtype == SS_INVALID { // if e2 is invalid, we live with whats in self
   130  		return nil
   131  	} else if e2Dtype == SS_DT_BACKFILL { // cant use e2 so return
   132  		return nil
   133  	} else if self.Ntype == SS_DT_BACKFILL { // if the first node happened to be backfill, then we use e2
   134  		self.Ntype = e2Dtype
   135  		switch e2Dtype {
   136  		case SS_DT_UNSIGNED_NUM, SS_DT_SIGNED_NUM:
   137  			self.IntgrVal = e2int64
   138  		case SS_DT_FLOAT:
   139  			self.FloatVal = e2float64
   140  		}
   141  		return nil
   142  	}
   143  
   144  	// cannot reduce with incoming as string
   145  	if e2Dtype == SS_DT_STRING {
   146  		return nil
   147  	}
   148  
   149  	// if self is float and e2 is not, then convert e2
   150  	if self.Ntype == SS_DT_FLOAT && e2Dtype != SS_DT_FLOAT {
   151  		switch e2Dtype {
   152  		case SS_DT_UNSIGNED_NUM, SS_DT_SIGNED_NUM:
   153  			e2float64 = float64(e2int64)
   154  		}
   155  	}
   156  
   157  	// if e2 is float and self is not, then convert self
   158  	if e2Dtype == SS_DT_FLOAT && self.Ntype != SS_DT_FLOAT {
   159  		switch self.Ntype {
   160  		case SS_DT_UNSIGNED_NUM, SS_DT_SIGNED_NUM:
   161  			self.Ntype = SS_DT_FLOAT
   162  			self.FloatVal = float64(self.IntgrVal)
   163  		}
   164  	}
   165  
   166  	// TODO: what if one is int64 and the other is uint64? Is there any way to avoid annoying conversions?
   167  	// by now both sides are of same type
   168  	switch self.Ntype {
   169  	case SS_DT_SIGNED_NUM, SS_DT_UNSIGNED_NUM:
   170  		switch fun {
   171  		case Sum:
   172  			self.IntgrVal = self.IntgrVal + e2int64
   173  			return nil
   174  		case Min:
   175  			self.IntgrVal = MinInt64(self.IntgrVal, e2int64)
   176  			return nil
   177  		case Max:
   178  			self.IntgrVal = MaxInt64(self.IntgrVal, e2int64)
   179  			return nil
   180  		case Count:
   181  			self.IntgrVal = self.IntgrVal + e2int64
   182  			return nil
   183  		}
   184  	case SS_DT_FLOAT:
   185  		switch fun {
   186  		case Sum:
   187  			self.FloatVal = self.FloatVal + e2float64
   188  			return nil
   189  		case Min:
   190  			self.FloatVal = math.Min(self.FloatVal, e2float64)
   191  			return nil
   192  		case Max:
   193  			self.FloatVal = math.Max(self.FloatVal, e2float64)
   194  			return nil
   195  		case Count:
   196  			self.FloatVal = self.FloatVal + e2float64
   197  			return nil
   198  		}
   199  	default:
   200  		return fmt.Errorf("Reduce: unsupported self CVal Dtype: %v", self.Ntype)
   201  	}
   202  	return fmt.Errorf("Reduce: unsupported reduce function: %v", fun)
   203  }