| 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 |
| // SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
// Eth Heap
// Authors: Alexandre Tolstenko
// Original Author: Zac Mitton
// License: MIT
// ref.: https://github.com/zmitton/eth-heap/blob/master/contracts/Heap.sol
// ref.: https://github.com/zmitton/eth-heap/blob/master/contracts/OrderBookHeap.sol
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
// todo: get node by timestamp
// todo: remove nodes older than a provided time
library Heap { // default max-heap
using SafeMath for uint256;
uint256 constant ROOT_INDEX = 1;
struct Data{
uint256 idCount;
Node[] nodes; // root is index 1; index 0 not used
mapping (uint256 => uint256) indices; // unique id => node index
mapping (uint256 => uint256) timeByIndices; //unique time => node index
}
struct Node{
uint256 id;
uint256 price;
uint256 time;
}
//call init before anything else
function init(Data storage self) internal{
if(self.nodes.length == 0) self.nodes.push(Node(0,0,0));
}
function insert(Data storage self, uint256 price, uint256 time) internal returns(Node memory){//√
if(self.nodes.length == 0){ init(self); }// test on-the-fly-init
self.idCount.add(1);
// self.nodes.length++; // old approach
self.nodes.push(Node(0,0,0));
Node memory n = Node(self.idCount, price, time);
_bubbleUp(self, n, self.nodes.length-1);
return n;
}
function extractMax(Data storage self) internal returns(Node memory){//√
return _extract(self, ROOT_INDEX);
}
function extractById(Data storage self, uint256 id) internal returns(Node memory){//√
return _extract(self, self.indices[id]);
}
//view
function dump(Data storage self) internal view returns(Node[] memory){
//note: Empty set will return `[Node(0,0,0,0)]`. uninitialized will return `[]`.
return self.nodes;
}
function getById(Data storage self, uint256 id) internal view returns(Node storage){
return getByIndex(self, self.indices[id]);//test that all these return the emptyNode
}
function getByIndex(Data storage self, uint256 i) internal view returns(Node storage) {
require(self.nodes.length > i, "index not found");
// return self.nodes.length > i ? self.nodes[i] : Node(0,0,0,0); // old approach
return self.nodes[i];
}
function getByTime(Data storage self, uint256 time) internal view returns(Node storage){
require(self.timeByIndices[time] != 0, "index not found");
return self.nodes[self.timeByIndices[time]];
}
function getMax(Data storage self) internal view returns(Node storage){
return getByIndex(self, ROOT_INDEX);
}
function size(Data storage self) internal view returns(uint256){
return self.nodes.length > 0 ? self.nodes.length-1 : 0; // todo: is it really possible to be negative???
}
function isNode(Node memory n) internal pure returns(bool){ return n.id > 0; }
//private
function _extract(Data storage self, uint256 i) private returns(Node memory){//√
if(self.nodes.length <= i || i <= 0){ return Node(0,0,0); } // todo: modifier add timesByIndeces
Node memory extractedNode = self.nodes[i];
delete self.indices[extractedNode.id];
Node memory tailNode = self.nodes[self.nodes.length-1];
// self.nodes.length--; // old approach
self.nodes.pop(); // updated approach todo: check this
if(i < self.nodes.length){ // if extracted node was not tail
_bubbleUp(self, tailNode, i);
_bubbleDown(self, self.nodes[i], i); // then try bubbling down
}
return extractedNode;
}
function _bubbleUp(Data storage self, Node memory n, uint256 i) private{//√
if(i==ROOT_INDEX || n.price <= self.nodes[i/2].price){
_insert(self, n, i);
}else{
_insert(self, self.nodes[i/2], i);
_bubbleUp(self, n, i/2);
}
}
function _bubbleDown(Data storage self, Node memory n, uint256 i) private{//
uint256 length = self.nodes.length;
uint256 cIndex = i*2; // left child index
if(length <= cIndex){
_insert(self, n, i);
}else{
Node memory largestChild = self.nodes[cIndex];
if(length > cIndex+1 && self.nodes[cIndex+1].price > largestChild.price ){
largestChild = self.nodes[++cIndex];
}
if(largestChild.price <= n.price){
_insert(self, n, i);
}else{
_insert(self, largestChild, i);
_bubbleDown(self, n, cIndex);
}
}
}
// todo: check all functions using id
function _insert(Data storage self, Node memory n, uint256 i) private{//√
self.nodes[i] = n;
self.indices[n.id] = i;
self.timeByIndices[n.time] = i; //todo: check is real
}
}
|