vector.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import { Vector } from '../types/dataFrame';
  2. export function vectorToArray<T>(v: Vector<T>): T[] {
  3. const arr: T[] = [];
  4. for (let i = 0; i < v.length; i++) {
  5. arr[i] = v.get(i);
  6. }
  7. return arr;
  8. }
  9. /**
  10. * Apache arrow vectors are Read/Write
  11. */
  12. export interface ReadWriteVector<T = any> extends Vector<T> {
  13. set: (index: number, value: T) => void;
  14. }
  15. /**
  16. * Vector with standard manipulation functions
  17. */
  18. export interface MutableVector<T = any> extends ReadWriteVector<T> {
  19. /**
  20. * Adds the value to the vector
  21. */
  22. add: (value: T) => void;
  23. /**
  24. * modifies the vector so it is now the oposite order
  25. */
  26. reverse: () => void;
  27. }
  28. export class ArrayVector<T = any> implements MutableVector<T> {
  29. buffer: T[];
  30. constructor(buffer?: T[]) {
  31. this.buffer = buffer ? buffer : [];
  32. }
  33. get length() {
  34. return this.buffer.length;
  35. }
  36. add(value: T) {
  37. this.buffer.push(value);
  38. }
  39. get(index: number): T {
  40. return this.buffer[index];
  41. }
  42. set(index: number, value: T) {
  43. this.buffer[index] = value;
  44. }
  45. reverse() {
  46. this.buffer.reverse();
  47. }
  48. toArray(): T[] {
  49. return this.buffer;
  50. }
  51. toJSON(): T[] {
  52. return this.buffer;
  53. }
  54. }
  55. export class ConstantVector<T = any> implements Vector<T> {
  56. constructor(private value: T, private len: number) {}
  57. get length() {
  58. return this.len;
  59. }
  60. get(index: number): T {
  61. return this.value;
  62. }
  63. toArray(): T[] {
  64. const arr = new Array<T>(this.length);
  65. return arr.fill(this.value);
  66. }
  67. toJSON(): T[] {
  68. return this.toArray();
  69. }
  70. }
  71. export class ScaledVector implements Vector<number> {
  72. constructor(private source: Vector<number>, private scale: number) {}
  73. get length(): number {
  74. return this.source.length;
  75. }
  76. get(index: number): number {
  77. return this.source.get(index) * this.scale;
  78. }
  79. toArray(): number[] {
  80. return vectorToArray(this);
  81. }
  82. toJSON(): number[] {
  83. return vectorToArray(this);
  84. }
  85. }
  86. /**
  87. * Values are returned in the order defined by the input parameter
  88. */
  89. export class SortedVector<T = any> implements Vector<T> {
  90. constructor(private source: Vector<T>, private order: number[]) {}
  91. get length(): number {
  92. return this.source.length;
  93. }
  94. get(index: number): T {
  95. return this.source.get(this.order[index]);
  96. }
  97. toArray(): T[] {
  98. return vectorToArray(this);
  99. }
  100. toJSON(): T[] {
  101. return vectorToArray(this);
  102. }
  103. }
  104. interface CircularOptions<T> {
  105. buffer?: T[];
  106. append?: 'head' | 'tail';
  107. capacity?: number;
  108. }
  109. /**
  110. * Circular vector uses a single buffer to capture a stream of values
  111. * overwriting the oldest value on add.
  112. *
  113. * This supports addting to the 'head' or 'tail' and will grow the buffer
  114. * to match a configured capacity.
  115. */
  116. export class CircularVector<T = any> implements MutableVector<T> {
  117. private buffer: T[];
  118. private index: number;
  119. private capacity: number;
  120. private tail: boolean;
  121. constructor(options: CircularOptions<T>) {
  122. this.buffer = options.buffer || [];
  123. this.capacity = this.buffer.length;
  124. this.tail = 'head' !== options.append;
  125. this.index = 0;
  126. this.add = this.getAddFunction();
  127. if (options.capacity) {
  128. this.setCapacity(options.capacity);
  129. }
  130. }
  131. /**
  132. * This gets the appropriate add function depending on the buffer state:
  133. * * head vs tail
  134. * * growing buffer vs overwriting values
  135. */
  136. private getAddFunction() {
  137. // When we are not at capacity, it should actually modify the buffer
  138. if (this.capacity > this.buffer.length) {
  139. if (this.tail) {
  140. return (value: T) => {
  141. this.buffer.push(value);
  142. if (this.buffer.length >= this.capacity) {
  143. this.add = this.getAddFunction();
  144. }
  145. };
  146. } else {
  147. return (value: T) => {
  148. this.buffer.unshift(value);
  149. if (this.buffer.length >= this.capacity) {
  150. this.add = this.getAddFunction();
  151. }
  152. };
  153. }
  154. }
  155. if (this.tail) {
  156. return (value: T) => {
  157. this.buffer[this.index] = value;
  158. this.index = (this.index + 1) % this.buffer.length;
  159. };
  160. }
  161. // Append values to the head
  162. return (value: T) => {
  163. let idx = this.index - 1;
  164. if (idx < 0) {
  165. idx = this.buffer.length - 1;
  166. }
  167. this.buffer[idx] = value;
  168. this.index = idx;
  169. };
  170. }
  171. setCapacity(v: number) {
  172. if (this.capacity === v) {
  173. return;
  174. }
  175. // Make a copy so it is in order and new additions can be at the head or tail
  176. const copy = this.toArray();
  177. if (v > this.length) {
  178. this.buffer = copy;
  179. } else if (v < this.capacity) {
  180. // Shrink the buffer
  181. const delta = this.length - v;
  182. if (this.tail) {
  183. this.buffer = copy.slice(delta, copy.length); // Keep last items
  184. } else {
  185. this.buffer = copy.slice(0, copy.length - delta); // Keep first items
  186. }
  187. }
  188. this.capacity = v;
  189. this.index = 0;
  190. this.add = this.getAddFunction();
  191. }
  192. setAppendMode(mode: 'head' | 'tail') {
  193. const tail = 'head' !== mode;
  194. if (tail !== this.tail) {
  195. this.buffer = this.toArray().reverse();
  196. this.index = 0;
  197. this.tail = tail;
  198. this.add = this.getAddFunction();
  199. }
  200. }
  201. reverse() {
  202. this.buffer.reverse();
  203. }
  204. /**
  205. * Add the value to the buffer
  206. */
  207. add: (value: T) => void;
  208. get(index: number) {
  209. return this.buffer[(index + this.index) % this.buffer.length];
  210. }
  211. set(index: number, value: T) {
  212. this.buffer[(index + this.index) % this.buffer.length] = value;
  213. }
  214. get length() {
  215. return this.buffer.length;
  216. }
  217. toArray(): T[] {
  218. return vectorToArray(this);
  219. }
  220. toJSON(): T[] {
  221. return vectorToArray(this);
  222. }
  223. }
  224. interface AppendedVectorInfo<T> {
  225. start: number;
  226. end: number;
  227. values: Vector<T>;
  228. }
  229. /**
  230. * This may be more trouble than it is worth. This trades some computation time for
  231. * RAM -- rather than allocate a new array the size of all previous arrays, this just
  232. * points the correct index to their original array values
  233. */
  234. export class AppendedVectors<T = any> implements Vector<T> {
  235. length = 0;
  236. source: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
  237. constructor(startAt = 0) {
  238. this.length = startAt;
  239. }
  240. /**
  241. * Make the vector look like it is this long
  242. */
  243. setLength(length: number) {
  244. if (length > this.length) {
  245. // make the vector longer (filling with undefined)
  246. this.length = length;
  247. } else if (length < this.length) {
  248. // make the array shorter
  249. const sources: Array<AppendedVectorInfo<T>> = new Array<AppendedVectorInfo<T>>();
  250. for (const src of this.source) {
  251. sources.push(src);
  252. if (src.end > length) {
  253. src.end = length;
  254. break;
  255. }
  256. }
  257. this.source = sources;
  258. this.length = length;
  259. }
  260. }
  261. append(v: Vector<T>): AppendedVectorInfo<T> {
  262. const info = {
  263. start: this.length,
  264. end: this.length + v.length,
  265. values: v,
  266. };
  267. this.length = info.end;
  268. this.source.push(info);
  269. return info;
  270. }
  271. get(index: number): T {
  272. for (let i = 0; i < this.source.length; i++) {
  273. const src = this.source[i];
  274. if (index >= src.start && index < src.end) {
  275. return src.values.get(index - src.start);
  276. }
  277. }
  278. return (undefined as unknown) as T;
  279. }
  280. toArray(): T[] {
  281. return vectorToArray(this);
  282. }
  283. toJSON(): T[] {
  284. return vectorToArray(this);
  285. }
  286. }