How to implement a data-type Range (or Interval) in Solidity

For a Smart Contract, in Solidity, we want to implement ranges.

The reason is that many interactors do so on "ranges". Our Contract holds batches of coupons which we don't need, nor want to, track individually. Interactors simply say "give me the next 200 coupons" after which we update the state of Range. Which allows interactors to check for the state of "individual" Coupons as well.

A pseudo-interface would look like:

contract.issue(10000)
contract.amountAvailable() #=> 10.000
contract.reserve(200, { from: A })
contract.reserve(800, { from: B })
contract.reserve(9001, { from: C }) #=> Fails, not enough available.
contract.amountAvailable() #=> 9.000
contract.amountReserved(A) #=> 200
contract.amountReserved(B) #=> 800
contract.stateOf(16) #=>  1 (reserved)
contract.stateOf(9001) #=> 0 (free)


From here on, an interactor can perform an action on a single coupon, for the example, coupon with ID 16. Which changes the state of this coupon. This is where it becomes hairy.

contract.claim(16)
contract.amountReserved(A) #=> 199
contract.amountReserved(B) #=> 800
contract.claim(16) #=> Fails.


Because 16 is within the batch/range "reserved" by A, the mapping that maintains reserved-batches per address must be updated; most probably, it needs to be split into two ranges: 1-15 and 17-200. But this is an implementation detail that I'm not certain of.

What I'm trying to show, is that operations work on ranges, but that an interface allows for the checking of singular items. Those singular items could be considered "virtual" in the sense that they are not stored in some mapping or array.

A Range would be a pair of integers: [bottom, top] and have associated helper methods such as memberOf(item, range), length(range) or pop(amount, range).

We chose not to manage individual items, because (i) we don't need to and (ii) we found that e.g. ERC721 as provided by e.g. Solidity, is above all expensive here. E.g. in order to mint (issue) 100 unique tokens with ERC721 from Solidity, we already needed 14.164.312 gas. So we investigate operating on ranges instead.

So, is there a library, standard or common practice on how to implement a Range? Is this maybe covered in an ERC that I overlooked?

To support the test you describe, you don't need ranges at all:

pragma solidity ^0.4.24;

contract Coupons {
uint256 public totalSupply;
uint256 public amountAvailable;

function issue(uint256 howMany) external {
// overflow test
require(totalSupply + howMany >= totalSupply);

totalSupply += howMany;
amountAvailable += howMany;
}

function reserve(uint256 howMany) external {
require(amountAvailable >= howMany);

amountReserved[msg.sender] += howMany;
amountAvailable -= howMany;
}

function stateOf(uint256 which) external view returns (bool) {
return which <= totalSupply - amountAvailable;
}
}