github.com/tobgu/qframe@v0.4.0/internal/fcolumn/column.go (about)

     1  package fcolumn
     2  
     3  import (
     4  	"github.com/tobgu/qframe/internal/ryu"
     5  	"math"
     6  	"math/rand"
     7  	"reflect"
     8  	"strconv"
     9  	"unsafe"
    10  
    11  	"github.com/tobgu/qframe/internal/column"
    12  	"github.com/tobgu/qframe/internal/hash"
    13  	"github.com/tobgu/qframe/internal/index"
    14  	"github.com/tobgu/qframe/qerrors"
    15  	"github.com/tobgu/qframe/types"
    16  )
    17  
    18  func (c Column) DataType() types.DataType {
    19  	return types.Float
    20  }
    21  
    22  func (c Column) StringAt(i uint32, naRep string) string {
    23  	value := c.data[i]
    24  	if math.IsNaN(value) {
    25  		return naRep
    26  	}
    27  	return strconv.FormatFloat(c.data[i], 'f', -1, 64)
    28  }
    29  
    30  func (c Column) AppendByteStringAt(buf []byte, i uint32) []byte {
    31  	value := c.data[i]
    32  	if math.IsNaN(value) {
    33  		return append(buf, "null"...)
    34  	}
    35  
    36  	return ryu.AppendFloat64f(buf, value)
    37  }
    38  
    39  func (c Column) ByteSize() int {
    40  	// Slice header + data
    41  	return 2*8 + 8*cap(c.data)
    42  }
    43  
    44  func (c Column) Equals(index index.Int, other column.Column, otherIndex index.Int) bool {
    45  	otherI, ok := other.(Column)
    46  	if !ok {
    47  		return false
    48  	}
    49  
    50  	for ix, x := range index {
    51  		v1, v2 := c.data[x], otherI.data[otherIndex[ix]]
    52  		if v1 != v2 {
    53  			// NaN != NaN but for our purposes they are the same
    54  			if !(math.IsNaN(v1) && math.IsNaN(v2)) {
    55  				return false
    56  			}
    57  		}
    58  	}
    59  
    60  	return true
    61  }
    62  
    63  func (c Comparable) Compare(i, j uint32) column.CompareResult {
    64  	x, y := c.data[i], c.data[j]
    65  	if x < y {
    66  		return c.ltValue
    67  	}
    68  
    69  	if x > y {
    70  		return c.gtValue
    71  	}
    72  
    73  	if math.IsNaN(x) || math.IsNaN(y) {
    74  		if !math.IsNaN(x) {
    75  			return c.nullGtValue
    76  		}
    77  
    78  		if !math.IsNaN(y) {
    79  			return c.nullLtValue
    80  		}
    81  
    82  		return c.equalNullValue
    83  	}
    84  
    85  	return column.Equal
    86  }
    87  
    88  func (c Comparable) Hash(i uint32, seed uint64) uint64 {
    89  	f := c.data[i]
    90  	if math.IsNaN(f) && c.equalNullValue == column.NotEqual {
    91  		// Use a random value here to avoid hash collisions when
    92  		// we don't consider null to equal null.
    93  		return rand.Uint64()
    94  	}
    95  
    96  	bits := math.Float64bits(c.data[i])
    97  	b := (*[8]byte)(unsafe.Pointer(&bits))[:]
    98  	return hash.HashBytes(b, seed)
    99  }
   100  
   101  func (c Column) filterBuiltIn(index index.Int, comparator string, comparatee interface{}, bIndex index.Bool) error {
   102  	switch t := comparatee.(type) {
   103  	case float64:
   104  		if math.IsNaN(t) {
   105  			return qerrors.New("filter float", "NaN not allowed as filter argument")
   106  		}
   107  
   108  		compFunc, ok := filterFuncs1[comparator]
   109  		if !ok {
   110  			return qerrors.New("filter float", "invalid comparison operator to single argument filter, %v", comparator)
   111  		}
   112  		compFunc(index, c.data, t, bIndex)
   113  	case Column:
   114  		compFunc, ok := filterFuncs2[comparator]
   115  		if !ok {
   116  			return qerrors.New("filter float", "invalid comparison operator to column - column filter, %v", comparator)
   117  		}
   118  		compFunc(index, c.data, t.data, bIndex)
   119  	case nil:
   120  		compFunc, ok := filterFuncs0[comparator]
   121  		if !ok {
   122  			return qerrors.New("filter float", "invalid comparison operator to zero argument filter, %v", comparator)
   123  		}
   124  		compFunc(index, c.data, bIndex)
   125  	default:
   126  		return qerrors.New("filter float", "invalid comparison value type %v", reflect.TypeOf(comparatee))
   127  	}
   128  	return nil
   129  }
   130  
   131  func (c Column) filterCustom1(index index.Int, fn func(float64) bool, bIndex index.Bool) {
   132  	for i, x := range bIndex {
   133  		if !x {
   134  			bIndex[i] = fn(c.data[index[i]])
   135  		}
   136  	}
   137  }
   138  
   139  func (c Column) filterCustom2(index index.Int, fn func(float64, float64) bool, comparatee interface{}, bIndex index.Bool) error {
   140  	otherC, ok := comparatee.(Column)
   141  	if !ok {
   142  		return qerrors.New("filter float", "expected comparatee to be float column, was %v", reflect.TypeOf(comparatee))
   143  	}
   144  
   145  	for i, x := range bIndex {
   146  		if !x {
   147  			bIndex[i] = fn(c.data[index[i]], otherC.data[index[i]])
   148  		}
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func (c Column) Filter(index index.Int, comparator interface{}, comparatee interface{}, bIndex index.Bool) error {
   155  	var err error
   156  	switch t := comparator.(type) {
   157  	case string:
   158  		err = c.filterBuiltIn(index, t, comparatee, bIndex)
   159  	case func(float64) bool:
   160  		c.filterCustom1(index, t, bIndex)
   161  	case func(float64, float64) bool:
   162  		err = c.filterCustom2(index, t, comparatee, bIndex)
   163  	default:
   164  		err = qerrors.New("filter float", "invalid filter type %v", reflect.TypeOf(comparator))
   165  	}
   166  	return err
   167  }
   168  
   169  func (c Column) FunctionType() types.FunctionType {
   170  	return types.FunctionTypeFloat
   171  }
   172  
   173  func (c Column) Append(cols ...column.Column) (column.Column, error) {
   174  	// TODO Append
   175  	return nil, qerrors.New("Append", "Not implemented yet")
   176  }