15,793,609 members
Articles / Programming Languages / Objective C
Tip/Trick

# Value Ranges or Diapasons

Rate me:
26 Dec 2020CPOL1 min read 5.3K   41   4   2
Proposal for implementation diapasons of values or it ranges
In everyday life, we encounter various ranges or diapasons or ranges - time ranges, money ranges, etc. I propose to write an implementation for them.

## Introduction

Every time in every new job, I encounter the same problem. There is no tool to work with diapasons or ranges. I don't know why, but I forced to develop it again and again. For the sake of the future, I will write it once and will reuse.

## Solution

The simplest solution is:

C++
```//
// Simple diapason
//
struct ValueRange
{
double start = 0.0;
double stop = 0.0;

// returns distance |start, stop|
double length() const { return stop - start; }
// returns whether u inside this
bool includes(double u) const { return start <= u && u <= stop; }
// returns corrected value (inserted into range, if it was outside)
double included(double v) const;
};```

But somebody may want works with `float`s rather than `double`s. So the code will change to:

C++
```//
// Simple template diapason
//
template<typename T>
struct ValueRangeT
{
using Type = T;

// to be like std::
using value_type = T; // used type

T start{};
T stop{};

// returns distance |start, stop|
T length() const { return stop - start; }
// returns whether u inside this
bool includes(T u) const { return start <= u && u <= stop; }
// returns corrected value (inserted into range, if it was outside)
T included(T v) const;
};```

You will be warned that this `ValueRange` will work only with closed intervals `[start, stop]`. To solve this, let's delegate comparison to the strategy defined later:

C++
```//
// ValueRangeImpl - general diapason from start to stop
// It maybe:
// -closed [start, stop],
// -open (start, stop),
// -half closed/opSen {(start, stop] or [start, stop)}
// -exclude range from all possible values ]start, stop[ =>
//    [-inf, start][stop, inf] or [-inf, start)(stop, inf]
//
// specialization requires comparison strategies (traits)
template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
struct ValueRangeImpl
{
using LTrait = LeftComparisionTrait;
using RTrait = RightComparisionTrait;

// to be like std::
using value_type = T; // used type

T start = {};
T stop = {};

// returns distance |start, stop|
T length() const { return stop - start; }
// returns whether u inside this
bool includes(T u) const;
// returns corrected value (inserted into range, if it was outside)
T included(T v) const;
};```

The i`ncludes()` and `included()` are:

C++
```template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
bool ValueRangeImpl<T, LeftComparisionTrait, RightComparisionTrait>::includes(T u) const
{
return LTrait()(start, u) && RTrait()(stop, u);
}

template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
T ValueRangeImpl<T, LeftComparisionTrait, RightComparisionTrait>::included(const T v) const
{
return !LTrait()(start, v) ? start : !RTrait()(stop, v) ? stop : v;
}```

As the comparison strategy may be used `std::less_equal`:

C++
```// final value ranges implementation
// interval, with both boundary included: [a,b]
using ClosedValueRange = ValueRangeImpl<
double,
typename std::less_equal<double>,
typename std::greater_equal<double>>;

// interval, with both boundary excluded: (a,b)
using OpenValueRange = ValueRangeImpl<
double,
typename std::less<double>,
typename std::greater<double>>;

// interval, with excluded left boundary and included right one: (a,b]
using OpenClosedValueRange = ValueRangeImpl<
double,
typename std::less<double>,
typename std::greater_equal<double>>;

// interval, with included left boundary and excluded right one: [a,b)
using ClosedOpenValueRange = ValueRangeImpl<
double,
typename std::less_equal<double>,
typename std::greater<double>>;```

The simplest samples:

C++
```EXPECT_TRUE  (ClosedValueRange(1, 5).includes(1));
EXPECT_TRUE  (ClosedValueRange(1, 5).includes(5));
EXPECT_FALSE (OpenValueRange(1, 5).includes(1));
EXPECT_FALSE (OpenValueRange(1, 5).includes(5));
EXPECT_FALSE (OpenClosedValueRange(1, 5).includes(1));
EXPECT_TRUE  (OpenClosedValueRange(1, 5).includes(5));
EXPECT_TRUE  (ClosedOpenValueRange(1, 5).includes(1));
EXPECT_FALSE (ClosedOpenValueRange(1, 5).includes(5));```

## Usage

Let's imagine that we are working under `SuitSellerSolution`. To put the suit on the market, we have to describe its size.

C++
```// first approach to SuitSize
class SuitSize
{
public:
std::string m_name;
ClosedValueRange m_waist;
};

// three sizes for simplicity:
SuitSize sizeS{"S", {78, 82}};
SuitSize sizeL{"M", {82, 86}};
SuitSize sizeM{"S", {86, 90}};```

But what about values `82` and `86`? What size do they belong? Let's use partially closed intervals.

C++
```// SuitSize, which used appropriate interval
template<typename WaistWrap>
class SuitSizeImpl
{
public:
std::string m_name;
WaistWrap m_waist;
};

using SuitSizeS = SuitSizeImpl<ClosedOpenValueRange>;
using SuitSizeM = SuitSizeImpl<ClosedOpenValueRange>;
using SuitSizeL = SuitSizeImpl<ClosedValueRange>;

SuitSizeS sizeS{"S", {78, 82}};
SuitSizeS sizeM{"M", {82, 86}};
SuitSizeM sizeL{"L", {86, 90}};```

Great! There are no conflicts in this `SuitSize` edition.

The next application calculates vacation salary. There is a requirement that vacation includes both boundaries, or somebody has a vacation from 11.07.2027 till 25.07.2027 exclusively, i.e., 26.07.2027 employee should be at work at 8:00 AM.

This implies the presence of class:

C++
```// this class describes vacation
class Vacation
{
using Date = boost::gregorian::date; // may be your own class for Date representation
// Date comparison functors:
// start vacation comparison
struct LeftDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 <= d2;
}
};
// end vacation comparison
struct RightDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 > d2; // open interval
}
};

// Date interval [vacationStart, vacationStop)
using DateValueRange = ValueRangeImpl<Date, LeftDateCompare, RightDateCompare>;

DateValueRange m_duration;
};```

While then the system was demonstrated to users, they found, that usually vacation duration calculates inclusively. The only thing to fix is:

C++
```class Vacation
{
...
// end vacation comparison
struct RightDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 >= d2; // open interval
}
};
....
};```

I use `ValueRange` in the definition of the domain of a parametric function, and I will talk about it next time.

## History

• 6th December, 2020: Initial version

Written By
Software Developer (Senior) Ormco
Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

 Either change `includes` to C++ `return LTrait()(start, u) && RTrait()(stop, u);` or adapt the boundary predicates