#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