// ---------------------------------------------------------------------------
//   Optimizer.cc (created on Thu Nov 17 18:25:35 BRT 2005)
// 
//   Genetic Algorithm File Fitter (gaffitter)
//
//   Copyright (C) 2005-2015 Douglas A. Augusto
// 
// This file is part of gaffitter.
// 
// gaffitter is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
// 
// gaffitter is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with gaffitter; if not, see <http://www.gnu.org/licenses/>.
//
// ---------------------------------------------------------------------------

#include "Optimizer.h"

#include <utility>
#include <iterator>
#include <algorithm>
#include <cmath>

using namespace std;

// ---------------------------------------------------------------------------
bool 
Intersect( Bin& a, vector<Bin>::iterator g1, vector<Bin>::iterator g2, 
                   vector<const SizeName*>& unassigned )
{
   // TODO: Maintain all time sorted?!?
   vector<const SizeName*> tmp, diff( a.Count() );

   // Add all items of each bin in tmp vector
   while( g1 != g2 )
   {
      tmp.insert( tmp.end(), g1->begin(), g1->end() );
      ++g1;
   }

   sort( a.begin(), a.end() );
   sort( tmp.begin(), tmp.end() );

   /* Diff is the difference of 'a' and 'tmp'. The difference of two sets is
    * formed by the elements that are present in the first set, but not in the
    * second one. 
    *
    * Iterator it is the iterator to the end of the constructed range.
    * */
   vector<const SizeName*>::iterator it = set_difference( 
                   a.begin(), a.end(), tmp.begin(), tmp.end(), diff.begin() );

   if( it - diff.begin() == a.Count() )  
      return false;  // No intersection! 
   else // Intersection! Some elements of 'a' are present in 'tmp'
      return unassigned.insert( unassigned.end(), diff.begin(), it ), true;
}

// ---------------------------------------------------------------------------
Params::Size_t
Optimizer::Evaluate( const BinSet& bins ) const
{
   /* According to Falkenauer, the 'f' cost function is given by:
    *        ___
    *        \             k
    *        /__  (F_i / C)
    *  f =  ________________
    *
    *             N
    * 
    * where:
    *    N: the number of the bins used in the solution
    *    F_i: the sum of sizes of the items in the bin i (how much is
    *    used)
    *    C: the bin capacity
    *    k: a constant, k>1 (usually k=2)
    */
   const int k = 2;

   Params::Size_t f = 0.0;

   for( unsigned i = 0; i < bins.Count(); ++i )
   {
      f += pow( bins[i].Used() / bins[i].Capacity(), k );
   }

   f /= bins.Count();

   return f;
}

// ---------------------------------------------------------------------------
void
Optimizer::Output()
{
   /* Sort the bins from more filled (lesser waste) to less filled
    * (greater waste). This is particularly useful when a maximum number
    * of volumes (bins) is specified, so only the better ones are
    * selected. */
   if( !m_params.m_split )
      sort( m_solution->begin(), m_solution->end() );

   // comparison function. Should be "by size" or "by name" (reverse or not)
   bool ( *cmp_function ) ( const SizeName*, const SizeName* );

   // sort by name or size, ascending or descending
   if (m_params.m_sort_by_size)
      if (m_params.m_sort_reverse) 
         cmp_function = SizeName::CmpSizeRev;
      else 
         cmp_function = SizeName::CmpSize;
   else // sort by name
      if (m_params.m_sort_reverse)
         if (m_params.m_no_case)
            cmp_function = SizeName::CmpNameRevNocase;
         else
            cmp_function = SizeName::CmpNameRev;
      else
         if (m_params.m_no_case)
            cmp_function = SizeName::CmpNameNocase;
         else
            cmp_function = SizeName::CmpName;

  if( m_params.m_verbose ) cout << endl;

  for( unsigned i = 0; i < m_solution->Count() && i < m_params.m_max_bins; ++i )
  {
     if( !m_params.m_hide_items )
     {
        // sort the output
        sort((*m_solution)[i].begin(), (*m_solution)[i].end(), cmp_function);

        for( unsigned int j=0; j<(*m_solution)[i].Count(); ++j )
        {
           if( m_params.m_enclose )
              cout << m_params.m_enclose_chr << (*m_solution)[i][j]->Name() 
                   << m_params.m_enclose_chr;
           else
              cout << (*m_solution)[i][j]->Name();

           if( m_params.m_show_size ) 
              cout << '\t' << m_params.PrettySize((*m_solution)[i][j]->Size());

           cout << m_params.m_delimit_chr;
        }
     }

     // print the summary
     if( !m_params.m_hide_summary )
     {
        if( !m_params.m_hide_items ) cout << endl;

        cout << "[" << i + 1 << "] " 
           << m_params.PrettySize( (*m_solution)[i].Used() ) << "/"
           << m_params.PrettySize( (*m_solution)[i].Capacity() ) << " of "
           << m_params.PrettySize( m_total_size ) 
           << ", Diff: " << m_params.PrettySize((*m_solution)[i].Free())
           << ", Items: "
           << (*m_solution)[i].Count()<< "/" << m_files.size() << endl;
     }

     // Print the bin separator
     if( !m_params.m_hide_items && i != m_solution->Count() - 1 
                                && i != m_params.m_max_bins - 1 )
        cout << m_params.m_bins_separator;
  }
}

// ---------------------------------------------------------------------------
