// A small context defining the notion of an ordered otype.  (The standard
// library should probably contain a context for this purpose.)
trait ordered(otype T) {
    int ?<?(T, T), ?<=?(T, T);
};

// A subrange otype resembling an Ada subotype with a base otype and a range
// constraint.
otype subrange(otype base_t | ordered(base_t), base_t low = 0, base_t high = 8) = base_t;

// Note that subrange() can be applied to floating-point and pointer otypes, not
// just integral otypes.
//   This requires a "otype generator" extension to Cforall.  Type generators
// must accept otype and non-otype parameters, which is beyond what we discussed
// previously.  Type parameters must be usable in the declaration of
// subsequent parameters: parameter T is used to declare parameters "low"
// and "high".

// Example usage:
subrange(unsigned, 1, 31) day_of_month;
subrange(char, 'a', 'z')  lcase;
subrange(int, 0, (rand() & 0xF) ) foo;

// What sorts of expressions can be used as arguments of otype generators?  Is
// "subrange(int, 0, rand() & 0xF)" legal?  Probably.  The nearest C equivalent
// to the "low" and "high" arguments is the array size in a variable-length
// array declaration, and C allows assignment expressions there.

// Convenient access to subrange bounds, for instance for iteration:
forall (otype T, T low, T high)
T lbound( subrange(T, low, high) v) {
    return low;
}

forall (otype T, T low, T high)
T hbound( subrange(T, low, high) v) {
    return high;
}

// Example usage:
unsigned lday = lbound(day_of_month);

// Assignment from the base otype, with bounds checking.  I'll ignore the issue
// of exception handling here.  Inlining allows the compiler to eliminate
// bounds checks.
forall (otype T | ordered(T), T low, T high)
inline subrange(T, low, high) ?=?(subrange(T, low, high)* target, T source) {
    if (low <= source && source <= high) *((T*)target) = source;
    else abort();
    return target;
}

// Assignment between subranges with a common base otype.  The bounds check
// compares range bounds so that the compiler can optimize checks away when the
// ranges are known to overlap.
forall (otype T | ordered(T), T t_low, T t_high, T s_low, T s_high)
inline subrange(T, t_low, t_high) ?=?(subrange(T, t_low, t_high)* target,
				      subrange(T, s_low, s_high) source) {
    if ( (t_low <= s_low || t_low <= source)
	 && (s_high <= t_high || source <= t_high) ) *((T*)target) = source;
    else abort();
    return target;
}
