cuelang.org/go@v0.10.1/pkg/list/sort.go (about)

     1  // Copyright 2018 The CUE Authors
     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  // Copyright 2018 The Go Authors. All rights reserved.
    16  // Use of this source code is governed by a BSD-style
    17  // license that can be found in the LICENSE file.
    18  
    19  package list
    20  
    21  import (
    22  	"sort"
    23  
    24  	"cuelang.org/go/cue"
    25  	"cuelang.org/go/internal"
    26  	"cuelang.org/go/internal/core/adt"
    27  	"cuelang.org/go/internal/types"
    28  )
    29  
    30  // valueSorter defines a sort.Interface; implemented in cue/builtinutil.go.
    31  type valueSorter struct {
    32  	ctx *adt.OpContext
    33  	a   []cue.Value
    34  	err error
    35  
    36  	cmp  *adt.Vertex
    37  	less *adt.Vertex
    38  	x    *adt.Vertex
    39  	y    *adt.Vertex
    40  }
    41  
    42  func (s *valueSorter) ret() ([]cue.Value, error) {
    43  	if s.err != nil {
    44  		return nil, s.err
    45  	}
    46  	// The input slice is already a copy and that we can modify it safely.
    47  	return s.a, nil
    48  }
    49  
    50  func (s *valueSorter) Len() int      { return len(s.a) }
    51  func (s *valueSorter) Swap(i, j int) { s.a[i], s.a[j] = s.a[j], s.a[i] }
    52  func (s *valueSorter) Less(i, j int) bool {
    53  	if s.err != nil {
    54  		return false
    55  	}
    56  
    57  	if s.ctx.Version == internal.DevVersion {
    58  		return s.lessNew(i, j)
    59  	}
    60  
    61  	var x, y types.Value
    62  	s.a[i].Core(&x)
    63  	s.a[j].Core(&y)
    64  
    65  	// Save the state of all relevant arcs and restore later for the
    66  	// next comparison.
    67  	saveCmp := *s.cmp
    68  	saveLess := *s.less
    69  	saveX := *s.x
    70  	saveY := *s.y
    71  
    72  	for _, c := range x.V.Conjuncts {
    73  		s.x.AddConjunct(c)
    74  	}
    75  	for _, c := range y.V.Conjuncts {
    76  		s.y.AddConjunct(c)
    77  	}
    78  
    79  	// TODO(perf): if we can determine that the comparator values for
    80  	// x and y are idempotent (no arcs and a basevalue being top or
    81  	// a struct or list marker), then we do not need to reevaluate the input.
    82  	// In that case, we can use the below code instead of the above two loops
    83  	// setting the conjuncts. This may improve performance significantly.
    84  	//
    85  	// s.x.BaseValue = x.V.BaseValue
    86  	// s.x.Arcs = x.V.Arcs
    87  	// s.y.BaseValue = y.V.BaseValue
    88  	// s.y.Arcs = y.V.Arcs
    89  
    90  	s.less.Finalize(s.ctx)
    91  	isLess := s.ctx.BoolValue(s.less)
    92  	if b := s.less.Err(s.ctx); b != nil && s.err == nil {
    93  		s.err = b.Err
    94  		return true
    95  	}
    96  
    97  	*s.less = saveLess
    98  	*s.cmp = saveCmp
    99  	*s.x = saveX
   100  	*s.y = saveY
   101  
   102  	return isLess
   103  }
   104  
   105  func (s *valueSorter) lessNew(i, j int) bool {
   106  	ctx := s.ctx
   107  
   108  	n := &adt.Vertex{
   109  		Label:     s.cmp.Label,
   110  		Parent:    s.cmp.Parent,
   111  		Conjuncts: s.cmp.Conjuncts,
   112  	}
   113  
   114  	n.Init(ctx)
   115  
   116  	less := getArc(ctx, n, "less")
   117  	xa := getArc(ctx, n, "x")
   118  	ya := getArc(ctx, n, "y")
   119  
   120  	var x, y types.Value
   121  	s.a[i].Core(&x)
   122  	s.a[j].Core(&y)
   123  
   124  	for _, c := range x.V.Conjuncts {
   125  		xa.AddConjunct(c)
   126  	}
   127  	for _, c := range y.V.Conjuncts {
   128  		ya.AddConjunct(c)
   129  	}
   130  
   131  	// TODO(perf): if we can determine that the comparator values for
   132  	// x and y are idempotent (no arcs and a basevalue being top or
   133  	// a struct or list marker), then we do not need to reevaluate the input.
   134  	// In that case, we can use the below code instead of the above two loops
   135  	// setting the conjuncts. This may improve performance significantly.
   136  	//
   137  	// s.x.BaseValue = x.V.BaseValue
   138  	// s.x.Arcs = x.V.Arcs
   139  	// s.y.BaseValue = y.V.BaseValue
   140  	// s.y.Arcs = y.V.Arcs
   141  
   142  	less.Finalize(s.ctx)
   143  
   144  	isLess := s.ctx.BoolValue(less)
   145  	if b := less.Err(s.ctx); b != nil && s.err == nil {
   146  		s.err = b.Err
   147  		return true
   148  	}
   149  
   150  	return isLess
   151  }
   152  
   153  var less = cue.ParsePath("less")
   154  
   155  func makeValueSorter(list []cue.Value, cmp cue.Value) (s valueSorter) {
   156  	if v := cmp.LookupPath(less); !v.Exists() {
   157  		return valueSorter{err: v.Err()}
   158  	}
   159  
   160  	var v types.Value
   161  	cmp.Core(&v)
   162  	ctx := adt.NewContext(v.R, v.V)
   163  
   164  	n := &adt.Vertex{
   165  		Label:     v.V.Label,
   166  		Parent:    v.V.Parent,
   167  		Conjuncts: v.V.Conjuncts,
   168  	}
   169  	n.CompleteArcs(ctx)
   170  
   171  	s = valueSorter{
   172  		a:    list,
   173  		ctx:  ctx,
   174  		cmp:  n,
   175  		less: getArc(ctx, n, "less"),
   176  		x:    getArc(ctx, n, "x"),
   177  		y:    getArc(ctx, n, "y"),
   178  	}
   179  
   180  	// TODO(perf): see comment in the Less method. If we can determine
   181  	// the conjuncts for x and y are idempotent, we can pre finalize here and
   182  	// ignore the values in the Less method.
   183  	// s.x.UpdateStatus(adt.Finalized)
   184  	// s.y.UpdateStatus(adt.Finalized)
   185  
   186  	return s
   187  }
   188  
   189  // Sort sorts data while keeping the original order of equal elements.
   190  // It does O(n*log(n)) comparisons.
   191  //
   192  // cmp is a struct of the form {T: _, x: T, y: T, less: bool}, where
   193  // less should reflect x < y.
   194  //
   195  // Example:
   196  //
   197  //	Sort([2, 3, 1], list.Ascending)
   198  //
   199  //	Sort([{a: 2}, {a: 3}, {a: 1}], {x: {}, y: {}, less: x.a < y.a})
   200  func Sort(list []cue.Value, cmp cue.Value) (sorted []cue.Value, err error) {
   201  	s := makeValueSorter(list, cmp)
   202  
   203  	// The input slice is already a copy and that we can modify it safely.
   204  	sort.Stable(&s)
   205  	return s.ret()
   206  }
   207  
   208  func getArc(ctx *adt.OpContext, v *adt.Vertex, s string) *adt.Vertex {
   209  	f := ctx.StringLabel(s)
   210  	arc, _ := v.GetArc(ctx, f, 0)
   211  	return arc
   212  }
   213  
   214  // Deprecated: use [Sort], which is always stable
   215  func SortStable(list []cue.Value, cmp cue.Value) (sorted []cue.Value, err error) {
   216  	s := makeValueSorter(list, cmp)
   217  	sort.Stable(&s)
   218  	return s.ret()
   219  }
   220  
   221  // SortStrings sorts a list of strings in increasing order.
   222  func SortStrings(a []string) []string {
   223  	sort.Strings(a)
   224  	return a
   225  }
   226  
   227  // IsSorted tests whether a list is sorted.
   228  //
   229  // See Sort for an example comparator.
   230  func IsSorted(list []cue.Value, cmp cue.Value) bool {
   231  	s := makeValueSorter(list, cmp)
   232  	return sort.IsSorted(&s)
   233  }
   234  
   235  // IsSortedStrings tests whether a list is a sorted lists of strings.
   236  func IsSortedStrings(a []string) bool {
   237  	return sort.StringsAreSorted(a)
   238  }