github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/setalgebra/intersection.go (about)

     1  // Copyright 2020 Dolthub, 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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package setalgebra
    16  
    17  import (
    18  	"github.com/dolthub/dolt/go/store/hash"
    19  	"github.com/dolthub/dolt/go/store/types"
    20  )
    21  
    22  // finiteSetIntersection returns the set of points that are in both fs1 and fs2
    23  func finiteSetIntersection(fs1, fs2 FiniteSet) (Set, error) {
    24  	hashToVal := make(map[hash.Hash]types.Value)
    25  	for h, v := range fs1.HashToVal {
    26  		if _, ok := fs2.HashToVal[h]; ok {
    27  			hashToVal[h] = v
    28  		}
    29  	}
    30  
    31  	if len(hashToVal) == 0 {
    32  		return EmptySet{}, nil
    33  	} else {
    34  		return FiniteSet{HashToVal: hashToVal}, nil
    35  	}
    36  }
    37  
    38  // finiteSetInterval will return the set of points that are in the interval, or an EmptySet instance
    39  func finiteSetIntervalIntersection(fs FiniteSet, in Interval) (Set, error) {
    40  	hashToVal := make(map[hash.Hash]types.Value)
    41  	for h, v := range fs.HashToVal {
    42  		inRange, err := in.Contains(v)
    43  
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  
    48  		if inRange {
    49  			hashToVal[h] = v
    50  		}
    51  	}
    52  
    53  	if len(hashToVal) == 0 {
    54  		return EmptySet{}, nil
    55  	} else {
    56  		return FiniteSet{HashToVal: hashToVal}, nil
    57  	}
    58  }
    59  
    60  // finiteSetInterval will return the set of points that are in the composite set, or an EmptySet instance
    61  func finiteSetCompositeSetIntersection(fs FiniteSet, composite CompositeSet) (Set, error) {
    62  	hashToVal := make(map[hash.Hash]types.Value)
    63  	for h, v := range fs.HashToVal {
    64  		if _, ok := composite.Set.HashToVal[h]; ok {
    65  			hashToVal[h] = v
    66  		}
    67  	}
    68  
    69  	for _, r := range composite.Intervals {
    70  		for h, v := range fs.HashToVal {
    71  			inRange, err := r.Contains(v)
    72  
    73  			if err != nil {
    74  				return nil, err
    75  			}
    76  
    77  			if inRange {
    78  				hashToVal[h] = v
    79  			}
    80  		}
    81  	}
    82  
    83  	if len(hashToVal) == 0 {
    84  		return EmptySet{}, nil
    85  	} else {
    86  		return FiniteSet{HashToVal: hashToVal}, nil
    87  	}
    88  }
    89  
    90  // intervalIntersection will return the intersection of two intervals.  This will either be the interval where they
    91  // overlap, or an EmptySet instance.
    92  func intervalIntersection(in1, in2 Interval) (Set, error) {
    93  	intComparison, err := compareIntervals(in1, in2)
    94  
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	var resIntervToReduce Interval
   100  	if intComparison == noOverlapLess || intComparison == noOverlapGreater {
   101  		// No overlap
   102  		return EmptySet{}, nil
   103  	} else if intComparison[start1start2] <= 0 {
   104  		if intComparison[end1end2] >= 0 {
   105  			// in2 fully contained in in1
   106  			return in2, nil
   107  		} else {
   108  			// in1 starts first and in2 ends last.  take the inside points for the intersection
   109  			resIntervToReduce = Interval{in1.nbf, in2.Start, in1.End}
   110  		}
   111  	} else {
   112  		if intComparison[end1end2] <= 0 {
   113  			// in1 fully contained in in2
   114  			return in1, nil
   115  		} else {
   116  			// in2 starts first and in1 ends last.  take the inside points for the intersection
   117  			resIntervToReduce = Interval{in1.nbf, in1.Start, in2.End}
   118  		}
   119  	}
   120  
   121  	return simplifyInterval(resIntervToReduce)
   122  }
   123  
   124  // intervalCompositeSetIntersection will intersect the interval with all sets in the composite and return the resulting
   125  // set of points and intervals that are in the interval and the CompositeSet
   126  func intervalCompositeSetIntersection(in Interval, cs CompositeSet) (Set, error) {
   127  	hashToVal := make(map[hash.Hash]types.Value)
   128  
   129  	// check the existing finite set and eliminate values not in the new interval
   130  	for h, v := range cs.Set.HashToVal {
   131  		contained, err := in.Contains(v)
   132  
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  
   137  		if contained {
   138  			hashToVal[h] = v
   139  		}
   140  	}
   141  
   142  	// intersect the new interval with all the existing intervals
   143  	intervals, err := intersectIntervalWithMultipleIntervals(in, cs.Intervals, hashToVal)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	if len(hashToVal) == 0 && len(intervals) == 1 {
   149  		// could possibly be universal set
   150  		return simplifyInterval(intervals[0])
   151  	} else if len(hashToVal) > 0 && len(intervals) == 0 {
   152  		return FiniteSet{hashToVal}, nil
   153  	} else if len(hashToVal) > 0 && len(intervals) > 0 {
   154  		return CompositeSet{FiniteSet{hashToVal}, intervals}, nil
   155  	} else {
   156  		return EmptySet{}, nil
   157  	}
   158  }
   159  
   160  // intersectIntervalWithMultipleIntervals returns a slice of Interval objects containing all intersections between the
   161  // input interval and the slice of multiple intervals.
   162  func intersectIntervalWithMultipleIntervals(in Interval, multipleIntervals []Interval, hashToVal map[hash.Hash]types.Value) ([]Interval, error) {
   163  	intervals := make([]Interval, 0, len(multipleIntervals))
   164  	for _, curr := range multipleIntervals {
   165  		intersection, err := intervalIntersection(in, curr)
   166  
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  
   171  		switch typedSet := intersection.(type) {
   172  		case EmptySet:
   173  			continue
   174  		case FiniteSet:
   175  			for h, v := range typedSet.HashToVal {
   176  				hashToVal[h] = v
   177  			}
   178  		case Interval:
   179  			intervals = append(intervals, typedSet)
   180  		default:
   181  			panic("unexpected set type")
   182  		}
   183  	}
   184  	return intervals, nil
   185  }
   186  
   187  // compositeIntersection finds the intersection of two composite sets
   188  func compositeIntersection(cs1, cs2 CompositeSet) (Set, error) {
   189  	// intersect cs1.Set with cs2 and cs2.Set with cs1 to get the discreet values in the resulting intersection.
   190  	temp1, err := finiteSetCompositeSetIntersection(cs1.Set, cs2)
   191  
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	temp2, err := finiteSetCompositeSetIntersection(cs2.Set, cs1)
   197  
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	resSet, err := temp1.Union(temp2)
   203  
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	var hashToVal map[hash.Hash]types.Value
   209  	switch typedResSet := resSet.(type) {
   210  	case EmptySet:
   211  		hashToVal = make(map[hash.Hash]types.Value)
   212  	case FiniteSet:
   213  		hashToVal = typedResSet.HashToVal
   214  	default:
   215  		panic("unexpected set type")
   216  	}
   217  
   218  	// intersect the intervals
   219  	var intervals []Interval
   220  	for _, curr := range cs1.Intervals {
   221  		newIntervals, err := intersectIntervalWithMultipleIntervals(curr, cs2.Intervals, hashToVal)
   222  
   223  		if err != nil {
   224  			return nil, err
   225  		}
   226  
   227  		intervals = append(intervals, newIntervals...)
   228  	}
   229  
   230  	// combine the intervals and the discreet values into a result
   231  	if len(hashToVal) == 0 && len(intervals) == 1 {
   232  		return simplifyInterval(intervals[0])
   233  	} else if len(hashToVal) > 0 && len(intervals) == 0 {
   234  		return FiniteSet{hashToVal}, nil
   235  	} else if len(hashToVal) > 0 && len(intervals) > 0 {
   236  		return CompositeSet{FiniteSet{hashToVal}, intervals}, nil
   237  	} else {
   238  		return EmptySet{}, nil
   239  	}
   240  }