/***************************************************************************
 *   Copyright (C) 2005 by Andreas Pokorny                                 *
 *   andreas.pokorny@biozentrum.uni-wuerzburg.de                           *
 *                                                                         *
 *   This file is part of profdist and cbcanalyzer                         *
 *                                                                         *
 *   Both profdist and cbcanalyzer are 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 2 of the * 
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   Profdist and cbcanalyzer are 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <cstring>
#include <iterator>
#include <algorithm>
#include <numeric>
#include "bootstrap.h"
namespace impl {
  void generate_random_numbers( std::vector<std::size_t> & numbers ) 
  {
    size_t num_entries = numbers.size();
    for ( size_t i = num_entries; i ; --i )
      ++numbers[size_t( (float(num_entries) *rand())/float(RAND_MAX+1.0f)) ];
  }
}
//--------------------------- Methods for use with AlignCode ---------------------
void bootstrap( AlignCode const& s, AlignCode & d, std::string const& extension )
{
  size_t num_entries = s.get_num_sites();

  std::vector<std::size_t> random_entries( num_entries, 0 );
  // Generate random numbers,
  impl::generate_random_numbers( random_entries );

  size_t num_seq = s.get_num_sequences();
  if( ! num_seq ) 
    return;

  // Generate sequence iterators on source, to read all data simultaneously
  typedef vector<AlignCode::const_sequence_iterator> iterators_vec; 
  iterators_vec iterators( num_seq, s.begin(0) ); 
  
  size_t i = 0;
  for( iterators_vec::iterator it_it = iterators.begin(), 
      it_e = iterators.end(); it_it != it_e; ++it_it, ++i )
    *it_it = s.begin( i );

  vector<char> items( num_seq );

  // Fast resize destination!
  d.resize( num_seq, 0 );


  for( std::size_t i = 0; i < num_seq; ++i ) 
    d.set_sequence_name( s.get_sequence_name(i) + extension, i );

  // Fill destination column by column using push_back
  for( vector<std::size_t >::iterator it = random_entries.begin(),
      e = random_entries.end(); it != e; ++it )
  {
    if( *it )
    {
      vector<char>::iterator items_it = items.begin();
      for( 
          iterators_vec::iterator it_it = iterators.begin(), it_e = iterators.end(); 
          it_it != it_e; 
          ++it_it, ++items_it 
          )
      {
        *items_it = d.mapper.code2dna[**it_it];
      }

      for( size_t i = 0; i < *it; ++i )
        d.push_back( items );
 
    }
    
    for( iterators_vec::iterator it_it = iterators.begin(), 
        it_e = iterators.end(); it_it != it_e; ++it_it )
      ++(*it_it); 
  }
}

void  bootstrap( Profile const& source, Profile & dest )
{
  std::vector<std::size_t> random_entries( source.get_num_sites(), 0 );
  impl::generate_random_numbers( random_entries );
  dest.init( source.get_num_sites(), source.get_num_profiles(), source.get_num_rows() );

  std::size_t n_profiles = source.get_num_profiles(), n_rows = source.get_num_rows(), dest_index = 0, site_index = 0;
  for( vector<std::size_t>::iterator it = random_entries.begin(),
      e = random_entries.end(); it != e; ++it, ++site_index)
  {
    if( *it ) {
      for( std::size_t i = 0; i != n_profiles; ++i )
      {
        Profile::ConstSingleProfile s = source.get_profile(i);
        Profile::SingleProfile d = dest.get_profile(i);
        size_t dest_site = dest_index;
        for( std::size_t j = 0; j != *it; ++j )
          std::memcpy( d.get_column(dest_site++), s.get_column(site_index), sizeof( Profile::value_type ) * n_rows );
      }
      dest_index += *it;
    }
  }

}

