diff --git a/cpp_api/KDB/kdb_equations.cpp b/cpp_api/KDB/kdb_equations.cpp index 7ac0ba458..388881c72 100644 --- a/cpp_api/KDB/kdb_equations.cpp +++ b/cpp_api/KDB/kdb_equations.cpp @@ -30,16 +30,18 @@ int KDBEquations::add(const std::string& name, const Equation& obj) char* c_name = to_char_array(name); // throw exception if object with passed name already exist - if (K_find(get_KDB(), c_name) >= 0) - throw IodeExceptionFunction("Cannot add " + iode_type_name + " with name " + name, - iode_type_name + " with name " + name + " already exists. Use update() method instead."); + if(K_find(get_KDB(), c_name) >= 0) + throw std::invalid_argument("Cannot add " + iode_type_name + " with name '" + name + "'.\n" + + iode_type_name + " with name '" + name + "' already exists. Use the update() method instead."); + + Equation eq(obj); + int pos = KDBTemplate::add(name, static_cast(&eq), c_name); - int pos = KDBTemplate::add(name, obj.c_equation, c_name); return pos; } -int KDBEquations::add(const std::string& name, const std::string& lec, const std::string& comment, const std::string& method, - Sample* sample, const std::string& instruments, const std::string& block, const std::array& tests, const bool date) +int KDBEquations::add(const std::string& name, const std::string& lec, const std::string& method, const std::string& from, + const std::string& to, const std::string& comment, const std::string& instruments, const std::string& block, const bool date) { check_name(name, iode_type); @@ -47,36 +49,32 @@ int KDBEquations::add(const std::string& name, const std::string& lec, const std // throw exception if object with passed name already exist if (K_find(get_KDB(), c_name) >= 0) - throw IodeExceptionFunction("Cannot add " + iode_type_name + " with name " + name, - iode_type_name + " with name " + name + " already exists. Use update() method instead."); - - bool new_sample = sample == nullptr; - if (new_sample) sample = new Sample(KSMPL(K_WS[I_VARIABLES])); - EQ* eq = prepare_equation(name, "create new", lec, comment, method, sample, instruments, block, &tests, date); + throw std::invalid_argument("Cannot add " + iode_type_name + " with name '" + name + "'.\n" + + iode_type_name + " with name '" + name + "' already exists. Use the update() method instead."); - int pos = KDBTemplate::add(name, eq, c_name); + Equation eq(name, lec, method, from, to, comment, instruments, block, date); - if(new_sample) delete sample; + int pos = KDBTemplate::add(name, static_cast(&eq), c_name); return pos; } -void KDBEquations::update(const std::string& name, const std::string& lec, const std::string& comment, const std::string& method, - Sample* sample, const std::string& instruments, const std::string& block, const std::array* tests, const bool date) +void KDBEquations::update(const std::string& name, const std::string& lec, const std::string& method, const std::string& from, + const std::string& to, const std::string& comment, const std::string& instruments, const std::string& block, const bool date) { char* c_name = to_char_array(name); - EQ* eq = prepare_equation(name, "update", lec, comment, method, sample, instruments, block, tests, date); + Equation eq(name, lec, method, from, to, comment, instruments, block, date); - KDBTemplate::update(name, eq, c_name); + KDBTemplate::update(name, static_cast(&eq), c_name); } -void KDBEquations::update(const int pos, const std::string& lec, const std::string& comment, const std::string& method, - Sample* sample, const std::string& instruments, const std::string& block, const std::array* tests, const bool date) +void KDBEquations::update(const int pos, const std::string& lec, const std::string& method, const std::string& from, + const std::string& to, const std::string& comment, const std::string& instruments, const std::string& block, const bool date) { std::string name = get_name(pos); char* c_name = to_char_array(name); - EQ* eq = prepare_equation(name, "update", lec, comment, method, sample, instruments, block, tests, date); + Equation eq(name, lec, method, from, to, comment, instruments, block, date); - KDBTemplate::update(name, eq, c_name); + KDBTemplate::update(name, static_cast(&eq), c_name); } diff --git a/cpp_api/KDB/kdb_equations.h b/cpp_api/KDB/kdb_equations.h index f09eb501a..f0034200e 100644 --- a/cpp_api/KDB/kdb_equations.h +++ b/cpp_api/KDB/kdb_equations.h @@ -24,14 +24,14 @@ class KDBEquations : public KDBTemplate int add(const std::string& name, const Equation& obj); - int add(const std::string& name, const std::string& lec, const std::string& comment = "", const std::string& method = "", - Sample* sample = nullptr, const std::string& instruments = "", const std::string& block = "", const std::array& tests = {0.}, const bool date = true); + int add(const std::string& name, const std::string& lec, const std::string& method = "LSQ", const std::string& from = "", const std::string& to = "", + const std::string& comment = "", const std::string& instruments = "", const std::string& block = "", const bool date = true); - void update(const std::string& name, const std::string& lec = "", const std::string& comment = "", const std::string& method = "", - Sample* sample = nullptr, const std::string& instruments = "", const std::string& block = "", const std::array* tests = nullptr, const bool date = false); + void update(const std::string& name, const std::string& lec, const std::string& method = "LSQ", const std::string& from = "", const std::string& to = "", + const std::string& comment = "", const std::string& instruments = "", const std::string& block = "", const bool date = false); - void update(const int pos, const std::string& lec = "", const std::string& comment = "", const std::string& method = "", - Sample* sample = nullptr, const std::string& instruments = "", const std::string& block = "", const std::array* tests = nullptr, const bool date = false); + void update(const int pos, const std::string& lec, const std::string& method = "LSQ", const std::string& from = "", const std::string& to = "", + const std::string& comment = "", const std::string& instruments = "", const std::string& block = "", const bool date = false); }; diff --git a/cpp_api/objects/equation.cpp b/cpp_api/objects/equation.cpp index b7fee4b8b..58451c192 100644 --- a/cpp_api/objects/equation.cpp +++ b/cpp_api/objects/equation.cpp @@ -2,141 +2,29 @@ #include "equation.h" -EQ* create_equation_deep_copy(EQ* original_equation) +void Equation::copy_from_EQ_obj(const EQ* obj) { - EQ* copy_eq = (EQ*) SW_nalloc(sizeof(EQ)); + this->lec = copy_char_array(obj->lec); // NOTE : we do not use memcpy() because memcpy() actually makes // a shallow copy of a struct instead of a deep copy - copy_eq->clec = clec_deep_copy(original_equation->clec); - copy_eq->lec = copy_char_array(original_equation->lec); - copy_eq->solved = original_equation->solved; - copy_eq->method = original_equation->method; + this->clec = clec_deep_copy(obj->clec); + this->solved = obj->solved; + this->method = obj->method; // NOTE : we can use memcpy() on SAMPLE because SAMPLE does // not contain attributes which are pointers - memcpy(©_eq->smpl, &original_equation->smpl, sizeof(SAMPLE)); - copy_eq->cmt = copy_char_array(original_equation->cmt); - copy_eq->blk = copy_char_array(original_equation->blk); - copy_eq->instr = copy_char_array(original_equation->instr); - copy_eq->date = original_equation->date; - memcpy(&(copy_eq->tests), &(original_equation->tests), EQS_NBTESTS * sizeof(float)); - - return copy_eq; -} - -bool equation_equal(EQ* c_eq1, EQ* c_eq2) -{ - if (c_eq1 == NULL || c_eq2 == NULL) return false; - - if (strcmp(c_eq1->lec, c_eq2->lec) != 0) return false; - if (c_eq1->method != c_eq2->method) return false; - if (memcmp(&(c_eq1->smpl), &(c_eq2->smpl), sizeof(SAMPLE)) != 0) return false; - if (strcmp(c_eq1->cmt, c_eq2->cmt) != 0) return false; - if (strcmp(c_eq1->blk, c_eq2->blk) != 0) return false; - if (strcmp(c_eq1->instr, c_eq2->instr) != 0) return false; - - return true; -} - -EQ* prepare_equation(const std::string& name, const bool add_obj, const std::string& lec, const std::string& comment, - const std::string& method, Sample* sample, const std::string& instruments, const std::string& block, const std::array* tests, - const bool date) -{ - char* c_name = to_char_array(name); - - EQ* eq; - if (add_obj) eq = (EQ*) SW_nalloc(sizeof(EQ)); - else - { - int pos = K_find(K_WS[I_EQUATIONS], c_name); - eq = KEVAL(K_WS[K_EQS], pos); - } - - if (add_obj && lec.empty()) - throw IodeExceptionInitialization("Equation with name " + name, "Empty LEC expression"); - - if (!lec.empty()) - { - SW_nfree(eq->lec); - eq->lec = copy_string_to_char(lec); - - // check LEC expression is valid - CLEC* clec = L_solve(eq->lec, c_name); - if (clec == NULL) - { - SW_nfree(eq->lec); - std::string action = add_obj ? "create new" : "update"; - IodeExceptionInvalidArguments error("Cannot " + action + " Equation with name " + name); - error.add_argument("lec", lec); - throw error; - } - SW_nfree(clec); - } - - if (!comment.empty()) - { - SW_nfree(eq->cmt); - eq->cmt = copy_string_to_char(comment); - } - if (!instruments.empty()) - { - SW_nfree(eq->instr); - eq->instr = copy_string_to_char(instruments); - } - if (!block.empty()) - { - SW_nfree(eq->blk); - eq->blk = copy_string_to_char(block); - } - - if (!method.empty()) - { - int i_method = 0; - for (int i = 0; i < I_NB_EQ_METHODS; i++) if (method == vEquationMethods[i]) i_method = i; - eq->method = i_method; - } - - if (date) eq->date = SCR_current_date(); - else eq->date = 0L; - - if (tests) - { - float* c_tests = const_cast(tests->data()); - memcpy(&(eq->tests), c_tests, EQS_NBTESTS * sizeof(float)); /* FLOAT 12-04-98 */ - } - else memset(&(eq->tests), 0, EQS_NBTESTS * sizeof(float)); /* JMP 12-04-98 */ - - if (sample) memcpy(&(eq->smpl), sample->c_sample, sizeof(SAMPLE)); - - return eq; -} - -std::size_t hash_value(EQ const& c_eq) -{ - std::size_t seed = 0; - - // need to wrapp with std::string() because hash_value() and - // hash_combine() only compare pointer addresses when applied - // on char* arrays - boost::hash_combine(seed, std::string(c_eq.lec)); - boost::hash_combine(seed, c_eq.method); - boost::hash_combine(seed, c_eq.smpl); - boost::hash_combine(seed, std::string(c_eq.cmt)); - boost::hash_combine(seed, std::string(c_eq.blk)); - boost::hash_combine(seed, std::string(c_eq.instr)); - - return seed; -} - - -Equation::Equation() -{ - c_equation = nullptr; - endo = ""; + memcpy(&this->smpl, &obj->smpl, sizeof(SAMPLE)); + this->cmt = copy_char_array(obj->cmt); + this->blk = copy_char_array(obj->blk); + this->instr = copy_char_array(obj->instr); + this->date = obj->date; + memcpy(&(this->tests), &(obj->tests), EQS_NBTESTS * sizeof(float)); } Equation::Equation(const int pos, KDB* kdb) { - if (!kdb) kdb = K_WS[I_EQUATIONS]; + if (!kdb) + kdb = K_WS[I_EQUATIONS]; + if (pos < 0 || pos > kdb->k_nb) { IodeExceptionInvalidArguments error("Cannot extract Equation", "Equation position must be in range [0, " + @@ -144,37 +32,96 @@ Equation::Equation(const int pos, KDB* kdb) error.add_argument("equation position", std::to_string(pos)); throw error; } + // Note: KEVAL allocate a new pointer EQ* - c_equation = KEVAL(kdb, pos); - endo = std::string(KONAME(kdb, pos)); + EQ* c_eq = KEVAL(kdb, pos); + copy_from_EQ_obj(c_eq); + E_free(c_eq); + // re-compute CLEC + char* name = KONAME(kdb, pos); + this->clec = L_solve(this->lec, name); } Equation::Equation(const std::string& name, KDB* kdb) { - if (!kdb) kdb = K_WS[I_EQUATIONS]; + if (!kdb) + kdb = K_WS[I_EQUATIONS]; + int pos = K_find(kdb, to_char_array(name)); - if (pos < 0) throw IodeExceptionFunction("Cannot extract Equation", "Equation with name " + name + " does not exist."); + if (pos < 0) + throw IodeExceptionFunction("Cannot extract Equation", "Equation with name " + name + " does not exist."); + // Note: KEVAL allocate a new pointer EQ* - c_equation = KEVAL(kdb, pos); - endo = name; + EQ* c_eq = KEVAL(kdb, pos); + copy_from_EQ_obj(c_eq); + E_free(c_eq); + // re-compute CLEC + this->clec = L_solve(this->lec, to_char_array(name)); +} + +Equation::Equation(const std::string& name, const std::string& lec, const int method, const std::string& from, const std::string& to, + const std::string& comment, const std::string& instruments, const std::string& block, const bool date) +{ + this->lec = NULL, + this->clec = NULL, + this->cmt = NULL, + this->instr = NULL, + this->blk = NULL; + this->date = 0L; + this->solved = '\0'; + + set_lec(lec, name); + set_method(method); + set_sample(from, to); + set_comment(comment); + set_instruments(instruments); + set_block(block); + set_tests({ 0.0f }); + if(date) + update_date(); +} + +Equation::Equation(const std::string& name, const std::string& lec, const std::string& method, const std::string& from, const std::string& to, + const std::string& comment, const std::string& instruments, const std::string& block, const bool date) +{ + this->lec = NULL, + this->clec = NULL, + this->cmt = NULL, + this->instr = NULL, + this->blk = NULL; + this->date = 0L; + this->solved = '\0'; + + set_lec(lec, name); + set_method(method); + set_sample(from, to); + set_comment(comment); + set_instruments(instruments); + set_block(block); + set_tests({ 0.0f }); + if(date) + update_date(); } -Equation::Equation(const Equation& eq) + +Equation::Equation(const Equation& other) { - c_equation = create_equation_deep_copy(eq.c_equation); - endo = eq.endo; + copy_from_EQ_obj(static_cast(&other)); } Equation::~Equation() { - if (c_equation) E_free(c_equation); + SW_nfree(this->lec); + SW_nfree(this->clec); + SW_nfree(this->cmt); + SW_nfree(this->blk); + SW_nfree(this->instr); } // required to be used in std::map -Equation& Equation::operator=(const Equation& eq) +Equation& Equation::operator=(const Equation& other) { - this->c_equation = create_equation_deep_copy(eq.c_equation); - this->endo = eq.endo; + copy_from_EQ_obj(static_cast(&other)); return *this; } @@ -182,97 +129,121 @@ Equation& Equation::operator=(const Equation& eq) std::string Equation::get_lec() const { - return std::string(c_equation->lec); + return std::string(this->lec); } -void Equation::set_lec(const std::string& lec) +void Equation::set_lec(const std::string& lec, const std::string& endo) { - if (lec.empty() || endo.empty()) - { - c_equation->lec = NULL; - } - else - { - char* c_lec = to_char_array(lec); - char* c_endo = to_char_array(endo); - // check if LEC expression is valid - c_equation->clec = L_solve(c_lec, c_endo); - if (c_equation->clec == 0) - { - IodeExceptionInvalidArguments error("Cannot set LEC to equation named " + endo); - error.add_argument("lec", lec); - throw error; - } - c_equation->lec = copy_char_array(c_lec); - } + if(lec.empty()) + throw std::invalid_argument("Passed LEC expression is empty"); + + if(endo.empty()) + throw std::invalid_argument("Passed value for endo (equation name) is empty"); + + if(this->lec != NULL) + SW_nfree(this->lec); + + char* c_lec = to_char_array(lec); + char* c_endo = to_char_array(endo); + + // check if LEC expression is valid + this->clec = L_solve(c_lec, c_endo); + if(this->clec == NULL) + throw std::invalid_argument("Cannot set LEC '" + lec + "' to the equation named '" + endo + "'"); + + this->lec = copy_char_array(c_lec); } // -- solved -- char Equation::get_solved() const { - return c_equation->solved; + return this->solved; } // -- method -- int Equation::get_method_as_int() const { - return (int) c_equation->method; + return (int) this->method; +} + +void Equation::set_method(const int method) +{ + if(method >= I_NB_EQ_METHODS) + throw std::invalid_argument("Invalid value " + std::to_string(method) + " for the equation method. " + + "The passed value must be in the range [0, " + std::to_string(I_NB_EQ_METHODS - 1) + "]"); + + this->method = (char) method; } std::string Equation::get_method() const { - int m = get_method_as_int(); - if (m > I_NB_EQ_METHODS) m = 0; + int m = (int) this->method; return vEquationMethods[m]; } void Equation::set_method(const std::string& method) { - int m = 0; - for (int i = 0; i < I_NB_EQ_METHODS; i++) if (method == vEquationMethods[i]) m = i; - c_equation->method = (char) m; + int m = -1; + for(int i = 0; i < I_NB_EQ_METHODS; i++) + if(method == vEquationMethods[i]) m = i; + + if(m < 0) + throw std::invalid_argument("The equation method '" + method + "' is not valid.\n" + + "Accepted methods are: " + boost::algorithm::join(vEquationMethods, ", ")); + + this->method = (char) m; } // -- block -- std::string Equation::get_block() const { - return std::string(c_equation->blk); + return std::string(this->blk); } void Equation::set_block(const std::string& block) { - SW_nfree(c_equation->blk); - if (block.empty()) c_equation->blk = NULL; - else c_equation->blk = copy_string_to_char(block); + if(this->blk != NULL) + SW_nfree(this->blk); + + if (block.empty()) + this->blk = copy_char_array(""); + else + this->blk = copy_string_to_char(block); } // -- sample -- Sample Equation::get_sample() const { - return Sample(&c_equation->smpl); + SAMPLE* smpl = const_cast(&this->smpl); + return Sample(smpl); } void Equation::set_sample(std::string from, std::string to) { if (from.empty() || to.empty()) { - Sample sample = get_sample(); - if (from.empty()) from = sample.start_period().to_string(); - if (to.empty()) to = sample.end_period().to_string(); + Sample sample(KSMPL(KV_WS)); + + if(from.empty()) + from = sample.start_period().to_string(); + + if(to.empty()) + to = sample.end_period().to_string(); } + Sample new_sample(from, to); - memcpy(&(c_equation->smpl), new_sample.c_sample, sizeof(SAMPLE)); + memcpy(&(this->smpl), new_sample.c_sample, sizeof(SAMPLE)); } // -- comment -- std::string Equation::get_comment() const { - std::string comment_oem = std::string(c_equation->cmt); + std::string comment_oem = std::string(this->cmt); std::string comment = oem_to_utf8(comment_oem); return comment; } @@ -280,12 +251,15 @@ std::string Equation::get_comment() const // we assume that comment string is in UTF8 format void Equation::set_comment(const std::string& comment) { - SW_nfree(c_equation->cmt); - if (comment.empty()) c_equation->cmt = NULL; + if(this->cmt != NULL) + SW_nfree(this->cmt); + + if(comment.empty()) + this->cmt = copy_char_array(""); else { std::string comment_oem = utf8_to_oem(comment); - c_equation->cmt = copy_string_to_char(comment_oem); + this->cmt = copy_string_to_char(comment_oem); } } @@ -293,40 +267,45 @@ void Equation::set_comment(const std::string& comment) std::string Equation::get_instruments() const { - return std::string(c_equation->instr); + return std::string(this->instr); } void Equation::set_instruments(const std::string& instruments) { - SW_nfree(c_equation->instr); - if (instruments.empty()) c_equation->instr = NULL; - else c_equation->instr = copy_string_to_char(instruments); + if(this->instr != NULL) + SW_nfree(this->instr); + + if(instruments.empty()) + this->instr = copy_char_array(""); + else + this->instr = copy_string_to_char(instruments); } // -- date -- long Equation::get_date() const { - return c_equation->date; + return this->date; } std::string Equation::get_date_as_string(const std::string& date_format) const { std::string date; - long l_date = c_equation->date; - if (l_date > 0) + long l_date = this->date; + if(l_date > 0) { char c_date[12]; char* c_date_format; c_date_format = to_char_array(date_format); date = SCR_long_to_fdate(l_date, c_date, c_date_format); } + return date; } void Equation::update_date() { - c_equation->date = SCR_current_date(); + this->date = SCR_current_date(); } // -- tests -- @@ -334,23 +313,22 @@ void Equation::update_date() std::array Equation::get_tests() const { std::array tests; - for (int i = 0; i < EQS_NBTESTS; i++) tests[i] = c_equation->tests[i]; + for(int i = 0; i < EQS_NBTESTS; i++) + tests[i] = this->tests[i]; return tests; } void Equation::set_tests(const std::array tests) { - for (int i = 0; i < EQS_NBTESTS; i++) c_equation->tests[i] = tests[i]; + for(int i = 0; i < EQS_NBTESTS; i++) + this->tests[i] = tests[i]; } // -- misc -- std::vector Equation::get_coefficients_list(const bool create_if_not_exit) { - // make sure CLEC is up to date - set_lec(get_lec()); - - std::vector coeffs = get_scalars_from_clec(c_equation->clec); + std::vector coeffs = get_scalars_from_clec(this->clec); // create scalars not yet present in the Scalars Database if(create_if_not_exit) @@ -371,18 +349,14 @@ std::vector Equation::get_coefficients_list(const bool create_if_no std::vector Equation::get_variables_list(const bool create_if_not_exit) { - // make sure CLEC is up to date - set_lec(get_lec()); - - std::vector vars = get_variables_from_clec(c_equation->clec); + std::vector vars = get_variables_from_clec(this->clec); // create variables not yet present in the Variables Database if(create_if_not_exit) { SAMPLE* sample = KSMPL(K_WS[I_VARIABLES]); if(sample == NULL || sample->s_nb == 0) - throw IodeException("Cannot return the list of variables associated with the equation " + endo +"\n" + - "The global sample is not yet defined"); + throw std::runtime_error("Cannot return the list of variables.\nThe global sample is not yet defined."); char* c_name; int nb_obs = sample->s_nb; @@ -421,7 +395,26 @@ std::pair Equation::split_equation() bool Equation::operator==(const Equation& other) const { - return equation_equal(c_equation, other.c_equation) && endo == other.endo; + if (strcmp(this->lec, other.lec) != 0) return false; + if (this->method != other.method) return false; + if (memcmp(&(this->smpl), &(other.smpl), sizeof(SAMPLE)) != 0) return false; + if (strcmp(this->cmt, other.cmt) != 0) return false; + if (strcmp(this->blk, other.blk) != 0) return false; + if (strcmp(this->instr, other.instr) != 0) return false; + + return true; +} + +std::string Equation::to_string() const +{ + std::string s_tests = "[" + std::to_string(this->tests[0]); + for(int i=1; i < EQS_NBTESTS; i++) + s_tests += ", " + std::to_string(this->tests[i]); + s_tests += "]"; + + return "Equation(" + get_lec() + ", " + get_method() + ", " + get_sample().to_string() + ", " + + get_comment() + ", " + get_block() + ", " + get_instruments() + ", " + s_tests + ", " + + get_date_as_string() + ")"; } NamedEquation::NamedEquation(const std::string& name) : name(name), eq(Equation(name)) @@ -431,3 +424,37 @@ NamedEquation::NamedEquation(const std::string& name) : name(name), eq(Equation( NamedEquation::NamedEquation(const std::string& name, const Equation& eq) : name(name), eq(eq) { } + +std::size_t hash_value(EQ const& c_eq) +{ + std::size_t seed = 0; + + // need to wrapp with std::string() because hash_value() and + // hash_combine() only compare pointer addresses when applied + // on char* arrays + boost::hash_combine(seed, std::string(c_eq.lec)); + boost::hash_combine(seed, c_eq.method); + boost::hash_combine(seed, c_eq.smpl); + boost::hash_combine(seed, std::string(c_eq.cmt)); + boost::hash_combine(seed, std::string(c_eq.blk)); + boost::hash_combine(seed, std::string(c_eq.instr)); + + return seed; +} + +std::size_t hash_value(Equation const& eq) +{ + std::size_t seed = 0; + + // need to wrapp with std::string() because hash_value() and + // hash_combine() only compare pointer addresses when applied + // on char* arrays + boost::hash_combine(seed, std::string(eq.lec)); + boost::hash_combine(seed, eq.method); + boost::hash_combine(seed, eq.smpl); + boost::hash_combine(seed, std::string(eq.cmt)); + boost::hash_combine(seed, std::string(eq.blk)); + boost::hash_combine(seed, std::string(eq.instr)); + + return seed; +} diff --git a/cpp_api/objects/equation.h b/cpp_api/objects/equation.h index 0769187e0..094a0f44f 100644 --- a/cpp_api/objects/equation.h +++ b/cpp_api/objects/equation.h @@ -1,13 +1,15 @@ #pragma once +#include +#include +#include +#include + #include "common.h" #include "utils/utils.h" #include "time/period.h" #include "time/sample.h" #include "lec/lec.h" -#include -#include - // TODO: replace K by I as below in C api + group them in an enum enum EnumIodeEquationMethod @@ -56,46 +58,34 @@ const static std::array vEquationTests = { }; -EQ* create_equation_deep_copy(EQ* original_equation); -bool equation_equal(EQ* c_eq1, EQ* c_eq2); -EQ* prepare_equation(const std::string& name, const bool add_obj, const std::string& lec, const std::string& comment, - const std::string& method, Sample* sample, const std::string& instruments, const std::string& block, const std::array* tests, - const bool date); - -/** - * @brief compute a hash value for an equation. - * - * @note see https://www.boost.org/doc/libs/1_55_0/doc/html/hash/custom.html - * and https://www.boost.org/doc/libs/1_55_0/doc/html/hash/combine.html - * - * @return std::size_t - */ -std::size_t hash_value(EQ const& c_eq); - -struct Equation +struct Equation : public EQ { - EQ* c_equation; - std::string endo; +private: + void copy_from_EQ_obj(const EQ* obj); public: - Equation(); - Equation(const int pos, KDB* kdb = nullptr); Equation(const std::string& name, KDB* kdb = nullptr); - Equation(const Equation& eq); + Equation(const std::string& name, const std::string& lec, const int method = 0, const std::string& from = "", const std::string& to = "", + const std::string& comment = "", const std::string& instruments = "", const std::string& block = "", const bool date = true); + + Equation(const std::string& name, const std::string& lec, const std::string& method = "LSQ", const std::string& from = "", const std::string& to = "", + const std::string& comment = "", const std::string& instruments = "", const std::string& block = "", const bool date = true); + + Equation(const Equation& other); ~Equation(); // required to be used in std::map - Equation& operator=(const Equation& eq); + Equation& operator=(const Equation& other); // -- lec -- std::string get_lec() const; - void set_lec(const std::string& lec); + void set_lec(const std::string& lec, const std::string& endo); // -- solved -- @@ -105,6 +95,8 @@ struct Equation int get_method_as_int() const; + void set_method(const int method); + std::string get_method() const; void set_method(const std::string& method); @@ -165,6 +157,10 @@ struct Equation // -- operators -- bool operator==(const Equation& other) const; + + // -- to_string -- + + std::string to_string() const; }; @@ -176,3 +172,24 @@ struct NamedEquation NamedEquation(const std::string& name); NamedEquation(const std::string& name, const Equation& eq); }; + + +/** + * @brief compute a hash value for an object of type EQ (C API). + * + * @note see https://www.boost.org/doc/libs/1_55_0/doc/html/hash/custom.html + * and https://www.boost.org/doc/libs/1_55_0/doc/html/hash/combine.html + * + * @return std::size_t + */ +std::size_t hash_value(EQ const& c_eq); + +/** + * @brief compute a hash value for an equation. + * + * @note see https://www.boost.org/doc/libs/1_55_0/doc/html/hash/custom.html + * and https://www.boost.org/doc/libs/1_55_0/doc/html/hash/combine.html + * + * @return std::size_t + */ +std::size_t hash_value(Equation const& eq); \ No newline at end of file diff --git a/tests/test_cpp_api/test_equation.cpp b/tests/test_cpp_api/test_equation.cpp index 951a121fa..98a345de2 100644 --- a/tests/test_cpp_api/test_equation.cpp +++ b/tests/test_cpp_api/test_equation.cpp @@ -21,6 +21,56 @@ class EquationTest : public KDBTest, public ::testing::Test }; +TEST_F(EquationTest, Equivalence_C_CPP) +{ + char* c_name = const_cast(name.c_str()); + std::string lec = equation->get_lec(); + std::string method = equation->get_method(); + Sample sample = equation->get_sample(); + std::string from = sample.start_period().to_string(); + std::string to = sample.end_period().to_string(); + std::string comment = equation->get_comment(); + std::string instruments = equation->get_instruments(); + std::string block = equation->get_block(); + + kdb.remove(name); + + // test if a Equation object can be added to the Equations KDB via K_add() + Equation eq(name, lec, method, from, to, comment, instruments, block, true); + // NOTE: for equations --> K_add(KDB* kdb, char* name, EQ* eq, char* endo) [where endo = name] + K_add(KE_WS, c_name, static_cast(&eq), c_name); + int pos = K_find(KE_WS, c_name); + ASSERT_GT(pos, -1); + + EQ* c_eq = KEVAL(KE_WS, pos); + ASSERT_EQ(std::string(c_eq->lec), eq.get_lec()); + ASSERT_EQ((int) c_eq->method, eq.get_method_as_int()); + ASSERT_EQ(Sample(&c_eq->smpl), eq.get_sample()); + ASSERT_EQ(std::string(c_eq->cmt), eq.get_comment()); + ASSERT_EQ(std::string(c_eq->instr), eq.get_instruments()); + ASSERT_EQ(std::string(c_eq->blk), eq.get_block()); + ASSERT_EQ(c_eq->date, eq.get_date()); + + // test memcpy between a Equation object and a EQ object + eq.set_lec("(ACAF/VAF[-1]) :=acaf2*GOSF[-1]+\nacaf4*(TIME=1995)", "ACAF"); + eq.set_sample("2000Y1", "2020Y1"); + eq.set_method("MAX_LIKELIHOOD"); + memcpy(c_eq, &eq, sizeof(EQ)); + ASSERT_EQ(std::string(c_eq->lec), eq.get_lec()); + ASSERT_EQ((int) c_eq->method, eq.get_method_as_int()); + ASSERT_EQ(Sample(&c_eq->smpl), eq.get_sample()); + ASSERT_EQ(std::string(c_eq->cmt), eq.get_comment()); + ASSERT_EQ(std::string(c_eq->instr), eq.get_instruments()); + ASSERT_EQ(std::string(c_eq->blk), eq.get_block()); + ASSERT_EQ(c_eq->date, eq.get_date()); + + // test if a Equation object can be passed to the hash function for the objects of type EQ. + boost::hash eq_hasher; + std::size_t c_hash = eq_hasher(*c_eq); + std::size_t cpp_hash = eq_hasher(static_cast(eq)); + ASSERT_EQ(c_hash, cpp_hash); +} + TEST_F(EquationTest, Lec) { // get @@ -28,7 +78,7 @@ TEST_F(EquationTest, Lec) // set std::string new_lec = "(ACAF/VAF[-1]) :=acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; - equation->set_lec(new_lec); + equation->set_lec(new_lec, name); EXPECT_EQ(equation->get_lec(), new_lec); } @@ -171,30 +221,30 @@ TEST_F(EquationTest, GetVariables) TEST_F(EquationTest, Hash) { - boost::hash equation_hasher; + boost::hash equation_hasher; std::size_t hash_before; std::size_t hash_after; - hash_before = equation_hasher(*equation->c_equation); + hash_before = equation_hasher(*equation); // same equation Equation* same_equation = new Equation(name); EXPECT_EQ(*equation, *same_equation); - hash_after = equation_hasher(*same_equation->c_equation); + hash_after = equation_hasher(*same_equation); EXPECT_EQ(hash_before, hash_after); delete same_equation; // different lec std::string new_lec = "(ACAF/VAF[-1]) :=acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; - equation->set_lec(new_lec); - hash_after = equation_hasher(*equation->c_equation); + equation->set_lec(new_lec, name); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); // different method hash_before = hash_after; std::string new_method = vEquationMethods[1]; equation->set_method(new_method); - hash_after = equation_hasher(*equation->c_equation); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); // different sample @@ -202,27 +252,27 @@ TEST_F(EquationTest, Hash) std::string from = "2000Y1"; std::string to = "2020Y1"; equation->set_sample(from, to); - hash_after = equation_hasher(*equation->c_equation); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); // different comment hash_before = hash_after; std::string new_comment = "New Comment"; equation->set_comment(new_comment); - hash_after = equation_hasher(*equation->c_equation); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); // different block hash_before = hash_after; std::string new_block = "ACAF;AGAF"; equation->set_block(new_block); - hash_after = equation_hasher(*equation->c_equation); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); // different instrument hash_before = hash_after; std::string new_instruments = "random_text"; equation->set_instruments(new_instruments); - hash_after = equation_hasher(*equation->c_equation); + hash_after = equation_hasher(*equation); EXPECT_NE(hash_before, hash_after); } diff --git a/tests/test_cpp_api/test_estimation.cpp b/tests/test_cpp_api/test_estimation.cpp index 821269417..78d5902b6 100644 --- a/tests/test_cpp_api/test_estimation.cpp +++ b/tests/test_cpp_api/test_estimation.cpp @@ -70,8 +70,8 @@ TEST_F(EstimationTest, Estimate) // -- go back to first eq next_named_eq = est->next_equation(); - EXPECT_EQ(named_eq.name, eq_name); - EXPECT_EQ(named_eq.eq.get_lec(), eq.get_lec()); + EXPECT_EQ(next_named_eq.name, eq_name); + EXPECT_EQ(next_named_eq.eq.get_lec(), eq.get_lec()); // Coeff values EXPECT_DOUBLE_EQ(round(1e6 * kdb_scl.get("acaf1").val) / 1e6, 0.01577); @@ -181,6 +181,7 @@ TEST_F(EstimationTest, Estimate) EXPECT_DOUBLE_EQ(round(1e6 * MATE(cm, 1, 0)) / 1e6, -0.042291); EXPECT_DOUBLE_EQ(MATE(cm, 1, 1), 1.); + delete est; } TEST_F(EstimationTest, EstimateBlock) diff --git a/tests/test_cpp_api/test_kdb_equations.cpp b/tests/test_cpp_api/test_kdb_equations.cpp index aa01e72b1..9f859bc3e 100644 --- a/tests/test_cpp_api/test_kdb_equations.cpp +++ b/tests/test_cpp_api/test_kdb_equations.cpp @@ -4,6 +4,9 @@ class KDBEquationsTest : public KDBTest, public ::testing::Test { protected: + std::array tests = { 1.0f, 0.0042699f, 0.00818467f, 5.19945e-05f, 0.0019271461f, + 23.545813f, 32.2732f, 0.82176137f, 0.79629868f, 2.3293459f, 83.8075f }; + void SetUp() override { load_global_kdb(I_EQUATIONS, input_test_dir + "fun.eqs"); @@ -111,14 +114,14 @@ TEST_F(KDBEquationsTest, CreateRemove) std::string lec = "(ACAF/VAF[-1]) :=acaf1+acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; std::string method = "LSQ"; - Sample sample("1980Y1", "1996Y1"); + std::string from = "1980Y1"; + std::string to = "1996Y1"; std::string comment = "Equation comment"; std::string block = "ACAF"; std::string instruments = "Equation instruments"; - std::array tests = { 1, 0.0042699, 0.00818467, 5.19945e-05, 0.0019271461, 23.545813, 32.2732, 0.82176137, 0.79629868, 2.3293459, 83.8075 }; bool date = true; - Equations.add(name, lec, comment, method, &sample, instruments, block, tests, date); + Equations.add(name, lec, method, from, to, comment, instruments, block, date); #endif } @@ -189,13 +192,13 @@ TEST_F(KDBEquationsTest, Filter) // been added to the global KDB std::string lec = "(ACAF/VAF[-1]) :=acaf1+acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; std::string method = "LSQ"; - Sample sample("1980Y1", "1996Y1"); + std::string from = "1980Y1"; + std::string to = "1996Y1"; std::string comment = "Equation comment"; std::string block = "ACAF"; std::string instruments = "Equation instruments"; - std::array tests = { 1, 0.0042699, 0.00818467, 5.19945e-05, 0.0019271461, 23.545813, 32.2732, 0.82176137, 0.79629868, 2.3293459, 83.8075 }; bool date = true; - local_kdb->add(name, lec, comment, method, &sample, instruments, block, tests, date); + local_kdb->add(name, lec, method, from, to, comment, instruments, block, date); EXPECT_EQ(local_kdb->get_lec(name), lec); EXPECT_EQ(Equations.get_lec(name), lec); @@ -270,13 +273,13 @@ TEST_F(KDBEquationsTest, Merge) std::string new_name = "ACAF2"; std::string new_lec = "(ACAF2/VAF[-1]) :=acaf1+acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; std::string method = "LSQ"; - Sample sample("1980Y1", "1996Y1"); + std::string from = "1980Y1"; + std::string to = "1996Y1"; std::string comment = "Equation comment"; std::string block = "ACAF"; std::string instruments = "Equation instruments"; - std::array tests = { 1, 0.0042699, 0.00818467, 5.19945e-05, 0.0019271461, 23.545813, 32.2732, 0.82176137, 0.79629868, 2.3293459, 83.8075 }; bool date = true; - kdb_to_merge.add(new_name, new_lec, comment, method, &sample, instruments, block, tests, date); + kdb_to_merge.add(new_name, new_lec, method, from, to, comment, instruments, block, date); // modify an existing element of the KDB to be merge std::string name = "ACAF"; @@ -360,13 +363,13 @@ TEST_F(KDBEquationsTest, Hash) hash_val = hash_val_modified; std::string lec = "(ACAF/VAF[-1]) :=acaf1+acaf2*GOSF[-1]+\nacaf4*(TIME=1995)"; std::string method = "LSQ"; - Sample sample("1980Y1", "1996Y1"); + std::string from = "1980Y1"; + std::string to = "1996Y1"; std::string comment = "Equation comment"; std::string block = "ACAF"; std::string instruments = "Equation instruments"; - std::array tests = { 1, 0.0042699, 0.00818467, 5.19945e-05, 0.0019271461, 23.545813, 32.2732, 0.82176137, 0.79629868, 2.3293459, 83.8075 }; bool date = true; - Equations.add("ACAF", lec, comment, method, &sample, instruments, block, tests, date); + Equations.add("ACAF", lec, method, from, to, comment, instruments, block, date); hash_val_modified = kdb_hasher(Equations); EXPECT_NE(hash_val, hash_val_modified); std::cout << "(new equation) orignal vs modified hash: " << std::to_string(hash_val) << " vs " << std::to_string(hash_val_modified) << std::endl;