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 }