Home Page Downloads Contact Us

Free Open Source Artificial Intelligence C++ Parts

aiParts Open Source Project  

Home Page
Open Source
Contact
Free Downloads
Releases and Credits
Software Status
License
v0.9.3 README File
About rrproj
assigning people and/or equipment to projects
High-Hope Technique
History of the AI
View Source Files
and how they are organized
Making Programs
Development Notes
Using aiParts
Your Application
Support and Services

aiParts Source File

//======================================================================
//  rrproj_prob.cpp  -  function bodies for rrproj_prob.h
//
//  Copyright (c)  2008  Brian Marshall
//
//  See the license at end of this file.
//
//  Developers/Contributers:
//    [BRM] Brian Marshall - Calgary - bmarshal@agt.net
//
//  08/01/27  [BRM] began development
//
//----------------------------------------------------------------------

#include "rrproj_prob.h"
#include "rrproj_fctry.h"

#include <string.h>
#include <stdlib.h>

// #include    // for debugging
// #include 
// using namespace std;

//======================================================================
//  local functions
//
//----------------------------------------------------------------------
//  save the solution from a solver-problem in a problem-problem

void save_solution (rppProblem *rpp_problem, rpsProblem *rps_problem) {

  rpsDecision * rd;
  rpsOption   * ro;
  rpsPosDay   * rpd;
  rpsPosition * rp;
  rpsEmployee * re;
  long          syyyymmdd;
  aipHHOption * shhopt;

  aipHHOptionItr oitr;
  rpsDecisionItr ditr = rps_problem->decision_iterator();
  for ( rd=ditr.first(); rd; rd=ditr.next() ) {

    oitr = rd->bsf_opt_iterator();
    for ( shhopt=oitr.first(); shhopt; shhopt=oitr.next() ) {

      ro = (rpsOption*)shhopt;

      rpd = ro->bsf_posday();
      rp  = rpd->pos();
      re  = ro->emp();

      syyyymmdd = rpd->yyyymmdd();

      rpp_problem->add_posdayemp ( rpd->proj_id(), rp->pos_id(),
                                   syyyymmdd, re->emp_id() );

    }   // end of loop through options in best-so-far solution

  }   // end of loop through decisions

}

//======================================================================
//  rppProblem
//
//----------------------------------------------------------------------
//  Constructor

rppProblem::rppProblem () {

  m_start_yyyymmdd = 19000101;
  m_end_yyyymmdd   = 19000101;
  m_num_tries      = 0;

  m_logger = new aipStringLogger(Max_Log_Len);
  if (m_logger) set_logger(m_logger);

  m_posdays     = new aipPandemonium;
  m_empdows     = new aipPandemonium;
  m_empoffs     = new aipPandemonium;
  m_posemps     = new aipPandemonium;
  m_assignments = new aipPandemonium;
  m_empdaypos   = new aipPandemonium;
  m_posdayemps  = new aipPandemonium;

  m_posdayemp_itr = 0;  // managed by get_pos_day_emp()

}

//----------------------------------------------------------------------
//  Destructor

rppProblem::~rppProblem () {

  if (m_logger)      delete m_logger;
  if (m_posdays)     delete m_posdays;
  if (m_empdows)     delete m_empdows;
  if (m_empoffs)     delete m_empoffs;
  if (m_posemps)     delete m_posemps;
  if (m_assignments) delete m_assignments;
  if (m_empdaypos)   delete m_empdaypos;
  if (m_posdayemps)  delete m_posdayemps;

  if (m_posdayemp_itr) delete m_posdayemp_itr;

}

//----------------------------------------------------------------------
//  Return true if the problem is valid after construction.
//  Log problems.

