module interval; import std.string; import std.traits : isIntegral; struct Interval(T) { static assert( isIntegral!(T), "only intervals over the integers are supported."); T min = T.init, max = T.init-1, stride = 1; this(T min, T max) { if( max < min ) max = min; this.min = min; this.max = max; this.stride = stride; } this(T min, T max, T stride) { // Ensure max is actually in the interval max = min + stride*((max-min)/stride); if( max < min ) max = min; this.min = min; this.max = max; this.stride = stride; } string toString() const { if( stride == 1 ) return format("inter[%s, %s]", min, max); else return format("inter[%s, %s] / %s", min, max, stride); } pure bool opIn_r(T v) const { if( stride == 1 ) return (min <= v && v <= max); else { return (min <= v && v <= max) && ((v-min) % stride == 0); } } Interval opDiv(T stride) const { if( stride == 0 ) throw new Exception("intervals cannot have a zero stride"); return Interval(min, max, this.stride*stride); } Interval opMod(T divisor) const { auto new_stride = stride*divisor; return Interval( min + min%(new_stride), max, new_stride); } /+ Interval opAnd(Interval other) const { auto new_min = (this.min > other.min) ? this.min : other.min; auto new_max = (this.max < other.max) ? this.max : other.max; if( this.stride == 1 && other.stride == 1 ) return Interval(new_min, new_max); /* The problem now is determining the new stride and the new minimum. Consider the following: (inter[0..10] / 2) & (inter[0..10] / 3) == [0, 2, 4, 6, 8] & [0, 3, 6, 9] == [0, 6] (inter[0..10] / 2) & (inter[1..10] / 2) == [] (inter[0..10] / 2) & (inter[1..10] / 3) == [0, 2, 4, 6, 8] & [1, 4, 7] == [4] */ } +/ pure size_t length() const { if( stride == 1 ) return (max-min) + 1; else return (max-min)/stride + 1; } pure T[] toArray() const { auto arr = new T[length]; auto p = &arr[0]; for( T i = min; i <= max; i += stride ) *(p++) = i; return arr; } int opApply(int delegate(ref T) dg) const { int r = 0; for( T i = min; i <= max; i += stride ) { auto v = i; r = dg(v); if( r ) break; } return r; } /* * * Range interface * */ pure bool empty() const { return (max T.max-stride ) max -= stride; else min += stride; } void retreat() { if( max < T.min+stride ) min += stride; else max -= stride; } pure T opIndex(size_t offset) const { if( offset >= length ) throw new Exception("out of bounds of interval"); return min + stride*offset; } Interval opSlice(size_t a, size_t b) const { auto l = length; if( a >= length || b >= length ) throw new Exception("out of bounds of interval"); return Interval( min + stride*a, max + stride*b, stride); } } struct inter { static Interval!(T) opCall(T)(T a, T b) { return Interval!(T)(a+1, b-1); } static Interval!(T) opIndex(T)(T a, T b) { return Interval!(T)(a, b); } static Interval!(T) opSlice(T)(T a, T b) { return Interval!(T)(a, b-1); } } import std.stdio : writef, writefln; string trim(string s) { while( s.length > 0 && (s[0] == '\t' || s[0] == ' ') ) s = s[1..$]; while( s.length > 0 && (s[$-1] == '\t' || s[$-1] == ' ') ) s = s[0..$-1]; return s; } string dump(string expr) { return ` writefln("%s = %s"`~`, "`~trim(expr)~`", `~trim(expr)~`); `; } void main() { mixin(dump(q{ inter[0, 10] })); mixin(dump(q{ inter[0..10] })); mixin(dump(q{ inter(0, 10) })); writefln(""); mixin(dump(q{ inter[0..10] % 2 })); mixin(dump(q{ inter[1..10] % 2 })); mixin(dump(q{ inter[1..10] / 2 })); mixin(dump(q{ 6 in inter[0..10] % 2 })); mixin(dump(q{ 7 in inter[0..10] % 2 })); writefln(""); mixin(dump(q{ inter[0, 30] / 2 })); mixin(dump(q{ (inter[0, 30] / 2).length })); mixin(dump(q{ (inter[0, 30] / 2) / 3 })); mixin(dump(q{ ((inter[0, 30] / 2) / 3).length })); mixin(dump(q{ ((inter[0, 30] / 2) / 3).toArray })); } /* Output: inter[0, 10] = inter[0, 10] inter[0..10] = inter[0, 9] inter(0, 10) = inter[1, 9] inter[0..10] % 2 = inter[0, 8] / 2 inter[1..10] % 2 = inter[2, 8] / 2 inter[1..10] / 2 = inter[1, 9] / 2 6 in inter[0..10] % 2 = true 7 in inter[0..10] % 2 = false inter[0, 30] / 2 = inter[0, 30] / 2 (inter[0, 30] / 2).length = 16 (inter[0, 30] / 2) / 3 = inter[0, 30] / 6 ((inter[0, 30] / 2) / 3).length = 6 ((inter[0, 30] / 2) / 3).toArray = [0 6 12 18 24 30] */