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_fctry.cpp   function bodies for rrproj_fctry.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/28  [BRM] began development
//
//----------------------------------------------------------------------

#include "rrproj_fctry.h"

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

//======================================================================
//  Factory Data
//
//      3 datasets:
//  - problem - setup with data from the user
//  - solver  - factory creates it from the problem dataset
//  - factory - temporary data owned by the factory
//
//      variable names in functions:
//  The first letter of some variables in functions indicates the
//  dataset from which the function is set:
//     pemp   is an emp from the problem
//     semp   is an emp from the solver
//     fempid is an empid from the factory
//
//      class name prefix
//  rpp   rrproj  problem  (setup by application)
//  rps   rrproj  solver   (used by the problem)
//  rpf   rrproj  factory  (solver-factory)
//
//----------------------------------------------------------------------
//  Constructor

rpfFactory::rpfFactory (rppProblem *pdata) {

  m_problem_data = pdata;

  m_emp_ids = new aipPandemonium;
  if (m_emp_ids) m_emp_ids->set_is_distinct();

  m_opts = new aipPandemonium;
  if (m_opts) m_opts->set_is_distinct();

}

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

rpfFactory::~rpfFactory () { 

  if (m_emp_ids)        delete m_emp_ids;
  if (m_opts)           delete m_opts;

}

//----------------------------------------------------------------------
//  create and setup a new rpsProblem from the problem data.

rpsProblem * rpfFactory::make_rps_problem () {

  if (!m_problem_data) return 0;

  rpsProblem *sp = new rpsProblem ();
  if (!sp) return 0;

  sp->set_num_try (m_problem_data->num_tries());

  int status = 1;
  if (status) status = setup(sp);

  if (!status) return 0;

  return sp;

}

//----------------------------------------------------------------------
//  setup an rpsProblem - return true on success

int rpfFactory::setup (rpsProblem *sprob) {

  int status = 1;

                             // iterators in the problem data
  rppPosDayItr  pditr = pprob()->posday_iterator();
  rppPosEmpItr  peitr = pprob()->posemp_iterator();
  rppAssgnItr   aitr  = pprob()->assgn_iterator();

                             // iterators in the factory data
  rpfOptItr    oitr  = opt_iterator();

                      // setup pos, posday, dcsn
  if (status) status = handle_pd (pditr, sprob);

                      // setup emp, empday
  if (status) status = handle_pe_a (peitr, aitr, sprob);

                      // add to the opt list
  if (status) status = handle_a_ed (aitr, sprob);

                      // add to opt list
  if (status) status = handle_pe_pd_ed (peitr, sprob);

                      // setup options from opt list
  if (status) status = handle_o (oitr, sprob);

  return status;

}

//----------------------------------------------------------------------
//  handle the set of PosDays in the problem data
//
//  create positions and handle individual posdays

int rpfFactory::handle_pd (rppPosDayItr pditr, rpsProblem *sprob) {

  int status = 1;

  long min_yyyymmdd = 20200101;
  long max_yyyymmdd = 19800101;

  long prev_pos_id   = very_unlikely_id();

  long         pner;       // num-emps_required in the problem
  rppPosDay   *ppd;        // posday in the problem
  rpsPosDay   *spd;        // posday in the solver
  rpsPosition *spos;       // pos in the solver

  if (status) {                      // create (Pos), (PosDay), (Dcsn)
    for (ppd = pditr.first(); ppd; ppd = pditr.next()) {

      if (ppd->pos_id() != prev_pos_id) {
        spos = new rpsPosition(ppd->pos_id(), sprob);
        if (!spos) { status = 0;  log(" Err new rpsPosition"); break; }
        sprob->add_position(spos);
        prev_pos_id = ppd->pos_id();
      }

      spd = new rpsPosDay(spos, ppd->yyyymmdd(), ppd->proj_id(),
                                ppd->start_hhmm(), ppd->end_hhmm());
      if (!spd) { status = 0;  log("Err new rpsPosDay"); break; }
      spos->add_posday(spd);

      pner = ppd->num_emps_req();
      if (pner < 1 || pner > 1000) {
        status = 0;   log("Err bad num-emps-required");  break;
      }
      rpsDecision *sdcsn = new rpsDecision(spd, pner);
      if (!sdcsn) {status = 0;  log("Err new rpsDecision"); break; }
      sprob->add_decision(sdcsn);
      spd->set_dcsn(sdcsn);

      long ppd_yyyymmdd = ppd->yyyymmdd();
      if (ppd_yyyymmdd < min_yyyymmdd) min_yyyymmdd = ppd_yyyymmdd;
      if (ppd_yyyymmdd > max_yyyymmdd) max_yyyymmdd = ppd_yyyymmdd;
    
    }
  }

  if (status) {
    sprob->set_day_range (min_yyyymmdd, max_yyyymmdd);
  }

  return status;

}

