Skip to content

Commit

Permalink
Some changes
Browse files Browse the repository at this point in the history
  • Loading branch information
k06a committed Nov 30, 2018
1 parent a781c7a commit 049d59a
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 124 deletions.
246 changes: 146 additions & 100 deletions software/smartContract/contracts/OrderedIntervalList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pragma solidity ^0.4.24;
library OrderedIntervalList {
struct Interval {
uint256 begin; // inclusive
uint256 end; // exclusive
uint256 end; // exclusive

uint256 next;
uint256 prev;
Expand All @@ -33,12 +33,37 @@ library OrderedIntervalList {
* @param id interval index in the list
* @return interval tuple
*/
function get(Data storage self, uint id) internal view returns(Interval storage interval) {
function get(Data storage self, uint256 id) internal view returns(Interval storage interval) {
require(id <= self.intervals.length, "interval id doesn't exists in interval set");
interval = self.intervals[id];
require(interval.end != 0, "interval id doesn't exsits in interval set");
}

/**
* @notice Check interval existance by the index
* @param id interval index in the list
* @return is existing or not
*/
function exist(Data storage self, uint256 id) internal view returns (bool) {
return self.intervals[id].end != 0;
}

/**
* @notice Add interval after the lates interval
* @param size length of the new interval
* @return id of the latest interval
*/
function append(
Data storage self,
uint256 size
)
internal
returns(uint256)
{
Interval storage lastInterval = self.intervals[self.lastIndex];
return insert(self, self.lastIndex, 0, lastInterval.end, lastInterval.end + size - 1);
}

/**
* @notice Insert interval in the specific place in a list
* @dev Method also check that new interval doesn't intersect with existed intervals in list
Expand All @@ -49,90 +74,105 @@ library OrderedIntervalList {
* @return id of the interval that contain new interval. Could be a new interval or an existed with
* extended bounds in case of adjacent bounds of the inserted interval with his neighbors.
*/
function insert(Data storage self, uint prev, uint next, uint begin, uint end) internal returns(uint id) {
function insert(
Data storage self,
uint256 prev,
uint256 next,
uint256 begin,
uint256 end
)
internal
returns(uint256 id)
{
require(begin < end, "right bound less or equal to left bound");

bool concatLeft = false;
bool concatRight = false;
id = self.intervals.length;
if (id == 0) {
self.intervals.length += 1;
id += 1;
if (self.intervals.length == 0) {
self.intervals.push(Interval(0,0,0,0));
}

if (self.firstIndex == 0) {
// require(prev == 0 && next == 0, "insert in empty list with non zero link to neighbor elements");
self.firstIndex = id;
self.lastIndex = id;
} else {
require(prev > 0 || next > 0, "previous and next element doesn't exists");
Interval storage prevInterval = self.intervals[prev];
Interval storage nextInterval = self.intervals[next];

if (prev > 0 && next > 0) { // insert between to existed intervals
require(prevInterval.end != 0, "previous interval doesn't exists");
require(nextInterval.end != 0, "next interval doesn't exists");
require(prevInterval.end <= begin && nextInterval.begin >= end, "new interval out of bounds of neighbors intervals");
if (prevInterval.end == begin) {
concatLeft = true;
prevInterval.end = end;
}
if (nextInterval.begin == end) {
concatRight = true;
nextInterval.begin = begin;
}
if (! concatLeft && !concatRight ) {
Interval storage prevInterval = self.intervals[prev];
Interval storage nextInterval = self.intervals[next];

nextInterval.prev = id;
prevInterval.next = id;
}
if (concatLeft && concatRight) {
prevInterval.end = nextInterval.end;
prevInterval.next = nextInterval.next;
delete self.intervals[next];
}
} else if (prev > 0 && next == 0) { // insert as last elemnt
require(prevInterval.end != 0, "prev interval doesn't exists");
require(prevInterval.next == 0, "prev element is not last element");
require(prevInterval.end <= begin, "new interval out of bounds of prev interval");

if (prevInterval.end == begin) {
concatLeft = true;
prevInterval.end = end;
} else {
self.lastIndex = id;
prevInterval.next = id;
}
} else if (prev == 0 && next >= 0) { // insert as first element
require(nextInterval.end != 0, "next interval doesn't exists");
require(nextInterval.prev == 0, "next element is not first element");
require(nextInterval.begin >= end, "new interval out of bounds of next interval");

if (nextInterval.begin == end) {
concatRight = true;
nextInterval.begin = begin;
} else {
require(prev == 0 || begin >= prevInterval.end, "begin could not intersect prev interval");
require(next == 0 || end <= nextInterval.begin, "end could not intersect next interval");

bool concatPrev = (prev > 0 && begin == prevInterval.end);
bool concatNext = (next > 0 && end == nextInterval.begin);

if ((prev > 0) == (next > 0)) {
// Adding between existing intervals or very first interval

require(
prevInterval.next == next && nextInterval.prev == prev,
"prev and next should refer to the neighboring intervals"
);

if (!concatPrev && !concatNext) {
id = self.intervals.length;
self.intervals.push(Interval({
begin: begin,
end: end,
prev: prev,
next: next
}));

if (prev == 0 && next == 0) {
self.firstIndex = id;
nextInterval.prev = id;
self.lastIndex = id;
}
}
} else
if (next > 0) {
// Adding before first interval

require(
self.firstIndex == next && nextInterval.prev == 0,
"next should refer to the first interval"
);
} else
if (prev > 0) {
// Adding after last interval

require(
self.lastIndex == prev && prevInterval.next == 0,
"prev should refer to the last interval"
);
require(
prev != self.lastIndex || prevInterval.end == begin,
"should begin from the end of latest interval when adding to the end"
);
}

if (!concatRight && !concatLeft) {
self.intervals.push(Interval({
begin: begin,
end: end,
prev: prev,
next: next
}));
} else {
if (concatLeft) {
id = prev;
} else{
if (concatRight) {
id = next;
}
if (!concatPrev && !concatNext) {
nextInterval.prev = id;
prevInterval.next = id;
} else
if (concatPrev && concatNext) {
prevInterval.end = nextInterval.end;
prevInterval.next = nextInterval.next;
delete self.intervals[prev];
id = prev;

// When attaching pre last to last
if (next == self.lastIndex) {
self.lastIndex = id;
}
} else
if (concatPrev) {
prevInterval.end = end;
id = prev;
} else
if (concatNext) {
nextInterval.begin = begin;
id = next;
}

// Update first and last indexes if needed
if (next == self.firstIndex && self.firstIndex != id) {
self.firstIndex = id;
}
if (prev == self.lastIndex && self.lastIndex != id) {
self.lastIndex = id;
}
}

Expand All @@ -143,43 +183,49 @@ library OrderedIntervalList {
* @param end right range bound
* @return index of the new interval if new one was created (was made a hole insided existed interval) or zero.
*/
function remove(Data storage self, uint index, uint begin, uint end) internal returns (uint newInterval) {
function remove(Data storage self, uint index, uint begin, uint end) internal returns (uint256 newInterval) {
require(begin < end, "right bound less than left bound");
require(index <= self.intervals.length, "valid index bounds");

require(index < self.intervals.length, "valid index bounds");

Interval storage modifiedInterval = self.intervals[index];
Interval storage prevInterval = self.intervals[modifiedInterval.prev];
Interval storage nextInterval = self.intervals[modifiedInterval.next];
require(modifiedInterval.end != 0, "removed interval doesn't exists");
require(modifiedInterval.begin <= begin && modifiedInterval.end >= end, "incorrect removed range bounds");
require(modifiedInterval.begin <= begin && end <= modifiedInterval.end, "incorrect removed range bounds");

if (begin > modifiedInterval.begin ) {
bool shrinkBegin = (begin == modifiedInterval.begin);
bool shrinkEnd = (end == modifiedInterval.end);

uint oldEnd = modifiedInterval.end;
modifiedInterval.end = begin;
if (end < oldEnd) {
newInterval = insert(self, index, modifiedInterval.next, end, oldEnd);
modifiedInterval.next = newInterval;
}
} else {
modifiedInterval.begin = end;
}
if (shrinkBegin && shrinkEnd) {
// Remove whole interval

if (modifiedInterval.begin == modifiedInterval.end) {
if (modifiedInterval.prev > 0) {
Interval storage prevInterval = self.intervals[modifiedInterval.prev];
if (exist(self, modifiedInterval.prev)) {
prevInterval.next = modifiedInterval.next;
}
if (modifiedInterval.next > 0) {
Interval storage nextInterval = self.intervals[modifiedInterval.next];
nextInterval.prev = modifiedInterval.prev;
}
if (modifiedInterval.prev == 0) {
} else {
self.firstIndex = modifiedInterval.next;
}
if (modifiedInterval.next == 0) {
self.lastIndex = modifiedInterval.next;

if (exist(self, modifiedInterval.next)) {
nextInterval.prev = modifiedInterval.prev;
} else {
self.lastIndex = modifiedInterval.prev;
}

delete self.intervals[index];
} else
if (shrinkBegin) {
// Shrink from left side
modifiedInterval.begin = end;
} else
if (shrinkEnd) {
// Shrink from right side
modifiedInterval.end = begin;
} else {
// Make a hole
uint256 oldEnd = modifiedInterval.end;
modifiedInterval.end = begin;
modifiedInterval.next = insert(self, index, modifiedInterval.next, end, oldEnd);
newInterval = modifiedInterval.next;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ contract OrderedIntervalListWrapper {
uint private _lastInserted;

function lastInserted() public view returns(uint) {
return _lastInserted;
return _lastInserted;
}

function maxIndex() public view returns(uint) {
Expand Down
45 changes: 22 additions & 23 deletions software/smartContract/test/OrderedIntervalList.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ async function validateList (listContract, size) {

if (nextIndex > 0) {
let next = await listContract.get.call(nextIndex);

assert(next[0] >= currentInterval[1]);
}

Expand Down Expand Up @@ -69,6 +68,7 @@ contract('OrderedIntervalList', function () {

it('insert one', async function () {
await this.orderedList.set(0, 0, 0, 100);

const interval = await this.orderedList.get(1);

interval[0].should.be.bignumber.equal(0);
Expand All @@ -79,41 +79,40 @@ contract('OrderedIntervalList', function () {

it('insert twice', async function () {
await this.orderedList.set(0, 0, 0, 100);
await this.orderedList.set(1, 0, 101, 200);
await this.orderedList.set(1, 0, 100, 200);

const intervalFirst = await this.orderedList.get(1);
const intervalSecond = await this.orderedList.get(2);

intervalFirst[0].should.be.bignumber.equal(0);
intervalFirst[1].should.be.bignumber.equal(100);
intervalSecond[0].should.be.bignumber.equal(101);
intervalSecond[1].should.be.bignumber.equal(200);
intervalFirst[1].should.be.bignumber.equal(200);

await validateList(this.orderedList, 2);
await validateList(this.orderedList, 1);
});

it('insert error', async function () {
it('insert twice with gap should be failed', async function () {
await this.orderedList.set(0, 0, 0, 100);
await this.orderedList.set(1, 0, 101, 200);

// already inserted position
await this.orderedList.set(2, 0, 100, 200).should.rejectedWith(EVMRevert);
await this.orderedList.set(1, 0, 101, 200).should.be.rejectedWith(EVMRevert);
});

// range collision
await this.orderedList.set(1, 2, 150, 200).should.rejectedWith(EVMRevert);
it('insert error', async function () {
await this.orderedList.set(0, 0, 300, 400);
await this.orderedList.set(0, 1, 100, 200);

await this.orderedList.set(2, 0, 201, 300);
// wrong begin/end
await this.orderedList.set(0, 0, 100, 50).should.rejectedWith(EVMRevert);

const interval = await this.orderedList.get(3);
interval[0].should.be.bignumber.equal(201);
interval[1].should.be.bignumber.equal(300);
// wrong prev/next
await this.orderedList.set(2, 0, 90, 110).should.rejectedWith(EVMRevert);
await this.orderedList.set(0, 0, 90, 110).should.rejectedWith(EVMRevert);
await this.orderedList.set(0, 2, 90, 110).should.rejectedWith(EVMRevert);

// zero interval size
assertRevert(this.orderedList.set(3, 0, 300, 300));
// begin and end swapped
assertRevert(this.orderedList.set(3, 0, 305, 302));
// already inserted position
await this.orderedList.set(0, 1, 90, 110).should.rejectedWith(EVMRevert);
await this.orderedList.set(1, 0, 190, 210).should.rejectedWith(EVMRevert);
await this.orderedList.set(1, 0, 190, 310).should.rejectedWith(EVMRevert);

await validateList(this.orderedList, 3);
// too distant from last
await this.orderedList.set(1, 2, 450, 500).should.rejectedWith(EVMRevert);
});
});

Expand Down

0 comments on commit 049d59a

Please sign in to comment.