github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/soliton/ranger/points.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package ranger
    15  
    16  import (
    17  	"fmt"
    18  	"math"
    19  	"sort"
    20  
    21  	"github.com/whtcorpsinc/errors"
    22  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    23  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    24  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    25  	"github.com/whtcorpsinc/milevadb/errno"
    26  	"github.com/whtcorpsinc/milevadb/memex"
    27  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    28  	"github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx"
    29  	"github.com/whtcorpsinc/milevadb/types"
    30  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    31  	"github.com/whtcorpsinc/milevadb/soliton/defCauslate"
    32  )
    33  
    34  // Error instances.
    35  var (
    36  	ErrUnsupportedType = terror.ClassOptimizer.New(errno.ErrUnsupportedType, errno.MyALLEGROSQLErrName[errno.ErrUnsupportedType])
    37  )
    38  
    39  // RangeType is alias for int.
    40  type RangeType int
    41  
    42  // RangeType constants.
    43  const (
    44  	IntRangeType RangeType = iota
    45  	DeferredCausetRangeType
    46  	IndexRangeType
    47  )
    48  
    49  // Point is the end point of range interval.
    50  type point struct {
    51  	value types.Causet
    52  	excl  bool // exclude
    53  	start bool
    54  }
    55  
    56  func (rp point) String() string {
    57  	val := rp.value.GetValue()
    58  	if rp.value.HoTT() == types.HoTTMinNotNull {
    59  		val = "-inf"
    60  	} else if rp.value.HoTT() == types.HoTTMaxValue {
    61  		val = "+inf"
    62  	}
    63  	if rp.start {
    64  		symbol := "["
    65  		if rp.excl {
    66  			symbol = "("
    67  		}
    68  		return fmt.Sprintf("%s%v", symbol, val)
    69  	}
    70  	symbol := "]"
    71  	if rp.excl {
    72  		symbol = ")"
    73  	}
    74  	return fmt.Sprintf("%v%s", val, symbol)
    75  }
    76  
    77  type pointSorter struct {
    78  	points []point
    79  	err    error
    80  	sc     *stmtctx.StatementContext
    81  }
    82  
    83  func (r *pointSorter) Len() int {
    84  	return len(r.points)
    85  }
    86  
    87  func (r *pointSorter) Less(i, j int) bool {
    88  	a := r.points[i]
    89  	b := r.points[j]
    90  	less, err := rangePointLess(r.sc, a, b)
    91  	if err != nil {
    92  		r.err = err
    93  	}
    94  	return less
    95  }
    96  
    97  func rangePointLess(sc *stmtctx.StatementContext, a, b point) (bool, error) {
    98  	cmp, err := a.value.CompareCauset(sc, &b.value)
    99  	if cmp != 0 {
   100  		return cmp < 0, nil
   101  	}
   102  	return rangePointEqualValueLess(a, b), errors.Trace(err)
   103  }
   104  
   105  func rangePointEqualValueLess(a, b point) bool {
   106  	if a.start && b.start {
   107  		return !a.excl && b.excl
   108  	} else if a.start {
   109  		return !a.excl && !b.excl
   110  	} else if b.start {
   111  		return a.excl || b.excl
   112  	}
   113  	return a.excl && !b.excl
   114  }
   115  
   116  func (r *pointSorter) Swap(i, j int) {
   117  	r.points[i], r.points[j] = r.points[j], r.points[i]
   118  }
   119  
   120  // fullRange is (-∞, +∞).
   121  var fullRange = []point{
   122  	{start: true},
   123  	{value: types.MaxValueCauset()},
   124  }
   125  
   126  // FullIntRange is used for causet range. Since causet range cannot accept MaxValueCauset as the max value.
   127  // So we need to set it to MaxInt64.
   128  func FullIntRange(isUnsigned bool) []*Range {
   129  	if isUnsigned {
   130  		return []*Range{{LowVal: []types.Causet{types.NewUintCauset(0)}, HighVal: []types.Causet{types.NewUintCauset(math.MaxUint64)}}}
   131  	}
   132  	return []*Range{{LowVal: []types.Causet{types.NewIntCauset(math.MinInt64)}, HighVal: []types.Causet{types.NewIntCauset(math.MaxInt64)}}}
   133  }
   134  
   135  // FullRange is [null, +∞) for Range.
   136  func FullRange() []*Range {
   137  	return []*Range{{LowVal: []types.Causet{{}}, HighVal: []types.Causet{types.MaxValueCauset()}}}
   138  }
   139  
   140  // FullNotNullRange is (-∞, +∞) for Range.
   141  func FullNotNullRange() []*Range {
   142  	return []*Range{{LowVal: []types.Causet{types.MinNotNullCauset()}, HighVal: []types.Causet{types.MaxValueCauset()}}}
   143  }
   144  
   145  // NullRange is [null, null] for Range.
   146  func NullRange() []*Range {
   147  	return []*Range{{LowVal: []types.Causet{{}}, HighVal: []types.Causet{{}}}}
   148  }
   149  
   150  // builder is the range builder struct.
   151  type builder struct {
   152  	err error
   153  	sc  *stmtctx.StatementContext
   154  	ctx *stochastikctx.Context
   155  }
   156  
   157  func (r *builder) build(expr memex.Expression) []point {
   158  	switch x := expr.(type) {
   159  	case *memex.DeferredCauset:
   160  		return r.buildFromDeferredCauset(x)
   161  	case *memex.ScalarFunction:
   162  		return r.buildFromScalarFunc(x)
   163  	case *memex.Constant:
   164  		return r.buildFromConstant(x)
   165  	}
   166  
   167  	return fullRange
   168  }
   169  
   170  func (r *builder) buildFromConstant(expr *memex.Constant) []point {
   171  	dt, err := expr.Eval(chunk.Row{})
   172  	if err != nil {
   173  		r.err = err
   174  		return nil
   175  	}
   176  	if dt.IsNull() {
   177  		return nil
   178  	}
   179  
   180  	val, err := dt.ToBool(r.sc)
   181  	if err != nil {
   182  		r.err = err
   183  		return nil
   184  	}
   185  
   186  	if val == 0 {
   187  		return nil
   188  	}
   189  	return fullRange
   190  }
   191  
   192  func (r *builder) buildFromDeferredCauset(expr *memex.DeferredCauset) []point {
   193  	// defCausumn name memex is equivalent to defCausumn name is true.
   194  	startPoint1 := point{value: types.MinNotNullCauset(), start: true}
   195  	endPoint1 := point{excl: true}
   196  	endPoint1.value.SetInt64(0)
   197  	startPoint2 := point{excl: true, start: true}
   198  	startPoint2.value.SetInt64(0)
   199  	endPoint2 := point{value: types.MaxValueCauset()}
   200  	return []point{startPoint1, endPoint1, startPoint2, endPoint2}
   201  }
   202  
   203  func (r *builder) buildFormBinOp(expr *memex.ScalarFunction) []point {
   204  	// This has been checked that the binary operation is comparison operation, and one of
   205  	// the operand is defCausumn name memex.
   206  	var (
   207  		op    string
   208  		value types.Causet
   209  		err   error
   210  		ft    *types.FieldType
   211  	)
   212  
   213  	// refineValue refines the constant causet for string type since we may eval the constant to another defCauslation instead of its own defCauslation.
   214  	refineValue := func(defCaus *memex.DeferredCauset, value *types.Causet) {
   215  		if defCaus.RetType.EvalType() == types.ETString && value.HoTT() == types.HoTTString {
   216  			value.SetString(value.GetString(), defCaus.RetType.DefCauslate)
   217  		}
   218  	}
   219  	if defCaus, ok := expr.GetArgs()[0].(*memex.DeferredCauset); ok {
   220  		ft = defCaus.RetType
   221  		value, err = expr.GetArgs()[1].Eval(chunk.Row{})
   222  		if err != nil {
   223  			return nil
   224  		}
   225  		refineValue(defCaus, &value)
   226  		op = expr.FuncName.L
   227  	} else {
   228  		defCaus, ok := expr.GetArgs()[1].(*memex.DeferredCauset)
   229  		if !ok {
   230  			return nil
   231  		}
   232  		ft = defCaus.RetType
   233  		value, err = expr.GetArgs()[0].Eval(chunk.Row{})
   234  		if err != nil {
   235  			return nil
   236  		}
   237  		refineValue(defCaus, &value)
   238  
   239  		switch expr.FuncName.L {
   240  		case ast.GE:
   241  			op = ast.LE
   242  		case ast.GT:
   243  			op = ast.LT
   244  		case ast.LT:
   245  			op = ast.GT
   246  		case ast.LE:
   247  			op = ast.GE
   248  		default:
   249  			op = expr.FuncName.L
   250  		}
   251  	}
   252  	if op != ast.NullEQ && value.IsNull() {
   253  		return nil
   254  	}
   255  
   256  	value, op, isValidRange := handleUnsignedIntDefCaus(ft, value, op)
   257  	if !isValidRange {
   258  		return nil
   259  	}
   260  
   261  	switch op {
   262  	case ast.NullEQ:
   263  		if value.IsNull() {
   264  			return []point{{start: true}, {}} // [null, null]
   265  		}
   266  		fallthrough
   267  	case ast.EQ:
   268  		startPoint := point{value: value, start: true}
   269  		endPoint := point{value: value}
   270  		return []point{startPoint, endPoint}
   271  	case ast.NE:
   272  		startPoint1 := point{value: types.MinNotNullCauset(), start: true}
   273  		endPoint1 := point{value: value, excl: true}
   274  		startPoint2 := point{value: value, start: true, excl: true}
   275  		endPoint2 := point{value: types.MaxValueCauset()}
   276  		return []point{startPoint1, endPoint1, startPoint2, endPoint2}
   277  	case ast.LT:
   278  		startPoint := point{value: types.MinNotNullCauset(), start: true}
   279  		endPoint := point{value: value, excl: true}
   280  		return []point{startPoint, endPoint}
   281  	case ast.LE:
   282  		startPoint := point{value: types.MinNotNullCauset(), start: true}
   283  		endPoint := point{value: value}
   284  		return []point{startPoint, endPoint}
   285  	case ast.GT:
   286  		startPoint := point{value: value, start: true, excl: true}
   287  		endPoint := point{value: types.MaxValueCauset()}
   288  		return []point{startPoint, endPoint}
   289  	case ast.GE:
   290  		startPoint := point{value: value, start: true}
   291  		endPoint := point{value: types.MaxValueCauset()}
   292  		return []point{startPoint, endPoint}
   293  	}
   294  	return nil
   295  }
   296  
   297  // handleUnsignedIntDefCaus handles the case when unsigned defCausumn meets negative integer value.
   298  // The three returned values are: fixed constant value, fixed operator, and a boolean
   299  // which indicates whether the range is valid or not.
   300  func handleUnsignedIntDefCaus(ft *types.FieldType, val types.Causet, op string) (types.Causet, string, bool) {
   301  	isUnsigned := allegrosql.HasUnsignedFlag(ft.Flag)
   302  	isIntegerType := allegrosql.IsIntegerType(ft.Tp)
   303  	isNegativeInteger := (val.HoTT() == types.HoTTInt64 && val.GetInt64() < 0)
   304  
   305  	if !isUnsigned || !isIntegerType || !isNegativeInteger {
   306  		return val, op, true
   307  	}
   308  
   309  	// If the operator is GT, GE or NE, the range should be [0, +inf].
   310  	// Otherwise the value is out of valid range.
   311  	if op == ast.GT || op == ast.GE || op == ast.NE {
   312  		op = ast.GE
   313  		val.SetUint64(0)
   314  		return val, op, true
   315  	}
   316  
   317  	return val, op, false
   318  }
   319  
   320  func (r *builder) buildFromIsTrue(expr *memex.ScalarFunction, isNot int, keepNull bool) []point {
   321  	if isNot == 1 {
   322  		if keepNull {
   323  			// Range is {[0, 0]}
   324  			startPoint := point{start: true}
   325  			startPoint.value.SetInt64(0)
   326  			endPoint := point{}
   327  			endPoint.value.SetInt64(0)
   328  			return []point{startPoint, endPoint}
   329  		}
   330  		// NOT TRUE range is {[null null] [0, 0]}
   331  		startPoint1 := point{start: true}
   332  		endPoint1 := point{}
   333  		startPoint2 := point{start: true}
   334  		startPoint2.value.SetInt64(0)
   335  		endPoint2 := point{}
   336  		endPoint2.value.SetInt64(0)
   337  		return []point{startPoint1, endPoint1, startPoint2, endPoint2}
   338  	}
   339  	// TRUE range is {[-inf 0) (0 +inf]}
   340  	startPoint1 := point{value: types.MinNotNullCauset(), start: true}
   341  	endPoint1 := point{excl: true}
   342  	endPoint1.value.SetInt64(0)
   343  	startPoint2 := point{excl: true, start: true}
   344  	startPoint2.value.SetInt64(0)
   345  	endPoint2 := point{value: types.MaxValueCauset()}
   346  	return []point{startPoint1, endPoint1, startPoint2, endPoint2}
   347  }
   348  
   349  func (r *builder) buildFromIsFalse(expr *memex.ScalarFunction, isNot int) []point {
   350  	if isNot == 1 {
   351  		// NOT FALSE range is {[-inf, 0), (0, +inf], [null, null]}
   352  		startPoint1 := point{start: true}
   353  		endPoint1 := point{excl: true}
   354  		endPoint1.value.SetInt64(0)
   355  		startPoint2 := point{start: true, excl: true}
   356  		startPoint2.value.SetInt64(0)
   357  		endPoint2 := point{value: types.MaxValueCauset()}
   358  		return []point{startPoint1, endPoint1, startPoint2, endPoint2}
   359  	}
   360  	// FALSE range is {[0, 0]}
   361  	startPoint := point{start: true}
   362  	startPoint.value.SetInt64(0)
   363  	endPoint := point{}
   364  	endPoint.value.SetInt64(0)
   365  	return []point{startPoint, endPoint}
   366  }
   367  
   368  func (r *builder) buildFromIn(expr *memex.ScalarFunction) ([]point, bool) {
   369  	list := expr.GetArgs()[1:]
   370  	rangePoints := make([]point, 0, len(list)*2)
   371  	hasNull := false
   372  	defCausDefCauslate := expr.GetArgs()[0].GetType().DefCauslate
   373  	for _, e := range list {
   374  		v, ok := e.(*memex.Constant)
   375  		if !ok {
   376  			r.err = ErrUnsupportedType.GenWithStack("expr:%v is not constant", e)
   377  			return fullRange, hasNull
   378  		}
   379  		dt, err := v.Eval(chunk.Row{})
   380  		if err != nil {
   381  			r.err = ErrUnsupportedType.GenWithStack("expr:%v is not evaluated", e)
   382  			return fullRange, hasNull
   383  		}
   384  		if dt.IsNull() {
   385  			hasNull = true
   386  			continue
   387  		}
   388  		if dt.HoTT() == types.HoTTString {
   389  			dt.SetString(dt.GetString(), defCausDefCauslate)
   390  		}
   391  		var startValue, endValue types.Causet
   392  		dt.Copy(&startValue)
   393  		dt.Copy(&endValue)
   394  		startPoint := point{value: startValue, start: true}
   395  		endPoint := point{value: endValue}
   396  		rangePoints = append(rangePoints, startPoint, endPoint)
   397  	}
   398  	sorter := pointSorter{points: rangePoints, sc: r.sc}
   399  	sort.Sort(&sorter)
   400  	if sorter.err != nil {
   401  		r.err = sorter.err
   402  	}
   403  	// check and remove duplicates
   404  	curPos, frontPos := 0, 0
   405  	for frontPos < len(rangePoints) {
   406  		if rangePoints[curPos].start == rangePoints[frontPos].start {
   407  			frontPos++
   408  		} else {
   409  			curPos++
   410  			rangePoints[curPos] = rangePoints[frontPos]
   411  			frontPos++
   412  		}
   413  	}
   414  	if curPos > 0 {
   415  		curPos++
   416  	}
   417  	return rangePoints[:curPos], hasNull
   418  }
   419  
   420  func (r *builder) newBuildFromPatternLike(expr *memex.ScalarFunction) []point {
   421  	_, defCauslation := expr.CharsetAndDefCauslation(expr.GetCtx())
   422  	if !defCauslate.CompatibleDefCauslate(expr.GetArgs()[0].GetType().DefCauslate, defCauslation) {
   423  		return fullRange
   424  	}
   425  	FIDelt, err := expr.GetArgs()[1].(*memex.Constant).Eval(chunk.Row{})
   426  	tpOfPattern := expr.GetArgs()[0].GetType()
   427  	if err != nil {
   428  		r.err = errors.Trace(err)
   429  		return fullRange
   430  	}
   431  	pattern, err := FIDelt.ToString()
   432  	if err != nil {
   433  		r.err = errors.Trace(err)
   434  		return fullRange
   435  	}
   436  	if pattern == "" {
   437  		startPoint := point{value: types.NewStringCauset(""), start: true}
   438  		endPoint := point{value: types.NewStringCauset("")}
   439  		return []point{startPoint, endPoint}
   440  	}
   441  	lowValue := make([]byte, 0, len(pattern))
   442  	edt, err := expr.GetArgs()[2].(*memex.Constant).Eval(chunk.Row{})
   443  	if err != nil {
   444  		r.err = errors.Trace(err)
   445  		return fullRange
   446  	}
   447  	escape := byte(edt.GetInt64())
   448  	var exclude bool
   449  	isExactMatch := true
   450  	for i := 0; i < len(pattern); i++ {
   451  		if pattern[i] == escape {
   452  			i++
   453  			if i < len(pattern) {
   454  				lowValue = append(lowValue, pattern[i])
   455  			} else {
   456  				lowValue = append(lowValue, escape)
   457  			}
   458  			continue
   459  		}
   460  		if pattern[i] == '%' {
   461  			// Get the prefix.
   462  			isExactMatch = false
   463  			break
   464  		} else if pattern[i] == '_' {
   465  			// Get the prefix, but exclude the prefix.
   466  			// e.g., "abc_x", the start point exclude "abc",
   467  			// because the string length is more than 3.
   468  			exclude = true
   469  			isExactMatch = false
   470  			break
   471  		}
   472  		lowValue = append(lowValue, pattern[i])
   473  	}
   474  	if len(lowValue) == 0 {
   475  		return []point{{value: types.MinNotNullCauset(), start: true}, {value: types.MaxValueCauset()}}
   476  	}
   477  	if isExactMatch {
   478  		val := types.NewDefCauslationStringCauset(string(lowValue), tpOfPattern.DefCauslate, tpOfPattern.Flen)
   479  		return []point{{value: val, start: true}, {value: val}}
   480  	}
   481  	startPoint := point{start: true, excl: exclude}
   482  	startPoint.value.SetBytesAsString(lowValue, tpOfPattern.DefCauslate, uint32(tpOfPattern.Flen))
   483  	highValue := make([]byte, len(lowValue))
   484  	copy(highValue, lowValue)
   485  	endPoint := point{excl: true}
   486  	for i := len(highValue) - 1; i >= 0; i-- {
   487  		// Make the end point value more than the start point value,
   488  		// and the length of the end point value is the same as the length of the start point value.
   489  		// e.g., the start point value is "abc", so the end point value is "abd".
   490  		highValue[i]++
   491  		if highValue[i] != 0 {
   492  			endPoint.value.SetBytesAsString(highValue, tpOfPattern.DefCauslate, uint32(tpOfPattern.Flen))
   493  			break
   494  		}
   495  		// If highValue[i] is 255 and highValue[i]++ is 0, then the end point value is max value.
   496  		if i == 0 {
   497  			endPoint.value = types.MaxValueCauset()
   498  		}
   499  	}
   500  	return []point{startPoint, endPoint}
   501  }
   502  
   503  func (r *builder) buildFromNot(expr *memex.ScalarFunction) []point {
   504  	switch n := expr.FuncName.L; n {
   505  	case ast.IsTruthWithoutNull:
   506  		return r.buildFromIsTrue(expr, 1, false)
   507  	case ast.IsTruthWithNull:
   508  		return r.buildFromIsTrue(expr, 1, true)
   509  	case ast.IsFalsity:
   510  		return r.buildFromIsFalse(expr, 1)
   511  	case ast.In:
   512  		var (
   513  			isUnsignedIntDefCaus bool
   514  			nonNegativePos   int
   515  		)
   516  		rangePoints, hasNull := r.buildFromIn(expr)
   517  		if hasNull {
   518  			return nil
   519  		}
   520  		if x, ok := expr.GetArgs()[0].(*memex.DeferredCauset); ok {
   521  			isUnsignedIntDefCaus = allegrosql.HasUnsignedFlag(x.RetType.Flag) && allegrosql.IsIntegerType(x.RetType.Tp)
   522  		}
   523  		// negative ranges can be directly ignored for unsigned int defCausumns.
   524  		if isUnsignedIntDefCaus {
   525  			for nonNegativePos = 0; nonNegativePos < len(rangePoints); nonNegativePos += 2 {
   526  				if rangePoints[nonNegativePos].value.HoTT() == types.HoTTUint64 || rangePoints[nonNegativePos].value.GetInt64() >= 0 {
   527  					break
   528  				}
   529  			}
   530  			rangePoints = rangePoints[nonNegativePos:]
   531  		}
   532  		retRangePoints := make([]point, 0, 2+len(rangePoints))
   533  		previousValue := types.Causet{}
   534  		for i := 0; i < len(rangePoints); i += 2 {
   535  			retRangePoints = append(retRangePoints, point{value: previousValue, start: true, excl: true})
   536  			retRangePoints = append(retRangePoints, point{value: rangePoints[i].value, excl: true})
   537  			previousValue = rangePoints[i].value
   538  		}
   539  		// Append the interval (last element, max value].
   540  		retRangePoints = append(retRangePoints, point{value: previousValue, start: true, excl: true})
   541  		retRangePoints = append(retRangePoints, point{value: types.MaxValueCauset()})
   542  		return retRangePoints
   543  	case ast.Like:
   544  		// Pattern not like is not supported.
   545  		r.err = ErrUnsupportedType.GenWithStack("NOT LIKE is not supported.")
   546  		return fullRange
   547  	case ast.IsNull:
   548  		startPoint := point{value: types.MinNotNullCauset(), start: true}
   549  		endPoint := point{value: types.MaxValueCauset()}
   550  		return []point{startPoint, endPoint}
   551  	}
   552  	return nil
   553  }
   554  
   555  func (r *builder) buildFromScalarFunc(expr *memex.ScalarFunction) []point {
   556  	switch op := expr.FuncName.L; op {
   557  	case ast.GE, ast.GT, ast.LT, ast.LE, ast.EQ, ast.NE, ast.NullEQ:
   558  		return r.buildFormBinOp(expr)
   559  	case ast.LogicAnd:
   560  		return r.intersection(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1]))
   561  	case ast.LogicOr:
   562  		return r.union(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1]))
   563  	case ast.IsTruthWithoutNull:
   564  		return r.buildFromIsTrue(expr, 0, false)
   565  	case ast.IsTruthWithNull:
   566  		return r.buildFromIsTrue(expr, 0, true)
   567  	case ast.IsFalsity:
   568  		return r.buildFromIsFalse(expr, 0)
   569  	case ast.In:
   570  		retPoints, _ := r.buildFromIn(expr)
   571  		return retPoints
   572  	case ast.Like:
   573  		return r.newBuildFromPatternLike(expr)
   574  	case ast.IsNull:
   575  		startPoint := point{start: true}
   576  		endPoint := point{}
   577  		return []point{startPoint, endPoint}
   578  	case ast.UnaryNot:
   579  		return r.buildFromNot(expr.GetArgs()[0].(*memex.ScalarFunction))
   580  	}
   581  
   582  	return nil
   583  }
   584  
   585  func (r *builder) intersection(a, b []point) []point {
   586  	return r.merge(a, b, false)
   587  }
   588  
   589  func (r *builder) union(a, b []point) []point {
   590  	return r.merge(a, b, true)
   591  }
   592  
   593  func (r *builder) mergeSorted(a, b []point) []point {
   594  	ret := make([]point, 0, len(a)+len(b))
   595  	i, j := 0, 0
   596  	for i < len(a) && j < len(b) {
   597  		less, err := rangePointLess(r.sc, a[i], b[j])
   598  		if err != nil {
   599  			r.err = err
   600  			return nil
   601  		}
   602  		if less {
   603  			ret = append(ret, a[i])
   604  			i++
   605  		} else {
   606  			ret = append(ret, b[j])
   607  			j++
   608  		}
   609  	}
   610  	if i < len(a) {
   611  		ret = append(ret, a[i:]...)
   612  	} else if j < len(b) {
   613  		ret = append(ret, b[j:]...)
   614  	}
   615  	return ret
   616  }
   617  
   618  func (r *builder) merge(a, b []point, union bool) []point {
   619  	mergedPoints := r.mergeSorted(a, b)
   620  	if r.err != nil {
   621  		return nil
   622  	}
   623  
   624  	var (
   625  		inRangeCount         int
   626  		requiredInRangeCount int
   627  	)
   628  	if union {
   629  		requiredInRangeCount = 1
   630  	} else {
   631  		requiredInRangeCount = 2
   632  	}
   633  	curTail := 0
   634  	for _, val := range mergedPoints {
   635  		if val.start {
   636  			inRangeCount++
   637  			if inRangeCount == requiredInRangeCount {
   638  				// Just reached the required in range count, a new range started.
   639  				mergedPoints[curTail] = val
   640  				curTail++
   641  			}
   642  		} else {
   643  			if inRangeCount == requiredInRangeCount {
   644  				// Just about to leave the required in range count, the range is ended.
   645  				mergedPoints[curTail] = val
   646  				curTail++
   647  			}
   648  			inRangeCount--
   649  		}
   650  	}
   651  	return mergedPoints[:curTail]
   652  }