//----------------------------------------------------------------------
//  handle the sets of posemps and assignments in the problem data
//
//  create a distinct list of empids, and then create emps and empdays
//     (which also uses empdows and empoffs)

int rpfFactory::handle_pe_a (rppPosEmpItr peitr, rppAssgnItr aitr, 
                                                   rpsProblem *sprob) {

  int status = 1;

  rpfEmpId   *feid;            // an empid owned by the factory
  long prev_eid = very_unlikely_id();

  if (status) {                  // start {EmpId}
    rppPosEmp *ppe;            // a posemp in the problem
    for (ppe = peitr.first(); ppe; ppe = peitr.next()) {
      if (ppe->emp_id() != prev_eid) {
        feid = new rpfEmpId(ppe->emp_id());
        if (!feid) { status = 0;  log("Err new rpfEmpId"); break; }
        m_emp_ids->add (feid);  // list is forced distinct
        prev_eid = ppe->emp_id();
      }
    }
  }

  if (status) {                  // add to {EmpId}
    rppAssignment *pa;         // an assignment in the problem
    for (pa = aitr.first(); pa; pa = aitr.next()) {
      if (pa->emp_id() != prev_eid) {
        feid = new rpfEmpId(pa->emp_id());
        if (!feid) { status = 0;  log("Err new rpfEmpId"); break; }
        m_emp_ids->add (feid);  // list is forced distinct
        prev_eid = pa->emp_id();
      }
    }
  }

    // we now have a distinct list of emp_ids

  rpsEmployee *semp;           // an emp in the solver

  if (status) {                  // create (Emp)
   rpfEmpIdItr eiditr = empid_iterator();
    for (feid = eiditr.first(); feid; feid = eiditr.next()) {

      semp = new rpsEmployee(feid->emp_id(), sprob);
      if (!semp) { status = 0; log("Err new rpsEmployee"); break; }
      sprob->add_employee(semp);

      status = handle_e (semp, sprob);
      if (!status) break;

    }
  }

  return status;

}

//----------------------------------------------------------------------
//  handle a solver emp - create empday 

int rpfFactory::handle_e (rpsEmployee *semp, rpsProblem *sprob) {

  int status = 1;

  if (status) {
    apply_empdaypos(semp);
  }

  if (status) {                  // create (EmpDay)
    aipTime curday( (sprob->start_day()) );
    aipTime lastday( (sprob->end_day()) );
    for ( ; curday <= lastday; curday.add_days(1) ) {

      long x_yyyymmdd = curday.yyyymmdd();
      if (!x_yyyymmdd) { status = 0; log("Err h_e yyyymmdd"); break; }

      if ( emp_is_off (semp->emp_id(), curday) ) continue;

      long x_start_hhmm, x_end_hhmm;
      long x_dow = curday.days_since_sunday();
      
      get_dow_data (semp->emp_id(), x_dow, 
                    &x_start_hhmm, &x_end_hhmm);
      if (x_start_hhmm == x_end_hhmm) continue;

      rpsEmpDay *sed = new rpsEmpDay (semp, x_yyyymmdd,
                                      x_start_hhmm, x_end_hhmm);
      if (!sed) { status = 0; log("Err new rpsEmpDay"); break; }
      semp->add_empday(sed);

    }
  }

  return status;

}

//----------------------------------------------------------------------
//  handle the set of assignments (plus solution EmpDays)
//
//  start a list of options (that is forced to have distinct keys)