int rppProblem::is_valid () {

  if ( ! m_posdays ) {
    log("Error creating m_posdays");
    return 0;
  }

  if ( ! m_empdows ) {
    log("Error creating m_empdows");
    return 0;
  }

  if ( ! m_empoffs ) {
    log("Error creating m_empoffs");
    return 0;
  }

  if ( ! m_posemps ) {
    log("Error creating m_posemps");
    return 0;
  }

  if ( ! m_assignments ) {
    log("Error creating m_assignments");
    return 0;
  }

  if ( ! m_empdaypos ) {
    log("Error creating m_empdaypos");
    return 0;
  }

  if ( ! m_posdayemps ) {
    log("Error creating m_posdayemps");
    return 0;
  }

  if (m_posdayemp_itr != 0) {
    log("m_posdayemp_itr should be zero");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  Read the input data from a file; return 1 (true) on success

int rppProblem::read (const char *filename) {

  FILE *ifp = fopen (filename, "r");
  if (!ifp) {
    log("Error opening input file: ");
    log(filename);
    return 0;
  }

  int istatus = 1;
  int rstatus;

  char buf[201], record_type[31];
  const char *p;

  while (istatus) {

    rstatus = fgets(buf,200,ifp) != 0;
    if (!rstatus) break;

    buf[200] = '\0';           // make sure it is terminated

    char *q = buf;
    int anything = 0;
    while (*q) {               // terminate at the newline
      if ( (*q >= 'A' && *q <= 'Z') ||
           (*q >= 'a' && *q <= 'z') ||
           (*q >= '0' && *q <= '9') ) {
        anything = 1;       // has some data
      } else if (*q == '\n') { 
        *q = '\0'; 
        break; 
      } 
      q++; 
    }

    if (buf[0] == '#' || !anything) continue;

    p = buf;
    if (*p) p = aip_get_str(p, 30, record_type, '|');
    if (!p) continue;

    if ( strcmp (record_type, "rppProblem") == 0) {

      istatus = read_str(p);

    } else if ( strcmp (record_type, "rppPosDay") == 0) {

      rppPosDay *pd = new rppPosDay;
      if (!pd) { log("new pd failure"); return 0; }
      istatus =  pd->read_str(p);
      if (istatus && m_posdays) {
        m_posdays->add(pd);
      } else {
        delete pd;
      }

    } else if ( strcmp (record_type, "rppPosEmp") == 0) {

      rppPosEmp *pqe = new rppPosEmp;
      if (!pqe) { log("new pqe failure"); return 0; }
      istatus =  pqe->read_str(p);
      if (istatus && m_posemps) {
        m_posemps->add(pqe);
      } else {
        delete pqe;
      }

    } else if ( strcmp (record_type, "rppEmpDow") == 0) {

      rppEmpDow *edow = new rppEmpDow;
      if (!edow) { log("new edow failure"); return 0; }
      istatus = edow->read_str(p);
      if (istatus && m_empdows) {
        m_empdows->add(edow);
      } else {
        delete edow;
      }

    } else if ( strcmp (record_type, "rppEmpOff") == 0) {

      rppEmpOff *eoff = new rppEmpOff;
      if (!eoff) { log("new eoff failure"); return 0; }
      istatus = eoff->read_str(p);
      if (istatus && m_empoffs) {
        m_empoffs->add(eoff);
      } else {
        delete eoff;
      }

    } else if ( strcmp (record_type, "rppAssignment") == 0) {

      rppAssignment *assg = new rppAssignment;
      if (!assg) { log("new assg failure"); return 0; }
      istatus = assg->read_str(p);
      if (istatus && m_assignments) {
        m_assignments->add(assg);
      } else {
        delete assg;
      }

    } else if ( strcmp (record_type, "rppEmpDayPos") == 0) {

      rppEmpDayPos *edp = new rppEmpDayPos;
      if (!edp) { log("new edp failure"); return 0; }
      istatus = edp->read_str(p);
      if (istatus && m_empdaypos) {
        m_empdaypos->add(edp);
      } else {
        delete edp;
      }

    } else {

      log("Read - bad record_type: ");
      log(record_type);
      return 0;

    }

  }  // end of loop through records in input file

  if (!istatus) {
    log("  Error reading input: ");
    log(buf);
  }

  fclose (ifp);

  return istatus;

}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppProblem::read_str (const char *x) {

  m_start_yyyymmdd = m_end_yyyymmdd = 19000101;
  m_week_start_day = m_num_tries = 0;

  char s_start_date[31], s_end_date[31];

  if (x && *x) x = aip_get_str(x, 30, s_start_date,      '|');
  if (x && *x) x = aip_get_str(x, 30, s_end_date,        '|');
  if (x && *x) x = aip_get_val(x, 10, &m_week_start_day, '|');
  if (x && *x) x = aip_get_val(x, 10, &m_num_tries,      '|');

  if (!x) return 0;

  rppFileDate x_start_date(s_start_date);
  if ( !x_start_date.is_valid() ) {
    log("problem start-date must be set and valid");
    return 0;
  }
  m_start_yyyymmdd = x_start_date.long_date();
  if (m_start_yyyymmdd < 19000101 || m_start_yyyymmdd > 21000101) {
    log("problem start-date must be between 1900 and 2100");
    return 0;
  }

  rppFileDate x_end_date(s_end_date);
  if ( !x_end_date.is_valid() ) {
    log("problem end-date must be set and valid");
    return 0;
  }
  m_end_yyyymmdd = x_end_date.long_date();
  if (m_end_yyyymmdd < 19000101 || m_end_yyyymmdd > 21000101) {
    log("problem end-date must be between 1900 and 2100");
    return 0;
  }
  if (m_end_yyyymmdd < m_start_yyyymmdd) {
    log("problem end-date must be after problem start-date");
    return 0;
  }

  if (m_week_start_day < 1 || m_week_start_day > 7) {
    log("week_start_day must be between 1 and 7");
    return 0;
  }

  if (m_num_tries < 1 || m_num_tries > 100000) {
    log("num_tries must be between 1 and 100000");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppProblem::write_str (char *x) const {

  rppFileDate x_start_date(m_start_yyyymmdd);
  if ( !x_start_date.is_valid() ) return 0;

  rppFileDate x_end_date(m_end_yyyymmdd);
  if ( !x_end_date.is_valid() ) return 0;

  int n = sprintf (x, "rppProblem|%s|%s|%ld|%ld",
                             x_start_date.file_date(), 
                             x_end_date.file_date(), 
                             m_week_start_day,
                             m_num_tries);

  return (n ? 1 : 0);

}

//----------------------------------------------------------------------
//  Write the input data to a file; return 1 (true) on success

int rppProblem::write_input (const char *filename) {

  FILE *ofp = fopen (filename, "w");
  if (!ofp) {
    log("Error opening export file");
    return 0;
  }

  int n = 1;

  if (n) n = fprintf (ofp, "#  rrproj export file\n");
  if (n) n = fprintf (ofp, "# \n");

  char rec[1001];

  if (n) n = write_str(rec);
  if (n) n = fprintf (ofp, "%s\n", rec);  // rppProblem record

  rppPosDayItr pditr = posday_iterator();
  rppPosDay *pd;
  for (pd = pditr.first(); pd && n; pd = pditr.next()) {
    if (n) n = pd->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  rppPosEmpItr peitr = posemp_iterator();
  rppPosEmp *pe;
  for (pe = peitr.first(); pe && n; pe = peitr.next()) {
    if (n) n = pe->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  rppEmpDowItr edowitr = empdow_iterator();
  rppEmpDow *edow;
  for (edow = edowitr.first(); edow && n; edow = edowitr.next()) {
    if (n) n = edow->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  rppEmpOffItr eoffitr = empoff_iterator();
  rppEmpOff *eoff;
  for (eoff = eoffitr.first(); eoff && n; eoff = eoffitr.next()) {
    if (n) n = eoff->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  rppAssgnItr assgnitr = assgn_iterator();
  rppAssignment *assgn;
  for (assgn = assgnitr.first(); assgn && n; assgn = assgnitr.next()) {
    if (n) n = assgn->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  rppEmpDayPosItr edpitr = empdaypos_iterator();
  rppEmpDayPos *edp;
  for (edp = edpitr.first(); edp && n; edp = edpitr.next()) {
    if (n) n = edp->write_str(rec);
    if (n) n = fprintf (ofp, "%s\n", rec);
  }

  fclose(ofp);

  if (!n) {
    log("Error writing export file");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  Return a pointer to a description of the results of trying to
//  to solve this problem.

const char * rppProblem::get_rslt_desc () const {

  return m_logger->get();

}

//----------------------------------------------------------------------
//  Add a Position-Day 

void rppProblem::add_pos_day (long a_proj_id, long a_pos_id, 
                              long a_yyyymmdd, long a_num_emps_req) {

  rppPosDay *x = new rppPosDay (a_proj_id, a_pos_id, 
                                a_yyyymmdd, a_num_emps_req);

  if (x) m_posdays->add(x);

}

//----------------------------------------------------------------------
//  Add an Employee-Day-of-week

void rppProblem::add_emp_dow (long a_emp_id, long a_dow,
                              long a_start_hhmm, long a_end_hhmm) {

  rppEmpDow *x = new rppEmpDow (a_emp_id, a_dow,
                                a_start_hhmm, a_end_hhmm);

  if (x) m_empdows->add(x);

}

//----------------------------------------------------------------------
//  Add a Employee-Off

void rppProblem::add_emp_off (long a_emp_id,
                              long a_start_yyyymmdd, 
                              long a_end_yyyymmdd) {

  rppEmpOff *x = new rppEmpOff (a_emp_id,
                                a_start_yyyymmdd, a_end_yyyymmdd);

  if (x) m_empoffs->add(x);

}

//----------------------------------------------------------------------
//  Add a Position-Employee pair

void rppProblem::add_pos_emp (long a_pos_id, long a_emp_id) {

  rppPosEmp *x = new rppPosEmp (a_pos_id, a_emp_id);

  if (x) m_posemps->add(x);

}

//----------------------------------------------------------------------
//  Add an Assignment

void rppProblem::add_assignment (long a_proj_id, long a_pos_id, 
                                 long a_emp_id, 
                                 long a_start_yyyymmdd, 
                                 long a_end_yyyymmdd) {

  rppAssignment *x = new rppAssignment (a_proj_id, a_pos_id, a_emp_id,
                                     a_start_yyyymmdd, a_end_yyyymmdd);

  if (x) m_assignments->add(x);

}

//----------------------------------------------------------------------
//  Add an historic Emp-Day-Pos

void rppProblem::add_empdaypos (long a_emp_id, long a_yyyymmdd,
                                long a_proj_id, long a_pos_id) {

  rppEmpDayPos *x = new rppEmpDayPos (a_emp_id, a_yyyymmdd,
                                      a_proj_id, a_pos_id); 

  if (x) m_empdaypos->add(x);

}

//----------------------------------------------------------------------
//  Add a Position-Day-Employee

void rppProblem::add_posdayemp (long a_proj_id, long a_pos_id, 
                                long a_yyyymmdd, long a_emp_id) {

  rppPosDayEmp *x = new rppPosDayEmp (a_proj_id, a_pos_id, 
                                      a_yyyymmdd, a_emp_id);

  if (x) m_posdayemps->add(x);

}

//----------------------------------------------------------------------
//  Attempt to solve the rrproj problem
//
//  A description of the result of calling this function is logged.
//
//  Return 1 (true) if a solution is found, 0 otherwise.

int rppProblem::solve () {

  rpfFactory *factory = new rpfFactory(this);
  if (!factory) {
    log("ABORT - could not make factory");
    return 0;
  }

  rpsProblem * rps_problem = factory->make_rps_problem();  

  if (!rps_problem) {
    log("ABORT - error creating rps_problem");
    return 0;
  }

  delete factory;

//  rps_problem->enable_log_tries();
  rps_problem->disable_log_tries();

//  rps_problem->enable_log_options();
  rps_problem->disable_log_options();

  int status = rps_problem->solve_rr_problem();
  if (status) {
    log("Solution found.");
    save_solution(this, rps_problem);
  } else {
    log("Solution was not found.");
  }

  delete rps_problem;

  return status;

}

//----------------------------------------------------------------------
//  get_pos_day_emp  -  get a solution datum, or zero after last

int rppProblem::get_pos_day_emp (long *a_proj_id, long *a_pos_id, 
                                 long *a_yyyymmdd, long *a_emp_id) {

  rppPosDayEmp  *pde;

  if (m_posdayemp_itr) {
    pde = m_posdayemp_itr->next();
  } else {
    m_posdayemp_itr = new rppPosDayEmpItr(m_posdayemps);
    pde = m_posdayemp_itr->first();
  }

  if (!pde) return 0;

  *a_proj_id  = pde->proj_id();
  *a_pos_id   = pde->pos_id();
  *a_yyyymmdd = pde->yyyymmdd();
  *a_emp_id   = pde->emp_id();

  return 1;

}
//----------------------------------------------------------------------
//  Get an iterator for Position-Days in a problem

rppPosDayItr  rppProblem::posday_iterator () const {

  rppPosDayItr i(m_posdays);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for Employee-Days-of-week in a problem

rppEmpDowItr  rppProblem::empdow_iterator () const {

  rppEmpDowItr i(m_empdows);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for Employee-days-Off in a problem

rppEmpOffItr  rppProblem::empoff_iterator () const {

  rppEmpOffItr i(m_empoffs);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for Position-Employee pairs in a problem

rppPosEmpItr  rppProblem::posemp_iterator () const {

  rppPosEmpItr i(m_posemps);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for Assignments in a problem

rppAssgnItr  rppProblem::assgn_iterator () const {

  rppAssgnItr i(m_assignments);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for historic Emp-Day-Pos in a problem

rppEmpDayPosItr  rppProblem::empdaypos_iterator () const {

  rppEmpDayPosItr i(m_empdaypos);

  return i;

}

//----------------------------------------------------------------------
//  Get an iterator for Position-Day-Employees in a problem

rppPosDayEmpItr  rppProblem::posdayemp_iterator () const {

  rppPosDayEmpItr i(m_posdayemps);

  return i;

}

//======================================================================
//  rppPosDay
//
//----------------------------------------------------------------------
//  Constructor

rppPosDay::rppPosDay (long a_proj_id, long a_pos_id, 
                      long a_yyyymmdd, long a_num_emps_req,
                      long a_start_hhmm, long a_end_hhmm) {

  m_proj_id      = a_proj_id;
  m_pos_id       = a_pos_id;
  m_yyyymmdd     = a_yyyymmdd;
  m_num_emps_req = a_num_emps_req;
  m_start_hhmm   = a_start_hhmm;
  m_end_hhmm     = a_end_hhmm;

}

//----------------------------------------------------------------------
//  Destructor

rppPosDay::~rppPosDay () {}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppPosDay::read_str (const char *x) {

  m_proj_id = m_pos_id = m_yyyymmdd = 0;
  m_num_emps_req = m_start_hhmm = m_end_hhmm = 0;

  char s_date[31], s_start_time[31], s_end_time[31];

  if (x && *x) x = aip_get_val(x, 10, &m_proj_id,      '|');
  if (x && *x) x = aip_get_val(x, 10, &m_pos_id,       '|');
  if (x && *x) x = aip_get_str(x, 30, s_date,          '|');
  if (x && *x) x = aip_get_val(x, 10, &m_num_emps_req, '|');
  if (x && *x) x = aip_get_str(x, 30, s_start_time,    '|');
  if (x && *x) x = aip_get_str(x, 30, s_end_time,      '|');

  if (!x) return 0;

  rppFileDate x_date(s_date);
  if ( !x_date.is_valid() ) return 0;
  m_yyyymmdd = x_date.long_date();

  rppFileTime x_start_time(s_start_time);
  if ( !x_start_time.is_valid() ) return 0;
  m_start_hhmm = x_start_time.long_time();

  rppFileTime x_end_time(s_end_time);
  if ( !x_end_time.is_valid() ) return 0;
  m_end_hhmm = x_end_time.long_time();

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppPosDay::write_str (char *x) const {

  rppFileDate x_date(m_yyyymmdd);
  if ( !x_date.is_valid() ) return 0;

  rppFileTime x_start_time(m_start_hhmm);
  if ( !x_start_time.is_valid() ) return 0;

  rppFileTime x_end_time(m_end_hhmm);
  if ( !x_end_time.is_valid() ) return 0;


  int n = sprintf (x, "rppPosDay|%ld|%ld|%s|%ld|%s|%s",
                         m_proj_id, m_pos_id, 
                         x_date.file_date(),
                         m_num_emps_req, 
                         x_start_time.file_time(), 
                         x_end_time.file_time());

  return (n ? 1 : 0);

}

//======================================================================
//  rppEmpDow
//
//----------------------------------------------------------------------
//  Constructor

rppEmpDow::rppEmpDow (long a_emp_id, long a_dow,
                      long a_start_hhmm, long a_end_hhmm) {

  m_emp_id     = a_emp_id;
  m_dow        = a_dow;
  m_start_hhmm = a_start_hhmm;
  m_end_hhmm   = a_end_hhmm;

}

//----------------------------------------------------------------------
//  Destructor

rppEmpDow::~rppEmpDow () {}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppEmpDow::read_str (const char *x) {

  char s_start_time[31], s_end_time[31];

  if (x && *x) x = aip_get_val(x, 10, &m_emp_id,     '|');
  if (x && *x) x = aip_get_val(x, 10, &m_dow,        '|');
  if (x && *x) x = aip_get_str(x, 30, s_start_time,  '|');
  if (x && *x) x = aip_get_str(x, 30, s_end_time,    '|');

  if (!x) return 0;

  rppFileTime x_start_time(s_start_time);
  if ( !x_start_time.is_valid() ) return 0;
  m_start_hhmm = x_start_time.long_time();

  rppFileTime x_end_time(s_end_time);
  if ( !x_end_time.is_valid() ) return 0;  
  m_end_hhmm = x_end_time.long_time();

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppEmpDow::write_str (char *x) const {

  rppFileTime x_start_time(m_start_hhmm);
  if ( !x_start_time.is_valid() ) return 0;

  rppFileTime x_end_time(m_end_hhmm);
  if ( !x_end_time.is_valid() ) return 0;

  int n = sprintf (x, "rppEmpDow|%ld|%ld|%s|%s",
                         m_emp_id, m_dow, 
                         x_start_time.file_time(), 
                         x_end_time.file_time());

  return (n ? 1 : 0);

}

//======================================================================
//  rppEmpOff
//
//----------------------------------------------------------------------
//  Constructor

rppEmpOff::rppEmpOff (long a_emp_id, 
                      long a_start_yyyymmdd, long a_end_yyyymmdd) {

  m_emp_id         = a_emp_id;
  m_start_yyyymmdd = a_start_yyyymmdd;
  m_end_yyyymmdd   = a_end_yyyymmdd;

  derive_data();

}

//----------------------------------------------------------------------
//  Destructor

rppEmpOff::~rppEmpOff () {}

//----------------------------------------------------------------------
//  Derive data

void rppEmpOff::derive_data() {

  m_start_day = aipTime(m_start_yyyymmdd);
  m_end_day   = aipTime(m_end_yyyymmdd);

}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppEmpOff::read_str (const char *x) {

  char s_start_date[31], s_end_date[31];

  if (x && *x) x = aip_get_val(x, 10, &m_emp_id,    '|');
  if (x && *x) x = aip_get_str(x, 30, s_start_date, '|');
  if (x && *x) x = aip_get_str(x, 30, s_end_date,   '|');

  if (!x) return 0;

  rppFileDate x_start_date(s_start_date);
  if ( !x_start_date.is_valid() ) return 0;
  m_start_yyyymmdd = x_start_date.long_date();

  rppFileDate x_end_date(s_end_date);
  if ( !x_end_date.is_valid() ) return 0;
  m_end_yyyymmdd = x_end_date.long_date();

  if (x) derive_data();     // must get called

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppEmpOff::write_str (char *x) const {

  rppFileDate x_start_date(m_start_yyyymmdd);
  if ( !x_start_date.is_valid() ) return 0;

  rppFileDate x_end_date(m_end_yyyymmdd);
  if ( !x_end_date.is_valid() ) return 0;

  int n = sprintf (x, "rppEmpOff|%ld|%s|%s",
                         m_emp_id, 
                         x_start_date.file_date(), 
                         x_end_date.file_date());

  return (n ? 1 : 0);

}

//======================================================================
//  rppPosEmp
//
//----------------------------------------------------------------------
//  Constructor

rppPosEmp::rppPosEmp (long a_pos_id, long a_emp_id) {

  m_pos_id = a_pos_id;
  m_emp_id = a_emp_id;

}

//----------------------------------------------------------------------
//  Destructor

rppPosEmp::~rppPosEmp () {}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppPosEmp::read_str (const char *x) {

  if (x && *x) x = aip_get_val(x, 10, &m_pos_id, '|');
  if (x && *x) x = aip_get_val(x, 10, &m_emp_id, '|');

  return (x ? 1 : 0);

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppPosEmp::write_str (char *x) const {

  int n = sprintf (x, "rppPosEmp|%ld|%ld",
                       m_pos_id, m_emp_id);

  return (n ? 1 : 0);

}

//======================================================================
//  rppAssignment
//
//----------------------------------------------------------------------
//  Constructor

rppAssignment::rppAssignment (long a_proj_id, long a_pos_id, 
                              long a_emp_id,
                              long a_start_yyyymmdd, 
                              long a_end_yyyymmdd) {

  m_proj_id        = a_proj_id;
  m_pos_id         = a_pos_id;
  m_emp_id         = a_emp_id;
  m_start_yyyymmdd = a_start_yyyymmdd;
  m_end_yyyymmdd   = a_end_yyyymmdd;

}

//----------------------------------------------------------------------
//  Destructor

rppAssignment::~rppAssignment () {}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppAssignment::read_str (const char *x) {

  char s_start_date[31], s_end_date[31];

  if (x && *x) x = aip_get_val(x, 10, &m_proj_id,   '|');
  if (x && *x) x = aip_get_val(x, 10, &m_pos_id,    '|');
  if (x && *x) x = aip_get_val(x, 10, &m_emp_id,    '|');
  if (x && *x) x = aip_get_str(x, 10, s_start_date, '|');
  if (x && *x) x = aip_get_str(x, 10, s_end_date,   '|');

  if (!x) return 0;

  rppFileDate x_start_date(s_start_date);
  if ( !x_start_date.is_valid() ) return 0;
  m_start_yyyymmdd = x_start_date.long_date();

  rppFileDate x_end_date(s_end_date);
  if ( !x_end_date.is_valid() ) return 0;
  m_end_yyyymmdd = x_end_date.long_date();

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppAssignment::write_str (char *x) const {

  rppFileDate x_start_date(m_start_yyyymmdd);
  if ( !x_start_date.is_valid() ) return 0;

  rppFileDate x_end_date(m_end_yyyymmdd);
  if ( !x_end_date.is_valid() ) return 0;

  int n = sprintf (x, "rppAssignment|%ld|%ld|%ld|%s|%s",
                         m_proj_id, m_pos_id, m_emp_id, 
                         x_start_date.file_date(),
                         x_end_date.file_date());

  return (n ? 1 : 0);

}

//======================================================================
//  rppEmpDayPos
//
//----------------------------------------------------------------------
//  Constructor

rppEmpDayPos::rppEmpDayPos (long a_emp_id, long a_yyyymmdd,
                            long a_proj_id, long a_pos_id) { 

  m_emp_id   = a_emp_id;
  m_yyyymmdd = a_yyyymmdd;
  m_proj_id  = a_proj_id;
  m_pos_id   = a_pos_id;

}

//----------------------------------------------------------------------
//  Destructor

rppEmpDayPos::~rppEmpDayPos () {}

//----------------------------------------------------------------------
//  Read data from string to setup this object

int rppEmpDayPos::read_str (const char *x) {

  char s_date[31];

  if (x && *x) x = aip_get_val(x, 10, &m_emp_id,  '|');
  if (x && *x) x = aip_get_str(x, 30, s_date,     '|');
  if (x && *x) x = aip_get_val(x, 10, &m_proj_id, '|');
  if (x && *x) x = aip_get_val(x, 10, &m_pos_id,  '|');

  if (!x) return 0;

  rppFileDate x_date(s_date);
  if ( !x_date.is_valid() ) return 0;
  m_yyyymmdd = x_date.long_date();

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppEmpDayPos::write_str (char *x) const {

  rppFileDate x_date(m_yyyymmdd);
  if ( !x_date.is_valid() ) return 0;

  int n = sprintf (x, "rppEmpDayPos|%ld|%s|%ld|%ld",
                      m_emp_id, x_date.file_date(),
                      m_proj_id, m_pos_id);

  return (n ? 1 : 0);

}

//======================================================================
//  rppProjWeekEmpDay
//
//----------------------------------------------------------------------
//  Constructor

rppProjWeekEmpDay::rppProjWeekEmpDay () {

  m_proj_id  = m_week_num = m_emp_id = 0;
  m_week_pos = m_yyyymmdd = m_pos_id = 0;

}

//----------------------------------------------------------------------
//  Destructor

rppProjWeekEmpDay::~rppProjWeekEmpDay () {}

//----------------------------------------------------------------------
//  Set the data members

void rppProjWeekEmpDay::set (long a_proj_id, long a_week_num, 
                             long a_emp_id,  long a_week_pos, 
                             long a_yyyymmdd, long a_pos_id) {

  m_proj_id = a_proj_id;
  m_week_num = a_week_num;
  m_emp_id   = a_emp_id;
  m_week_pos = a_week_pos;
  m_yyyymmdd = a_yyyymmdd;
  m_pos_id   = a_pos_id;

}

//======================================================================
//  rppPosDayEmp
//
//----------------------------------------------------------------------
//  Constructor

rppPosDayEmp::rppPosDayEmp (long a_proj_id, long a_pos_id, 
                            long a_yyyymmdd, long a_emp_id) {

  m_proj_id  = a_proj_id;
  m_pos_id   = a_pos_id;
  m_yyyymmdd = a_yyyymmdd;
  m_emp_id   = a_emp_id;

}

//----------------------------------------------------------------------
//  Destructor

rppPosDayEmp::~rppPosDayEmp () {}

//----------------------------------------------------------------------
//  Write the header for the data in this object

int rppPosDayEmp::write_hdr (char *x) const {

  int n = sprintf (x, "\n#rppPosDayEmp|proj_id|pos_id|date|emp_id");

  return (n ? 1 : 0);

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppPosDayEmp::write_str (char *x) const {

  rppFileDate x_date(m_yyyymmdd);
  if ( !x_date.is_valid() ) return 0;

  int n = sprintf (x, "rppPosDayEmp|%ld|%ld|%s|%ld",
                      m_proj_id, m_pos_id, 
                      x_date.file_date(), m_emp_id);

  return (n ? 1 : 0);

}

//======================================================================
//  rppProbWrite
//
//----------------------------------------------------------------------
//  Constructor

rppProbWrite::rppProbWrite (const rppProblem *x, 
                            const char * out_file_name)
                                               : m_prob(x) {

  if (out_file_name) {
    aip_strncpy (m_out_file_name, out_file_name, Max_File_Name);
  }

  m_any_pos_day_emp_written = 0;

}

//----------------------------------------------------------------------
//  Destructor

rppProbWrite::~rppProbWrite () {}

//----------------------------------------------------------------------
//  Write the solution data - return zero on error

int rppProbWrite::write () {

  FILE *ofp = fopen (m_out_file_name, "w");
  if (!ofp) {
    log("Error opening output file: ");
    log(m_out_file_name);
    return 0;
  }

  if ( !pre_proc(ofp) ) return 0;

  rppPosDayEmpItr pdeitr = m_prob->posdayemp_iterator();
  rppPosDayEmp *pde;
  for (pde = pdeitr.first(); pde; pde = pdeitr.next()) {

    if ( !handle_pos_day_emp(ofp, pde) ) return 0;

  }   // end of loop through pos-day-emp data

  if ( !post_proc(ofp) ) return 0;

  fclose(ofp);

  return 1;

}

//----------------------------------------------------------------------
//  pre_proc - called before solution-detail data is handled

int rppProbWrite::pre_proc (FILE *ofp) {

  if (!ofp) return 0;

  int n = 1;

  if (n) n = fprintf (ofp, "#  rrproj output file\n\n");

  if (n) n = fprintf (ofp, "rppRsltDscr|%s\n", 
                           m_prob->get_rslt_desc());

  if (!n) {
    log("Error writing beginning of output file");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  handle a normalized solution detail

int rppProbWrite::handle_pos_day_emp (FILE *ofp, 
                                      const rppPosDayEmp *pde) {

  if (!ofp || !pde) return 0;

  char rec[201];

  if (!m_any_pos_day_emp_written) {

    if ( !pde->write_hdr(rec) ) {
      log("Error writing pos-emp-day header to the output string");
      return 0;
    }

    if ( !fprintf (ofp, "%s\n", rec) ) {
      log("Error writing pos-emp-day header to the output file");
      return 0;
    }

    m_any_pos_day_emp_written = 1;

  }

  if ( !pde->write_str(rec) ) {
    log("Error writing pos-emp-day data to the output string");
    return 0;
  }

  if ( !fprintf (ofp, "%s\n", rec) ) {
    log("Error writing pos-emp-day data to the output file");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  post_proc - called after solution-detail data is handled

int rppProbWrite::post_proc (FILE *ofp) {

  if (!ofp) return 0;

  return 1;

}

//----------------------------------------------------------------------
//  write a blank line to the output file

int rppProbWrite::write_blank_line (FILE *ofp) {

  if (!ofp) return 0;

  if ( !fprintf (ofp, "\n") ) {
    log("Error writing blank line to the output file");
    return 0;
  }

  return 1;

}

//======================================================================
//  rppProbWriteWeek1
//
//----------------------------------------------------------------------
//  Constructor

rppProbWriteWeek1::rppProbWriteWeek1 (const rppProblem *x, 
                                      const char *out_file_name)
                                   : rppProbWrite(x,out_file_name) {

  set_week1_start_dt();

  m_proj_week_emp_days = new aipPandemonium;
  if (!m_proj_week_emp_days) { log("new m_proj_week_emp_days error"); }

  m_proj_week = new rppProjWeek;
  if (!m_proj_week) { log("new m_proj_week error"); }

  m_proj_week_emp = new rppProjWeekEmp;
  if (!m_proj_week_emp) { log("new m_proj_week_emp error"); }

  reset();

}

//----------------------------------------------------------------------
//  Destructor

rppProbWriteWeek1::~rppProbWriteWeek1 () {

  if (m_proj_week_emp_days) delete m_proj_week_emp_days;

  if (m_proj_week) delete m_proj_week;
  if (m_proj_week_emp) delete m_proj_week_emp;

}

//----------------------------------------------------------------------
//  Return zero if this object is not valid

int rppProbWriteWeek1::is_valid () const {

  if (!m_proj_week_emp_days || 
      !m_proj_week          || !m_proj_week_emp) return 0;

  return 1;

}

//----------------------------------------------------------------------
//  Reset current data - ie. everything except m_week1_start_dt

void rppProbWriteWeek1::reset () {

  if (!is_valid()) return;

  m_proj_week->reset();
  m_proj_week_emp->reset();

  m_any_week_data = m_any_emp_data = 0;
  
}

//----------------------------------------------------------------------
//  Set the date of the first day of week 1

void rppProbWriteWeek1::set_week1_start_dt () {

  aipTime prob_start_dt(prob()->start_yyyymmdd());
  aipTime prob_end_dt(prob()->end_yyyymmdd());

  m_week1_start_dt = prob_start_dt;

  long prob_start_day = prob_start_dt.days_since_sunday();
  long wk_offset = (prob_start_day + 7 - prob()->week_start_day()) % 7;
  if (wk_offset) {
    wk_offset = 0 - wk_offset;    // now zero or negative
    m_week1_start_dt.add_days(wk_offset);
  }

    // if the problem will fit into a week, force it if necessary
  long prob_num_days = prob_end_dt.days_since(prob_start_dt) + 1;
  if (prob_num_days <= 7) {
    long wnum1, wpos1, wnum2, wpos2;
    if( !week_dt(prob_start_dt, &wnum1, &wpos1) ) return;
    if( !week_dt(prob_end_dt,   &wnum2, &wpos2) ) return;
    if (wnum1 != wnum2) {
      m_week1_start_dt = prob_start_dt;
    }
  }

}

//----------------------------------------------------------------------
//  Determine the week-number and week_position (1 to 7) of a date.
//  Return zero on failure.

int rppProbWriteWeek1::week_dt (aipTime x,
                                long *week_num, long *week_pos) const {

  if ( !week_num || !week_pos ) return 0;

  long a, b;

  long ddif = x.days_after(m_week1_start_dt);

  if (ddif < -50000 || ddif > 50000) return 0;

  if (ddif >= 0) {
    a = 0;    b = 1;
  } else {
    a = 1;    b = 0;
  }

  *week_num = (ddif + a) / 7 + b;

  *week_pos = ((ddif + 70000) % 7) + 1;

  return 1;

}

//----------------------------------------------------------------------
//  handle a normalized solution detail
//
//  add the data to m_proj_week_emp_days to reorder it

int rppProbWriteWeek1::handle_pos_day_emp (FILE *ofp,
                                      const rppPosDayEmp *pde) {

  if (!ofp || !pde || !is_valid()) return 0;

  rppProjWeekEmpDay *pwed = new rppProjWeekEmpDay;
  if (!pwed) { log("new rppProjWeekEmpDay error"); return 0; }

  long wnum, wpos;
  week_dt (pde->yyyymmdd(), &wnum, &wpos);

  pwed->set ( pde->proj_id(), wnum, pde->emp_id(),
              wpos, pde->yyyymmdd(), pde->pos_id() );

  if (m_proj_week_emp_days) m_proj_week_emp_days->add(pwed);

  return 1;

}

//----------------------------------------------------------------------
//  post_proc - called after solution-detail data is handled

int rppProbWriteWeek1::post_proc (FILE *ofp) {

  if (!ofp || !is_valid()) return 0;

  int have_written_week_hdr = 0;
  int have_written_emp_hdr  = 0;

  if ( !write_blank_line(ofp) ) return 0;

  rppProjWeekEmpDayItr pwed_itr = projweekempday_iterator();
  rppProjWeekEmpDay *pwed;
  for (pwed=pwed_itr.first(); pwed; pwed=pwed_itr.next()) {

    if (m_any_week_data) {
      if (pwed->proj_id()  != m_proj_week->proj_id() ||
        pwed->week_num() != m_proj_week->week_num()) {
        m_proj_week->reset();
        m_any_week_data = 0;
      }
    }

    if (m_any_emp_data) {
      if (pwed->proj_id()  != m_proj_week_emp->proj_id()  ||
          pwed->week_num() != m_proj_week_emp->week_num() ||
          pwed->emp_id()   != m_proj_week_emp->emp_id()) {
        if (!have_written_emp_hdr) {
          if ( !m_proj_week_emp->write_hdr(ofp) ) return 0;
          have_written_emp_hdr = 1;
        }
        if ( !m_proj_week_emp->write(ofp) ) return 0;
        m_proj_week_emp->reset();
        m_any_emp_data = 0;
      }
    }

    if (!m_any_week_data) {
      if (!have_written_week_hdr) {
        if ( !m_proj_week->write_hdr(ofp) ) return 0;
        have_written_week_hdr = 1;
      }
      if ( !set_proj_week(pwed) ) return 0;  // set whole week
      if ( !m_proj_week->write(ofp) ) return 0;
      if ( !m_any_week_data ) m_any_week_data = 1;
    }

    if ( !add_to_proj_week_emp(pwed) ) return 0;  // set one day
    if (!m_any_emp_data) m_any_emp_data = 1;

  }   // end of loop through proj-week-emp-days

  if (m_any_emp_data) {
    if (!have_written_emp_hdr) {
      if ( !m_proj_week_emp->write_hdr(ofp) ) return 0;
      // have_written_emp_hdr = 1;   not used
    }
    if ( !m_proj_week_emp->write(ofp) ) return 0;
    m_proj_week->reset();
    m_proj_week_emp->reset();
    m_any_emp_data = 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  set m_proj_week for the week containing pwed

int rppProbWriteWeek1::set_proj_week (rppProjWeekEmpDay *pwed) {

  m_proj_week->set_pw (pwed->proj_id(), pwed->week_num());

  aipTime x_dt(pwed->yyyymmdd());
  long diff = 1 - pwed->week_pos();   // zero or negative
  if (diff) x_dt.add_days(diff);
  long x_yyyymmdd;

  for (int iday=1; iday<=7; iday++) {
    x_yyyymmdd = x_dt.yyyymmdd();
    m_proj_week->set_day (iday, x_yyyymmdd);
    x_dt.add_days(1);
  }

  return 1;

}

//----------------------------------------------------------------------
//  set m_proj_week_emp for the day of pwed
//  set key data members if required

int rppProbWriteWeek1::add_to_proj_week_emp (rppProjWeekEmpDay *pwed) {

  if (!m_any_emp_data) {
    m_proj_week_emp->set_pwe (pwed->proj_id(), 
                              pwed->week_num(), pwed->emp_id());
  }

  m_proj_week_emp->set_day(pwed->week_pos(), pwed->pos_id());

  return 1;

}

//----------------------------------------------------------------------
//  Get an iterator for Proj-Week-Emp-Day in a Prob-Write-Week1

rppProjWeekEmpDayItr 
            rppProbWriteWeek1::projweekempday_iterator () const {

  rppProjWeekEmpDayItr i(m_proj_week_emp_days);

  return i;

}

//======================================================================
//  rppProjWeek
//
//----------------------------------------------------------------------
//  reset this master-data

void rppProjWeek::reset () {

  m_proj_id = m_week_num = -1;

  for (int i=0; i<7; i++) m_yyyymmdd[i] = -1;

}

//----------------------------------------------------------------------
//  Write the header for the data in this object to the string; 
//  return 0 on failure

int rppProjWeek::write_hdr (char *x) const {

  int n = sprintf (x, "#rppProjWeek|proj|week|...");

  return (n ? 1 : 0);

}

//----------------------------------------------------------------------
//  Write the header for the data in this object to a file; 
//  return 0 on failure

int rppProjWeek::write_hdr (FILE *ofp) const {

  char rec[201];

  if ( !write_hdr(rec) ) {
    log("Error writing proj_week to header string");
    return 0;
  }

  if ( !fprintf (ofp, "%s\n", rec) ) {
    log("Error writing proj_week to header file");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppProjWeek::write_str (char *x) const {

  long  l_dom;                 // dom = day-of-month
  char  s_mon[4], s_dow[4];    // dow = day-of-week

  int nn = sprintf (x, "rppProjWeek|%ld|%ld", m_proj_id, m_week_num);
  if (!nn) return 0;
  int n = nn;         // total chars written = chars just written

  for (int i=0; i<7; i++) {
    if (m_yyyymmdd[i] <= 0) return 0;
    aipTime dt(m_yyyymmdd[i]);
    if (!dt.is_valid()) return 0;
    dt.get (0, 0, &l_dom, 0, 0, 0, s_mon, s_dow);
    if (i != 0 && l_dom != 1) strcpy (s_mon, " ");
    nn = sprintf (x+n, "|%s|%ld|%s", s_mon, l_dom, s_dow);
    if (!nn) return 0;
    n += nn;
  }   // end of loop through day in week

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to a file; return 0 on failure

int rppProjWeek::write (FILE *ofp) const {

  char rec[201];

  if ( !write_str(rec) ) {
    log("Error writing proj_week to output string");
    return 0;
  }

  if ( !fprintf (ofp, "%s\n", rec) ) {
    log("Error writing proj_week to output file");
    return 0;
  }

  return 1;

}

//======================================================================
//  rppProjWeekEmp
//
//----------------------------------------------------------------------
//  Reset this set of detail data

void rppProjWeekEmp::reset () {

  m_proj_id = m_week_num = m_emp_id = -1;

  for (int i=0; i<7; i++) m_pos_id[i] = -1;

}

//----------------------------------------------------------------------
//  Write the header for the data in this object to the string; 
//  return 0 on failure

int rppProjWeekEmp::write_hdr (char *x) const {

  char fmt[201];
  strcpy (fmt, "#rppProjWeekEmp|proj|week|emp");
  strcat (fmt, "|day1pos_id|day2pos_id|...");
  int n = sprintf (x, fmt);

  return (n ? 1 : 0);

}

//----------------------------------------------------------------------
//  Write the header for the data in this object to a file; 
//  return 0 on failure

int rppProjWeekEmp::write_hdr (FILE *ofp) const {

  char rec[201];

  if ( !write_hdr(rec) ) {
    log("Error writing proj_week_emp to header string");
    return 0;
  }

  if ( !fprintf (ofp, "%s\n", rec) ) {
    log("Error writing proj_week_emp to header file");
    return 0;
  }

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to the string; return 0 on failure

int rppProjWeekEmp::write_str (char *x) const {

  int nn = sprintf (x, "rppProjWeekEmp|%ld|%ld|%ld",
                       m_proj_id, m_week_num, m_emp_id);
  if (!nn) return 0;
  int n = nn;         // total chars written = chars just written

  for (int i=0; i<7; i++) {
    if (m_pos_id[i] >= 0) {
      nn = sprintf (x+n, "|%ld", m_pos_id[i]);
    } else {
      nn = sprintf (x+n, "| ");
    }
    if (!nn) return 0;
    n += nn;
  }   // end of loop through day in week

  return 1;

}

//----------------------------------------------------------------------
//  Write the data in this object to a file; return 0 on failure

int rppProjWeekEmp::write (FILE *ofp) const {

  char rec[201];

  if ( !write_str(rec) ) {
    log("Error writing proj_week_emp to output string");
    return 0;
  }

  if ( !fprintf (ofp, "%s\n", rec) ) {
    log("Error writing proj_week_emp to output file");
    return 0;
  }

  return 1;

}

//======================================================================
//  Iterators
//
//  All function bodies are in the header file 
//
//======================================================================
//  rppFileDate
//
//----------------------------------------------------------------------
//  Constructor  -  from a string "yyyy-mm-dd"

rppFileDate::rppFileDate (const char *a_yyyy_mm_dd) {

  char *p = m_yyyy_mm_dd;

  aip_strncpy (p, a_yyyy_mm_dd, File_Date_Len);

  if ( strlen(p) != File_Date_Len) { reset(); return; }

  p[4] = p[7] = '\0';

  long yr = atol(p);
  long mo = atol(p+5);
  long dy = atol(p+8);

  if ( yr < 1800 || yr > 2300 ||
       mo <    1 || mo >   12 ||
       dy <    1 || dy > aip_days_in_month(yr,mo) ) { reset(); return; }

  m_yyyymmdd =  yr*10000 + mo*100 + dy;

}

//----------------------------------------------------------------------
//  Constructor  -  from a long yyyymmdd

rppFileDate::rppFileDate (long a_yyyymmdd) {

  long yr = a_yyyymmdd / 10000;
  a_yyyymmdd -= (yr * 10000);
  long mo = a_yyyymmdd / 100;
  a_yyyymmdd -= (mo * 100);
  long dy = a_yyyymmdd;

  if ( yr < 1800 || yr > 2300 ||
       mo <    1 || mo >   12 ||
       dy <    1 || dy > aip_days_in_month(yr,mo) ) { reset(); return; }

  sprintf (m_yyyy_mm_dd, "%ld-%02ld-%02ld", yr, mo, dy);

}

//----------------------------------------------------------------------
//  Destructor

rppFileDate::~rppFileDate () {}

//----------------------------------------------------------------------
//  Return true if the date is valid

int rppFileDate::is_valid () { 

  return ( m_yyyymmdd ? 1 : 0 );

}

//======================================================================
//  rppFileTime
//
//----------------------------------------------------------------------
//  Constructor  -  from a string "hh:mm"

rppFileTime::rppFileTime (const char *a_hh_mm) {

  char *p = m_hh_mm;

  aip_strncpy (p, a_hh_mm, File_Time_Len);

  if ( strlen(p) != File_Time_Len) { reset(); return; }

  if (p[1] == ':') {
    p[1] = '\0';
  } else {
    p[2] = '\0';
  }

  long hr = atol(p);
  long mn = atol(p+3);

  if ( hr <  0 || hr > 23 || 
       mn <  0 || mn > 59 ) { reset(); return; }

  m_hhmm =  hr*100 + mn;

}

//----------------------------------------------------------------------
//  Constructor  -  from a long hhmm

rppFileTime::rppFileTime (long a_hhmm) {

  long hr = a_hhmm / 100;
  a_hhmm -= (hr * 100);
  long mn = a_hhmm;

  if ( hr <  0 || hr > 23 || 
       mn <  0 || mn > 59 ) { reset(); return; }

  sprintf (m_hh_mm, "%02ld:%02ld", hr, mn);

}

//----------------------------------------------------------------------
//  Destructor

rppFileTime::~rppFileTime () {}

//----------------------------------------------------------------------
//  Return true if the time is valid

int rppFileTime::is_valid () { 

  return ( m_hhmm<2400 ? 1 : 0 );

}

//======================================================================
//                           License
//
//   Permission is hereby granted, free of charge, to any
//   person obtaining a copy of this software and associated
//   documentation files (the "Software"), to deal in the Software
//   without restriction, including without limitation the rights
//   to use, copy, modify, merge, publish, distribute, sublicense,
//   and/or sell copies of the Software, and to permit persons to
//   whom the Software is furnished to do so, subject to the
//   following conditions:
//
//   The copyright notice and this license shall be included in all
//   copies or substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
//
//======================================================================