1 #Region "Microsoft.VisualBasic::fd9fd9a6014fb2dcdf7f43388f6142f4, Microsoft.VisualBasic.Core\ComponentModel\Ranges\Extensions.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 Extensions
35     
36     '         Function: (+2 OverloadsGetScaler, (+2 Overloads) RangeTransform, SymmetricalRange, Union
37     
38     '         Sub: Parser
39     
40     
41     ' /********************************************************************************/
42
43 #End Region
44
45 Imports System.Runtime.CompilerServices
46 Imports System.Text.RegularExpressions
47 Imports Microsoft.VisualBasic.ComponentModel.Ranges.Model
48 Imports Microsoft.VisualBasic.Language
49 Imports Microsoft.VisualBasic.Linq
50 Imports Microsoft.VisualBasic.Scripting.Runtime
51 Imports r = System.Text.RegularExpressions.Regex
52 Imports sys = System.Math
53
54 Namespace ComponentModel.Ranges
55
56     Public Module Extensions
57
58         ''' <summary>
59         ''' 对称的的范围,假若X为正数,那么其为max,而-x为min。假若x为负数,那么-x为max
60         ''' </summary>
61         ''' <param name="x#"></param>
62         ''' <returns></returns>
63         <Extension>
64         Public Function SymmetricalRange(x#) As DoubleRange
65             If x > 0 Then
66                 Return {-x, x}
67             Else
68                 Return {x, -x}
69             End If
70         End Function
71
72         Const RegexpFloatRange$ = RegexpFloat & "\s*,\s*" & RegexpFloat
73
74         ''' <summary>
75         ''' + ``min -> max``
76         ''' + ``min—max``
77         ''' + ``min~max``
78         ''' + ``[min,max]``
79         ''' + ``{min,max}``
80         ''' + ``(min,max)``
81         ''' + ``min,max``
82         ''' </summary>
83         ''' <param name="exp$"></param>
84         ''' <param name="min#"></param>
85         ''' <param name="max#"></param>
86         <Extension> Public Sub Parser(exp$, ByRef min#, ByRef max#)
87             Dim t$()
88             Dim raw$ = exp
89
90             If InStr(exp, "->") > 0 Then
91                 t = Strings.Split(exp, "->")
92             ElseIf InStr(exp, "—") > 0 Then
93                 ' 使用的是中文的分隔符
94                 t = Strings.Split(exp, "—")
95             ElseIf InStr(exp, "~") > 0 Then
96                 t = Strings.Split(exp, "~")
97             ElseIf exp.IsPattern(RegexpDouble & "\s*[-]\s*" & RegexpDouble) Then
98                 ' 使用的是英文的分隔符
99                 ' 因为可能会和负号弄混,所以在这里需要使用正则表达式来匹配出这个分隔符
100                 ' 因为是这种格式的range: dd-dd
101                 ' 故而分隔符的pattern肯定是数字加连接符本身,将这个pattern匹配出来,然后利用这个pattern进行分割即可
102                 Dim del$ = r.Match(exp, "\d\s*[-]").Value
103                 t = Strings.Split(exp, del)
104                 ' 需要将存在于del的pattern之中的前面的数字的最后一个数值补回来
105                 t(0) = t(0) & del.Trim("-"c)
106             Else
107                 exp = r _
108                     .Match(exp, RegexpFloatRange, RegexOptions.Singleline) _
109                     .Value
110
111                 If String.IsNullOrEmpty(exp) Then
112                     exp = $"'{raw}' is not a valid expression format!"
113                     Throw New FormatException(exp)
114                 Else
115                     t = exp.Split(","c)
116                 End If
117             End If
118
119             t = t _
120                 .Select(AddressOf Trim) _
121                 .ToArray
122
123             min = Casting.ParseNumeric(t(Scan0))
124             max = Casting.ParseNumeric(t(1))
125         End Sub
126
127         ''' <summary>
128         ''' 返回一个实数区间的范围百分比的生成函数:``[0-1]``之间
129         ''' </summary>
130         ''' <param name="range"></param>
131         ''' <returns></returns>
132         <Extension>
133         Public Function GetScaler(range As DoubleRange) As Func(Of DoubleDouble)
134             Dim length# = range.Length
135             Dim min# = range.Min
136
137             Return Function(x#)
138                        Return (x - min) / length
139                    End Function
140         End Function
141
142         <Extension>
143         Public Function GetScaler(vector As IEnumerable(Of Double)) As Func(Of DoubleDouble)
144             With vector.ToArray
145                 Return New DoubleRange(.Min, .Max).GetScaler
146             End With
147         End Function
148
149         ''' <summary>
150         ''' 将目标区间内的任意实数全部转换为<paramref name="to"/>区间内的实数
151         ''' </summary>
152         ''' <param name="from"></param>
153         ''' <param name="[to]"></param>
154         ''' <returns></returns>
155         <Extension>
156         Public Function RangeTransform(from As IEnumerable(Of Double), [to] As DoubleRange) As Double()
157             If from Is Nothing Then
158                 Return {}
159             End If
160
161             Dim vector#() = from.ToArray
162             Dim scale = New DoubleRange(vector).GetScaler
163             Dim percentages#() = vector.Select(scale).ToArray
164             Dim length# = [to].Length
165             Dim min# = [to].Min
166             Dim maps#() = percentages.Select(Function(x) x * length + min).ToArray
167             Return maps
168         End Function
169
170         ''' <summary>
171         ''' 将目标区间内的任意实数全部转换为<paramref name="to"/>区间内的实数
172         ''' </summary>
173         ''' <param name="from"></param>
174         ''' <param name="[to]"></param>
175         ''' <returns></returns>
176         ''' 
177         <MethodImpl(MethodImplOptions.AggressiveInlining)>
178         <Extension>
179         Public Function RangeTransform(from As IEnumerable(Of Integer), [to] As IntRange) As Integer()
180             Return from _
181                    .Select(Function(x) CDbl(x)) _
182                    .RangeTransform(New DoubleRange([to])) _
183                    .Select(Function(x) CInt(x)) _
184                    .ToArray
185         End Function
186
187         <Extension>
188         Public Function Union(fragments As IEnumerable(Of IntRange)) As IEnumerable(Of IntRange)
189             Dim unions As New List(Of IntRange)
190
191             For Each f In fragments.OrderBy(Function(r) r.Min)
192                 If unions = 0 Then
193                     unions += New IntRange(f.Min, f.Max)
194                 Else
195                     Dim isUnion As Boolean = False
196
197                     For Each region In unions
198                         If region.IsOverlapping(f) OrElse region.IsInside(f) Then
199                             region.Min = sys.Min(region.Min, f.Min)
200                             region.Max = sys.Max(region.Max, f.Max)
201
202                             isUnion = True
203                         End If
204                     Next
205
206                     If Not isUnion Then
207                         unions += New IntRange(f.Min, f.Max)
208                     End If
209                 End If
210             Next
211
212             Return unions
213         End Function
214     End Module
215 End Namespace