int rpfFactory::handle_a_ed (rppAssgnItr aitr, rpsProblem *sprob) {

  int status = 1;

  if (status) {                  // create {opt} from [Assgnmnt]

    rppAssignment *pa;      // an assignment in the problem
    rpsPosDay     *spd;     // a  posday in the solver
    rpsEmpDay     *sed;     // an empday in the solver

    for (pa = aitr.first(); pa; pa = aitr.next()) {

      aipTime curday(pa->start_yyyymmdd());
      aipTime lastday(pa->end_yyyymmdd());

      for ( ; curday <= lastday; curday.add_days(1) ) {

        spd = find_posday (pa->pos_id(), curday.yyyymmdd(), 
                           pa->proj_id(), sprob);
        if (!spd) continue;

        sed = find_empday (pa->emp_id(), curday.yyyymmdd(), sprob);
        if (!sed) continue;

        rpfOpt *fopt = new rpfOpt (spd, sed, aipManditory);
        if (!fopt) { status = 0;  log("Err new rpfOpt"); break; }
        m_opts->add (fopt);  // list is forced distinct

      }
    }
  }

  return status;

}

//----------------------------------------------------------------------
//  handle the set of PosEmps (plus solution PosDays and EmpDays)
//
//  add to the opt-list

int rpfFactory::handle_pe_pd_ed (rppPosEmpItr peitr, 
                                 rpsProblem *sprob) {

  int status = 1;

  if (status) {                  // create {opt} from [PosEmp]

    rppPosEmp    * ppe;    // a posemp from the problem

    rpsPosition  * sp;     // pointers to objects in the solver
    rpsPosDay    * spd;
    rpsEmployee  * se;
    rpsEmpDay    * sed;

    rpsPositionItr pitr = sprob->position_iterator();
    rpsEmployeeItr eitr = sprob->employee_iterator();

    pitr.first();
    eitr.first();
                                            //  for each pos_emp...
    for ( ppe = peitr.first(); ppe; ppe = peitr.next() ) {

      sp = (rpsPosition*)(pitr.find(ppe->pos_id()));
      if (!sp) continue;

      se = (rpsEmployee*)(eitr.find(ppe->emp_id()));
      if (!se) continue;

      rpsPosDayItr pditr = sp->posday_iterator();
      rpsEmpDayItr editr = se->empday_iterator();

                                 // for each pos_day and emp_day...
      for ( spd = pditr.first(); spd; spd = pditr.next() ) {
        for ( sed = editr.first(); sed; sed = editr.next() ) {
          if (spd->yyyymmdd() == sed->yyyymmdd()) {

            if ( sed->start_time() > spd->start_time() ||
                 sed->end_time()   < spd->end_time() ) continue;

            rpfOpt *fopt = new rpfOpt (spd, sed, aipPrettyGood);
            if (!fopt) { status = 0; log("Err new rpfOpt"); break; }
            m_opts->add (fopt);  // list is forced distinct

          }
        }   // end of loop through emp_days (in the solver)
      }   // end of loop through pos_days (in the solver)

    }   // end of loop through posemps (in the problem)

  }

  return status;

}

//----------------------------------------------------------------------
//  handle the list of opts
//
//  create solver-options and attach them to decisions

int rpfFactory::handle_o (rpfOptItr foitr, rpsProblem *sprob) {

  int status = 1;

  if (!sprob) { status = 0; log("handle_o sprob"); }

  if (status) {                  // create (Option) from {opt}

    rpfOpt * fo;        // an opt-list element (stored in the factory)

    for ( fo = foitr.first(); fo; fo = foitr.next() ) {

      rpsDecision *sd = fo->pos_day()->dcsn();
      if (!sd) { status = 0; log("Err h_o dcsn"); break; }

      rpsOption *sopt = new rpsOption (fo->emp_day(), 
                                       fo->g_constant());
      if (!sopt) { status = 0; log("new rpsOption"); break; }

      sd->add_rps_option (sopt);

    }   // end of elements in distinct opt-list

  }

  return status;

}

//----------------------------------------------------------------------
//  return a posday from the solver given an pos_id and a date

rpsPosDay * rpfFactory::find_posday (long a_pos_id, long a_yyyymmdd, 
                                 long a_proj_id, rpsProblem *sprob) {

  if (!sprob) return 0;

  rpsPositionItr pitr = sprob->position_iterator();
  rpsPosition * sp = (rpsPosition*)(pitr.find(a_pos_id));
  if (!sp) return 0;

  rpsPosDayItr pditr = sp->posday_iterator();
  rpsPosDay  * spd = (rpsPosDay*) 
                        (pditr.find(a_pos_id, a_yyyymmdd, a_proj_id));

  return spd;

}

//----------------------------------------------------------------------
//  return an empday from the solver given an emp_id and a date

