From 2c56144277f546e9468532cfce8910a0d8614a6f Mon Sep 17 00:00:00 2001 From: Florian Schwendinger Date: Wed, 25 Dec 2024 12:13:14 +0100 Subject: [PATCH] submit changes necessary for cran --- app/cxxopts.hpp | 2 +- extern/filereaderlp/reader.cpp | 2638 ++++++++++++------------ extern/filereaderlp/reader.hpp | 20 +- src/parallel/HighsBinarySemaphore.h | 2 +- src/parallel/HighsCacheAlign.h | 2 +- src/parallel/HighsCombinable.h | 2 +- src/parallel/HighsMutex.h | 2 +- src/parallel/HighsRaceTimer.h | 2 +- src/parallel/HighsSchedulerConstants.h | 2 +- src/pdlp/cupdlp/cupdlp_utils.c | 4 +- src/pdlp/cupdlp/cupdlp_utils.h | 4 +- src/presolve/ICrashUtil.h | 2 +- 12 files changed, 1341 insertions(+), 1341 deletions(-) diff --git a/app/cxxopts.hpp b/app/cxxopts.hpp index 30575e4659..5bef619c8f 100644 --- a/app/cxxopts.hpp +++ b/app/cxxopts.hpp @@ -2875,4 +2875,4 @@ Options::group_help(const std::string& group) const } // namespace cxxopts -#endif //CXXOPTS_HPP_INCLUDED \ No newline at end of file +#endif //CXXOPTS_HPP_INCLUDED diff --git a/extern/filereaderlp/reader.cpp b/extern/filereaderlp/reader.cpp index 0a289c0ea8..a90ea00ca1 100644 --- a/extern/filereaderlp/reader.cpp +++ b/extern/filereaderlp/reader.cpp @@ -1,1319 +1,1319 @@ -#include "reader.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "HConfig.h" // for ZLIB_FOUND -#include "builder.hpp" -#include "def.hpp" -#ifdef ZLIB_FOUND -#include "../extern/zstr/zstr.hpp" -#endif - -// Cygwin doesn't come with an implementation for strdup if compiled with -// std=cxx -#ifdef __CYGWIN__ -#include -char* strdup(const char* s) { - size_t slen = strlen(s); - char* result = (char*)malloc(slen + 1); - if (result == NULL) { - return NULL; - } - - memcpy(result, s, slen + 1); - return result; -} -#endif - -enum class RawTokenType { - NONE, - STR, - CONS, - LESS, - GREATER, - EQUAL, - COLON, - LNEND, - FLEND, - BRKOP, - BRKCL, - PLUS, - MINUS, - HAT, - SLASH, - ASTERISK -}; - -struct RawToken { - RawTokenType type = RawTokenType::NONE; - std::string svalue; - double dvalue = 0.0; - - inline bool istype(RawTokenType t) const { return this->type == t; } - - RawToken& operator=(RawTokenType t) { - type = t; - return *this; - } - RawToken& operator=(const std::string& v) { - svalue = v; - type = RawTokenType::STR; - return *this; - } - RawToken& operator=(const double v) { - dvalue = v; - type = RawTokenType::CONS; - return *this; - } -}; - -enum class ProcessedTokenType { - NONE, - SECID, - VARID, - CONID, - CONST, - FREE, - BRKOP, - BRKCL, - COMP, - LNEND, - SLASH, - ASTERISK, - HAT, - SOSTYPE -}; - -enum class LpSectionKeyword { - NONE, - OBJMIN, - OBJMAX, - CON, - BOUNDS, - GEN, - BIN, - SEMI, - SOS, - END -}; - -static const std::unordered_map - sectionkeywordmap{{"minimize", LpSectionKeyword::OBJMIN}, - {"min", LpSectionKeyword::OBJMIN}, - {"minimum", LpSectionKeyword::OBJMIN}, - {"maximize", LpSectionKeyword::OBJMAX}, - {"max", LpSectionKeyword::OBJMAX}, - {"maximum", LpSectionKeyword::OBJMAX}, - {"subject to", LpSectionKeyword::CON}, - {"such that", LpSectionKeyword::CON}, - {"st", LpSectionKeyword::CON}, - {"s.t.", LpSectionKeyword::CON}, - {"bounds", LpSectionKeyword::BOUNDS}, - {"bound", LpSectionKeyword::BOUNDS}, - {"binary", LpSectionKeyword::BIN}, - {"binaries", LpSectionKeyword::BIN}, - {"bin", LpSectionKeyword::BIN}, - {"general", LpSectionKeyword::GEN}, - {"generals", LpSectionKeyword::GEN}, - {"gen", LpSectionKeyword::GEN}, - {"integer", LpSectionKeyword::GEN}, - {"integers", LpSectionKeyword::GEN}, - {"semi-continuous", LpSectionKeyword::SEMI}, - {"semi", LpSectionKeyword::SEMI}, - {"semis", LpSectionKeyword::SEMI}, - {"sos", LpSectionKeyword::SOS}, - {"end", LpSectionKeyword::END}}; - -enum class SosType { SOS1, SOS2 }; - -enum class LpComparisonType { LEQ, L, EQ, G, GEQ }; - -struct ProcessedToken { - ProcessedTokenType type; - union { - LpSectionKeyword keyword; - SosType sostype; - char* name; - double value; - LpComparisonType dir; - }; - - ProcessedToken(const ProcessedToken&) = delete; - ProcessedToken(ProcessedToken&& t) : type(t.type) { - switch (type) { - case ProcessedTokenType::SECID: - keyword = t.keyword; - break; - case ProcessedTokenType::SOSTYPE: - sostype = t.sostype; - break; - case ProcessedTokenType::CONID: - case ProcessedTokenType::VARID: - name = t.name; - break; - case ProcessedTokenType::CONST: - value = t.value; - break; - case ProcessedTokenType::COMP: - dir = t.dir; - break; - default:; - } - t.type = ProcessedTokenType::NONE; - } - - ProcessedToken(ProcessedTokenType t) : type(t){}; - - ProcessedToken(LpSectionKeyword kw) - : type(ProcessedTokenType::SECID), keyword(kw){}; - - ProcessedToken(SosType sos) - : type(ProcessedTokenType::SOSTYPE), sostype(sos){}; - - ProcessedToken(ProcessedTokenType t, const std::string& s) : type(t) { - assert(t == ProcessedTokenType::CONID || t == ProcessedTokenType::VARID); -#ifndef _WIN32 - name = strdup(s.c_str()); -#else - name = _strdup(s.c_str()); -#endif - }; - - ProcessedToken(double v) : type(ProcessedTokenType::CONST), value(v){}; - - ProcessedToken(LpComparisonType comp) - : type(ProcessedTokenType::COMP), dir(comp){}; - - ~ProcessedToken() { - if (type == ProcessedTokenType::CONID || type == ProcessedTokenType::VARID) - free(name); - } -}; - -// how many raw tokens to cache -// set to how many tokens we may need to look ahead -#define NRAWTOKEN 3 - -const double kHighsInf = std::numeric_limits::infinity(); -class Reader { - private: -#ifdef ZLIB_FOUND - zstr::ifstream file; -#else - std::ifstream file; -#endif - std::string linebuffer; - std::size_t linebufferpos; - std::array rawtokens; - std::vector processedtokens; - // store for each section a pointer to its begin and end (pointer to element - // after last) - std::map::iterator, - std::vector::iterator> > - sectiontokens; - - Builder builder; - - bool readnexttoken(RawToken&); - void nextrawtoken(size_t howmany = 1); - void processtokens(); - void splittokens(); - void processsections(); - void processnonesec(); - void processobjsec(); - void processconsec(); - void processboundssec(); - void processbinsec(); - void processgensec(); - void processsemisec(); - void processsossec(); - void processendsec(); - void parseexpression(std::vector::iterator& it, - std::vector::iterator end, - std::shared_ptr expr, bool isobj); - - public: - Reader(std::string filename) { -#ifdef ZLIB_FOUND - try { - file.open(filename); - } catch (const strict_fstream::Exception& e) { - } -#else - file.open(filename); -#endif - lpassert(file.is_open()); - }; - - ~Reader() { file.close(); } - - Model read(); -}; - -Model readinstance(std::string filename) { - Reader reader(filename); - return reader.read(); -} - -// convert string to lower-case, modifies string -static inline void tolower(std::string& s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c) { return std::tolower(c); }); -} - -static inline bool iskeyword(const std::string& str, - const std::string* keywords, const int nkeywords) { - for (int i = 0; i < nkeywords; i++) { - if (str == keywords[i]) { - return true; - } - } - return false; -} - -static inline LpSectionKeyword parsesectionkeyword(const std::string& str) { - // look up lower case - auto it(sectionkeywordmap.find(str)); - if (it != sectionkeywordmap.end()) return it->second; - - return LpSectionKeyword::NONE; -} - -Model Reader::read() { - // std::clog << "Reading input, tokenizing..." << std::endl; - this->linebufferpos = 0; - // read first NRAWTOKEN token - // if file ends early, then all remaining tokens are set to FLEND - for (size_t i = 0; i < NRAWTOKEN; ++i) - while (!readnexttoken(rawtokens[i])) - ; - - processtokens(); - - linebuffer.clear(); - linebuffer.shrink_to_fit(); - - // std::clog << "Splitting tokens..." << std::endl; - splittokens(); - - // std::clog << "Setting up model..." << std::endl; - // - // Since - // - // "The problem statement must begin with the word MINIMIZE or - // MAXIMIZE, MINIMUM or MAXIMUM, or the abbreviations MIN or MAX, in - // any combination of upper- and lower-case characters. The word - // introduces the objective function section." - // - // Use positivity of sectiontokens.count(LpSectionKeyword::OBJMIN) + - // sectiontokens.count(LpSectionKeyword::OBJMAX) to identify garbage file - // - - const int num_objective_section = - sectiontokens.count(LpSectionKeyword::OBJMIN) + - sectiontokens.count(LpSectionKeyword::OBJMAX); - lpassert(num_objective_section>0); - - processsections(); - processedtokens.clear(); - processedtokens.shrink_to_fit(); - - return builder.model; -} - -void Reader::processnonesec() { - lpassert(sectiontokens.count(LpSectionKeyword::NONE) == 0); -} - -void Reader::parseexpression(std::vector::iterator& it, - std::vector::iterator end, - std::shared_ptr expr, bool isobj) { - if (it != end && it->type == ProcessedTokenType::CONID) { - expr->name = it->name; - ++it; - } - - while (it != end) { - std::vector::iterator next = it; - ++next; - // const var - if (next != end && it->type == ProcessedTokenType::CONST && - next->type == ProcessedTokenType::VARID) { - std::string name = next->name; - - std::shared_ptr linterm = - std::shared_ptr(new LinTerm()); - linterm->coef = it->value; - linterm->var = builder.getvarbyname(name); - // printf("LpReader: Term %+g %s\n", linterm->coef, - //name.c_str()); - expr->linterms.push_back(linterm); - - ++it; - ++it; - continue; - } - - // const - if (it->type == ProcessedTokenType::CONST) { - // printf("LpReader: Offset change from %+g by %+g\n", expr->offset, it->value); - expr->offset += it->value; - ++it; - continue; - } - - // var - if (it->type == ProcessedTokenType::VARID) { - std::string name = it->name; - - std::shared_ptr linterm = - std::shared_ptr(new LinTerm()); - linterm->coef = 1.0; - linterm->var = builder.getvarbyname(name); - // printf("LpReader: Term %+g %s\n", linterm->coef, - //name.c_str()); - expr->linterms.push_back(linterm); - - ++it; - continue; - } - - // quadratic expression - if (next != end && it->type == ProcessedTokenType::BRKOP) { - ++it; - while (it != end && it->type != ProcessedTokenType::BRKCL) { - // const var hat const - std::vector::iterator next1 = it; // token after it - std::vector::iterator next2 = it; // token 2nd-after it - std::vector::iterator next3 = it; // token 3rd-after it - ++next1; - ++next2; - ++next3; - if (next1 != end) { - ++next2; - ++next3; - } - if (next2 != end) ++next3; - - if (next3 != end && it->type == ProcessedTokenType::CONST && - next1->type == ProcessedTokenType::VARID && - next2->type == ProcessedTokenType::HAT && - next3->type == ProcessedTokenType::CONST) { - std::string name = next1->name; - - lpassert(next3->value == 2.0); - - std::shared_ptr quadterm = - std::shared_ptr(new QuadTerm()); - quadterm->coef = it->value; - quadterm->var1 = builder.getvarbyname(name); - quadterm->var2 = builder.getvarbyname(name); - expr->quadterms.push_back(quadterm); - - it = ++next3; - continue; - } - - // var hat const - if (next2 != end && it->type == ProcessedTokenType::VARID && - next1->type == ProcessedTokenType::HAT && - next2->type == ProcessedTokenType::CONST) { - std::string name = it->name; - - lpassert(next2->value == 2.0); - - std::shared_ptr quadterm = - std::shared_ptr(new QuadTerm()); - quadterm->coef = 1.0; - quadterm->var1 = builder.getvarbyname(name); - quadterm->var2 = builder.getvarbyname(name); - expr->quadterms.push_back(quadterm); - - it = next3; - continue; - } - - // const var asterisk var - if (next3 != end && it->type == ProcessedTokenType::CONST && - next1->type == ProcessedTokenType::VARID && - next2->type == ProcessedTokenType::ASTERISK && - next3->type == ProcessedTokenType::VARID) { - std::string name1 = next1->name; - std::string name2 = next3->name; - - std::shared_ptr quadterm = - std::shared_ptr(new QuadTerm()); - quadterm->coef = it->value; - quadterm->var1 = builder.getvarbyname(name1); - quadterm->var2 = builder.getvarbyname(name2); - expr->quadterms.push_back(quadterm); - - it = ++next3; - continue; - } - - // var asterisk var - if (next2 != end && it->type == ProcessedTokenType::VARID && - next1->type == ProcessedTokenType::ASTERISK && - next2->type == ProcessedTokenType::VARID) { - std::string name1 = it->name; - std::string name2 = next2->name; - - std::shared_ptr quadterm = - std::shared_ptr(new QuadTerm()); - quadterm->coef = 1.0; - quadterm->var1 = builder.getvarbyname(name1); - quadterm->var2 = builder.getvarbyname(name2); - expr->quadterms.push_back(quadterm); - - it = next3; - continue; - } - break; - } - if (isobj) { - // only in the objective function, a quadratic term is followed by - // "/2.0" - std::vector::iterator next1 = it; // token after it - std::vector::iterator next2 = it; // token 2nd-after it - ++next1; - ++next2; - if (next1 != end) ++next2; - - lpassert(next2 != end); - lpassert(it->type == ProcessedTokenType::BRKCL); - lpassert(next1->type == ProcessedTokenType::SLASH); - lpassert(next2->type == ProcessedTokenType::CONST); - lpassert(next2->value == 2.0); - it = ++next2; - } else { - lpassert(it != end); - lpassert(it->type == ProcessedTokenType::BRKCL); - ++it; - } - continue; - } - - break; - } -} - -void Reader::processobjsec() { - builder.model.objective = std::shared_ptr(new Expression); - if (sectiontokens.count(LpSectionKeyword::OBJMIN)) { - builder.model.sense = ObjectiveSense::MIN; - parseexpression(sectiontokens[LpSectionKeyword::OBJMIN].first, - sectiontokens[LpSectionKeyword::OBJMIN].second, - builder.model.objective, true); - lpassert(sectiontokens[LpSectionKeyword::OBJMIN].first == - sectiontokens[LpSectionKeyword::OBJMIN] - .second); // all section tokens should have been processed - } else if (sectiontokens.count(LpSectionKeyword::OBJMAX)) { - builder.model.sense = ObjectiveSense::MAX; - parseexpression(sectiontokens[LpSectionKeyword::OBJMAX].first, - sectiontokens[LpSectionKeyword::OBJMAX].second, - builder.model.objective, true); - lpassert(sectiontokens[LpSectionKeyword::OBJMAX].first == - sectiontokens[LpSectionKeyword::OBJMAX] - .second); // all section tokens should have been processed - } -} - -void Reader::processconsec() { - if (!sectiontokens.count(LpSectionKeyword::CON)) return; - std::vector::iterator& begin( - sectiontokens[LpSectionKeyword::CON].first); - std::vector::iterator& end( - sectiontokens[LpSectionKeyword::CON].second); - while (begin != end) { - std::shared_ptr con = - std::shared_ptr(new Constraint); - parseexpression(begin, end, con->expr, false); - // should not be at end of section yet, but a comparison operator should be - // next - lpassert(begin != sectiontokens[LpSectionKeyword::CON].second); - lpassert(begin->type == ProcessedTokenType::COMP); - LpComparisonType dir = begin->dir; - ++begin; - - // should still not be at end of section yet, but a right-hand-side value - // should be next - lpassert(begin != sectiontokens[LpSectionKeyword::CON].second); - lpassert(begin->type == ProcessedTokenType::CONST); - switch (dir) { - case LpComparisonType::EQ: - con->lowerbound = con->upperbound = begin->value; - break; - case LpComparisonType::LEQ: - con->upperbound = begin->value; - break; - case LpComparisonType::GEQ: - con->lowerbound = begin->value; - break; - default: - lpassert(false); - } - builder.model.constraints.push_back(con); - ++begin; - } -} - -void Reader::processboundssec() { - if (!sectiontokens.count(LpSectionKeyword::BOUNDS)) return; - std::vector::iterator& begin( - sectiontokens[LpSectionKeyword::BOUNDS].first); - std::vector::iterator& end( - sectiontokens[LpSectionKeyword::BOUNDS].second); - while (begin != end) { - std::vector::iterator next1 = begin; // token after begin - ++next1; - - // VAR free - if (next1 != end && begin->type == ProcessedTokenType::VARID && - next1->type == ProcessedTokenType::FREE) { - std::string name = begin->name; - std::shared_ptr var = builder.getvarbyname(name); - var->lowerbound = -kHighsInf; - var->upperbound = kHighsInf; - begin = ++next1; - continue; - } - - std::vector::iterator next2 = - next1; // token 2nd-after begin - std::vector::iterator next3 = - next1; // token 3rd-after begin - std::vector::iterator next4 = - next1; // token 4th-after begin - if (next1 != end) { - ++next2; - ++next3; - ++next4; - } - if (next2 != end) { - ++next3; - ++next4; - } - if (next3 != end) ++next4; - - // CONST COMP VAR COMP CONST - if (next4 != end && begin->type == ProcessedTokenType::CONST && - next1->type == ProcessedTokenType::COMP && - next2->type == ProcessedTokenType::VARID && - next3->type == ProcessedTokenType::COMP && - next4->type == ProcessedTokenType::CONST) { - lpassert(next1->dir == LpComparisonType::LEQ); - lpassert(next3->dir == LpComparisonType::LEQ); - - double lb = begin->value; - double ub = next4->value; - - std::string name = next2->name; - std::shared_ptr var = builder.getvarbyname(name); - - var->lowerbound = lb; - var->upperbound = ub; - - begin = ++next4; - continue; - } - - // CONST COMP VAR - if (next2 != end && begin->type == ProcessedTokenType::CONST && - next1->type == ProcessedTokenType::COMP && - next2->type == ProcessedTokenType::VARID) { - double value = begin->value; - std::string name = next2->name; - std::shared_ptr var = builder.getvarbyname(name); - LpComparisonType dir = next1->dir; - - lpassert(dir != LpComparisonType::L && dir != LpComparisonType::G); - - switch (dir) { - case LpComparisonType::LEQ: - var->lowerbound = value; - break; - case LpComparisonType::GEQ: - var->upperbound = value; - break; - case LpComparisonType::EQ: - var->lowerbound = var->upperbound = value; - break; - default: - lpassert(false); - } - begin = next3; - continue; - } - - // VAR COMP CONST - if (next2 != end && begin->type == ProcessedTokenType::VARID && - next1->type == ProcessedTokenType::COMP && - next2->type == ProcessedTokenType::CONST) { - double value = next2->value; - std::string name = begin->name; - std::shared_ptr var = builder.getvarbyname(name); - LpComparisonType dir = next1->dir; - - lpassert(dir != LpComparisonType::L && dir != LpComparisonType::G); - - switch (dir) { - case LpComparisonType::LEQ: - var->upperbound = value; - break; - case LpComparisonType::GEQ: - var->lowerbound = value; - break; - case LpComparisonType::EQ: - var->lowerbound = var->upperbound = value; - break; - default: - lpassert(false); - } - begin = next3; - continue; - } - - lpassert(false); - } -} - -void Reader::processbinsec() { - const LpSectionKeyword this_section_keyword = LpSectionKeyword::BIN; - if (!sectiontokens.count(this_section_keyword)) return; - std::vector::iterator& begin( - sectiontokens[this_section_keyword].first); - std::vector::iterator& end( - sectiontokens[this_section_keyword].second); - for (; begin != end; ++begin) { - if (begin->type == ProcessedTokenType::SECID) { - // Possible to have repeat of keyword for this section type - lpassert(begin->keyword == this_section_keyword); - continue; - } - lpassert(begin->type == ProcessedTokenType::VARID); - std::string name = begin->name; - std::shared_ptr var = builder.getvarbyname(name); - var->type = VariableType::BINARY; - // Respect any bounds already declared - if (var->upperbound == kHighsInf) var->upperbound = 1.0; - } -} - -void Reader::processgensec() { - const LpSectionKeyword this_section_keyword = LpSectionKeyword::GEN; - if (!sectiontokens.count(this_section_keyword)) return; - std::vector::iterator& begin( - sectiontokens[this_section_keyword].first); - std::vector::iterator& end( - sectiontokens[this_section_keyword].second); - for (; begin != end; ++begin) { - if (begin->type == ProcessedTokenType::SECID) { - // Possible to have repeat of keyword for this section type - lpassert(begin->keyword == this_section_keyword); - continue; - } - lpassert(begin->type == ProcessedTokenType::VARID); - std::string name = begin->name; - std::shared_ptr var = builder.getvarbyname(name); - if (var->type == VariableType::SEMICONTINUOUS) { - var->type = VariableType::SEMIINTEGER; - } else { - var->type = VariableType::GENERAL; - } - } -} - -void Reader::processsemisec() { - const LpSectionKeyword this_section_keyword = LpSectionKeyword::SEMI; - if (!sectiontokens.count(this_section_keyword)) return; - std::vector::iterator& begin( - sectiontokens[this_section_keyword].first); - std::vector::iterator& end( - sectiontokens[this_section_keyword].second); - for (; begin != end; ++begin) { - if (begin->type == ProcessedTokenType::SECID) { - // Possible to have repeat of keyword for this section type - lpassert(begin->keyword == this_section_keyword); - continue; - } - lpassert(begin->type == ProcessedTokenType::VARID); - std::string name = begin->name; - std::shared_ptr var = builder.getvarbyname(name); - if (var->type == VariableType::GENERAL) { - var->type = VariableType::SEMIINTEGER; - } else { - var->type = VariableType::SEMICONTINUOUS; - } - } -} - -void Reader::processsossec() { - const LpSectionKeyword this_section_keyword = LpSectionKeyword::SOS; - if (!sectiontokens.count(this_section_keyword)) return; - std::vector::iterator& begin( - sectiontokens[this_section_keyword].first); - std::vector::iterator& end( - sectiontokens[this_section_keyword].second); - while (begin != end) { - std::shared_ptr sos = std::shared_ptr(new SOS); - - // sos1: S1 :: x1 : 1 x2 : 2 x3 : 3 - - // name of SOS is mandatory - lpassert(begin->type == ProcessedTokenType::CONID); - sos->name = begin->name; - ++begin; - - // SOS type - lpassert(begin != end); - lpassert(begin->type == ProcessedTokenType::SOSTYPE); - sos->type = begin->sostype == SosType::SOS1 ? 1 : 2; - ++begin; - - while (begin != end) { - // process all "var : weight" entries - // when processtokens() sees a string followed by a colon, it classifies - // this as a CONID but in a SOS section, this is actually a variable - // identifier - if (begin->type != ProcessedTokenType::CONID) break; - std::string name = begin->name; - std::vector::iterator next = begin; - ++next; - if (next != end && next->type == ProcessedTokenType::CONST) { - auto var = builder.getvarbyname(name); - double weight = next->value; - - sos->entries.push_back({var, weight}); - - begin = ++next; - continue; - } - - break; - } - - builder.model.soss.push_back(sos); - } -} - -void Reader::processendsec() { - lpassert(sectiontokens.count(LpSectionKeyword::END) == 0); -} - -void Reader::processsections() { - processnonesec(); - processobjsec(); - processconsec(); - processboundssec(); - processgensec(); - processbinsec(); - processsemisec(); - processsossec(); - processendsec(); -} - -void Reader::splittokens() { - LpSectionKeyword currentsection = LpSectionKeyword::NONE; - - bool debug_open_section = false; - for (std::vector::iterator it(processedtokens.begin()); - it != processedtokens.end(); ++it) { - // Look for section keywords - if (it->type != ProcessedTokenType::SECID) continue; - // currentsection is initially LpSectionKeyword::NONE, so the - // first section ID will be a new section type - // - // Only record change of section and check for repeated - // section if the keyword is for a different section. Allows - // repetition of Integers and General (cf #1299) for example - const bool new_section_type = currentsection != it->keyword; - if (new_section_type) { - if (currentsection != LpSectionKeyword::NONE) { - // Current section is non-trivial, so mark its end, using the - // value of currentsection to indicate that there is no open - // section - lpassert(debug_open_section); - sectiontokens[currentsection].second = it; - debug_open_section = false; - currentsection = LpSectionKeyword::NONE; - } - } - std::vector::iterator next = it; - ++next; - if (next == processedtokens.end() || - next->type == ProcessedTokenType::SECID) { - // Reached the end of the tokens or the new section is empty - // - // currentsection will be LpSectionKeyword::NONE unless the - // second of two sections of the same type is empty and the - // next section is of a new type, in which case mark the end of - // the current section - if (currentsection != LpSectionKeyword::NONE && - currentsection != next->keyword) { - lpassert(debug_open_section); - sectiontokens[currentsection].second = it; - debug_open_section = false; - } - currentsection = LpSectionKeyword::NONE; - lpassert(!debug_open_section); - continue; - } - // Next section is non-empty - if (new_section_type) { - // Section type change - currentsection = it->keyword; - // Make sure the new section type has not occurred previously - lpassert(sectiontokens.count(currentsection) == 0); - // Remember the beginning of the new section: its the token - // following the current one - lpassert(!debug_open_section); - sectiontokens[currentsection].first = next; - debug_open_section = true; - } - // Always ends with either an open section or a section type of - // LpSectionKeyword::NONE - lpassert(debug_open_section != (currentsection == LpSectionKeyword::NONE)); - } - // Check that the last section has been closed - lpassert(currentsection == LpSectionKeyword::NONE); -} - -void Reader::processtokens() { - std::string svalue_lc; - while (!rawtokens[0].istype(RawTokenType::FLEND)) { - fflush(stdout); - - // Slash + asterisk: comment, skip everything up to next asterisk + slash - if (rawtokens[0].istype(RawTokenType::SLASH) && - rawtokens[1].istype(RawTokenType::ASTERISK)) { - do { - nextrawtoken(2); - } while (!(rawtokens[0].istype(RawTokenType::ASTERISK) && - rawtokens[1].istype(RawTokenType::SLASH)) && - !rawtokens[0].istype(RawTokenType::FLEND)); - nextrawtoken(2); - continue; - } - - if (rawtokens[0].istype(RawTokenType::STR)) { - svalue_lc = rawtokens[0].svalue; - tolower(svalue_lc); - } - - // long section keyword semi-continuous - if (rawtokens[0].istype(RawTokenType::STR) && - rawtokens[1].istype(RawTokenType::MINUS) && - rawtokens[2].istype(RawTokenType::STR)) { - std::string temp = rawtokens[2].svalue; - tolower(temp); - LpSectionKeyword keyword = parsesectionkeyword(svalue_lc + "-" + temp); - if (keyword != LpSectionKeyword::NONE) { - processedtokens.emplace_back(keyword); - nextrawtoken(3); - continue; - } - } - - // long section keyword subject to/such that - if (rawtokens[0].istype(RawTokenType::STR) && - rawtokens[1].istype(RawTokenType::STR)) { - std::string temp = rawtokens[1].svalue; - tolower(temp); - LpSectionKeyword keyword = parsesectionkeyword(svalue_lc + " " + temp); - if (keyword != LpSectionKeyword::NONE) { - processedtokens.emplace_back(keyword); - nextrawtoken(2); - continue; - } - } - - // other section keyword - if (rawtokens[0].istype(RawTokenType::STR)) { - LpSectionKeyword keyword = parsesectionkeyword(svalue_lc); - if (keyword != LpSectionKeyword::NONE) { - processedtokens.emplace_back(keyword); - nextrawtoken(); - continue; - } - } - - // sos type identifier? "S1 ::" or "S2 ::" - if (rawtokens[0].istype(RawTokenType::STR) && - rawtokens[1].istype(RawTokenType::COLON) && - rawtokens[2].istype(RawTokenType::COLON)) { - lpassert(rawtokens[0].svalue.length() == 2); - lpassert(rawtokens[0].svalue[0] == 'S' || rawtokens[0].svalue[0] == 's'); - lpassert(rawtokens[0].svalue[1] == '1' || rawtokens[0].svalue[1] == '2'); - processedtokens.emplace_back( - rawtokens[0].svalue[1] == '1' ? SosType::SOS1 : SosType::SOS2); - nextrawtoken(3); - continue; - } - - // constraint identifier? - if (rawtokens[0].istype(RawTokenType::STR) && - rawtokens[1].istype(RawTokenType::COLON)) { - processedtokens.emplace_back(ProcessedTokenType::CONID, - rawtokens[0].svalue); - nextrawtoken(2); - continue; - } - - // check if free - if (rawtokens[0].istype(RawTokenType::STR) && - iskeyword(svalue_lc, LP_KEYWORD_FREE, LP_KEYWORD_FREE_N)) { - processedtokens.emplace_back(ProcessedTokenType::FREE); - nextrawtoken(); - continue; - } - - // check if infinity - if (rawtokens[0].istype(RawTokenType::STR) && - iskeyword(svalue_lc, LP_KEYWORD_INF, LP_KEYWORD_INF_N)) { - processedtokens.emplace_back(kHighsInf); - nextrawtoken(); - continue; - } - - // assume var identifier - if (rawtokens[0].istype(RawTokenType::STR)) { - processedtokens.emplace_back(ProcessedTokenType::VARID, - rawtokens[0].svalue); - nextrawtoken(); - continue; - } - - // + or - - if (rawtokens[0].istype(RawTokenType::PLUS) || - rawtokens[0].istype(RawTokenType::MINUS)) { - double sign = rawtokens[0].istype(RawTokenType::PLUS) ? 1.0 : -1.0; - nextrawtoken(); - - // another + or - for #948, #950 - if (rawtokens[0].istype(RawTokenType::PLUS) || - rawtokens[0].istype(RawTokenType::MINUS)) { - sign *= rawtokens[0].istype(RawTokenType::PLUS) ? 1.0 : -1.0; - nextrawtoken(); - } - - // +/- Constant - if (rawtokens[0].istype(RawTokenType::CONS)) { - processedtokens.emplace_back(sign * rawtokens[0].dvalue); - nextrawtoken(); - continue; - } - - // + [, + + [, - - [ - if (rawtokens[0].istype(RawTokenType::BRKOP) && sign == 1.0) { - processedtokens.emplace_back(ProcessedTokenType::BRKOP); - nextrawtoken(); - continue; - } - - // - [, + - [, - + [ - if (rawtokens[0].istype(RawTokenType::BRKOP)) lpassert(false); - - // +/- variable name - if (rawtokens[0].istype(RawTokenType::STR)) { - processedtokens.emplace_back(sign); - continue; - } - - // +/- (possibly twice) followed by something that isn't a constant, - // opening bracket, or string (variable name) - if (rawtokens[0].istype(RawTokenType::GREATER)) { - // ">" suggests that the file contains indicator constraints - printf( - "File appears to contain indicator constraints: cannot currently " - "be handled by HiGHS\n"); - } - lpassert(false); - } - - // constant [ - if (rawtokens[0].istype(RawTokenType::CONS) && - rawtokens[1].istype(RawTokenType::BRKOP)) { - lpassert(false); - } - - // constant - if (rawtokens[0].istype(RawTokenType::CONS)) { - processedtokens.emplace_back(rawtokens[0].dvalue); - nextrawtoken(); - continue; - } - - // [ - if (rawtokens[0].istype(RawTokenType::BRKOP)) { - processedtokens.emplace_back(ProcessedTokenType::BRKOP); - nextrawtoken(); - continue; - } - - // ] - if (rawtokens[0].istype(RawTokenType::BRKCL)) { - processedtokens.emplace_back(ProcessedTokenType::BRKCL); - nextrawtoken(); - continue; - } - - // / - if (rawtokens[0].istype(RawTokenType::SLASH)) { - processedtokens.emplace_back(ProcessedTokenType::SLASH); - nextrawtoken(); - continue; - } - - // * - if (rawtokens[0].istype(RawTokenType::ASTERISK)) { - processedtokens.emplace_back(ProcessedTokenType::ASTERISK); - nextrawtoken(); - continue; - } - - // ^ - if (rawtokens[0].istype(RawTokenType::HAT)) { - processedtokens.emplace_back(ProcessedTokenType::HAT); - nextrawtoken(); - continue; - } - - // <= - if (rawtokens[0].istype(RawTokenType::LESS) && - rawtokens[1].istype(RawTokenType::EQUAL)) { - processedtokens.emplace_back(LpComparisonType::LEQ); - nextrawtoken(2); - continue; - } - - // < - if (rawtokens[0].istype(RawTokenType::LESS)) { - processedtokens.emplace_back(LpComparisonType::L); - nextrawtoken(); - continue; - } - - // >= - if (rawtokens[0].istype(RawTokenType::GREATER) && - rawtokens[1].istype(RawTokenType::EQUAL)) { - processedtokens.emplace_back(LpComparisonType::GEQ); - nextrawtoken(2); - continue; - } - - // > - if (rawtokens[0].istype(RawTokenType::GREATER)) { - processedtokens.emplace_back(LpComparisonType::G); - nextrawtoken(); - continue; - } - - // = - if (rawtokens[0].istype(RawTokenType::EQUAL)) { - processedtokens.emplace_back(LpComparisonType::EQ); - nextrawtoken(); - continue; - } - - // FILEEND should have been handled in condition of while() - assert(!rawtokens[0].istype(RawTokenType::FLEND)); - - // catch all unknown symbols - lpassert(false); - break; - } -} - -void Reader::nextrawtoken(size_t howmany) { - assert(howmany > 0); - assert(howmany <= NRAWTOKEN); - static_assert(NRAWTOKEN == 3, - "code below need to be adjusted if NRAWTOKEN changes"); - switch (howmany) { - case 1: { - rawtokens[0] = std::move(rawtokens[1]); - rawtokens[1] = std::move(rawtokens[2]); - while (!readnexttoken(rawtokens[2])) - ; - break; - } - case 2: { - rawtokens[0] = std::move(rawtokens[2]); - while (!readnexttoken(rawtokens[1])) - ; - while (!readnexttoken(rawtokens[2])) - ; - break; - } - case 3: { - while (!readnexttoken(rawtokens[0])) - ; - while (!readnexttoken(rawtokens[1])) - ; - while (!readnexttoken(rawtokens[2])) - ; - break; - } - default: { - size_t i = 0; - // move tokens up - for (; i < NRAWTOKEN - howmany; ++i) - rawtokens[i] = std::move(rawtokens[i + howmany]); - // read new tokens at end positions - for (; i < NRAWTOKEN; ++i) - // call readnexttoken() to overwrite current token - // if it didn't actually read a token (returns false), then call again - while (!readnexttoken(rawtokens[i])) - ; - } - } -} - -// return true, if token has been set; return false if skipped over whitespace -// only -bool Reader::readnexttoken(RawToken& t) { - if (this->linebufferpos == this->linebuffer.size()) { - // read next line if any are left. - if (this->file.eof()) { - t = RawTokenType::FLEND; - return true; - } - std::getline(this->file, linebuffer); - - // drop \r - if (!linebuffer.empty() && linebuffer.back() == '\r') linebuffer.pop_back(); - - // reset linebufferpos - this->linebufferpos = 0; - } - - // check single character tokens - char nextchar = this->linebuffer[this->linebufferpos]; - - switch (nextchar) { - // check for comment - case '\\': - // skip rest of line - this->linebufferpos = this->linebuffer.size(); - return false; - - // check for bracket opening - case '[': - t = RawTokenType::BRKOP; - this->linebufferpos++; - return true; - - // check for bracket closing - case ']': - t = RawTokenType::BRKCL; - this->linebufferpos++; - return true; - - // check for less sign - case '<': - t = RawTokenType::LESS; - this->linebufferpos++; - return true; - - // check for greater sign - case '>': - t = RawTokenType::GREATER; - this->linebufferpos++; - return true; - - // check for equal sign - case '=': - t = RawTokenType::EQUAL; - this->linebufferpos++; - return true; - - // check for colon - case ':': - t = RawTokenType::COLON; - this->linebufferpos++; - return true; - - // check for plus - case '+': - t = RawTokenType::PLUS; - this->linebufferpos++; - return true; - - // check for hat - case '^': - t = RawTokenType::HAT; - this->linebufferpos++; - return true; - - // check for slash - case '/': - t = RawTokenType::SLASH; - this->linebufferpos++; - return true; - - // check for asterisk - case '*': - t = RawTokenType::ASTERISK; - this->linebufferpos++; - return true; - - // check for minus - case '-': - t = RawTokenType::MINUS; - this->linebufferpos++; - return true; - - // check for whitespace - case ' ': - case '\t': - this->linebufferpos++; - return false; - - // check for line end - case ';': - case '\n': // \n should not happen due to using getline() - this->linebufferpos = this->linebuffer.size(); - return false; - - case '\0': // empty line - lpassert(this->linebufferpos == this->linebuffer.size()); - return false; - } - - // check for double value - const char* startptr = this->linebuffer.data() + this->linebufferpos; - char* endptr; - double constant = strtod(startptr, &endptr); - if (endptr != startptr) { - t = constant; - this->linebufferpos += endptr - startptr; - return true; - } - - // assume it's an (section/variable/constraint) identifier - auto endpos = - this->linebuffer.find_first_of("\t\n\\:+<>^= /-*[]", this->linebufferpos); - if (endpos == std::string::npos) - endpos = this->linebuffer.size(); // take complete rest of string - if (endpos > this->linebufferpos) { - t = std::string(this->linebuffer, this->linebufferpos, - endpos - this->linebufferpos); - this->linebufferpos = endpos; - return true; - } - - lpassert(false); - return false; -} +#include "reader.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HConfig.h" // for ZLIB_FOUND +#include "builder.hpp" +#include "def.hpp" +#ifdef ZLIB_FOUND +#include "../extern/zstr/zstr.hpp" +#endif + +// Cygwin doesn't come with an implementation for strdup if compiled with +// std=cxx +#ifdef __CYGWIN__ +#include +char* strdup(const char* s) { + size_t slen = strlen(s); + char* result = (char*)malloc(slen + 1); + if (result == NULL) { + return NULL; + } + + memcpy(result, s, slen + 1); + return result; +} +#endif + +enum class RawTokenType { + NONE, + STR, + CONS, + LESS, + GREATER, + EQUAL, + COLON, + LNEND, + FLEND, + BRKOP, + BRKCL, + PLUS, + MINUS, + HAT, + SLASH, + ASTERISK +}; + +struct RawToken { + RawTokenType type = RawTokenType::NONE; + std::string svalue; + double dvalue = 0.0; + + inline bool istype(RawTokenType t) const { return this->type == t; } + + RawToken& operator=(RawTokenType t) { + type = t; + return *this; + } + RawToken& operator=(const std::string& v) { + svalue = v; + type = RawTokenType::STR; + return *this; + } + RawToken& operator=(const double v) { + dvalue = v; + type = RawTokenType::CONS; + return *this; + } +}; + +enum class ProcessedTokenType { + NONE, + SECID, + VARID, + CONID, + CONST, + FREE, + BRKOP, + BRKCL, + COMP, + LNEND, + SLASH, + ASTERISK, + HAT, + SOSTYPE +}; + +enum class LpSectionKeyword { + NONE, + OBJMIN, + OBJMAX, + CON, + BOUNDS, + GEN, + BIN, + SEMI, + SOS, + END +}; + +static const std::unordered_map + sectionkeywordmap{{"minimize", LpSectionKeyword::OBJMIN}, + {"min", LpSectionKeyword::OBJMIN}, + {"minimum", LpSectionKeyword::OBJMIN}, + {"maximize", LpSectionKeyword::OBJMAX}, + {"max", LpSectionKeyword::OBJMAX}, + {"maximum", LpSectionKeyword::OBJMAX}, + {"subject to", LpSectionKeyword::CON}, + {"such that", LpSectionKeyword::CON}, + {"st", LpSectionKeyword::CON}, + {"s.t.", LpSectionKeyword::CON}, + {"bounds", LpSectionKeyword::BOUNDS}, + {"bound", LpSectionKeyword::BOUNDS}, + {"binary", LpSectionKeyword::BIN}, + {"binaries", LpSectionKeyword::BIN}, + {"bin", LpSectionKeyword::BIN}, + {"general", LpSectionKeyword::GEN}, + {"generals", LpSectionKeyword::GEN}, + {"gen", LpSectionKeyword::GEN}, + {"integer", LpSectionKeyword::GEN}, + {"integers", LpSectionKeyword::GEN}, + {"semi-continuous", LpSectionKeyword::SEMI}, + {"semi", LpSectionKeyword::SEMI}, + {"semis", LpSectionKeyword::SEMI}, + {"sos", LpSectionKeyword::SOS}, + {"end", LpSectionKeyword::END}}; + +enum class SosType { SOS1, SOS2 }; + +enum class LpComparisonType { LEQ, L, EQ, G, GEQ }; + +struct ProcessedToken { + ProcessedTokenType type; + union { + LpSectionKeyword keyword; + SosType sostype; + char* name; + double value; + LpComparisonType dir; + }; + + ProcessedToken(const ProcessedToken&) = delete; + ProcessedToken(ProcessedToken&& t) : type(t.type) { + switch (type) { + case ProcessedTokenType::SECID: + keyword = t.keyword; + break; + case ProcessedTokenType::SOSTYPE: + sostype = t.sostype; + break; + case ProcessedTokenType::CONID: + case ProcessedTokenType::VARID: + name = t.name; + break; + case ProcessedTokenType::CONST: + value = t.value; + break; + case ProcessedTokenType::COMP: + dir = t.dir; + break; + default:; + } + t.type = ProcessedTokenType::NONE; + } + + ProcessedToken(ProcessedTokenType t) : type(t){}; + + ProcessedToken(LpSectionKeyword kw) + : type(ProcessedTokenType::SECID), keyword(kw){}; + + ProcessedToken(SosType sos) + : type(ProcessedTokenType::SOSTYPE), sostype(sos){}; + + ProcessedToken(ProcessedTokenType t, const std::string& s) : type(t) { + assert(t == ProcessedTokenType::CONID || t == ProcessedTokenType::VARID); +#ifndef _WIN32 + name = strdup(s.c_str()); +#else + name = _strdup(s.c_str()); +#endif + }; + + ProcessedToken(double v) : type(ProcessedTokenType::CONST), value(v){}; + + ProcessedToken(LpComparisonType comp) + : type(ProcessedTokenType::COMP), dir(comp){}; + + ~ProcessedToken() { + if (type == ProcessedTokenType::CONID || type == ProcessedTokenType::VARID) + free(name); + } +}; + +// how many raw tokens to cache +// set to how many tokens we may need to look ahead +#define NRAWTOKEN 3 + +const double kHighsInf = std::numeric_limits::infinity(); +class Reader { + private: +#ifdef ZLIB_FOUND + zstr::ifstream file; +#else + std::ifstream file; +#endif + std::string linebuffer; + std::size_t linebufferpos; + std::array rawtokens; + std::vector processedtokens; + // store for each section a pointer to its begin and end (pointer to element + // after last) + std::map::iterator, + std::vector::iterator> > + sectiontokens; + + Builder builder; + + bool readnexttoken(RawToken&); + void nextrawtoken(size_t howmany = 1); + void processtokens(); + void splittokens(); + void processsections(); + void processnonesec(); + void processobjsec(); + void processconsec(); + void processboundssec(); + void processbinsec(); + void processgensec(); + void processsemisec(); + void processsossec(); + void processendsec(); + void parseexpression(std::vector::iterator& it, + std::vector::iterator end, + std::shared_ptr expr, bool isobj); + + public: + Reader(std::string filename) { +#ifdef ZLIB_FOUND + try { + file.open(filename); + } catch (const strict_fstream::Exception& e) { + } +#else + file.open(filename); +#endif + lpassert(file.is_open()); + }; + + ~Reader() { file.close(); } + + Model read(); +}; + +Model readinstance(std::string filename) { + Reader reader(filename); + return reader.read(); +} + +// convert string to lower-case, modifies string +static inline void tolower(std::string& s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); }); +} + +static inline bool iskeyword(const std::string& str, + const std::string* keywords, const int nkeywords) { + for (int i = 0; i < nkeywords; i++) { + if (str == keywords[i]) { + return true; + } + } + return false; +} + +static inline LpSectionKeyword parsesectionkeyword(const std::string& str) { + // look up lower case + auto it(sectionkeywordmap.find(str)); + if (it != sectionkeywordmap.end()) return it->second; + + return LpSectionKeyword::NONE; +} + +Model Reader::read() { + // std::clog << "Reading input, tokenizing..." << std::endl; + this->linebufferpos = 0; + // read first NRAWTOKEN token + // if file ends early, then all remaining tokens are set to FLEND + for (size_t i = 0; i < NRAWTOKEN; ++i) + while (!readnexttoken(rawtokens[i])) + ; + + processtokens(); + + linebuffer.clear(); + linebuffer.shrink_to_fit(); + + // std::clog << "Splitting tokens..." << std::endl; + splittokens(); + + // std::clog << "Setting up model..." << std::endl; + // + // Since + // + // "The problem statement must begin with the word MINIMIZE or + // MAXIMIZE, MINIMUM or MAXIMUM, or the abbreviations MIN or MAX, in + // any combination of upper- and lower-case characters. The word + // introduces the objective function section." + // + // Use positivity of sectiontokens.count(LpSectionKeyword::OBJMIN) + + // sectiontokens.count(LpSectionKeyword::OBJMAX) to identify garbage file + // + + const int num_objective_section = + sectiontokens.count(LpSectionKeyword::OBJMIN) + + sectiontokens.count(LpSectionKeyword::OBJMAX); + lpassert(num_objective_section>0); + + processsections(); + processedtokens.clear(); + processedtokens.shrink_to_fit(); + + return builder.model; +} + +void Reader::processnonesec() { + lpassert(sectiontokens.count(LpSectionKeyword::NONE) == 0); +} + +void Reader::parseexpression(std::vector::iterator& it, + std::vector::iterator end, + std::shared_ptr expr, bool isobj) { + if (it != end && it->type == ProcessedTokenType::CONID) { + expr->name = it->name; + ++it; + } + + while (it != end) { + std::vector::iterator next = it; + ++next; + // const var + if (next != end && it->type == ProcessedTokenType::CONST && + next->type == ProcessedTokenType::VARID) { + std::string name = next->name; + + std::shared_ptr linterm = + std::shared_ptr(new LinTerm()); + linterm->coef = it->value; + linterm->var = builder.getvarbyname(name); + // printf("LpReader: Term %+g %s\n", linterm->coef, + //name.c_str()); + expr->linterms.push_back(linterm); + + ++it; + ++it; + continue; + } + + // const + if (it->type == ProcessedTokenType::CONST) { + // printf("LpReader: Offset change from %+g by %+g\n", expr->offset, it->value); + expr->offset += it->value; + ++it; + continue; + } + + // var + if (it->type == ProcessedTokenType::VARID) { + std::string name = it->name; + + std::shared_ptr linterm = + std::shared_ptr(new LinTerm()); + linterm->coef = 1.0; + linterm->var = builder.getvarbyname(name); + // printf("LpReader: Term %+g %s\n", linterm->coef, + //name.c_str()); + expr->linterms.push_back(linterm); + + ++it; + continue; + } + + // quadratic expression + if (next != end && it->type == ProcessedTokenType::BRKOP) { + ++it; + while (it != end && it->type != ProcessedTokenType::BRKCL) { + // const var hat const + std::vector::iterator next1 = it; // token after it + std::vector::iterator next2 = it; // token 2nd-after it + std::vector::iterator next3 = it; // token 3rd-after it + ++next1; + ++next2; + ++next3; + if (next1 != end) { + ++next2; + ++next3; + } + if (next2 != end) ++next3; + + if (next3 != end && it->type == ProcessedTokenType::CONST && + next1->type == ProcessedTokenType::VARID && + next2->type == ProcessedTokenType::HAT && + next3->type == ProcessedTokenType::CONST) { + std::string name = next1->name; + + lpassert(next3->value == 2.0); + + std::shared_ptr quadterm = + std::shared_ptr(new QuadTerm()); + quadterm->coef = it->value; + quadterm->var1 = builder.getvarbyname(name); + quadterm->var2 = builder.getvarbyname(name); + expr->quadterms.push_back(quadterm); + + it = ++next3; + continue; + } + + // var hat const + if (next2 != end && it->type == ProcessedTokenType::VARID && + next1->type == ProcessedTokenType::HAT && + next2->type == ProcessedTokenType::CONST) { + std::string name = it->name; + + lpassert(next2->value == 2.0); + + std::shared_ptr quadterm = + std::shared_ptr(new QuadTerm()); + quadterm->coef = 1.0; + quadterm->var1 = builder.getvarbyname(name); + quadterm->var2 = builder.getvarbyname(name); + expr->quadterms.push_back(quadterm); + + it = next3; + continue; + } + + // const var asterisk var + if (next3 != end && it->type == ProcessedTokenType::CONST && + next1->type == ProcessedTokenType::VARID && + next2->type == ProcessedTokenType::ASTERISK && + next3->type == ProcessedTokenType::VARID) { + std::string name1 = next1->name; + std::string name2 = next3->name; + + std::shared_ptr quadterm = + std::shared_ptr(new QuadTerm()); + quadterm->coef = it->value; + quadterm->var1 = builder.getvarbyname(name1); + quadterm->var2 = builder.getvarbyname(name2); + expr->quadterms.push_back(quadterm); + + it = ++next3; + continue; + } + + // var asterisk var + if (next2 != end && it->type == ProcessedTokenType::VARID && + next1->type == ProcessedTokenType::ASTERISK && + next2->type == ProcessedTokenType::VARID) { + std::string name1 = it->name; + std::string name2 = next2->name; + + std::shared_ptr quadterm = + std::shared_ptr(new QuadTerm()); + quadterm->coef = 1.0; + quadterm->var1 = builder.getvarbyname(name1); + quadterm->var2 = builder.getvarbyname(name2); + expr->quadterms.push_back(quadterm); + + it = next3; + continue; + } + break; + } + if (isobj) { + // only in the objective function, a quadratic term is followed by + // "/2.0" + std::vector::iterator next1 = it; // token after it + std::vector::iterator next2 = it; // token 2nd-after it + ++next1; + ++next2; + if (next1 != end) ++next2; + + lpassert(next2 != end); + lpassert(it->type == ProcessedTokenType::BRKCL); + lpassert(next1->type == ProcessedTokenType::SLASH); + lpassert(next2->type == ProcessedTokenType::CONST); + lpassert(next2->value == 2.0); + it = ++next2; + } else { + lpassert(it != end); + lpassert(it->type == ProcessedTokenType::BRKCL); + ++it; + } + continue; + } + + break; + } +} + +void Reader::processobjsec() { + builder.model.objective = std::shared_ptr(new Expression); + if (sectiontokens.count(LpSectionKeyword::OBJMIN)) { + builder.model.sense = ObjectiveSense::MIN; + parseexpression(sectiontokens[LpSectionKeyword::OBJMIN].first, + sectiontokens[LpSectionKeyword::OBJMIN].second, + builder.model.objective, true); + lpassert(sectiontokens[LpSectionKeyword::OBJMIN].first == + sectiontokens[LpSectionKeyword::OBJMIN] + .second); // all section tokens should have been processed + } else if (sectiontokens.count(LpSectionKeyword::OBJMAX)) { + builder.model.sense = ObjectiveSense::MAX; + parseexpression(sectiontokens[LpSectionKeyword::OBJMAX].first, + sectiontokens[LpSectionKeyword::OBJMAX].second, + builder.model.objective, true); + lpassert(sectiontokens[LpSectionKeyword::OBJMAX].first == + sectiontokens[LpSectionKeyword::OBJMAX] + .second); // all section tokens should have been processed + } +} + +void Reader::processconsec() { + if (!sectiontokens.count(LpSectionKeyword::CON)) return; + std::vector::iterator& begin( + sectiontokens[LpSectionKeyword::CON].first); + std::vector::iterator& end( + sectiontokens[LpSectionKeyword::CON].second); + while (begin != end) { + std::shared_ptr con = + std::shared_ptr(new Constraint); + parseexpression(begin, end, con->expr, false); + // should not be at end of section yet, but a comparison operator should be + // next + lpassert(begin != sectiontokens[LpSectionKeyword::CON].second); + lpassert(begin->type == ProcessedTokenType::COMP); + LpComparisonType dir = begin->dir; + ++begin; + + // should still not be at end of section yet, but a right-hand-side value + // should be next + lpassert(begin != sectiontokens[LpSectionKeyword::CON].second); + lpassert(begin->type == ProcessedTokenType::CONST); + switch (dir) { + case LpComparisonType::EQ: + con->lowerbound = con->upperbound = begin->value; + break; + case LpComparisonType::LEQ: + con->upperbound = begin->value; + break; + case LpComparisonType::GEQ: + con->lowerbound = begin->value; + break; + default: + lpassert(false); + } + builder.model.constraints.push_back(con); + ++begin; + } +} + +void Reader::processboundssec() { + if (!sectiontokens.count(LpSectionKeyword::BOUNDS)) return; + std::vector::iterator& begin( + sectiontokens[LpSectionKeyword::BOUNDS].first); + std::vector::iterator& end( + sectiontokens[LpSectionKeyword::BOUNDS].second); + while (begin != end) { + std::vector::iterator next1 = begin; // token after begin + ++next1; + + // VAR free + if (next1 != end && begin->type == ProcessedTokenType::VARID && + next1->type == ProcessedTokenType::FREE) { + std::string name = begin->name; + std::shared_ptr var = builder.getvarbyname(name); + var->lowerbound = -kHighsInf; + var->upperbound = kHighsInf; + begin = ++next1; + continue; + } + + std::vector::iterator next2 = + next1; // token 2nd-after begin + std::vector::iterator next3 = + next1; // token 3rd-after begin + std::vector::iterator next4 = + next1; // token 4th-after begin + if (next1 != end) { + ++next2; + ++next3; + ++next4; + } + if (next2 != end) { + ++next3; + ++next4; + } + if (next3 != end) ++next4; + + // CONST COMP VAR COMP CONST + if (next4 != end && begin->type == ProcessedTokenType::CONST && + next1->type == ProcessedTokenType::COMP && + next2->type == ProcessedTokenType::VARID && + next3->type == ProcessedTokenType::COMP && + next4->type == ProcessedTokenType::CONST) { + lpassert(next1->dir == LpComparisonType::LEQ); + lpassert(next3->dir == LpComparisonType::LEQ); + + double lb = begin->value; + double ub = next4->value; + + std::string name = next2->name; + std::shared_ptr var = builder.getvarbyname(name); + + var->lowerbound = lb; + var->upperbound = ub; + + begin = ++next4; + continue; + } + + // CONST COMP VAR + if (next2 != end && begin->type == ProcessedTokenType::CONST && + next1->type == ProcessedTokenType::COMP && + next2->type == ProcessedTokenType::VARID) { + double value = begin->value; + std::string name = next2->name; + std::shared_ptr var = builder.getvarbyname(name); + LpComparisonType dir = next1->dir; + + lpassert(dir != LpComparisonType::L && dir != LpComparisonType::G); + + switch (dir) { + case LpComparisonType::LEQ: + var->lowerbound = value; + break; + case LpComparisonType::GEQ: + var->upperbound = value; + break; + case LpComparisonType::EQ: + var->lowerbound = var->upperbound = value; + break; + default: + lpassert(false); + } + begin = next3; + continue; + } + + // VAR COMP CONST + if (next2 != end && begin->type == ProcessedTokenType::VARID && + next1->type == ProcessedTokenType::COMP && + next2->type == ProcessedTokenType::CONST) { + double value = next2->value; + std::string name = begin->name; + std::shared_ptr var = builder.getvarbyname(name); + LpComparisonType dir = next1->dir; + + lpassert(dir != LpComparisonType::L && dir != LpComparisonType::G); + + switch (dir) { + case LpComparisonType::LEQ: + var->upperbound = value; + break; + case LpComparisonType::GEQ: + var->lowerbound = value; + break; + case LpComparisonType::EQ: + var->lowerbound = var->upperbound = value; + break; + default: + lpassert(false); + } + begin = next3; + continue; + } + + lpassert(false); + } +} + +void Reader::processbinsec() { + const LpSectionKeyword this_section_keyword = LpSectionKeyword::BIN; + if (!sectiontokens.count(this_section_keyword)) return; + std::vector::iterator& begin( + sectiontokens[this_section_keyword].first); + std::vector::iterator& end( + sectiontokens[this_section_keyword].second); + for (; begin != end; ++begin) { + if (begin->type == ProcessedTokenType::SECID) { + // Possible to have repeat of keyword for this section type + lpassert(begin->keyword == this_section_keyword); + continue; + } + lpassert(begin->type == ProcessedTokenType::VARID); + std::string name = begin->name; + std::shared_ptr var = builder.getvarbyname(name); + var->type = VariableType::BINARY; + // Respect any bounds already declared + if (var->upperbound == kHighsInf) var->upperbound = 1.0; + } +} + +void Reader::processgensec() { + const LpSectionKeyword this_section_keyword = LpSectionKeyword::GEN; + if (!sectiontokens.count(this_section_keyword)) return; + std::vector::iterator& begin( + sectiontokens[this_section_keyword].first); + std::vector::iterator& end( + sectiontokens[this_section_keyword].second); + for (; begin != end; ++begin) { + if (begin->type == ProcessedTokenType::SECID) { + // Possible to have repeat of keyword for this section type + lpassert(begin->keyword == this_section_keyword); + continue; + } + lpassert(begin->type == ProcessedTokenType::VARID); + std::string name = begin->name; + std::shared_ptr var = builder.getvarbyname(name); + if (var->type == VariableType::SEMICONTINUOUS) { + var->type = VariableType::SEMIINTEGER; + } else { + var->type = VariableType::GENERAL; + } + } +} + +void Reader::processsemisec() { + const LpSectionKeyword this_section_keyword = LpSectionKeyword::SEMI; + if (!sectiontokens.count(this_section_keyword)) return; + std::vector::iterator& begin( + sectiontokens[this_section_keyword].first); + std::vector::iterator& end( + sectiontokens[this_section_keyword].second); + for (; begin != end; ++begin) { + if (begin->type == ProcessedTokenType::SECID) { + // Possible to have repeat of keyword for this section type + lpassert(begin->keyword == this_section_keyword); + continue; + } + lpassert(begin->type == ProcessedTokenType::VARID); + std::string name = begin->name; + std::shared_ptr var = builder.getvarbyname(name); + if (var->type == VariableType::GENERAL) { + var->type = VariableType::SEMIINTEGER; + } else { + var->type = VariableType::SEMICONTINUOUS; + } + } +} + +void Reader::processsossec() { + const LpSectionKeyword this_section_keyword = LpSectionKeyword::SOS; + if (!sectiontokens.count(this_section_keyword)) return; + std::vector::iterator& begin( + sectiontokens[this_section_keyword].first); + std::vector::iterator& end( + sectiontokens[this_section_keyword].second); + while (begin != end) { + std::shared_ptr sos = std::shared_ptr(new SOS); + + // sos1: S1 :: x1 : 1 x2 : 2 x3 : 3 + + // name of SOS is mandatory + lpassert(begin->type == ProcessedTokenType::CONID); + sos->name = begin->name; + ++begin; + + // SOS type + lpassert(begin != end); + lpassert(begin->type == ProcessedTokenType::SOSTYPE); + sos->type = begin->sostype == SosType::SOS1 ? 1 : 2; + ++begin; + + while (begin != end) { + // process all "var : weight" entries + // when processtokens() sees a string followed by a colon, it classifies + // this as a CONID but in a SOS section, this is actually a variable + // identifier + if (begin->type != ProcessedTokenType::CONID) break; + std::string name = begin->name; + std::vector::iterator next = begin; + ++next; + if (next != end && next->type == ProcessedTokenType::CONST) { + auto var = builder.getvarbyname(name); + double weight = next->value; + + sos->entries.push_back({var, weight}); + + begin = ++next; + continue; + } + + break; + } + + builder.model.soss.push_back(sos); + } +} + +void Reader::processendsec() { + lpassert(sectiontokens.count(LpSectionKeyword::END) == 0); +} + +void Reader::processsections() { + processnonesec(); + processobjsec(); + processconsec(); + processboundssec(); + processgensec(); + processbinsec(); + processsemisec(); + processsossec(); + processendsec(); +} + +void Reader::splittokens() { + LpSectionKeyword currentsection = LpSectionKeyword::NONE; + + bool debug_open_section = false; + for (std::vector::iterator it(processedtokens.begin()); + it != processedtokens.end(); ++it) { + // Look for section keywords + if (it->type != ProcessedTokenType::SECID) continue; + // currentsection is initially LpSectionKeyword::NONE, so the + // first section ID will be a new section type + // + // Only record change of section and check for repeated + // section if the keyword is for a different section. Allows + // repetition of Integers and General (cf #1299) for example + const bool new_section_type = currentsection != it->keyword; + if (new_section_type) { + if (currentsection != LpSectionKeyword::NONE) { + // Current section is non-trivial, so mark its end, using the + // value of currentsection to indicate that there is no open + // section + lpassert(debug_open_section); + sectiontokens[currentsection].second = it; + debug_open_section = false; + currentsection = LpSectionKeyword::NONE; + } + } + std::vector::iterator next = it; + ++next; + if (next == processedtokens.end() || + next->type == ProcessedTokenType::SECID) { + // Reached the end of the tokens or the new section is empty + // + // currentsection will be LpSectionKeyword::NONE unless the + // second of two sections of the same type is empty and the + // next section is of a new type, in which case mark the end of + // the current section + if (currentsection != LpSectionKeyword::NONE && + currentsection != next->keyword) { + lpassert(debug_open_section); + sectiontokens[currentsection].second = it; + debug_open_section = false; + } + currentsection = LpSectionKeyword::NONE; + lpassert(!debug_open_section); + continue; + } + // Next section is non-empty + if (new_section_type) { + // Section type change + currentsection = it->keyword; + // Make sure the new section type has not occurred previously + lpassert(sectiontokens.count(currentsection) == 0); + // Remember the beginning of the new section: its the token + // following the current one + lpassert(!debug_open_section); + sectiontokens[currentsection].first = next; + debug_open_section = true; + } + // Always ends with either an open section or a section type of + // LpSectionKeyword::NONE + lpassert(debug_open_section != (currentsection == LpSectionKeyword::NONE)); + } + // Check that the last section has been closed + lpassert(currentsection == LpSectionKeyword::NONE); +} + +void Reader::processtokens() { + std::string svalue_lc; + while (!rawtokens[0].istype(RawTokenType::FLEND)) { + fflush(stdout); + + // Slash + asterisk: comment, skip everything up to next asterisk + slash + if (rawtokens[0].istype(RawTokenType::SLASH) && + rawtokens[1].istype(RawTokenType::ASTERISK)) { + do { + nextrawtoken(2); + } while (!(rawtokens[0].istype(RawTokenType::ASTERISK) && + rawtokens[1].istype(RawTokenType::SLASH)) && + !rawtokens[0].istype(RawTokenType::FLEND)); + nextrawtoken(2); + continue; + } + + if (rawtokens[0].istype(RawTokenType::STR)) { + svalue_lc = rawtokens[0].svalue; + tolower(svalue_lc); + } + + // long section keyword semi-continuous + if (rawtokens[0].istype(RawTokenType::STR) && + rawtokens[1].istype(RawTokenType::MINUS) && + rawtokens[2].istype(RawTokenType::STR)) { + std::string temp = rawtokens[2].svalue; + tolower(temp); + LpSectionKeyword keyword = parsesectionkeyword(svalue_lc + "-" + temp); + if (keyword != LpSectionKeyword::NONE) { + processedtokens.emplace_back(keyword); + nextrawtoken(3); + continue; + } + } + + // long section keyword subject to/such that + if (rawtokens[0].istype(RawTokenType::STR) && + rawtokens[1].istype(RawTokenType::STR)) { + std::string temp = rawtokens[1].svalue; + tolower(temp); + LpSectionKeyword keyword = parsesectionkeyword(svalue_lc + " " + temp); + if (keyword != LpSectionKeyword::NONE) { + processedtokens.emplace_back(keyword); + nextrawtoken(2); + continue; + } + } + + // other section keyword + if (rawtokens[0].istype(RawTokenType::STR)) { + LpSectionKeyword keyword = parsesectionkeyword(svalue_lc); + if (keyword != LpSectionKeyword::NONE) { + processedtokens.emplace_back(keyword); + nextrawtoken(); + continue; + } + } + + // sos type identifier? "S1 ::" or "S2 ::" + if (rawtokens[0].istype(RawTokenType::STR) && + rawtokens[1].istype(RawTokenType::COLON) && + rawtokens[2].istype(RawTokenType::COLON)) { + lpassert(rawtokens[0].svalue.length() == 2); + lpassert(rawtokens[0].svalue[0] == 'S' || rawtokens[0].svalue[0] == 's'); + lpassert(rawtokens[0].svalue[1] == '1' || rawtokens[0].svalue[1] == '2'); + processedtokens.emplace_back( + rawtokens[0].svalue[1] == '1' ? SosType::SOS1 : SosType::SOS2); + nextrawtoken(3); + continue; + } + + // constraint identifier? + if (rawtokens[0].istype(RawTokenType::STR) && + rawtokens[1].istype(RawTokenType::COLON)) { + processedtokens.emplace_back(ProcessedTokenType::CONID, + rawtokens[0].svalue); + nextrawtoken(2); + continue; + } + + // check if free + if (rawtokens[0].istype(RawTokenType::STR) && + iskeyword(svalue_lc, LP_KEYWORD_FREE, LP_KEYWORD_FREE_N)) { + processedtokens.emplace_back(ProcessedTokenType::FREE); + nextrawtoken(); + continue; + } + + // check if infinity + if (rawtokens[0].istype(RawTokenType::STR) && + iskeyword(svalue_lc, LP_KEYWORD_INF, LP_KEYWORD_INF_N)) { + processedtokens.emplace_back(kHighsInf); + nextrawtoken(); + continue; + } + + // assume var identifier + if (rawtokens[0].istype(RawTokenType::STR)) { + processedtokens.emplace_back(ProcessedTokenType::VARID, + rawtokens[0].svalue); + nextrawtoken(); + continue; + } + + // + or - + if (rawtokens[0].istype(RawTokenType::PLUS) || + rawtokens[0].istype(RawTokenType::MINUS)) { + double sign = rawtokens[0].istype(RawTokenType::PLUS) ? 1.0 : -1.0; + nextrawtoken(); + + // another + or - for #948, #950 + if (rawtokens[0].istype(RawTokenType::PLUS) || + rawtokens[0].istype(RawTokenType::MINUS)) { + sign *= rawtokens[0].istype(RawTokenType::PLUS) ? 1.0 : -1.0; + nextrawtoken(); + } + + // +/- Constant + if (rawtokens[0].istype(RawTokenType::CONS)) { + processedtokens.emplace_back(sign * rawtokens[0].dvalue); + nextrawtoken(); + continue; + } + + // + [, + + [, - - [ + if (rawtokens[0].istype(RawTokenType::BRKOP) && sign == 1.0) { + processedtokens.emplace_back(ProcessedTokenType::BRKOP); + nextrawtoken(); + continue; + } + + // - [, + - [, - + [ + if (rawtokens[0].istype(RawTokenType::BRKOP)) lpassert(false); + + // +/- variable name + if (rawtokens[0].istype(RawTokenType::STR)) { + processedtokens.emplace_back(sign); + continue; + } + + // +/- (possibly twice) followed by something that isn't a constant, + // opening bracket, or string (variable name) + if (rawtokens[0].istype(RawTokenType::GREATER)) { + // ">" suggests that the file contains indicator constraints + printf( + "File appears to contain indicator constraints: cannot currently " + "be handled by HiGHS\n"); + } + lpassert(false); + } + + // constant [ + if (rawtokens[0].istype(RawTokenType::CONS) && + rawtokens[1].istype(RawTokenType::BRKOP)) { + lpassert(false); + } + + // constant + if (rawtokens[0].istype(RawTokenType::CONS)) { + processedtokens.emplace_back(rawtokens[0].dvalue); + nextrawtoken(); + continue; + } + + // [ + if (rawtokens[0].istype(RawTokenType::BRKOP)) { + processedtokens.emplace_back(ProcessedTokenType::BRKOP); + nextrawtoken(); + continue; + } + + // ] + if (rawtokens[0].istype(RawTokenType::BRKCL)) { + processedtokens.emplace_back(ProcessedTokenType::BRKCL); + nextrawtoken(); + continue; + } + + // / + if (rawtokens[0].istype(RawTokenType::SLASH)) { + processedtokens.emplace_back(ProcessedTokenType::SLASH); + nextrawtoken(); + continue; + } + + // * + if (rawtokens[0].istype(RawTokenType::ASTERISK)) { + processedtokens.emplace_back(ProcessedTokenType::ASTERISK); + nextrawtoken(); + continue; + } + + // ^ + if (rawtokens[0].istype(RawTokenType::HAT)) { + processedtokens.emplace_back(ProcessedTokenType::HAT); + nextrawtoken(); + continue; + } + + // <= + if (rawtokens[0].istype(RawTokenType::LESS) && + rawtokens[1].istype(RawTokenType::EQUAL)) { + processedtokens.emplace_back(LpComparisonType::LEQ); + nextrawtoken(2); + continue; + } + + // < + if (rawtokens[0].istype(RawTokenType::LESS)) { + processedtokens.emplace_back(LpComparisonType::L); + nextrawtoken(); + continue; + } + + // >= + if (rawtokens[0].istype(RawTokenType::GREATER) && + rawtokens[1].istype(RawTokenType::EQUAL)) { + processedtokens.emplace_back(LpComparisonType::GEQ); + nextrawtoken(2); + continue; + } + + // > + if (rawtokens[0].istype(RawTokenType::GREATER)) { + processedtokens.emplace_back(LpComparisonType::G); + nextrawtoken(); + continue; + } + + // = + if (rawtokens[0].istype(RawTokenType::EQUAL)) { + processedtokens.emplace_back(LpComparisonType::EQ); + nextrawtoken(); + continue; + } + + // FILEEND should have been handled in condition of while() + assert(!rawtokens[0].istype(RawTokenType::FLEND)); + + // catch all unknown symbols + lpassert(false); + break; + } +} + +void Reader::nextrawtoken(size_t howmany) { + assert(howmany > 0); + assert(howmany <= NRAWTOKEN); + static_assert(NRAWTOKEN == 3, + "code below need to be adjusted if NRAWTOKEN changes"); + switch (howmany) { + case 1: { + rawtokens[0] = std::move(rawtokens[1]); + rawtokens[1] = std::move(rawtokens[2]); + while (!readnexttoken(rawtokens[2])) + ; + break; + } + case 2: { + rawtokens[0] = std::move(rawtokens[2]); + while (!readnexttoken(rawtokens[1])) + ; + while (!readnexttoken(rawtokens[2])) + ; + break; + } + case 3: { + while (!readnexttoken(rawtokens[0])) + ; + while (!readnexttoken(rawtokens[1])) + ; + while (!readnexttoken(rawtokens[2])) + ; + break; + } + default: { + size_t i = 0; + // move tokens up + for (; i < NRAWTOKEN - howmany; ++i) + rawtokens[i] = std::move(rawtokens[i + howmany]); + // read new tokens at end positions + for (; i < NRAWTOKEN; ++i) + // call readnexttoken() to overwrite current token + // if it didn't actually read a token (returns false), then call again + while (!readnexttoken(rawtokens[i])) + ; + } + } +} + +// return true, if token has been set; return false if skipped over whitespace +// only +bool Reader::readnexttoken(RawToken& t) { + if (this->linebufferpos == this->linebuffer.size()) { + // read next line if any are left. + if (this->file.eof()) { + t = RawTokenType::FLEND; + return true; + } + std::getline(this->file, linebuffer); + + // drop \r + if (!linebuffer.empty() && linebuffer.back() == '\r') linebuffer.pop_back(); + + // reset linebufferpos + this->linebufferpos = 0; + } + + // check single character tokens + char nextchar = this->linebuffer[this->linebufferpos]; + + switch (nextchar) { + // check for comment + case '\\': + // skip rest of line + this->linebufferpos = this->linebuffer.size(); + return false; + + // check for bracket opening + case '[': + t = RawTokenType::BRKOP; + this->linebufferpos++; + return true; + + // check for bracket closing + case ']': + t = RawTokenType::BRKCL; + this->linebufferpos++; + return true; + + // check for less sign + case '<': + t = RawTokenType::LESS; + this->linebufferpos++; + return true; + + // check for greater sign + case '>': + t = RawTokenType::GREATER; + this->linebufferpos++; + return true; + + // check for equal sign + case '=': + t = RawTokenType::EQUAL; + this->linebufferpos++; + return true; + + // check for colon + case ':': + t = RawTokenType::COLON; + this->linebufferpos++; + return true; + + // check for plus + case '+': + t = RawTokenType::PLUS; + this->linebufferpos++; + return true; + + // check for hat + case '^': + t = RawTokenType::HAT; + this->linebufferpos++; + return true; + + // check for slash + case '/': + t = RawTokenType::SLASH; + this->linebufferpos++; + return true; + + // check for asterisk + case '*': + t = RawTokenType::ASTERISK; + this->linebufferpos++; + return true; + + // check for minus + case '-': + t = RawTokenType::MINUS; + this->linebufferpos++; + return true; + + // check for whitespace + case ' ': + case '\t': + this->linebufferpos++; + return false; + + // check for line end + case ';': + case '\n': // \n should not happen due to using getline() + this->linebufferpos = this->linebuffer.size(); + return false; + + case '\0': // empty line + lpassert(this->linebufferpos == this->linebuffer.size()); + return false; + } + + // check for double value + const char* startptr = this->linebuffer.data() + this->linebufferpos; + char* endptr; + double constant = strtod(startptr, &endptr); + if (endptr != startptr) { + t = constant; + this->linebufferpos += endptr - startptr; + return true; + } + + // assume it's an (section/variable/constraint) identifier + auto endpos = + this->linebuffer.find_first_of("\t\n\\:+<>^= /-*[]", this->linebufferpos); + if (endpos == std::string::npos) + endpos = this->linebuffer.size(); // take complete rest of string + if (endpos > this->linebufferpos) { + t = std::string(this->linebuffer, this->linebufferpos, + endpos - this->linebufferpos); + this->linebufferpos = endpos; + return true; + } + + lpassert(false); + return false; +} diff --git a/extern/filereaderlp/reader.hpp b/extern/filereaderlp/reader.hpp index f292d1df14..b86a954281 100644 --- a/extern/filereaderlp/reader.hpp +++ b/extern/filereaderlp/reader.hpp @@ -1,10 +1,10 @@ -#ifndef __READERLP_READER_HPP__ -#define __READERLP_READER_HPP__ - -#include - -#include "model.hpp" - -Model readinstance(std::string filename); - -#endif +#ifndef __READERLP_READER_HPP__ +#define __READERLP_READER_HPP__ + +#include + +#include "model.hpp" + +Model readinstance(std::string filename); + +#endif diff --git a/src/parallel/HighsBinarySemaphore.h b/src/parallel/HighsBinarySemaphore.h index 5ca24e0f6b..ee40b5931b 100644 --- a/src/parallel/HighsBinarySemaphore.h +++ b/src/parallel/HighsBinarySemaphore.h @@ -110,4 +110,4 @@ class HighsBinarySemaphore { } }; -#endif \ No newline at end of file +#endif diff --git a/src/parallel/HighsCacheAlign.h b/src/parallel/HighsCacheAlign.h index b16e145a47..90ec627617 100644 --- a/src/parallel/HighsCacheAlign.h +++ b/src/parallel/HighsCacheAlign.h @@ -84,4 +84,4 @@ struct cache_aligned { } // namespace highs -#endif \ No newline at end of file +#endif diff --git a/src/parallel/HighsCombinable.h b/src/parallel/HighsCombinable.h index b7b877189e..ddfa8561e7 100644 --- a/src/parallel/HighsCombinable.h +++ b/src/parallel/HighsCombinable.h @@ -118,4 +118,4 @@ HighsCombinable makeHighsCombinable(FConstruct&& fconstruct) { return HighsCombinable(std::forward(fconstruct)); } -#endif \ No newline at end of file +#endif diff --git a/src/parallel/HighsMutex.h b/src/parallel/HighsMutex.h index 34d8b95541..787b492c9d 100644 --- a/src/parallel/HighsMutex.h +++ b/src/parallel/HighsMutex.h @@ -126,4 +126,4 @@ class HighsMutex { } }; -#endif \ No newline at end of file +#endif diff --git a/src/parallel/HighsRaceTimer.h b/src/parallel/HighsRaceTimer.h index 180b13a0b5..cc1629d293 100644 --- a/src/parallel/HighsRaceTimer.h +++ b/src/parallel/HighsRaceTimer.h @@ -40,4 +40,4 @@ class HighsRaceTimer { } }; -#endif \ No newline at end of file +#endif diff --git a/src/parallel/HighsSchedulerConstants.h b/src/parallel/HighsSchedulerConstants.h index 1121724d9d..ad21fdaf23 100644 --- a/src/parallel/HighsSchedulerConstants.h +++ b/src/parallel/HighsSchedulerConstants.h @@ -21,4 +21,4 @@ struct HighsSchedulerConstants { }; }; -#endif \ No newline at end of file +#endif diff --git a/src/pdlp/cupdlp/cupdlp_utils.c b/src/pdlp/cupdlp/cupdlp_utils.c index 58b14b2ac3..e648e9ad31 100644 --- a/src/pdlp/cupdlp/cupdlp_utils.c +++ b/src/pdlp/cupdlp/cupdlp_utils.c @@ -431,7 +431,7 @@ void PDHG_PrintPDHGParam(CUPDLPwork *w) { cupdlp_printf("\n"); } -void PDHG_PrintHugeCUPDHG() { +void PDHG_PrintHugeCUPDHG(void) { cupdlp_printf("\n"); cupdlp_printf(" ____ _ _ ____ ____ _ ____\n"); cupdlp_printf(" / ___| | | | _ \\| _ \\| | | _ \\\n"); @@ -441,7 +441,7 @@ void PDHG_PrintHugeCUPDHG() { cupdlp_printf("\n"); } -void PDHG_PrintUserParamHelper() { +void PDHG_PrintUserParamHelper(void) { PDHG_PrintHugeCUPDHG(); cupdlp_printf("CUPDHG User Parameters:\n"); diff --git a/src/pdlp/cupdlp/cupdlp_utils.h b/src/pdlp/cupdlp/cupdlp_utils.h index 49c9510a06..337a743c8e 100644 --- a/src/pdlp/cupdlp/cupdlp_utils.h +++ b/src/pdlp/cupdlp/cupdlp_utils.h @@ -37,9 +37,9 @@ cupdlp_int PDHG_Clear(CUPDLPwork *w); void PDHG_PrintPDHGParam(CUPDLPwork *w); -void PDHG_PrintHugeCUPDHG(); +void PDHG_PrintHugeCUPDHG(void); -void PDHG_PrintUserParamHelper(); +void PDHG_PrintUserParamHelper(void); cupdlp_retcode getUserParam(int argc, char **argv, cupdlp_bool *ifChangeIntParam, cupdlp_int *intParam, diff --git a/src/presolve/ICrashUtil.h b/src/presolve/ICrashUtil.h index 1d53d8a69d..93cb1c704e 100644 --- a/src/presolve/ICrashUtil.h +++ b/src/presolve/ICrashUtil.h @@ -62,4 +62,4 @@ void updateResidualFast(const HighsLp& lp, const HighsSolution& sol, // Allows negative residuals void updateResidualIca(const HighsLp& lp, const HighsSolution& sol, std::vector& residual); -#endif \ No newline at end of file +#endif