1 #Region "Microsoft.VisualBasic::723b2e75be07e7f61a2b912a0227cf55, Microsoft.VisualBasic.Core\Extensions\Collection\Vector.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     Module VectorExtensions
35     
36     '     Function: (+2 Overloads) After, Append, Coalesce, Delete, Fill
37     '               GetRange, IndexOf, Last, LoadAsNumericVector, MappingData
38     '               Midv, RepeatCalls, Replicate, (+2 Overloads) Sort, Split
39     '               VectorShadows
40     
41     '     Sub: (+4 Overloads) Add, InsertAt, (+2 OverloadsMemset
42     '     Enum DelimiterLocation
43     
44     '         NextFirst, NotIncludes, PreviousLast
45     
46     
47     
48     '  
49     
50     
51     
52     ' /********************************************************************************/
53
54 #End Region
55
56 Imports System.Runtime.CompilerServices
57 Imports System.Threading
58 Imports Microsoft.VisualBasic.ComponentModel
59 Imports Microsoft.VisualBasic.ComponentModel.Collection
60 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
61 Imports Microsoft.VisualBasic.Language
62 Imports Microsoft.VisualBasic.Language.Default
63 Imports Microsoft.VisualBasic.Language.Vectorization
64 Imports Microsoft.VisualBasic.Linq
65 Imports Microsoft.VisualBasic.Linq.Extensions
66 Imports Microsoft.VisualBasic.Linq.IteratorExtensions
67
68 ''' <summary>
69 ''' Extension methods for the .NET object sequence
70 ''' </summary>
71 Public Module VectorExtensions
72
73     <Extension>
74     Public Iterator Function Replicate(Of T)(template As T, n%) As IEnumerable(Of T)
75         For i As Integer = 0 To n - 1
76             Yield template
77         Next
78     End Function
79
80     <MethodImpl(MethodImplOptions.AggressiveInlining)>
81     <Extension>
82     Public Function RepeatCalls(Of T)(factory As Func(Of T), n%, Optional sleep% = 0) As T()
83         Return n _
84             .SeqIterator _
85             .Select(Function(i)
86                         If sleep > 0 Then
87                             Call Thread.Sleep(sleep)
88                         End If
89
90                         Return factory()
91                     End Function) _
92             .ToArray
93     End Function
94
95     ''' <summary>
96     ''' Dynamics add a element into the target array.
97     ''' </summary>
98     ''' <typeparam name="T"></typeparam>
99     ''' <param name="vector"></param>
100     ''' <param name="value"></param>
101     <Extension> Public Sub Add(Of T)(ByRef vector As T(), value As T)
102         If vector.IsNullOrEmpty Then
103             vector = {value}
104         Else
105             Dim appendBuffer As T() = New T(vector.Length) {}
106             Call Array.ConstrainedCopy(vector, Scan0, appendBuffer, Scan0, vector.Length)
107             appendBuffer(vector.Length) = value
108             vector = appendBuffer
109         End If
110     End Sub
111
112     ''' <summary>
113     ''' Append value collection to the end of the target <paramref name="vector"/>
114     ''' </summary>
115     ''' <typeparam name="T"></typeparam>
116     ''' <param name="vector"></param>
117     ''' <param name="values"></param>
118     <Extension> Public Sub Add(Of T)(ByRef vector As T(), values As IEnumerable(Of T))
119         Dim data = values.SafeQuery.ToArray
120         Dim appendBuffer As T() = New T(vector.Length + data.Length - 1) {}
121
122         With vector
123             Call Array.ConstrainedCopy(
124                 vector, Scan0, appendBuffer, Scan0, .Length)
125
126             For Each x As SeqValue(Of T) In data.SeqIterator
127                 appendBuffer(.Length + x.i) = x.value
128             Next
129         End With
130
131         vector = appendBuffer
132     End Sub
133
134     ''' <summary>
135     ''' Add given elements into an array object.(会自动跳过空集合,这个方法是安全的)
136     ''' </summary>
137     ''' <typeparam name="T"></typeparam>
138     ''' <param name="vector"></param>
139     ''' <param name="value"></param>
140     <Extension> Public Sub Add(Of T)(ByRef vector As T(), ParamArray value As T())
141         If value.IsNullOrEmpty Then
142             Return
143         End If
144         If vector Is Nothing Then
145             vector = New T() {}
146         End If
147
148         Dim chunkBuffer As T() = New T(vector.Length + value.Length - 1) {}
149         Call Array.ConstrainedCopy(vector, Scan0, chunkBuffer, Scan0, vector.Length)
150         Call Array.ConstrainedCopy(value, Scan0, chunkBuffer, vector.Length, value.Length)
151         vector = chunkBuffer
152     End Sub
153
154     ''' <summary>
155     ''' Add given elements into an array object and then returns the target array object <paramref name="buffer"/>.
156     ''' </summary>
157     ''' <typeparam name="T"></typeparam>
158     ''' <param name="buffer"></param>
159     ''' <param name="value"></param>
160     ''' <returns></returns>
161     <Extension> Public Function Append(Of T)(buffer As T(), value As IEnumerable(Of T)) As T()
162         If buffer Is Nothing Then
163             Return value.ToArray
164         End If
165
166         Call buffer.Add(value.ToArray)
167         Return buffer
168     End Function
169
170     ''' <summary>
171     ''' Add given elements into an array object.
172     ''' </summary>
173     ''' <typeparam name="T"></typeparam>
174     ''' <param name="array"></param>
175     ''' <param name="value"></param>
176     ''' 
177     <MethodImpl(MethodImplOptions.AggressiveInlining)>
178     <Extension> Public Sub Add(Of T)(ByRef array As T(), value As List(Of T))
179         Call Add(Of T)(array, value.ToArray)
180     End Sub
181
182     <Extension>
183     Public Function Fill(Of T)(vector As T(), item As T, count%) As T()
184         If count <= 0 Then
185             ' should returns a copy
186             Return vector.ToArray
187         Else
188             Dim newVector As T() = New T(vector.Length + count - 1) {}
189
190             Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, vector.Length)
191
192             For i As Integer = vector.Length To newVector.Length - 1
193                 newVector(i) = item
194             Next
195
196             Return newVector
197         End If
198     End Function
199
200     ''' <summary>
201     ''' Removes array element at index
202     ''' </summary>
203     ''' <typeparam name="T"></typeparam>
204     ''' <param name="vector"></param>
205     ''' <param name="index%"></param>
206     ''' <returns></returns>
207     <Extension>
208     Public Function Delete(Of T)(vector As T(), index%) As T()
209         Dim newVector As T() = New T(vector.Length - 2) {}
210
211         Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, index)
212         Call Array.ConstrainedCopy(vector, index + 1, newVector, index, newVector.Length - index)
213
214         Return newVector
215     End Function
216
217     <Extension>
218     Public Sub InsertAt(Of T)(ByRef vector As T(), value As T, index%)
219         Dim newVector As T() = New T(vector.Length) {}
220
221         Call Array.ConstrainedCopy(vector, Scan0, newVector, Scan0, index)
222         Call Array.ConstrainedCopy(vector, index, newVector, index + 1, vector.Length - index)
223
224         vector = newVector
225         vector(index) = value
226     End Sub
227
228     ''' <summary>
229     ''' Create a vector shadow of your data collection.
230     ''' </summary>
231     ''' <typeparam name="T"></typeparam>
232     ''' <param name="source"></param>
233     ''' <returns>返回<see cref="Object"/>类型是为了简化语法</returns>
234     ''' 
235     <MethodImpl(MethodImplOptions.AggressiveInlining)>
236     <Extension>
237     Public Function VectorShadows(Of T)(source As IEnumerable(Of T)) As Object
238         Return New VectorShadows(Of T)(source)
239     End Function
240
241     ''' <summary>
242     ''' 聚合,将nullable类型结构体转换为原来的值类型
243     ''' </summary>
244     ''' <typeparam name="T"></typeparam>
245     ''' <param name="source"></param>
246     ''' <returns></returns>
247     <Extension>
248     Public Function Coalesce(Of T As Structure)(source As IEnumerable(Of T?)) As IEnumerable(Of T)
249         Debug.Assert(source IsNot Nothing)
250         Return source.Where(Function(x) x.HasValue).[Select](Function(x) CType(x, T))
251     End Function
252
253     <Extension>
254     Public Function Sort(Of T)(source As IEnumerable(Of NamedValue(Of T)), by As Index(Of String), Optional throwNoOrder As Boolean = FalseAs NamedValue(Of T)()
255         Dim out As NamedValue(Of T)() = New NamedValue(Of T)(by.Count - 1) {}
256
257         For Each x In source
258             Dim i% = by(x.Name)
259
260             If i = -1 Then
261                 If throwNoOrder Then
262                     Throw New InvalidExpressionException(x.Name & " was not found in the index value.")
263                 Else
264                     Continue For
265                 End If
266             Else
267                 out(i) = x
268             End If
269         Next
270
271         Return out
272     End Function
273
274     <Extension> Public Function GetRange(Of T)(vector As T(), index%, count%) As T()
275         Dim fill As T() = New T(count - 1) {}
276         Dim ends% = index + count - 1
277
278         For i As Integer = index To ends
279             fill(i - index) = vector(i)
280         Next
281
282         Return fill
283     End Function
284
285     ''' <summary>
286     ''' 对目标序列进行排序生成新的序列,这个拓展函数尝试将升序排序和降序排序这两种操作的接口统一起来
287     ''' </summary>
288     ''' <typeparam name="T"></typeparam>
289     ''' <param name="source"></param>
290     ''' <param name="desc"></param>
291     ''' <returns></returns>
292     <Extension>
293     Public Function Sort(Of T)(source As IEnumerable(Of T), Optional by As Func(Of T, IComparable) = NothingOptional desc As Boolean = FalseAs IEnumerable(Of T)
294         If by Is Nothing Then
295             If Not desc Then
296                 Return From x In source Select x Order By x Ascending
297             Else
298                 Return From x In source Select x Order By x Descending
299             End If
300         Else
301             If Not desc Then
302                 Return source.OrderBy(by)
303             Else
304                 Return source.OrderByDescending(by)
305             End If
306         End If
307     End Function
308
309     Const DimNotAgree$ = "Both a and b their length should be equals or one of them should be length=1!"
310
311     ''' <summary>
312     ''' 用来生成map数据的,
313     ''' + 当两个向量长度相同,会不进行任何处理,即两个向量之间,元素都可以一一对应,
314     ''' + 但是当某一个向量的长度为1的时候,就会将该向量补齐,因为此时会是一对多的关系
315     ''' </summary>
316     ''' <typeparam name="T"></typeparam>
317     ''' <param name="a"></param>
318     ''' <param name="b"></param>
319     ''' <returns></returns>
320     <Extension>
321     Public Iterator Function MappingData(Of T)(a As T(), b As T()) As IEnumerable(Of Map(Of T, T))
322         If a.Length = 1 AndAlso b.Length > 1 Then
323             ' 补齐a
324             a = a(0).Repeats(b.Length)
325         ElseIf a.Length > 1 AndAlso b.Length = 1 Then
326             ' 补齐b
327             b = b(0).Repeats(a.Length)
328         ElseIf a.Length <> b.Length Then
329             ' 无法计算
330             Throw New ArgumentException(DimNotAgree)
331         End If
332
333         For i As Integer = 0 To a.Length - 1
334             Yield New Map(Of T, T) With {
335                 .Key = a(i),
336                 .Maps = b(i)
337             }
338         Next
339     End Function
340
341     ''' <summary>
342     ''' 在一个一维数组中搜索指定对象,并返回其首个匹配项的索引。
343     ''' </summary>
344     ''' <typeparam name="T">数组元素的类型。</typeparam>
345     ''' <param name="array">要搜索的从零开始的一维数组。</param>
346     ''' <param name="o">要在 array 中查找的对象。</param>
347     ''' <returns>如果在整个 array 中找到 value 的第一个匹配项,则为该项的从零开始的索引;否则为 -1。</returns>
348     ''' 
349     <MethodImpl(MethodImplOptions.AggressiveInlining)>
350     <Extension>
351     Public Function IndexOf(Of T)(array As T(), o As T) As Integer
352         Return System.Array.IndexOf(array, value:=o)
353     End Function
354
355     ''' <summary>
356     ''' 从后往前访问集合之中的元素,请注意请不要使用Linq查询表达式,尽量使用``list``或者``array``
357     ''' </summary>
358     ''' <typeparam name="T"></typeparam>
359     ''' <param name="source">请不要使用Linq查询表达式,尽量使用``list``或者``array``</param>
360     ''' <param name="index"></param>
361     ''' <returns></returns>
362     ''' 
363     <MethodImpl(MethodImplOptions.AggressiveInlining)>
364     <Extension>
365     Public Function Last(Of T)(source As IEnumerable(Of T), index As IntegerAs T
366         Return source(source.Count - index)
367     End Function
368
369     ''' <summary>
370     ''' 取出在x元素之后的所有元素
371     ''' </summary>
372     ''' <typeparam name="T"></typeparam>
373     ''' <param name="source"></param>
374     ''' <param name="x"></param>
375     ''' <returns></returns>
376     ''' 
377     <MethodImpl(MethodImplOptions.AggressiveInlining)>
378     <Extension>
379     Public Function After(Of T)(source As IEnumerable(Of T), x As T) As IEnumerable(Of T)
380         Return source.After(Function(o) x.Equals(o))
381     End Function
382
383     ''' <summary>
384     ''' Returns all of the elements which is after the element that detected by a specific 
385     ''' evaluation function <paramref name="predicate"/>.
386     ''' (取出在判定条件成立的元素之后的所有元素)
387     ''' </summary>
388     ''' <typeparam name="T"></typeparam>
389     ''' <param name="source"></param>
390     ''' <param name="predicate"></param>
391     ''' <returns></returns>
392     <Extension>
393     Public Iterator Function After(Of T)(source As IEnumerable(Of T), predicate As Predicate(Of T)) As IEnumerable(Of T)
394         Dim isAfter As Boolean = False
395
396         For Each x As T In source
397             If isAfter Then
398                 Yield x
399             Else
400                 If predicate(x) Then
401                     isAfter = True
402                 End If
403             End If
404         Next
405     End Function
406
407     ''' <summary>
408     ''' Replace target array data by using specific object value.(替换目标向量为指定的对象的填充数据)
409     ''' </summary>
410     ''' <typeparam name="T"></typeparam>
411     ''' <param name="array"></param>
412     ''' <param name="o"></param>
413     ''' <param name="len"></param>
414     <Extension>
415     Public Sub Memset(Of T)(ByRef array As T(), o As T, len As Integer)
416         If array Is Nothing OrElse array.Length < len Then
417             array = New T(len - 1) {}
418         End If
419
420         For i As Integer = 0 To len - 1
421             array(i) = o
422         Next
423     End Sub
424
425     ''' <summary>
426     ''' 替换<paramref name="s"/>字符串变量数据为新的字符填充数据
427     ''' </summary>
428     ''' <param name="s"></param>
429     ''' <param name="c"></param>
430     ''' <param name="len"></param>
431     ''' 
432     <MethodImpl(MethodImplOptions.AggressiveInlining)>
433     <Extension>
434     Public Sub Memset(ByRef s As String, c As Char, len As Integer)
435         s = New String(c, len)
436     End Sub
437
438     ''' <summary>
439     ''' <see cref="Strings.Mid"/> function like operation on any type collection data.
440     ''' </summary>
441     ''' <typeparam name="T"></typeparam>
442     ''' <param name="source"></param>
443     ''' <param name="start">0 base</param>
444     ''' <param name="length"></param>
445     ''' <returns></returns>
446     <Extension> Public Function Midv(Of T)(source As IEnumerable(Of T), start%, length%) As T()
447         If source Is Nothing Then
448             Return New T() {}
449         ElseIf source.Count < length Then
450             Return source.ToArray
451         End If
452
453         Dim array As T() = source.ToArray
454         Dim ends As Integer = start + length
455
456         If ends > array.Length Then
457             length -= array.Length - ends
458         End If
459
460         Dim buf As T() = New T(length - 1) {}
461         Call System.Array.ConstrainedCopy(array, start, buf, Scan0, buf.Length)
462         Return buf
463     End Function
464
465     ''' <summary>
466     ''' Load the text file as a numeric vector. Each line in the text file 
467     ''' should be a <see cref="Double"/> type numeric value.
468     ''' </summary>
469     ''' <param name="path"></param>
470     ''' <returns></returns>
471     <Extension> Public Function LoadAsNumericVector(path As StringAs Double()
472         Dim array As String() = IO.File.ReadAllLines(path)
473         Dim n As Double() = array.Select(AddressOf Val).ToArray
474         Return n
475     End Function
476
477     ''' <summary>
478     ''' Split the object array using a specific evaluation function.
479     ''' (Please note that, all of the object in the <paramref name="source"/> array 
480     ''' that match the <paramref name="delimiter"/> evaluation, will not includes 
481     ''' in the returned tokens.)
482     ''' </summary>
483     ''' <typeparam name="T"></typeparam>
484     ''' <param name="source"></param>
485     ''' <param name="delimiter">和字符串的Split函数一样,这里作为delimiter的元素都不会出现在结果之中</param>
486     ''' <param name="deliPosition">是否还应该在分区的结果之中包含有分隔符对象?默认不包含</param>
487     ''' <returns></returns>
488     <Extension> Public Iterator Function Split(Of T)(source As IEnumerable(Of T), delimiter As Assert(Of T), Optional deliPosition As DelimiterLocation = DelimiterLocation.NotIncludes) As IEnumerable(Of T())
489         Dim tmp As New List(Of T)
490
491         For Each x As T In source.SafeQuery
492             ' 当前的x元素是分隔符对象
493             If delimiter(x) = True Then
494
495                 ' 是否将这个分隔符也包含在分组内
496                 ' 如果是,则包含在下一个分组内
497                 If deliPosition <> DelimiterLocation.NotIncludes Then
498                     If deliPosition = DelimiterLocation.NextFirst Then
499                         Yield tmp.ToArray
500
501                         Call tmp.Clear()
502                         Call tmp.Add(x)
503                     Else
504                         ' 包含在上一个分块的末尾
505                         Call tmp.Add(x)
506                         Yield tmp.ToArray
507                         tmp *= 0
508                     End If
509                 Else
510                     Yield tmp.ToArray
511                     tmp *= 0
512                 End If
513             Else
514                 Call tmp.Add(x)
515             End If
516         Next
517
518         If Not tmp = 0 Then
519             Yield tmp.ToArray
520         End If
521     End Function
522
523     ''' <summary>
524     ''' 分隔符对象在分块之中的位置
525     ''' </summary>
526     Public Enum DelimiterLocation As Integer
527         ''' <summary>
528         ''' 上一个分块的最末尾
529         ''' </summary>
530         PreviousLast
531         ''' <summary>
532         ''' 不会再任何分块之中包含有分隔符
533         ''' </summary>
534         NotIncludes
535         ''' <summary>
536         ''' 包含在下一个分块之中的最开始的位置
537         ''' </summary>
538         NextFirst
539     End Enum
540 End Module