rpsEmpDay * rpfFactory::find_empday (long emp_id, long a_yyyymmdd, 
                                               rpsProblem *sprob) {

  if (!sprob) return 0;

  rpsEmployeeItr eitr = sprob->employee_iterator();

  rpsEmployee * se = (rpsEmployee*)(eitr.find(emp_id));
  if (!se) return 0;

  rpsEmpDayItr editr = se->empday_iterator();
  rpsEmpDay  * sed;

  for ( sed = editr.first(); sed; sed = editr.next() ) {
    if (sed->yyyymmdd() == a_yyyymmdd) break;
  }

  return sed;

}

//----------------------------------------------------------------------
//  apply historic EmpDayPos data to the emp

void rpfFactory::apply_empdaypos (rpsEmployee *e) {

  rppEmpDayPosItr edpitr = pprob()->empdaypos_iterator();

  rppEmpDayPos *edp = (rppEmpDayPos*)(edpitr.find(e->emp_id()));
  if (!edp) return;

  long last_yyyymmdd = edp->yyyymmdd();

  for ( edp = edpitr.next(); edp; edp = edpitr.next() ) {

    if (edp->emp_id() != e->emp_id()) break;

    if (edp->yyyymmdd() > last_yyyymmdd) {
      last_yyyymmdd = edp->yyyymmdd();
    }

  }

  aipTime lw(last_yyyymmdd);
  e->set_start_last_work(lw);

}

//----------------------------------------------------------------------
//  return true if emp is off on the specified day

int rpfFactory::emp_is_off (long a_emp_id, aipTime a_day) {

  int is_off = 0;

  rppEmpOffItr eoitr = pprob()->empoff_iterator();

  rppEmpOff *eoff = (rppEmpOff*)(eoitr.find(a_emp_id));

  for ( ; eoff; eoff = eoitr.next() ) {
    if (eoff->emp_id() != a_emp_id) break;
    if (a_day >= eoff->start_day() && a_day <= eoff->end_day() ) {
      is_off = 1;
      break;
    }
  }

  return is_off;

}

//----------------------------------------------------------------------
//  get data about the day of week for the emp or, if there is none
//  for this emp, set default values - return false on failure

void rpfFactory::get_dow_data (long a_emp_id, long a_dow,
                               long *a_start_hhmm, long *a_end_hhmm) {

  int is_any = 0;
  int is_match = 0;

  *a_start_hhmm = *a_end_hhmm = 0;

  rppEmpDowItr edowitr = pprob()->empdow_iterator();
  rppEmpDow *edow = (rppEmpDow*)(edowitr.find(a_emp_id));

  for ( ; edow; edow = edowitr.next()) {
    if (edow->emp_id() != a_emp_id) break;
    is_any = 1;
    if (edow->dow() == a_dow) {
      *a_start_hhmm = edow->start_hhmm();
      *a_end_hhmm   = edow->end_hhmm();
      is_match = 1;
      break;
    }
  }

  if (!is_match && !is_any) {
    *a_start_hhmm = 0;    // emp can work all days
    *a_end_hhmm   = 2359;
  }

}

//----------------------------------------------------------------------
// get an iterator for empids in the list owned by the factory

rpfEmpIdItr  rpfFactory::empid_iterator () const {

  rpfEmpIdItr i(m_emp_ids);

  return i;

}

//----------------------------------------------------------------------
// get an iterator for opts in the list owned by the factory

rpfOptItr  rpfFactory::opt_iterator () const {

  rpfOptItr i(m_opts);

  return i;

}

//======================================================================
//  rpfOpt
//
//----------------------------------------------------------------------
//  Constructor

rpfOpt::rpfOpt (rpsPosDay *a_pos_day, rpsEmpDay *a_emp_day,
                                           aipG a_g_constant) {

  m_pos_day = a_pos_day;
  m_emp_day = a_emp_day;
  m_g_constant = a_g_constant;
  m_proj_id  = m_pos_day ? m_pos_day->proj_id()  : 0;
  m_pos_id   = m_pos_day ? m_pos_day->pos_id()   : 0;
  m_emp_id   = m_emp_day ? m_emp_day->emp_id()   : 0;
  m_yyyymmdd = m_emp_day ? m_emp_day->yyyymmdd() : 0;

}

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

rpfOpt::~rpfOpt () {}

//======================================================================
//                           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.
//
//======================================================================