Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OrderedIntervalList #45

Merged
merged 14 commits into from
Dec 2, 2018
91 changes: 91 additions & 0 deletions software/smartContract/contracts/OrderedIntervalList.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
pragma solidity ^0.4.24;

/**
@title OrderedIntervalList
@dev List of ordered by intervals with non intersections checks.
*/
library OrderedIntervalList {
struct Interval {
uint64 begin; // inclusive
uint64 end; // exclusive
}
struct Data {
Interval[] intervals;
}

function getSize(Data storage self) internal view returns(uint256 size) {
return self.intervals.length;
}

/**
@notice Get interval by list index
@param _index interval index in list
@return interval tuple
*/
function get(Data storage self, uint _index) internal view returns(uint64 begin, uint64 end) {
require(self.intervals.length > 0 && _index < self.intervals.length, "check list is non empty and index have proper bounds");
patsak marked this conversation as resolved.
Show resolved Hide resolved
Interval memory interval = self.intervals[_index];
patsak marked this conversation as resolved.
Show resolved Hide resolved
return (interval.begin, interval.end);
}

/**
@notice Append interval to the end of list
@param _begin left bound of interval (inclusive)
@param _end right bound of interval (exclusive)
*/
function append(Data storage self, uint64 _begin, uint64 _end) internal {
insert(self, self.intervals.length, _begin, _end);
}

/**
@notice Insert interval by specific index in list
@dev Method also check that new interval doesn't intersect with existed intervals in list
@param _index target index in list
@param _begin left bound of interval (inclusive)
@param _end right bound of interval (exclusive)
*/
function insert(Data storage self, uint256 _index, uint64 _begin, uint64 _end) internal {
require(_begin < _end, "right bound greater than left bound");
require(_index <= self.intervals.length, "valid index bounds");
uint prev = 0;
uint prevIndex = _index;
uint next = 0;
uint nextRawIndex = _index + 1;


while(prevIndex > 0 && prev == 0) {
prevIndex = prevIndex - 1;
prev = self.intervals[prevIndex].end;
}

nextRawIndex = _index + 1;
while (nextRawIndex < self.intervals.length && next == 0) {
patsak marked this conversation as resolved.
Show resolved Hide resolved
Interval memory nextEl = self.intervals[nextRawIndex];
if (nextEl.end != 0) {
next = nextEl.end;
}
nextRawIndex = nextRawIndex + 1;
}

require((prev <= _begin || prev == 0) && (next == 0 || next >= _end), "valid interval bounds");
patsak marked this conversation as resolved.
Show resolved Hide resolved


if (_index == self.intervals.length) {
self.intervals.push(Interval({begin: _begin, end: _end}));
} else {
require(self.intervals[_index].end == 0, "free slot exists");
self.intervals[_index] = Interval({begin: _begin, end: _end});
}

}

/**
@notice Remove interval by index
@param _index interval index in list
*/
function remove(Data storage self, uint64 _index) internal {
patsak marked this conversation as resolved.
Show resolved Hide resolved
require(_index < self.intervals.length, "valid index bounds");
delete self.intervals[_index];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pragma solidity ^0.4.24;

import { OrderedIntervalList } from "../OrderedIntervalList.sol";

contract OrderedIntervalListWrapper {
using OrderedIntervalList for OrderedIntervalList.Data;

OrderedIntervalList.Data _data;

function getSize() public view returns(uint _size) {
return _data.intervals.length;
}


function get(uint index) public view returns(uint64 begin, uint64 end) {

(begin, end) = _data.get(index);
return (begin, end);

}

function append(uint64 _begin, uint64 _end) public {
_data.append(_begin, _end);
}

function set(uint64 index, uint64 _begin, uint64 _end) public {
_data.insert(index, _begin, _end);

}

function remove(uint64 _index) public {
_data.remove(_index);
}


}
155 changes: 155 additions & 0 deletions software/smartContract/test/OrderedIntervalList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@


const { keccak256, bufferToHex } = require('ethereumjs-util');
const { assertRevert } = require('./helpers/assertRevert.js');

const OrderedIntervalList = artifacts.require('OrderedIntervalListWrapper');
const BigNumber = web3.BigNumber;

require('chai')
.use(require('chai-bignumber')(BigNumber))
.should();

contract('OrderedIntervalList', function () {
beforeEach(async function() {
this.orderedList = await OrderedIntervalList.new()
});

describe('insert', function () {
it('check init state' , async function () {

assertRevert(this.orderedList.get(0))
assertRevert(this.orderedList.get(1));
assertRevert(this.orderedList.get(-1));

})

it('insert one', async function() {
await this.orderedList.append(0, 100);
const interval = await this.orderedList.get(0);

interval[0].should.be.bignumber.equal(0);
interval[1].should.be.bignumber.equal(100);
(await this.orderedList.getSize()).should.be.bignumber.equal(1)
})

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

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

intervalFirst[0].should.be.bignumber.equal(0);
intervalFirst[1].should.be.bignumber.equal(100);
intervalSecond[0].should.be.bignumber.equal(100);
intervalSecond[1].should.be.bignumber.equal(200);
(await this.orderedList.getSize()).should.be.bignumber.equal(2)
})

it('insert error', async function(){
await this.orderedList.append(0, 100);
await this.orderedList.append(100, 200);

// already inserted position
assertRevert(this.orderedList.set(1, 100, 200));

// range collision
assertRevert(this.orderedList.append(150, 200));


await this.orderedList.append(200, 300);

const interval = await this.orderedList.get(2);
interval[0].should.be.bignumber.equal(200);
interval[1].should.be.bignumber.equal(300);

// zero interval size
assertRevert(this.orderedList.append(300, 300))
// begin and end swapped
assertRevert(this.orderedList.append(305, 302))



})
})


describe('remove', function () {
it('check init state' , async function () {


assertRevert(this.orderedList.remove(0));
assertRevert(this.orderedList.remove(1));
assertRevert(this.orderedList.remove(-1));


})

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

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

interval[0].should.be.bignumber.equal(0);
interval[1].should.be.bignumber.equal(0);

(await this.orderedList.getSize()).should.be.bignumber.equal(1);
})



it('make and fill hole', async function() {
await this.orderedList.append(0, 100);
await this.orderedList.append(100, 200);
await this.orderedList.append(200, 300);

// already inserted position
assertRevert(this.orderedList.set(1, 100, 150));


await this.orderedList.remove(1);
var interval = await this.orderedList.get(1);
interval[0].should.be.bignumber.equal(0);
interval[1].should.be.bignumber.equal(0);


await this.orderedList.set(1, 100, 150);

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

interval[0].should.be.bignumber.equal(100);
interval[1].should.be.bignumber.equal(150);

(await this.orderedList.getSize()).should.be.bignumber.equal(3);

})

it('should find previous element on insert if hole size more than 1', async function () {
await this.orderedList.append(0, 100);
await this.orderedList.append(100, 200);
await this.orderedList.append(200, 300);
await this.orderedList.append(400, 500);

await this.orderedList.remove(1)
await this.orderedList.remove(2)


assertRevert(this.orderedList.set(2, 99, 110));
await this.orderedList.set(2, 101, 110);

var interval = await this.orderedList.get(2);
interval[0].should.be.bignumber.equal(101);
interval[1].should.be.bignumber.equal(110);


})

})





})