import std.stdio; import std.string; import std.exception; /* I intend to make a library out of IntervalMap. (1) How would I do so in dub? (2) How do I ensure Interval cannot be exported? */ struct Interval/*(T)*/ { immutable uint low; /* this will later change types to T payload. this is just sample code for me to learn to program in D. */ uint payload; }; class IntervalMap { private: Interval[] mappings; // See the comment in append. debug { ulong __oldLength; bool __mappingAdded; } public: uint upperEdge() @property { if (mappings.length > 0) return mappings[$ - 1].low; return 0; }; void append(uint index, uint value) /+ The intent is to specify that we only add an entry to mappings if the payload changes. But is there a better way to specify this contract than to use the in-block to set debug-scoped variables that the out-block later checks? Could there be one? After all, the debug-scoped __oldLength and __mappingsAdded values only need to exist for this function. If I could say instead: in { ulong __oldLength = mappings.length; bool __mappingAdded; } out { if (__mappingAdded) assert(mappings.length == __oldLength + 1); else assert(mappings.length == __oldLength); } body { /+ ... +/ debug { __mappingAdded = noMatch; } } That would be very useful. But as written, wouldn't it violate scoping of the contract variables? Would a separate block be appropriate, say "contract_vars", to specify these variables before the in-block executes? +/ in { __oldLength = mappings.length; } out { assert(find(index) == value); assert(find(index + 1) == value); if (__mappingAdded) assert(mappings.length == __oldLength + 1); else assert(mappings.length == __oldLength); // cleanup __oldLength = 0; __mappingAdded = false; } body { enforce(index > upperEdge, new StringException("index must be greater than upperEdge")); auto current = (mappings.length > 0) ? mappings[$ - 1].payload : 0; bool noMatch = (current != value); debug { /* Should the statements in this scope run if an exception was thrown in the postcondition? The current language reference is unclear on this. Similarly, should a scope(success) in a precondition run when the whole function exits normally? */ scope(failure) { __mappingAdded = false; __oldLength = 0; } __mappingAdded = noMatch; } if (noMatch) mappings ~= Interval(index, value); } uint find(uint index) { if (mappings.length == 0) return 0; ulong low = 0, high = mappings.length, mid = 0; while (low < high) { mid = (low + high) >> 1; auto mLow = mappings[mid].low; // this line is here so I can see mLow in dlangide. if (index >= mLow) { low = mid + 1; } else { high = mid; } } mid = (low + high) >> 1; return (mid != 0) ? mappings[mid - 1].payload : 0; }; }; unittest { { auto x = new IntervalMap; assert(x.find(0) == 0); assert(x.upperEdge == 0); x.append(15, 0); assert(x.upperEdge == 0); // XXX ajvincent How would I rewrite this using assertThrown? { try { x.append(0, 3); } catch (StringException e) { assert(e.msg == "index must be greater than upperEdge"); } } assert(x.find(0) == 0); x.append(3, 2); assert(x.upperEdge == 3); for (uint i = 0; i < 3; i++) assert(x.find(i) == 0); for (uint i = 3; i < 10; i++) assert(x.find(i) == 2); x.append(5, 2); assert(x.upperEdge == 3); for (uint i = 0; i < 3; i++) assert(x.find(i) == 0); for (uint i = 3; i < 10; i++) assert(x.find(i) == 2); { try { x.append(2, 1); } catch (StringException e) { assert(e.msg == "index must be greater than upperEdge"); } } x.append(6, 8); assert(x.upperEdge == 6); for (uint i = 0; i < 3; i++) assert(x.find(i) == 0); for (uint i = 3; i < 6; i++) assert(x.find(i) == 2); for (uint i = 6; i < 10; i++) assert(x.find(i) == 8); } } void main(string[] args) { auto foo = new IntervalMap; foo.append(1, 3); uint k = foo.find(0); uint j = foo.find(1); writeln("Press enter..."); readln(); }