I want to sort a vector of dates and I wrote compare function for it:
#include <iostream>
struct date {
int day;
int month;
int year;
};
int compare_dates(date a, date b) {
if (a.year < b.year) {
return -1;
} else if (a.year == b.year) {
if (a.month < b.month) {
return -1;
} else if (a.month == b.month) {
if (a.day < b.day) {
return -1;
} else if (a.day > b.day) {
return 1;
}
} else {
return 1;
}
} else {
return 1;
}
return 0;
}
int main() {
date a = {};
date a.day = 19;
date a.month = 11;
date a.year = 2016;
date b = {};
date b.day = 20;
date b.month = 11;
date b.year = 2016;
compare_dates(a, b) // -1
compare_dates(b, a) // 1
compare_dates(b, b) // 0
return 0;
}
It is working well, but compare_dates function looks awful. Is there any idea how can I improve it?
I'm not a C++ expert and the others are pointing out that std::sort() doesn't require three-way comparison, only a <. But to clean up your code as written:
Your compare_dates() keeps doing three-way comparisons for >/</==, and wants a +1/-1/0 return value. So declare a three-way cmp() helper function which does that, like we do in Python. Now your code reduces to:
int cmp(int x, int y) {
return (x>y) ? 1 : ((x<y) ? -1 : 0);
}
int compare_dates(date a, date b) {
if (cmp(a.year, b.year) != 0)
return cmp(a.year, b.year);
if (cmp(a.month, b.month) != 0)
return cmp(a.month, b.month);
return cmp(a.day, b.day);
}
You only fall-through into doing the lower-order comparisons if the higher-order comparison gave '=='. So that allows you to avoid all the else-clauses, braces and indenting, which keeps your indent level constant and is easy on the eyes. It also calls out the symmetry of the computations.
This will be enough for sorting a containers of dates into ascending order:
bool compareDates(date const& lhs, date const& rhs) const {
if(lhs.year == rhs.year) {
if(lhs.month == rhs.month) {
return lhs.day < rhs.day;
}
return lhs.month < rhs.month;
}
return lhs.year < rhs.year;
}
// sort(dates, dates + n, compareDates);
Edit
I intentionally didn't handle -1 separately as for overriding comparator of STL containers like std::sort(), priority_queue or std::set we don't need to provide integer return code and make to code relatively complex. Boolean is enough.
What about using the fact that a day use only 4 bit and a month only 5?
#include <iostream>
struct date
{
int day;
int month;
int year;
};
int compare_dates (date a, date b)
{
long da { (a.year << 9) + (a.month << 4) + a.day };
long db { (b.year << 9) + (b.month << 4) + b.day };
return da < db ? -1 : (da > db);
}
int main()
{
date a = { 19, 11, 2016 };
date b = { 20, 11, 2016 };
std::cout << compare_dates(a, b) << std::endl; // print -1
std::cout << compare_dates(b, a) << std::endl; // print 1
std::cout << compare_dates(b, b) << std::endl; // print 0
return 0;
}
--- EDIT ---
As pointed by Christian Hackl, this code is a little obscure.
I hope that can be more comprensible if you translate the bitfield part in the date struct, trasforming it in a union.
So you can initialize separate year, month and day components and use a full component for compares.
Something as follows
#include <iostream>
union date
{
struct
{
unsigned long day : 5U;
unsigned long month : 4U;
unsigned long year : 23U;
} s ;
unsigned long full;
};
int compare_dates (date const & a, date const & b)
{ return a.full < b.full ? -1 : (a.full > b.full); }
int main()
{
date a = { { 19, 11, 2016 } };
date b = { { 20, 11, 2016 } };
std::cout << compare_dates(a, b) << std::endl; // print -1
std::cout << compare_dates(b, a) << std::endl; // print 1
std::cout << compare_dates(b, b) << std::endl; // print 0
return 0;
}
Related
I have a list of {a,b} and i need all possible combinatations where say n=3.
so:
[a,b,a],
[b,a,b]
[a,a,a]
[b,b,b]
etc.
Is there a name of such a problem
My current solution just uses random sampling and is very inefficient:
void set_generator(const vector<int>& vec, int n){
map<string, vector<int>> imap;
int rcount = 0;
while(1){
string ms = "";
vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
cout << ms << endl;
}
}
set_generator({1,2},3);
Let us call b the size of the input vector.
The problem consists in generating all numbers from 0 to b^n - 1, in base b.
A simple solution increments the elements of an array one by one, each from 0 to b-1.
This is performed by the function increment in the code hereafter.
Output:
111
211
121
221
112
212
122
222
The code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
void set_generator_op (const std::vector<int>& vec, int n){
std::map<std::string, std::vector<int>> imap;
int rcount = 0;
while(1){
std::string ms = "";
std::vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
std::cout << ms << "\n";
}
}
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
void set_generator_new (const std::vector<int>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
while (true) {
std::string permut = "";
for (auto &k: cpt) {
permut += std::to_string (vec[k]);
}
std::cout << permut << "\n";
if (!increment(cpt, base)) return;
}
}
int main() {
set_generator_op ({1,2},3);
std::cout << "\n";
set_generator_new ({1,2},3);
}
Following advices of Jarod42, I have
suppressed the useless conversion to a string
used a more elegant do ... while instead of the while true
inversed the iterators for printing the result
Moreover, I have created a templated version of the program.
New output:
111
112
121
122
211
212
221
222
aaa
aab
aba
abb
baa
bab
bba
bbb
And the new code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
template <typename T>
void set_generator_new (const std::vector<T>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
do {
for (auto it = cpt.rbegin(); it != cpt.rend(); ++it) {
std::cout << vec[*it];
}
std::cout << "\n";
} while (increment(cpt, base));
}
int main() {
set_generator_new<int> ({1,2}, 3);
std::cout << "\n";
set_generator_new<char> ({'a','b'}, 3);
}
Besides the concrete answer for integer usage, I want to provide a generic way I needed during test case construction for scenarios with a wide spread of various parameter variations. Maybe it's helpful to you too, at least for similar scenarios.
#include <vector>
#include <memory>
class SingleParameterToVaryBase
{
public:
virtual bool varyNext() = 0;
virtual void reset() = 0;
};
template <typename _DataType, typename _ParamVariationContType>
class SingleParameterToVary : public SingleParameterToVaryBase
{
public:
SingleParameterToVary(
_DataType& param,
const _ParamVariationContType& valuesToVary) :
mParameter(param)
, mVariations(valuesToVary)
{
if (mVariations.empty())
throw std::logic_error("Empty variation container for parameter");
reset();
}
// Step to next parameter value, return false if end of value vector is reached
virtual bool varyNext() override
{
++mCurrentIt;
const bool finished = mCurrentIt == mVariations.cend();
if (finished)
{
return false;
}
else
{
mParameter = *mCurrentIt;
return true;
}
}
virtual void reset() override
{
mCurrentIt = mVariations.cbegin();
mParameter = *mCurrentIt;
}
private:
typedef typename _ParamVariationContType::const_iterator ConstIteratorType;
// Iterator to the actual values this parameter can yield
ConstIteratorType mCurrentIt;
_ParamVariationContType mVariations;
// Reference to the parameter itself
_DataType& mParameter;
};
class GenericParameterVariator
{
public:
GenericParameterVariator() : mFinished(false)
{
reset();
}
template <typename _ParameterType, typename _ParameterVariationsType>
void registerParameterToVary(
_ParameterType& param,
const _ParameterVariationsType& paramVariations)
{
mParametersToVary.push_back(
std::make_unique<SingleParameterToVary<_ParameterType, _ParameterVariationsType>>(
param, paramVariations));
}
const bool isFinished() const { return mFinished; }
void reset()
{
mFinished = false;
mNumTotalCombinationsVisited = 0;
for (const auto& upParameter : mParametersToVary)
upParameter->reset();
}
// Step into next state if possible
bool createNextParameterPermutation()
{
if (mFinished || mParametersToVary.empty())
return false;
auto itPToVary = mParametersToVary.begin();
while (itPToVary != mParametersToVary.end())
{
const auto& upParameter = *itPToVary;
// If we are the very first configuration at all, do not vary.
const bool variedSomething = mNumTotalCombinationsVisited == 0 ? true : upParameter->varyNext();
++mNumTotalCombinationsVisited;
if (!variedSomething)
{
// If we were not able to vary the last parameter in our list, we are finished.
if (std::next(itPToVary) == mParametersToVary.end())
{
mFinished = true;
return false;
}
++itPToVary;
continue;
}
else
{
if (itPToVary != mParametersToVary.begin())
{
// Reset all parameters before this one
auto itBackwd = itPToVary;
do
{
--itBackwd;
(*itBackwd)->reset();
} while (itBackwd != mParametersToVary.begin());
}
return true;
}
}
return true;
}
private:
// Linearized parameter set
std::vector<std::unique_ptr<SingleParameterToVaryBase>> mParametersToVary;
bool mFinished;
size_t mNumTotalCombinationsVisited;
};
Possible usage:
GenericParameterVariator paramVariator;
size_t param1;
int param2;
char param3;
paramVariator.registerParameterToVary(param1, std::vector<size_t>{ 1, 2 });
paramVariator.registerParameterToVary(param2, std::vector<int>{ -1, -2 });
paramVariator.registerParameterToVary(param3, std::vector<char>{ 'a', 'b' });
std::vector<std::tuple<size_t, int, char>> visitedCombinations;
while (paramVariator.createNextParameterPermutation())
visitedCombinations.push_back(std::make_tuple(param1, param2, param3));
Generates:
(1, -1, 'a')
(2, -1, 'a')
(1, -2, 'a')
(2, -2, 'a')
(1, -1, 'b')
(2, -1, 'b')
(1, -2, 'b')
(2, -2, 'b')
For sure, this can be further optimized/specialized. For instance you can simply add a hashing scheme and/or an avoid functor if you want to avoid effective repetitions. Also, since the parameters are held as references, one might consider to protect the generator from possible error-prone usage via deleting copy/assignement constructors and operators.
Time complexity is within the theoretical permutation complexity range.
So I'm trying to create this priority queue to handle my "Order" objects, I'm running into a problem where an object containing the same key/priority will be placed at an early earlier position than others initialized first. I have provided the expected and received output alongside the 83 lines of code of how I constructed my heap with notes
#include <iostream>
#include <vector>
struct Order {
int value = -1;
int priority = -1;
bool operator <(Order const& RHS) { return priority < RHS.priority; }
};
class heap {
private:
std::vector<Order> orders{ Order{} };
int size{}; //initalizes it at 0
int p(int index) { return index >> 1; }
int l(int index) { return index << 1; }
int r(int index) { return (index << 1) + 1; }
public:
bool isEmpty() const { return size == 0; }
void shiftUp(int position);
void shiftDown(int position);
void add(Order new_entry);
Order removeTop();
Order& getTop() { return orders[1]; }
};
template <typename T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
heap h;
h.add(Order{1,3}); h.add(Order{2,2});
h.add(Order{3,3}); h.add(Order{5,1});
h.add(Order{6,2}); h.add(Order{7,2});
h.add(Order{8,3}); h.add(Order{9,1});
h.add(Order{23,3});
std::cout << "value" << " key(priority)" << "\n";
for (int i = 0; i < 8; i++) {
Order temp = h.removeTop();
std::cout << temp.value << "\t " << temp.priority << "\n";
}
}
void heap::shiftUp(int position) {
if (position > size) return;
if (position == 1) return;
if (orders[p(position)] < orders[position]) {
mySwap(orders[position], orders[p(position)]);
shiftUp(p(position));
}
}
void heap::shiftDown(int position) {
if (position > size) return;
int greaterPosition = position;
if (l(position) <= size && orders[position] < orders[l(position)])
greaterPosition = l(position);
if (r(position) <= size && orders[greaterPosition] < orders[r(position)])
greaterPosition = r(position);
if (greaterPosition != position) {
mySwap(orders[position], orders[greaterPosition]);
shiftDown(greaterPosition);
}
}
void heap::add(Order new_entry) {
if (size + 1 >= orders.size()) orders.push_back(Order{});
orders[++size] = new_entry;
shiftUp(size);
}
Order heap::removeTop() {
Order temp = orders[1];
mySwap(orders[1],orders[orders.size() - 1]); size--;
orders.pop_back();
shiftDown(1);
return temp;
}
/*
Expected Output
Value key(priority)
1 3
3 3
8 3
23 3
2 2
6 2
7 2
5 1
9 1
Recieved/wrong Output
value key(priority)
1 3
23 3
3 3
8 3
2 2
6 2
7 2
5 1
*/
Fixed code from answered information above
#include <iostream>
#include <vector>
struct Order {
int value = -1;
int priority = -1;
int FIFO;
bool operator <(Order const& RHS) {
if (priority == RHS.priority)
return FIFO > RHS.FIFO;
else
return priority < RHS.priority;
} //compares keys for larger presidence
};
class heap {
private:
std::vector<Order> orders{ Order{} };
int size{}; //initalizes it at 0
int p(int index) { return index >> 1; }
int l(int index) { return index << 1; }
int r(int index) { return (index << 1) + 1; }
public:
bool isEmpty() const { return size == 0; }
void shiftUp(int position);
void shiftDown(int position);
void add(Order new_entry);
Order removeTop();
Order& getTop() { return orders[1]; }
};
template <typename T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
heap h;
h.add(Order{1,3}); h.add(Order{2,2});
h.add(Order{3,3}); h.add(Order{5,1});
h.add(Order{6,2}); h.add(Order{7,2});
h.add(Order{8,3}); h.add(Order{9,1});
h.add(Order{23,3});
std::cout << "value" << " key(priority)" << "\n";
for (int i = 0; i < 8; i++) {
Order temp = h.removeTop();
std::cout << temp.value << "\t " << temp.priority << "\n";
}
}
void heap::shiftUp(int position) {
if (position > size) return;
if (position == 1) return;
if (orders[p(position)] < orders[position]) {
mySwap(orders[position], orders[p(position)]);
shiftUp(p(position));
}
}
void heap::shiftDown(int position) {
if (position > size) return;
int greaterPosition = position;
if (l(position) <= size && orders[position] < orders[l(position)])
greaterPosition = l(position);
if (r(position) <= size && orders[greaterPosition] < orders[r(position)])
greaterPosition = r(position);
if (greaterPosition != position) {
mySwap(orders[position], orders[greaterPosition]);
shiftDown(greaterPosition);
}
}
void heap::add(Order new_entry) {
if (size + 1 >= orders.size()) orders.push_back(Order{});
new_entry.FIFO = size + 1;
orders[++size] = new_entry;
shiftUp(size);
}
Order heap::removeTop() {
Order temp = orders[1];
mySwap(orders[1],orders[orders.size() - 1]); size--;
orders.pop_back();
shiftDown(1);
return temp;
}
In general, heap does not have FIFO property until you implement something that helps doing so. In your order class, you are only comparing using the priority value. In your Order class, you are comparing two Orders by only their priority value. You need a additional variable that serves as the purpose for recording the timing when that value was inserted, and compare according to that.
If you are using the variable value for that purpose, you need to specify in your overloaded < method, what do you want to do when two Order's priority values are equal. Currently, you are only using the priority variable to compare. You are not specifying what do you want to do when the priority of two Orders are equal. You have to specify what do you want to do when the priority value of two variables are equal. Maybe compare a timing variable.
first thing to emphases is that my question is NOT about error handling in constructor.
So I'm doing this assignment to writing a Date class, and first thing is about the constructor, of course it has to be able to handle invalid date input, I already have my constructor implemented as shown below with the error handling part implemented using try-catch:
My Date Constructor:
Date(unsigned y, unsigned m, unsigned d)
{
try {
check_valid(y, m, d);
my_year = y; my_month = m; my_day = d;
}
catch (const std::exception& msg) {
std::cerr << msg.what() << std::endl;
}
}
check_valid function:
void Date::check_valid(unsigned y, unsigned m, unsigned d)
{
MYASSERT(y >= 1900 && y <2200, "The year is invalid");
MYASSERT(m >= 1 && m <= 12, "The year is invalid");
MYASSERT(d >= 1 && d <= dmax, "The year is invalid"); //dmax is just no. of days in the month
}
#define MYASSERT(cond, msg) \
{ \
if (!(cond)) \
{ \
throw std::invalid_argument(msg); \
} \
}
The Question:
I'm asked to write a back testing program: randomly generate a large number of INVALID date (with the seed recorded) to test if the constructor is able to perform error handling successfully. Since input is a invalid date, every test should throw an expectation. So, if some test fails (meaning doesn't throw an exception given a invalid date input) Print out the random seed used for the random number generator, so that a programmer can re-use the same seed and reproduce the error.
I'm stuck with how to do this, how do i check if an expectation msg is throw? what should go into the if statement?
while (counter < 1000) {
seed = rand();
srand(seed);
unsigned y = rand() % 500 + 1800; //rand year between (1800, 2299)
unsigned m = rand() % 20; //rand month between (0, 19)
unsigned d = rand() % 40; //rand day between (0, 39)
if (! isValidDate(y, m, d)) //some function to filter out the valid date
{
counter++;
Date somedate(y, m, d); //use the constructor
{
// the constructor is used above, but i have no idea if an expectation is thrown or not
// if an expectation is thrown, then print seed, how do i write this code?
}
}
}
I recently came across a blog about testing at Google, where they linked an example of how they write and test code. One of their cases there looks very much like something you could use here (testing that something should fail, and as other comments mentioned; throw an exception):
public void testStart_whileRunning() {
stopwatch.start();
try {
stopwatch.start();
fail();
} catch (IllegalStateException expected) {
}
assertTrue(stopwatch.isRunning());
}
The example is in Java, but the principle is identical in C++: Have a fail() method that fails the test unconditionally if it is run, but which is skipped if your code "correctly fails".
I played around a little - again not complete since no calender is checked:
See also code on cpp.sh
#include <iostream>
#include <string>
#include <stdexcept>
class Date
{
private:
unsigned my_year;
unsigned my_month;
unsigned my_day;
void assertValid( void );
void assertYear( void );
void assertMonth( void );
void assertDay( void );
void fail( std::string message );
public:
Date( unsigned y, unsigned m, unsigned d);
bool isLeapYear( void );
};
Date::Date(unsigned y, unsigned m, unsigned d)
{
my_year = y;
my_month = m;
my_day = d;
assertValid();
}
//simple checks for Gregorian calendar
void Date::assertValid( void )
{
assertYear();
assertMonth();
assertDay();
}
void Date::assertYear( void )
{
if( my_year < 1900 || my_year > 2200 )
{
fail( "invalid year, must be >1900 and < 2200" );
}
}
void Date::assertMonth( void )
{
if( my_month < 1 || my_month > 12 )
{
fail("invalid month, must be 1 - =12");
}
}
void Date::assertDay( void )
{
if( my_day == 0 )
{
fail("day must not be 0");
}
switch( my_month )
{
case 4:
case 6:
case 9:
case 11:
if( my_day > 30 )
{
fail("invalid day of month, must be <31");
}
break;
case 2:
if( isLeapYear() && my_day > 29 )
{
fail("invalid day of month, must be < 30");
}
else if( my_day > 28 )
{
fail("invalid day of month, must be < 30");
}
break;
default:
if( my_day > 31 )
{
fail("invalid day of month, must be < 32");
}
}
}
bool Date::isLeapYear( void )
{
return my_year % 4 == 0
&& ( my_year % 100 != 0
|| my_year % 400 == 0 )
;
}
void Date::fail( std::string message )
{
throw std::invalid_argument(message);
}
bool testDate( unsigned year, unsigned month, unsigned day, bool isValidDate )
{
try
{
Date( year, month, day );
}
catch( std::invalid_argument& x)
{
if( ! isValidDate )
{
return true;
}
}
return isValidDate;
}
int main()
{
int failures = 0;
int successes = 0;
testDate( 1,1,1, false) ? ++successes : ++failures;
testDate( 2000,2,29, true) ? ++successes : ++failures;
testDate( 1900, 2, 29, false ) ? ++successes : ++failures;
testDate( 1980, 1,1, true ) ? ++successes : ++failures;
std::cout << "Number of tetsts: " << successes + failures << std::endl;
std::cout << "Number of failures: " << failures <<std::endl;
}
I have a program that asks the user to input to dates then it displays which one is more recent I've done it like this
if (year1>year2 || month1>month2 || day1>day2)
return -1;
if (year1<year2 || month1<month2 || day1<day2)
return +1;
but the output is not quite correct.
Here's a clean way to do it:
#include <tuple> // for std::tie
auto date1 = std::tie(year1, month1, day1);
auto date2 = std::tie(year2, month2, day2);
if (date1 == date2)
return 0;
return (date1 < date2) ? -1 : 1;
The comparisons of std::tie objects are lexicographical, so this returns -1 if date1 is less than date2, 0 if they are the same, and 1 if date1 is greater than date2.
You might be better off defining your own date type (or use boost::datetime).
struct Date
{
unsigned year;
unsigned month;
unsigned day;
};
bool operator<(const Date& lhs, const Date& rhs)
{
return std::tie(lhs.year, lhs.month, lhs.day) <
std::tie(rhs.year, rhs.month, rhs.day);
}
bool operator>(const Date& lhs, const Date& rhs) { .... }
bool operator==(const Date& lhs, const Date& rhs) { .... }
int date_cmp(const Date& lhs, const Date& rhs)
{
// use operators above to return -1, 0, 1 accordingly
}
You need a much more complicated check than that:
if (year1 > year2)
return -1;
else if (year1 < year2)
return +1;
if (month1 > month2)
return -1;
else if (month1 < month2)
return +1;
if (day1 > day2)
return -1;
else if (day1 < day2)
return +1;
return 0;
NOTE: Returning -1 for first is greater than second seems counter-intuititive to me, however I have followed the semantics provided by the OP.
This statement
if (year1>year2 || month1>month2 || day1>day2)
return -1;
tests if any one of the three conditions is true. So, if year1 is higher than year 2, or month1 is higher than month2. Lets stop there. Consider
year1 = 2013, month1 = 12, day1 = 31;
year2 = 2014, month2 = 1, day1 = 1;
We know that, infact, year2 is a higher value, but what happens is
is year1 > year2? no
ok, but is month1 > month2? yes
This makes it look like the first year is a higher value, but it's not, it just a higher month value.
As you get further into C++ you'll find that it's a good idea to try and adopt a convention of making all your comparisons use a single operator (< or >), when you reach a point where you are working with operators you'll understand why.
if (year2 < year1)
return 1;
// we reach this line when year1 <= year2
if (year1 < year2) // elimnate the < case
return -1;
// having eliminated both non-matches,
// we know that by reaching point that both
// dates have the same year. Now repeat for
// the month value.
if (month2 < month1)
return 1;
if (month1 < month2)
return -1;
// year and month must be the same, repeat for day.
if (day2 < day1)
return 1;
if (day1 < day2)
return -1;
return 0; // exact match
//You can try this
int lday,lmonth,lyear;
int nday,nmonth,nyear;
int lhour,lminute;
int nhour,nminute;
sscanf(New_Time,"%d-%d",&nhour,&nminute); //reads the numbers
sscanf(Last_Time,"%d-%d",&lhour,&lminute); //from the string
sscanf(New_Date,"%d-%d-%d",&nday,&nmonth,&nyear);
sscanf(Last_Date,"%d-%d-%d",&lday,&lmonth,&lyear);
//cout << "Last date: " << lday << "-" << lmonth << "-" << lyear <<endl;
//cout << "New date: " << nday << "-" << nmonth << "-" << nyear <<endl;
if(nyear>lyear)
return 0;
if(nyear==lyear) {
if(nmonth > lmonth)
return 0;
if (nmonth == lmonth) {
if(nday > lday)
return 0;
if (nday == lday) {
if( nhour > lhour)
return 0;
if( nhour == lhour) {
if(nminute>lminute) {
//cout << "new time >= last time" << endl <<endl;
return 0;
}
else return 1;
}
else return 1;
}
else return 1;
}
else return 1;
}
else return 1;
struct Day
{
int value;
explicit Day(int value)
{
this->value = value;
}
};
struct Month
{
int value;
explicit Month(int value)
{
this->value = value;
}
};
struct Year
{
int value;
explicit Year(int value)
{
this->value = value;
}
};
class Date {
public:
Date(Day newDay, Month newMonth, Year newYear)
{
_day = newDay.value;
_month = newMonth.value;
_year = newYear.value;
}
int GetYear() const {
return _year;
};
int GetMonth() const {
return _month;
};
int GetDay() const {
return _day;
};
private:
int _year;
int _month;
int _day;
};
bool operator < (const Date& lhs, const Date& rhs)
{
if (lhs.GetYear() == rhs.GetYear()) {
if (lhs.GetMonth() == rhs.GetMonth()) {
if (lhs.GetDay() == rhs.GetDay()) {
return false;
}
return lhs.GetDay() < rhs.GetDay();
}
return lhs.GetMonth() < rhs.GetMonth();
}
return lhs.GetYear() < rhs.GetYear();
};
Currently I'm writing a program that has a section to determine the difference in days between two dates, but by overloading the minus operator.
I'm currently staring at my screen drawing a complete blank. I have some fleeting thoughts in my head but they are exactly that, fleeting.
What's to happen in the main.cpp is that there are going to be two variables, for instance beethovenDeathDate and beethovenBirthDate that will be subtracted to determine how long he lived for. Which is something around 22000 days if I recall correctly.
So without further ado, here is my code:
Date.cpp
const std::string Date::MONTH_STRINGS[] =
{
"", //one based indexing
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
const int Date::DAYS_PER_MONTH[] =
{
0, //one based indexing
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
Date::Date(int day, int month, int year) : _year(year), _month(month), _day(day)
{
isValid();
}
Date::Date()
{
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
_year = now -> tm_year + 1900;
_month = now -> tm_mon + 1;
_day = now -> tm_mday;
}
int Date::maxDay(int month, int year)
{
int ret = DAYS_PER_MONTH[month];
if(isLeapYear(year) == true && month == 2)
{
++ret;
}
return ret;
}
void Date::addDay(bool forward)
{
if(forward)
{
if(_day < maxDay(_month, _year))
{
++_day;
}
else
{
_day = MIN_DAY;
++_month;
if(_month > MAX_MONTH)
{
_month = MIN_MONTH;
++_year;
}
}
}
else
{
if(_day <= MIN_DAY)
{
--_month;
if(_month < MIN_MONTH)
{
_month = MAX_MONTH;
--_year;
}
_day = maxDay(_month, _year);
}
else
{
--_day;
}
}
}
std::string Date::toString() const
{
if(isValid() == false)
{
return std::string();
}
std::stringstream ss;
ss << MONTH_STRINGS[_month] << " " << _day << ", " << _year;
return ss.str();
}
bool Date::isValid() const
{
if(_month < MIN_MONTH || _month > MAX_MONTH)
{
std::cerr << "Invalid date " << std::endl;
return false;
}
int daysThisMonth = maxDay(_month, _year);
if(_day < MIN_DAY || _day > daysThisMonth)
{
std::cerr << "Invalid date " << std::endl;
return false;
}
return true;
}
bool Date::isLeapYear(int year)
{
if(!(year % 4))
{
if(!(year % 100))
{
if(!(year % 400))
{
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
else
{
return false;
}
}
bool Date::isLeapYear() const
{
return isLeapYear(_year);
}
bool Date::isLeapDay() const
{
return isLeapDay(_day, _month, _year);
}
bool Date::isLeapDay(int day, int month, int year)
{
if(day == 29 && month == 2 && isLeapYear(year) == true)
{
return true;
}
else
{
return false;
}
}
void Date::addYears(int years)
{
if(years == 0)
{
return;
}
if(isLeapDay() && !isLeapDay(_day, _month, _year + years))
{
_day = Date::DAYS_PER_MONTH[_month];
}
_year += years;
}
void Date::addMonths(int months)
{
if(months == 0)
{
return;
}
int deltayears = months / MAX_MONTH;
int deltamonths = months % MAX_MONTH;
int newMonth = 0;
if(months > 0)
{
newMonth = (_month + deltamonths) % MAX_MONTH;
if((_month + deltamonths) > MAX_MONTH)
{
++deltayears;
}
}
else
{
if((_month + deltamonths) < MIN_MONTH)
{
--deltayears;
newMonth = _month + deltamonths + MAX_MONTH;
}
else
{
newMonth = _month + deltamonths;
}
}
if(_day > maxDay(newMonth, _year + deltayears))
{
_day = maxDay(newMonth, _year + deltayears);
}
_year += deltayears;
_month = newMonth;
}
void Date::addDays(int days)
{
if(days == 0)
{
return;
}
if(days < 0)
{
for(int i = 0; i > days; --i)
{
addDay(false);
}
return;
}
for(int i = 0; i < days; ++i)
{
addDay(true);
}
}
std::ostream& operator<<(std::ostream& os, const Date& date)
{
os << date.toString();
return os;
}
Date Date::operator+(int days) const
{
Date ret = *this;
ret.addDays(days);
return ret;
}
Date& Date::operator+=(int days)
{
addDays(days);
return *this;
}
//This is where I get stumped (the parameters was just one of my failed experiments
Date& Date::operator-(int day, int month, int year)
{
}
The function can be written either as a member, or as a free function. The member function signature would look like this:
TimeDuration Date::operator-(Date const & rhs) const
The free function would look like this:
TimeDuration operator-(Date const & lhs, Date const & rhs)
TimeDuration here is a completely seperate type representing a length of time. If you want, you could just make it an int signifying the number of days, but it would be better, in my opinion, to have a more expressive type for this purpose. Whatever you decide regarding the return type, it doesn't make any sense for the type to be Date (and certainly not Date&).
A possible (albeit not incredibly efficient) implementation, given that you've already written a function to add a day to a date, would be something like this:
if lhs_date comes before rhs_date
add days to (a copy of) lhs_date until lhs_date == rhs_date
return the negative of number of days added
if rhs_date comes before lhs_date
add days to (a copy of) rhs_date until rhs_date == lhs_date
return the number of days added
else
return 0
Another function you might want (or maybe this is what you actually wanted originally, but your wording doesn't indicate it) is a function which can subtract a length of time from a Date. In that case, the return value would be another Date object (but not Date&), and the possible signatures would look something like this:
Date Date::operator-(TimeDuration rhs) const // member version
Date operator-(Date const & lhs, TimeDuration const & rhs) // non-member version
You should do this:
//This is where I get stumped (the parameters was just one of my failed experiments
TimeDuration& Date::operator-(Date const & d1)
{
// ... processing ...
// this - d1;
}
and call it as:
Date d1 = new Date(20, 01, 2013);
TimeDuration duration = d1 - (new const Date(20, 01, 1922));
// Calculate no. of days or years using duration
The logic is as follows:
Pass two Date objects (first could be implicit) to the overloading function and return TimeDuration
To invoke this operator, you may create a Date object with the data that you have, instead of passing each value separately.
Please check for exact syntax.