#ifndef modulus_range_h_
#define modulus_range_h_

#include "modulus.h"
/*
 * How do you tell if an hour is between two times?
 * Think about that for a second.
 *
 * Now, how do you tell if an hour is between 11(am) and 2(pm)?
 *
 * Not so easy now, is it?
 *
 * Here's ModulusRange to the rescue!
 *
 * Just give it two Moduluses, a start and an end, and it will tell you
 * if another Modulus is between them
 */
template<typename modulus_base>
class ModulusRange {

public:

ModulusRange(
modulus_base start=0,
modulus_base end=0 ) :start(start), end(end) {}

bool contains(modulus_base elem) const{
/*
There are two cases here:
CASE 1: start < end (mod max_value)
0      start          end     max_value
[--------{=============}-----------)
To be in this range, a Modulus needs to be between start and end:
  start <= elem <= end

CASE 2: end < start (mod max_value)
0       end          start    max_value
[========}-------------{===========)
To be in this range, a Modulus needs to be outside end and start:
 ! (end <= elem <= start)

*/
	if(start < end) {
		return start <= elem && elem <= end;
	}else{
		return start <= elem || elem <= end;
	}
}

/*
 * Determine if this range overlaps another range
 *
 * That is, if they have any elements in common
 */
bool overlaps(const ModulusRange &other) const{
	return this->contains(other.start) ||
		   other.contains(this->start);
}

/**
 * If this range overlaps the other, returns a range representing
 * their overlap.
 *
 * Otherwise, it's going to be jibberish
 */
ModulusRange get_overlap(const ModulusRange &other) const{
	if( this->contains(other.start) && this->contains(other.end)){
		return other;
	}
	else if(other.contains(this->start) && other.contains(this->end)){
		return *this;
	}
	else if(this->contains(other.start) ){
		return ModulusRange(other.start, this->end);
	}
	else{
		return ModulusRange(this->start, other.end);
	}
}

/*
 * Returns an element of this range somewhere in the middle
 * of the range
 */
modulus_base get_middle() const{
	//this math is wonky because if we average start and end,
	//we will end up with an instance outside of this range
	//in CASE 2, shown above
	return start + (end-start)/2;
}

bool operator()(const modulus_base &x) {return contains(x);}

public:
/* I am making these class members public.  Why? 3 reasons:
 *
 *	1) We aren't going to change them - they're const.
 * 		Even if we wanted to, we couldn't.
 *
 *	2) They're not really internal to the class
 *		They're more like template parameters or hidden arguments
 *		to the contains() method.  Hiding their access behind
 *		unnecessary getter and setters is asinine and adds no
 *		value to the class
 *
 *	3) If you want to change a range, you can't
 *		Just make another ModulusRange
 *		They're easy to make, impossible to modify
 *		Just like the integers
 *
*/
const modulus_base start;
const modulus_base end;
};

#endif
