1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
22 "github.com/apache/arrow/go/v6/arrow"
23 "github.com/apache/arrow/go/v6/arrow/float16"
24 "golang.org/x/xerrors"
27 // RecordEqual reports whether the two provided records are equal.
28 func RecordEqual(left, right Record) bool {
30 case left.NumCols() != right.NumCols():
32 case left.NumRows() != right.NumRows():
36 for i := range left.Columns() {
39 if !ArrayEqual(lc, rc) {
46 // RecordApproxEqual reports whether the two provided records are approximately equal.
47 // For non-floating point columns, it is equivalent to RecordEqual.
48 func RecordApproxEqual(left, right Record, opts ...EqualOption) bool {
50 case left.NumCols() != right.NumCols():
52 case left.NumRows() != right.NumRows():
56 opt := newEqualOption(opts...)
58 for i := range left.Columns() {
61 if !arrayApproxEqual(lc, rc, opt) {
68 // helper function to evaluate a function on two chunked object having possibly different
69 // chunk layouts. the function passed in will be called for each corresponding slice of the
70 // two chunked arrays and if the function returns false it will end the loop early.
71 func chunkedBinaryApply(left, right *Chunked, fn func(left Interface, lbeg, lend int64, right Interface, rbeg, rend int64) bool) {
74 length int64 = int64(left.length)
76 leftPos, rightPos int64
80 var cleft, cright Interface
82 cleft, cright = left.Chunk(leftIdx), right.Chunk(rightIdx)
83 if leftPos == int64(cleft.Len()) {
88 if rightPos == int64(cright.Len()) {
96 sz := int64(min(cleft.Len()-int(leftPos), cright.Len()-int(rightPos)))
98 if !fn(cleft, leftPos, leftPos+sz, cright, rightPos, rightPos+sz) {
107 // ChunkedEqual reports whether two chunked arrays are equal regardless of their chunkings
108 func ChunkedEqual(left, right *Chunked) bool {
112 case left.length != right.length:
114 case left.nulls != right.nulls:
116 case !arrow.TypeEqual(left.dtype, right.dtype):
121 chunkedBinaryApply(left, right, func(left Interface, lbeg, lend int64, right Interface, rbeg, rend int64) bool {
122 isequal = ArraySliceEqual(left, lbeg, lend, right, rbeg, rend)
129 // ChunkedApproxEqual reports whether two chunked arrays are approximately equal regardless of their chunkings
130 // for non-floating point arrays, this is equivalent to ChunkedEqual
131 func ChunkedApproxEqual(left, right *Chunked, opts ...EqualOption) bool {
135 case left.length != right.length:
137 case left.nulls != right.nulls:
139 case !arrow.TypeEqual(left.dtype, right.dtype):
144 chunkedBinaryApply(left, right, func(left Interface, lbeg, lend int64, right Interface, rbeg, rend int64) bool {
145 isequal = ArraySliceApproxEqual(left, lbeg, lend, right, rbeg, rend, opts...)
152 // TableEqual returns if the two tables have the same data in the same schema
153 func TableEqual(left, right Table) bool {
155 case left.NumCols() != right.NumCols():
157 case left.NumRows() != right.NumRows():
161 for i := 0; int64(i) < left.NumCols(); i++ {
163 rc := right.Column(i)
164 if !lc.field.Equal(rc.field) {
168 if !ChunkedEqual(lc.data, rc.data) {
175 // TableEqual returns if the two tables have the approximately equal data in the same schema
176 func TableApproxEqual(left, right Table, opts ...EqualOption) bool {
178 case left.NumCols() != right.NumCols():
180 case left.NumRows() != right.NumRows():
184 for i := 0; int64(i) < left.NumCols(); i++ {
186 rc := right.Column(i)
187 if !lc.field.Equal(rc.field) {
191 if !ChunkedApproxEqual(lc.data, rc.data, opts...) {
198 // ArrayEqual reports whether the two provided arrays are equal.
199 func ArrayEqual(left, right Interface) bool {
201 case !baseArrayEqual(left, right):
203 case left.Len() == 0:
205 case left.NullN() == left.Len():
209 // at this point, we know both arrays have same type, same length, same number of nulls
210 // and nulls at the same place.
211 // compare the values.
213 switch l := left.(type) {
217 r := right.(*Boolean)
218 return arrayEqualBoolean(l, r)
219 case *FixedSizeBinary:
220 r := right.(*FixedSizeBinary)
221 return arrayEqualFixedSizeBinary(l, r)
224 return arrayEqualBinary(l, r)
227 return arrayEqualString(l, r)
230 return arrayEqualInt8(l, r)
233 return arrayEqualInt16(l, r)
236 return arrayEqualInt32(l, r)
239 return arrayEqualInt64(l, r)
242 return arrayEqualUint8(l, r)
245 return arrayEqualUint16(l, r)
248 return arrayEqualUint32(l, r)
251 return arrayEqualUint64(l, r)
253 r := right.(*Float16)
254 return arrayEqualFloat16(l, r)
256 r := right.(*Float32)
257 return arrayEqualFloat32(l, r)
259 r := right.(*Float64)
260 return arrayEqualFloat64(l, r)
262 r := right.(*Decimal128)
263 return arrayEqualDecimal128(l, r)
266 return arrayEqualDate32(l, r)
269 return arrayEqualDate64(l, r)
272 return arrayEqualTime32(l, r)
275 return arrayEqualTime64(l, r)
277 r := right.(*Timestamp)
278 return arrayEqualTimestamp(l, r)
281 return arrayEqualList(l, r)
283 r := right.(*FixedSizeList)
284 return arrayEqualFixedSizeList(l, r)
287 return arrayEqualStruct(l, r)
289 r := right.(*MonthInterval)
290 return arrayEqualMonthInterval(l, r)
291 case *DayTimeInterval:
292 r := right.(*DayTimeInterval)
293 return arrayEqualDayTimeInterval(l, r)
294 case *MonthDayNanoInterval:
295 r := right.(*MonthDayNanoInterval)
296 return arrayEqualMonthDayNanoInterval(l, r)
298 r := right.(*Duration)
299 return arrayEqualDuration(l, r)
302 return arrayEqualMap(l, r)
304 r := right.(ExtensionArray)
305 return arrayEqualExtension(l, r)
307 panic(xerrors.Errorf("arrow/array: unknown array type %T", l))
311 // ArraySliceEqual reports whether slices left[lbeg:lend] and right[rbeg:rend] are equal.
312 func ArraySliceEqual(left Interface, lbeg, lend int64, right Interface, rbeg, rend int64) bool {
313 l := NewSlice(left, lbeg, lend)
315 r := NewSlice(right, rbeg, rend)
318 return ArrayEqual(l, r)
321 // ArraySliceApproxEqual reports whether slices left[lbeg:lend] and right[rbeg:rend] are approximately equal.
322 func ArraySliceApproxEqual(left Interface, lbeg, lend int64, right Interface, rbeg, rend int64, opts ...EqualOption) bool {
323 l := NewSlice(left, lbeg, lend)
325 r := NewSlice(right, rbeg, rend)
328 return ArrayApproxEqual(l, r, opts...)
331 const defaultAbsoluteTolerance = 1e-5
333 type equalOption struct {
334 atol float64 // absolute tolerance
335 nansEq bool // whether NaNs are considered equal.
338 func (eq equalOption) f16(f1, f2 float16.Num) bool {
339 v1 := float64(f1.Float32())
340 v2 := float64(f2.Float32())
343 return math.Abs(v1-v2) <= eq.atol || (math.IsNaN(v1) && math.IsNaN(v2))
345 return math.Abs(v1-v2) <= eq.atol
349 func (eq equalOption) f32(f1, f2 float32) bool {
354 return math.Abs(v1-v2) <= eq.atol || (math.IsNaN(v1) && math.IsNaN(v2))
356 return math.Abs(v1-v2) <= eq.atol
360 func (eq equalOption) f64(v1, v2 float64) bool {
363 return math.Abs(v1-v2) <= eq.atol || (math.IsNaN(v1) && math.IsNaN(v2))
365 return math.Abs(v1-v2) <= eq.atol
369 func newEqualOption(opts ...EqualOption) equalOption {
371 atol: defaultAbsoluteTolerance,
374 for _, opt := range opts {
381 // EqualOption is a functional option type used to configure how Records and Arrays are compared.
382 type EqualOption func(*equalOption)
384 // WithNaNsEqual configures the comparison functions so that NaNs are considered equal.
385 func WithNaNsEqual(v bool) EqualOption {
386 return func(o *equalOption) {
391 // WithAbsTolerance configures the comparison functions so that 2 floating point values
392 // v1 and v2 are considered equal if |v1-v2| <= atol.
393 func WithAbsTolerance(atol float64) EqualOption {
394 return func(o *equalOption) {
399 // ArrayApproxEqual reports whether the two provided arrays are approximately equal.
400 // For non-floating point arrays, it is equivalent to ArrayEqual.
401 func ArrayApproxEqual(left, right Interface, opts ...EqualOption) bool {
402 opt := newEqualOption(opts...)
403 return arrayApproxEqual(left, right, opt)
406 func arrayApproxEqual(left, right Interface, opt equalOption) bool {
408 case !baseArrayEqual(left, right):
410 case left.Len() == 0:
412 case left.NullN() == left.Len():
416 // at this point, we know both arrays have same type, same length, same number of nulls
417 // and nulls at the same place.
418 // compare the values.
420 switch l := left.(type) {
424 r := right.(*Boolean)
425 return arrayEqualBoolean(l, r)
426 case *FixedSizeBinary:
427 r := right.(*FixedSizeBinary)
428 return arrayEqualFixedSizeBinary(l, r)
431 return arrayEqualBinary(l, r)
434 return arrayEqualString(l, r)
437 return arrayEqualInt8(l, r)
440 return arrayEqualInt16(l, r)
443 return arrayEqualInt32(l, r)
446 return arrayEqualInt64(l, r)
449 return arrayEqualUint8(l, r)
452 return arrayEqualUint16(l, r)
455 return arrayEqualUint32(l, r)
458 return arrayEqualUint64(l, r)
460 r := right.(*Float16)
461 return arrayApproxEqualFloat16(l, r, opt)
463 r := right.(*Float32)
464 return arrayApproxEqualFloat32(l, r, opt)
466 r := right.(*Float64)
467 return arrayApproxEqualFloat64(l, r, opt)
469 r := right.(*Decimal128)
470 return arrayEqualDecimal128(l, r)
473 return arrayEqualDate32(l, r)
476 return arrayEqualDate64(l, r)
479 return arrayEqualTime32(l, r)
482 return arrayEqualTime64(l, r)
484 r := right.(*Timestamp)
485 return arrayEqualTimestamp(l, r)
488 return arrayApproxEqualList(l, r, opt)
490 r := right.(*FixedSizeList)
491 return arrayApproxEqualFixedSizeList(l, r, opt)
494 return arrayApproxEqualStruct(l, r, opt)
496 r := right.(*MonthInterval)
497 return arrayEqualMonthInterval(l, r)
498 case *DayTimeInterval:
499 r := right.(*DayTimeInterval)
500 return arrayEqualDayTimeInterval(l, r)
501 case *MonthDayNanoInterval:
502 r := right.(*MonthDayNanoInterval)
503 return arrayEqualMonthDayNanoInterval(l, r)
505 r := right.(*Duration)
506 return arrayEqualDuration(l, r)
509 return arrayApproxEqualList(l.List, r.List, opt)
511 r := right.(ExtensionArray)
512 return arrayApproxEqualExtension(l, r, opt)
514 panic(xerrors.Errorf("arrow/array: unknown array type %T", l))
520 func baseArrayEqual(left, right Interface) bool {
522 case left.Len() != right.Len():
524 case left.NullN() != right.NullN():
526 case !arrow.TypeEqual(left.DataType(), right.DataType()): // We do not check for metadata as in the C++ implementation.
528 case !validityBitmapEqual(left, right):
534 func validityBitmapEqual(left, right Interface) bool {
535 // TODO(alexandreyc): make it faster by comparing byte slices of the validity bitmap?
537 if n != right.Len() {
540 for i := 0; i < n; i++ {
541 if left.IsNull(i) != right.IsNull(i) {
548 func arrayApproxEqualFloat16(left, right *Float16, opt equalOption) bool {
549 for i := 0; i < left.Len(); i++ {
553 if !opt.f16(left.Value(i), right.Value(i)) {
560 func arrayApproxEqualFloat32(left, right *Float32, opt equalOption) bool {
561 for i := 0; i < left.Len(); i++ {
565 if !opt.f32(left.Value(i), right.Value(i)) {
572 func arrayApproxEqualFloat64(left, right *Float64, opt equalOption) bool {
573 for i := 0; i < left.Len(); i++ {
577 if !opt.f64(left.Value(i), right.Value(i)) {
584 func arrayApproxEqualList(left, right *List, opt equalOption) bool {
585 for i := 0; i < left.Len(); i++ {
590 l := left.newListValue(i)
592 r := right.newListValue(i)
594 return arrayApproxEqual(l, r, opt)
603 func arrayApproxEqualFixedSizeList(left, right *FixedSizeList, opt equalOption) bool {
604 for i := 0; i < left.Len(); i++ {
609 l := left.newListValue(i)
611 r := right.newListValue(i)
613 return arrayApproxEqual(l, r, opt)
622 func arrayApproxEqualStruct(left, right *Struct, opt equalOption) bool {
623 for i, lf := range left.fields {
624 rf := right.fields[i]
625 if !arrayApproxEqual(lf, rf, opt) {