COIN-OR::LEMON - Graph Library

Changeset 2364:3a5e67bd42d2 in lemon-0.x for lemon/lp_utils.h


Ignore:
Timestamp:
02/15/07 20:15:14 (17 years ago)
Author:
Balazs Dezso
Branch:
default
Phase:
public
Convert:
svn:c9d7d8f5-90d6-0310-b91f-818b3a526b0e/lemon/trunk@3175
Message:

Lp row and col getter function
lp section reader and writer for lemon IO

File:
1 edited

Legend:

Unmodified
Added
Removed
  • lemon/lp_utils.h

    r2363 r2364  
    6969  private:
    7070
     71    enum SubSection {
     72      CONSTRAINTS, BOUNDS, OBJECTIVE
     73    };
     74
    7175    enum Relation {
    7276      LE, EQ, GE
    7377    };
    7478
    75     std::istream& readConstraint(std::istream& is, LpSolverBase::Constr& c) {
     79    std::istream& readConstraint(std::istream& is) {
     80      LpSolverBase::Constr c;
    7681      char x;
    7782      LpSolverBase::Expr e1, e2;
     
    8388      is >> std::ws;
    8489      readExpression(is, e2);
     90      is >> std::ws;
    8591      if (!is.get(x)) {
    8692        if (op1 == LE) {
     93          if (e1.size() == 0) {
     94            c = e2 >= e1;
     95          } else {
     96            c = e1 <= e2;
     97          }
    8798          c = e1 <= e2;
    8899        } else if (op1 == GE) {
    89           c = e1 >= e2;
    90         } else {
    91           c = e1 == e2;
     100          if (e1.size() == 0) {
     101            c = e2 <= e1;
     102          } else {
     103            c = e1 >= e2;
     104          }
     105        } else {
     106          if (e1.size() == 0) {
     107            c = e2 == e1;
     108          } else {
     109            c = e1 == e2;
     110          }
    92111        }
    93112      } else {
     
    109128          c = e1.constComp() >= e2 >= e3.constComp();
    110129        }
    111       }
     130        is >> std::ws;
     131        if (is.get(x)) {
     132          throw DataFormatError("Wrong variable bounds format");
     133        }
     134      }
     135      lp.addRow(c);
     136      return is;
     137    }
     138
     139    std::istream& readObjective(std::istream& is) {
     140      is >> std::ws;
     141      std::string sense;
     142      is >> sense;
     143      if (sense != "min" && sense != "max") {
     144        throw DataFormatError("Wrong objective function format");
     145      }
     146      LpSolverBase::Expr expr;
     147      is >> std::ws;
     148      readExpression(is, expr);
     149      lp.setObj(expr);
     150      if (sense == "min") {
     151        lp.min();
     152      } else {
     153        lp.max();
     154      }
     155      return is;
     156    }
     157
     158    std::istream& readBounds(std::istream& is) {
     159      LpSolverBase::Col c;
     160      char x;
     161      is >> std::ws;
     162      if (!is.get(x)) {
     163        throw DataFormatError("Wrong variable bounds format");
     164      }
     165      if (varFirstChar(x)) {
     166        is.putback(x);
     167        readCol(is, c);
     168        is >> std::ws;
     169        Relation op1;
     170        readRelation(is, op1);
     171        double v;
     172        readNum(is, v);
     173        if (op1 == EQ) {
     174          lp.colLowerBound(c, v);
     175          lp.colUpperBound(c, v);
     176        } else if (op1 == LE) {
     177          lp.colUpperBound(c, v);
     178        } else {
     179          lp.colLowerBound(c, v);
     180        }
     181        is >> std::ws;
     182        if (is.get(x)) {
     183          throw DataFormatError("Wrong variable bounds format");
     184        }
     185      } else {
     186        is.putback(x);
     187        double v;
     188        readNum(is, v);
     189        is >> std::ws;
     190        Relation op1;
     191        readRelation(is, op1);
     192        is >> std::ws;
     193        readCol(is, c);
     194        is >> std::ws;
     195        if (is.get(x)) {
     196          is.putback(x);
     197          is >> std::ws;
     198          Relation op2;
     199          readRelation(is, op2);
     200          double w;
     201          is >> std::ws;
     202          readNum(is, w);
     203          if (op2 != op1 || op1 == EQ) {
     204            throw DataFormatError("Wrong range format");
     205          }
     206          if (op1 == LE) {
     207            lp.colLowerBound(c, v);
     208            lp.colUpperBound(c, w);
     209          } else {
     210            lp.colLowerBound(c, w);
     211            lp.colUpperBound(c, v);
     212          }
     213          if (is.get(x)) {
     214            throw DataFormatError("Wrong variable bounds format");
     215          }
     216        } else {
     217          if (op1 == EQ) {
     218            lp.colLowerBound(c, v);
     219            lp.colUpperBound(c, v);
     220          } else if (op1 == LE) {
     221            lp.colLowerBound(c, v);
     222          } else {
     223            lp.colUpperBound(c, v);
     224          }
     225        }
     226      }
     227      return is;
    112228    }
    113229
     
    278394    static bool varChar(char c) {
    279395      return (c >= '0' && c <= '9') ||
    280         (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    281     }
    282 
    283 
    284     void addConstraint(const LpSolverBase::Constr& constr) {
    285       if (constr.expr().size() != 1) {
    286         lp.addRow(constr);
    287       } else {
    288         Lp::Expr e = constr.expr();
    289         LpSolverBase::Col col = e.begin()->first;
    290         double coeff = e.begin()->second;
    291         double lb = LpSolverBase::NaN;
    292         double ub = LpSolverBase::NaN;
    293         if (coeff > 0) {
    294           if (constr.upperBounded()) {
    295             lb = (constr.lowerBound() - e.constComp()) / coeff;
    296           }
    297           if (constr.lowerBounded()) {
    298             ub = (constr.upperBound() - e.constComp()) / coeff;
    299           }
    300         } else if (coeff < 0) {
    301           if (constr.upperBounded()) {
    302             lb = (constr.upperBound() - e.constComp()) / coeff;
    303           }
    304           if (constr.lowerBounded()) {
    305             ub = (constr.lowerBound() - e.constComp()) / coeff;
    306           }
    307         } else {
    308           lb = -LpSolverBase::INF;
    309           ub = LpSolverBase::INF;
    310         }
    311         lp.colLowerBound(col, lb);
    312         lp.colUpperBound(col, ub);
    313       }
     396        (c >= 'a' && c <= 'z') ||
     397        (c >= 'A' && c <= 'Z') ||
     398        c == '[' || c == ']';
    314399    }
    315400   
     
    321406    virtual void read(std::istream& is) {
    322407      std::string line;
     408      SubSection sub = CONSTRAINTS;
    323409      while (getline(is, line)) {
    324410        std::istringstream ls(line);
    325         std::string sense;
    326         ls >> sense;
    327         if (sense == "min" || sense == "max") {
    328           LpSolverBase::Expr expr;
    329           ls >> std::ws;
    330           readExpression(ls, expr);
    331           lp.setObj(expr);
    332           if (sense == "min") {
    333             lp.min();
    334           } else {
    335             lp.max();
    336           }
     411        std::string type;
     412        ls >> type;
     413        if (type == "constraints") {
     414          sub = CONSTRAINTS;
     415        } else if (type == "bounds") {
     416          sub = BOUNDS;
     417        } else if (type == "objective") {
     418          sub = OBJECTIVE;
    337419        } else {
    338420          ls.str(line);
    339           LpSolverBase::Constr constr;
    340           ls >> std::ws;
    341           readConstraint(ls, constr);
    342           addConstraint(constr);
     421          switch (sub) {
     422          case CONSTRAINTS:
     423            readConstraint(ls);
     424            break;
     425          case BOUNDS:
     426            readBounds(ls);
     427            break;
     428          case OBJECTIVE:
     429            readObjective(ls);
     430            break;
     431          }
    343432        }       
    344433      }
     
    361450
    362451
    363 //   class LpWriter : public LemonWriter::SectionWriter {
    364 //     typedef LemonWriter::SectionWriter Parent;
    365 //   public:
    366 
    367 
    368 //     /// \brief Constructor.
    369 //     ///
    370 //     /// Constructor for LpWriter. It creates the LpWriter and attach
    371 //     /// it into the given LemonWriter. The lp writer will add
    372 //     /// variables, constraints and objective function to the
    373 //     /// given lp solver.
    374 //     LpWriter(LemonWriter& _writer, LpSolverBase& _lp,
    375 //              const std::string& _name = std::string())
    376 //       : Parent(_writer), lp(_lp), name(_name) {}
    377 
    378 
    379 //     /// \brief Destructor.
    380 //     ///
    381 //     /// Destructor for NodeSetWriter.
    382 //     virtual ~LpWriter() {}
    383 
    384 //   private:
    385 //     LpWriter(const LpWriter&);
    386 //     void operator=(const LpWriter&);
     452  class LpWriter : public LemonWriter::SectionWriter {
     453    typedef LemonWriter::SectionWriter Parent;
     454  public:
     455
     456
     457    /// \brief Constructor.
     458    ///
     459    /// Constructor for LpWriter. It creates the LpWriter and attach
     460    /// it into the given LemonWriter. The lp writer will add
     461    /// variables, constraints and objective function to the
     462    /// given lp solver.
     463    LpWriter(LemonWriter& _writer, LpSolverBase& _lp,
     464             const std::string& _name = std::string())
     465      : Parent(_writer), lp(_lp), name(_name) {}
     466
     467
     468    /// \brief Destructor.
     469    ///
     470    /// Destructor for NodeSetWriter.
     471    virtual ~LpWriter() {}
     472
     473  private:
     474    LpWriter(const LpWriter&);
     475    void operator=(const LpWriter&);
    387476 
    388 //   protected:
    389 
    390 //     /// \brief Gives back true when the SectionWriter can process
    391 //     /// the section with the given header line.
    392 //     ///
    393 //     /// It gives back the header line of the \c \@lp section.
    394 //     virtual std::string header() {
    395 //       std::ostringstream ls(line);
    396 //       ls << "@lp " << name;
    397 //       return ls.str();
    398 //     }
    399 
    400 //   private:
    401 
    402 //     std::ostream& writeConstraint(std::ostream& is, LpSolverBase::Constr& c) {
    403 //       char x;
    404 
     477  protected:
     478
     479    /// \brief Gives back true when the SectionWriter can process
     480    /// the section with the given header line.
     481    ///
     482    /// It gives back the header line of the \c \@lp section.
     483    virtual std::string header() {
     484      std::ostringstream ls;
     485      ls << "@lp " << name;
     486      return ls.str();
     487    }
     488
     489  private:
     490
     491    void createCols() {
     492      int num = 1;
     493
     494      std::map<std::string, LpSolverBase::Col> inverse;
     495
     496      for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
     497        std::string name = lp.colName(it);
     498        if (!name.empty() && inverse.find(name) == inverse.end()) {
     499          cols.insert(std::make_pair(it, name));
     500          inverse.insert(std::make_pair(name, it));
     501        } else {
     502          std::ostringstream ls;
     503          ls << "var" << num;
     504          ++num;
     505          cols.insert(std::make_pair(it, ls.str()));
     506          inverse.insert(std::make_pair(ls.str(), it));
     507        }
     508      }
     509    }
     510   
     511    void writeExpression(std::ostream& os, const LpSolverBase::Expr& e) {
     512      bool first = true;
     513      for (LpSolverBase::Expr::const_iterator jt = e.begin();
     514           jt != e.end(); ++jt) {
     515        if (jt->second < 0.0) {
     516          os << "- ";
     517          if (jt->second != -1.0) {
     518            os << -jt->second << " * ";
     519          }
     520        } else if (jt->second > 0.0) {
     521          if (!first) {
     522            os << "+ ";
     523          }
     524          if (jt->second != 1.0) {
     525            os << jt->second << " * ";
     526          }         
     527        }
     528        first = false;
     529        os << cols[jt->first] << " ";
     530      }
     531      if (e.constComp() < 0.0) {
     532        os << "- " << -e.constComp() << " ";
     533      } else if (e.constComp() > 0.0) {
     534        if (!first) {
     535          os << "+ ";
     536        }
     537        os << e.constComp() << " ";
     538      }
     539    }
     540
     541  protected:
     542
     543    /// \brief Writer function of the section.
     544    ///
     545    /// It writes the content of the section.
     546    virtual void write(std::ostream& os) {
     547      createCols();
     548
     549      os << "constraints" << std::endl;
    405550     
    406 //       LpSolverBase::Expr e1, e2;
    407 //       Relation op1;
    408 //       is >> std::ws;
    409 //       writexpression(is, e1);
    410 //       is >> std::ws;
    411 //       writeRelation(is, op1);
    412 //       is >> std::ws;
    413 //       writexpression(is, e2);
    414 //       if (!is.get(x)) {
    415 //         if (op1 == LE) {
    416 //           c = e1 <= e2;
    417 //         } else if (op1 == GE) {
    418 //           c = e1 >= e2;
    419 //         } else {
    420 //           c = e1 == e2;
    421 //         }
    422 //       } else {
    423 //         is.putback(x);
    424 //         LpSolverBase::Expr e3;
    425 //         Relation op2;
    426 //         writeRelation(is, op2);
    427 //         is >> std::ws;
    428 //         writexpression(is, e3);
    429 //         if (!e1.empty() || !e3.empty()) {
    430 //           throw DataFormatError("Wrong range format");
    431 //         }
    432 //         if (op2 != op1 || op1 == EQ) {
    433 //           throw DataFormatError("Wrong range format");
    434 //         }
    435 //         if (op1 == LE) {
    436 //           c = e1.constComp() <= e2 <= e3.constComp();
    437 //         } else {
    438 //           c = e1.constComp() >= e2 >= e3.constComp();
    439 //         }
    440 //       }
    441 //     }
    442 
    443 //     std::ostream& writexpression(std::ostream& is, LpSolverBase::Expr& e) {
    444 //       LpSolverBase::Col c;
    445 //       double d;
    446 //       char x;
    447 //       writelement(is, c, d);
    448 //       if (c != INVALID) {
    449 //         e += d * c;
    450 //       } else {
    451 //         e += d;
    452 //       }
    453 //       is >> std::ws;
    454 //       while (is.get(x) && (x == '+' || x == '-')) {
    455 //         is >> std::ws;
    456 //         writelement(is, c, d);
    457 //         if (c != INVALID) {
    458 //           e += (x == '+' ? d : -d) * c;
    459 //         } else {
    460 //           e += (x == '+' ? d : -d);
    461 //         }
    462 //         is >> std::ws;
    463 //       }
    464 //       if (!is) {
    465 //         is.clear();
    466 //       } else {
    467 //         is.putback(x);
    468 //       }
    469 //       return is;
    470 //     }
    471 
    472 //     std::ostream& writelement(std::ostream& is,
    473 //                               LpSolverBase::Col& c, double& d) {
    474 //       d = 1.0;
    475 //       c = INVALID;
    476 //       char x, y;
    477 //       if (!is.get(x)) throw DataFormatError("Cannot find lp element");
    478 //       if (x == '+' || x == '-') {
    479 //         is >> std::ws;
    480 //         d *= x == '-' ? -1 : 1;
    481 //         while (is.get(x) && (x == '+' || x == '-')) {
    482 //           d *= x == '-' ? -1 : 1;
    483 //           is >> std::ws;
    484 //         }
    485 //         if (!is) throw DataFormatError("Cannot find lp element");
    486 //       }
    487 //       if (numFirstChar(x)) {
    488 //         is.putback(x);
    489 //         double e;
    490 //         writeNum(is, e);
    491 //         d *= e;
    492 //       } else if (varFirstChar(x)) {
    493 //         is.putback(x);
    494 //         LpSolverBase::Col f;
    495 //         writeCol(is, f);
    496 //         c = f;
    497 //       } else {
    498 //         throw DataFormatError("Invalid expression format");         
    499 //       }
    500 //       is >> std::ws;
    501 //       while (is.get(y) && (y == '*' || y == '/')) {
    502 //         is >> std::ws;
    503 //         if (!is.get(x)) throw DataFormatError("Cannot find lp element");
    504 //         if (x == '+' || x == '-') {
    505 //           is >> std::ws;
    506 //           d *= x == '-' ? -1 : 1;
    507 //           while (is.get(x) && (x == '+' || x == '-')) {
    508 //             d *= x == '-' ? -1 : 1;
    509 //             is >> std::ws;
    510 //           }
    511 //           if (!is) throw DataFormatError("Cannot find lp element");
    512 //         }
    513 //         if (numFirstChar(x)) {
    514 //           is.putback(x);
    515 //           double e;
    516 //           writeNum(is, e);
    517 //           if (y == '*') {
    518 //             d *= e;
    519 //           } else {
    520 //             d /= e;
    521 //           }
    522 //         } else if (varFirstChar(x)) {
    523 //           is.putback(x);
    524 //           LpSolverBase::Col f;
    525 //           writeCol(is, f);
    526 //           if (y == '*') {
    527 //             if (c == INVALID) {
    528 //               c = f;
    529 //             } else {
    530 //               throw DataFormatError("Quadratic element in expression");
    531 //             }
    532 //           } else {
    533 //             throw DataFormatError("Division by variable");
    534 //           }
    535 //         } else {
    536 //           throw DataFormatError("Invalid expression format");         
    537 //         }
    538 //         is >> std::ws;
    539 //       }
    540 //       if (!is) {
    541 //         is.clear();
    542 //       } else {
    543 //         is.putback(y);
    544 //       }
    545 //       return is;
    546 //     }
    547 
    548 //     std::ostream& writeCol(std::ostream& is, LpSolverBase::Col& c) {
    549 //       char x;
    550 //       std::string var;
    551 //       while (is.get(x) && varChar(x)) {
    552 //         var += x;
    553 //       }
    554 //       if (!is) {
    555 //         is.clear();
    556 //       } else {
    557 //         is.putback(x);
    558 //       }
    559 //       ColMap::const_iterator it = cols.find(var);
    560 //       if (cols.find(var) != cols.end()) {
    561 //         c = it->second;
    562 //       } else {
    563 //         c = lp.addCol();
    564 //         cols.insert(std::make_pair(var, c));
    565 //         lp.colName(c, var);
    566 //       }
    567 //       return is;
    568 //     }
    569 
    570 //     std::ostream& writeNum(std::ostream& is, double& d) {
    571 //       is >> d;
    572 //       if (!is) throw DataFormatError("Wrong number format");
    573 //       return is;
    574 //     }
    575 
    576 //     std::ostream& writeRelation(std::ostream& is, Relation& op) {
    577 //       char x, y;
    578 //       if (!is.get(x) || !(x == '<' || x == '=' || x == '>')) {
    579 //         throw DataFormatError("Wrong relation operator");
    580 //       }
    581 //       if (!is.get(y) || y != '=') {
    582 //         throw DataFormatError("Wrong relation operator");
    583 //       }
    584 //       switch (x) {
    585 //       case '<': op = LE;
    586 //         break;
    587 //       case '=': op = EQ;
    588 //         break;
    589 //       case '>': op = GE;
    590 //         break;
    591 //       }
    592 //       return is;
    593 //     }
    594 
    595 //     static bool relationFirstChar(char c) {
    596 //       return c == '<' || c == '=' || c == '>';
    597 //     }
    598 
    599 //     static bool varFirstChar(char c) {
    600 //       return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    601 //     }
    602 
    603 //     static bool numFirstChar(char c) {
    604 //       return (c >= '0' && c <= '9') || c == '.';
    605 //     }
    606 
    607 //     static bool varChar(char c) {
    608 //       return (c >= '0' && c <= '9') ||
    609 //         (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
    610 //     }
    611 
    612 
    613 //     void addConstraint(const LpSolverBase::Constr& constr) {
    614 //       if (constr.expr().size() != 1) {
    615 //         lp.addRow(constr);
    616 //       } else {
    617 //         Lp::Expr e = constr.expr();
    618 //         LpSolverBase::Col col = e.begin()->first;
    619 //         double coeff = e.begin()->second;
    620 //         double lb = LpSolverBase::NaN;
    621 //         double ub = LpSolverBase::NaN;
    622 //         if (coeff > 0) {
    623 //           if (constr.upperBounded()) {
    624 //             lb = (constr.lowerBound() - e.constComp()) / coeff;
    625 //           }
    626 //           if (constr.lowerBounded()) {
    627 //             ub = (constr.upperBound() - e.constComp()) / coeff;
    628 //           }
    629 //         } else if (coeff < 0) {
    630 //           if (constr.upperBounded()) {
    631 //             lb = (constr.upperBound() - e.constComp()) / coeff;
    632 //           }
    633 //           if (constr.lowerBounded()) {
    634 //             ub = (constr.lowerBound() - e.constComp()) / coeff;
    635 //           }
    636 //         } else {
    637 //           lb = -LpSolverBase::INF;
    638 //           ub = LpSolverBase::INF;
    639 //         }
    640 //         lp.colLowerBound(col, lb);
    641 //         lp.colUpperBound(col, ub);
    642 //       }
    643 //     }
    644    
    645 //   protected:
    646 
    647 //     /// \brief Writer function of the section.
    648 //     ///
    649 //     /// It writes the content of the section.
    650 //     virtual void write(std::ostream& is) {
    651 //       std::string line;
    652 //       std::map<std::string, LpSolverBase::Col> vars;
    653 //       while (getline(is, line)) {
    654 //         std::istringstream ls(line);
    655 //         std::string sense;
    656 //         ls >> sense;
    657 //         if (sense == "min" || sense == "max") {
    658 //           LpSolverBase::Expr expr;
    659 //           ls >> std::ws;
    660 //           writeExpression(ls, expr);
    661 //           lp.setObj(expr);
    662 //           if (sense == "min") {
    663 //             lp.min();
    664 //           } else {
    665 //             lp.max();
    666 //           }
    667 //         } else {
    668 //           ls.str(line);
    669 //           LpSolverBase::Constr constr;
    670 //           ls >> std::ws;
    671 //           writeConstraint(ls, constr);
    672 //           addConstraint(constr);
    673 //         }       
    674 //       }
    675 //     }
     551      for (LpSolverBase::RowIt it(lp); it != INVALID; ++it) {
     552        double lower, upper;
     553        lp.getRowBounds(it, lower, upper);
     554        if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF)
     555          continue;
     556        os << "   ";
     557       
     558        if (lower != upper) {
     559          if (lower != -LpSolverBase::INF) {
     560            os << lower << " <= ";
     561          }
     562         
     563          writeExpression(os, lp.row(it));
     564         
     565          if (upper != LpSolverBase::INF) {
     566            os << "<= " << upper << " ";
     567          }
     568        } else {
     569
     570          writeExpression(os, lp.row(it));
     571          os << "== " << upper << " ";
     572         
     573        }
     574
     575        os << std::endl;
     576      }
     577
     578      os << "bounds" << std::endl;
    676579     
    677 //     virtual void missing() {
    678 //       ErrorMessage msg;
    679 //       msg << "Lp section not found in file: @lp " << name;
    680 //       throw IoParameterError(msg.message());
    681 //     }
    682 
    683 //   private:
    684 
    685 //     typedef std::map<std::string, LpSolverBase::Col> ColMap;
     580      for (LpSolverBase::ColIt it(lp); it != INVALID; ++it) {
     581        double lower = lp.colLowerBound(it);
     582        double upper = lp.colUpperBound(it);
     583        if (lower == -LpSolverBase::INF && upper == LpSolverBase::INF)
     584          continue;
     585        os << "   ";
     586
     587        if (lower != upper) {
     588          if (lower != -LpSolverBase::INF) {
     589            os << lower << " <= ";
     590          }
     591         
     592          os << cols[it] << " ";
     593         
     594          if (upper != LpSolverBase::INF) {
     595            os << "<= " << upper << " ";
     596          }
     597        } else {
     598          os << cols[it] << " == " << upper << " ";
     599        }
     600        os << std::endl;
     601      }
     602
     603      os << "objective" << std::endl;
     604      os << "   ";
     605      if (lp.is_min()) {
     606        os << "min ";
     607      } else {
     608        os << "max ";
     609      }
     610      writeExpression(os, lp.obj());
     611      os << std::endl;
     612    }
    686613     
    687 //     LpSolverBase& lp;
    688 //     std::string name;
    689 //     ColMap cols;
    690 //   };
     614
     615  private:
     616
     617    typedef std::map<LpSolverBase::Col, std::string> ColMap;
     618     
     619    LpSolverBase& lp;
     620    std::string name;
     621    ColMap cols;
     622  };
    691623
    692624}
Note: See TracChangeset for help on using the changeset viewer.