-
Notifications
You must be signed in to change notification settings - Fork 37
/
Lag.cs
247 lines (200 loc) · 8.07 KB
/
Lag.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
using Spreads.Cursors.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
// ReSharper disable once CheckNamespace
namespace Spreads
{
// TODO Lag1/Previous could use just a single cursor and be much faster
// TODO Call it Shift with int - negative as lag, positive as lead. Shift(n, Window(n)) is a forward Window, no need for
// a special implementation
public struct Lag<TKey, TValue, TCursor> :
ICursorSeries<TKey, (TValue, (TKey, TValue)), Lag<TKey, TValue, TCursor>>
where TCursor : ISpecializedCursor<TKey, TValue, TCursor>
{
#region Cursor state
// This region must contain all cursor state that is passed via constructor.
// No additional state must be created.
// All state elements should be assigned in Initialize and Clone methods
// All inner cursors must be disposed in the Dispose method but references to them must be kept (they could be used as factories)
// for re-initialization.
// NB must be mutable, could be a struct
// ReSharper disable once FieldCanBeMadeReadOnly.Local
internal LagStepImpl<TKey, TValue, TCursor> _cursor;
// ReSharper disable once FieldCanBeMadeReadOnly.Local
internal LagStepImpl<TKey, TValue, TCursor> _lookUpCursor;
#endregion Cursor state
#region Constructors
internal Lag(TCursor cursor, int width = 1, int step = 1) : this()
{
if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width));
if (step <= 0) throw new ArgumentOutOfRangeException(nameof(width));
_cursor = new LagStepImpl<TKey, TValue, TCursor>(cursor, width, step);
}
#endregion Constructors
#region Lifetime management
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lag<TKey, TValue, TCursor> Clone()
{
var instance = new Lag<TKey, TValue, TCursor>
{
_cursor = _cursor.Clone(),
};
return instance;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Lag<TKey, TValue, TCursor> Initialize()
{
var instance = new Lag<TKey, TValue, TCursor>
{
_cursor = _cursor.Initialize(),
};
return instance;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
// NB keep cursor state for reuse
// dispose is called on the result of Initialize(), the cursor from
// constructor could be uninitialized but contain some state, e.g. _value for FillCursor
_cursor.Dispose();
if (!_lookUpCursor.Equals(default(LagStepImpl<TKey, TValue, TCursor>)))
{
_lookUpCursor.Dispose();
}
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
_cursor.Reset();
}
ICursor<TKey, (TValue, (TKey, TValue))> ICursor<TKey, (TValue, (TKey, TValue))>.Clone()
{
return Clone();
}
#endregion Lifetime management
#region ICursor members
/// <inheritdoc />
public KeyValuePair<TKey, (TValue, (TKey, TValue))> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new KeyValuePair<TKey, (TValue, (TKey, TValue))>(CurrentKey, CurrentValue); }
}
/// <inheritdoc />
public TKey CurrentKey
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _cursor.CurrentKey; }
}
/// <inheritdoc />
public (TValue, (TKey, TValue)) CurrentValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return (_cursor.CurrentCursor.CurrentValue, (_cursor.LaggedCursor.CurrentKey, _cursor.LaggedCursor.CurrentValue)); }
}
/// <inheritdoc />
public IReadOnlySeries<TKey, (TValue, (TKey, TValue))> CurrentBatch => null;
/// <inheritdoc />
public KeyComparer<TKey> Comparer => _cursor.Comparer;
object IEnumerator.Current => Current;
/// <summary>
/// Lag cursor is discrete even if its input cursor is continuous.
/// </summary>
public bool IsContinuous => false;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(TKey key, out (TValue, (TKey, TValue)) value)
{
if (_lookUpCursor.Equals(default(TCursor)))
{
_lookUpCursor = _cursor.Clone();
}
if (_lookUpCursor.MoveAt(key, Lookup.EQ))
{
value = (_lookUpCursor.CurrentCursor.CurrentValue, (_lookUpCursor.LaggedCursor.CurrentKey, _lookUpCursor.LaggedCursor.CurrentValue));
return true;
}
value = default((TValue, (TKey, TValue)));
return false;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveAt(TKey key, Lookup direction)
{
var moved = _cursor.MoveAt(key, direction);
return moved;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.NoInlining)] // NB NoInlining is important to speed-up MoveNext
public bool MoveFirst()
{
var moved = _cursor.MoveFirst();
return moved;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.NoInlining)] // NB NoInlining is important to speed-up MovePrevious
public bool MoveLast()
{
var moved = _cursor.MoveLast();
return moved;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
return _cursor.MoveNext();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Task<bool> MoveNextBatch(CancellationToken cancellationToken)
{
return Utils.TaskUtil.FalseTask;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MovePrevious()
{
return _cursor.MovePrevious();
}
/// <inheritdoc />
IReadOnlySeries<TKey, (TValue, (TKey, TValue))> ICursor<TKey, (TValue, (TKey, TValue))>.Source => new Series<TKey, (TValue, (TKey, TValue)), Lag<TKey, TValue, TCursor>>(this);
/// <summary>
/// Get a <see cref="Series{TKey,TValue,TCursor}"/> based on this cursor.
/// </summary>
public Series<TKey, (TValue, (TKey, TValue)), Lag<TKey, TValue, TCursor>> Source => new Series<TKey, (TValue, (TKey, TValue)), Lag<TKey, TValue, TCursor>>(this);
/// <inheritdoc />
public Task<bool> MoveNext(CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
#endregion ICursor members
#region ICursorSeries members
/// <inheritdoc />
public bool IsIndexed => _cursor.Source.IsIndexed;
/// <inheritdoc />
public bool IsReadOnly
{
// NB this property is repeatedly called from MNA
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _cursor.Source.IsReadOnly; }
}
/// <inheritdoc />
public Task<bool> Updated
{
// NB this property is repeatedly called from MNA
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _cursor.Source.Updated; }
}
#endregion ICursorSeries members
}
}