aboutsummaryrefslogtreecommitdiffstats
/*
 * Author: Sven Gothel <[email protected]>
 * Copyright (c) 1994-2022 Gothel Software e.K.
 * Copyright (c) 1994 Christian Mueller
 *
 * Proprietary licenses are available
 * via Gothel Software e.K. <[email protected]>.
 *
 * This library 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 2 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * You can also check the GNU General Public License
 * in the internet at http://www.gnu.org/copyleft/gpl.html !
 */

#include <gentech/gentech.hpp>
#include <gentech/random.hpp>

#include <jau/basic_algos.hpp>

#include <climits>
#include <limits>
#include <iostream>

#include <cassert>

using namespace jau::gentech;

#define THIS (*this)

std::ostream& std::operator<< (std::ostream& os, const jau::gentech::nucleotide_array_t& list)
{
    const nucleotide_array_t::size_type Zeilenlaenge = 10;

    os << "( " << list.size() << " ) < ";
    for (nucleotide_array_t::size_type i=0; i<list.size(); i++) {
        if (i % Zeilenlaenge == 0 &&  i)   os << "\n\t";

        os << list[i];
        if (i % Zeilenlaenge != Zeilenlaenge-1 && i != list.size()-1) {
            os << ", ";
        }
    }
    os << " >";
	return os;
}

// *******************************************************************
// *******************************************************************
//  C H R O M O S O M
// *******************************************************************
// *******************************************************************

// STATISCHE-KLASSEN-GLOBALE VARIABLEN !!!

Chromosom::Chromosom ( Chromosomen & env, size_type initial_length )
: nucleotide_array_t(initial_length), m_env(env), m_fitness(0)
// Zufaellig erzeugte Chromosomen
{
    for ( Chromosom::size_type i=initial_length; i > 0; --i) {
        push_back( Random<size_type>(env.m_min_nucleotide_value,env.m_max_nucleotide_value) );
    }
    assert (size() == initial_length);
}

Chromosom::Chromosom ( Chromosomen & env, const std::string& FileName )
: nucleotide_array_t(), m_env(env), m_fitness(0)
{
    Load(FileName);
}

void Chromosom::Copy (const Chromosom &a)
{
    m_fitness = a.m_fitness;
    assert (size() == a.size());
}

Chromosom& Chromosom::operator=(const Chromosom& m)
{
    if (this == &m) { return *this; }

    // Neue Liste der Nukleotide anlegen.
    // In Liste<NukleoTyp> wird der *this-Zeiger auf Gleichheit ueberprueft
    // Das Environment env (Chromosomen) bleibt !
    nucleotide_array_t::operator=(m);
    Copy(m);
    return *this;
}

bool Chromosom::operator==(const Chromosom& ch) const noexcept
{
    if (this == &ch) { return true; };     // gleiches Chromosom
    if (size() != ch.size()) {    // garantiert Ungleich
        return false;
    }
    size_type i;
    for(i=0; i < size() && THIS[i] == ch[i]; ++i ) ; // Ueberprufung
    return i == size();
}

void Chromosom::Save(const std::string& FileName) const
{
    FILE *file;
    if( ( file=fopen(FileName.c_str(),"wb") ) == nullptr ) {
        ABORT("Couldn't open file '%s'", FileName.c_str());
    }

    {
        // 4-7-94, 2:51 PM
        // Speichert die Groesse des NukleoDatenTyps als erstes in der Datei
        const uint16_t NukleoLen = sizeof(nucleotide_t);
        if( fwrite (&NukleoLen, sizeof (NukleoLen), 1, file) != 1 ) {
            ABORT("Couldn't write to file '%s'", FileName.c_str());
        }
        // 4-7-94, 2:51 PM
    }
    const size_type len=size();
    if( fwrite (&len, sizeof(len), 1, file) != 1 ) {
        ABORT("Couldn't write to file '%s'", FileName.c_str());
    }
    for(size_type i=0; i<len; ++i) {
        if( fwrite (&(THIS[i]), sizeof (nucleotide_t), 1, file) != 1 ) {
            ABORT("Couldn't write to file '%s'", FileName.c_str());
        }
    }
    fclose(file);
}

void Chromosom::Load(const std::string& FileName) {
    FILE *file;
    if ((file=fopen(FileName.c_str(), "rb")) == nullptr) {
        ABORT("Couldn't open file '%s'", FileName.c_str());
    }

    {
        // 4-7-94, 3:3 PM
        // Liest als erstes die Groesse des abgespeicherten NukleoTyps
        // aus dem File
        uint16_t NukleoLen;
        if( fread (&NukleoLen, sizeof (NukleoLen), 1, file) != 1 ) {
            ABORT("Couldn't read file '%s'", FileName.c_str());
        }
        if( NukleoLen != sizeof(nucleotide_t) ) {
            std::cerr << "!! ERROR !!\n"
                    << "   Laenge des Datentyps der gespeicherten Nuklotide\n"
                    << "   ist kleiner als das abgespeicherte Format\n";
            exit (0);
        }
        // 4-7-94, 3:3 PM
    }
    // Puffer der richtigen Laenge bereit stellen
    nucleotide_t value;
    size_type len;
    if (fread (&len, sizeof (len), 1, file) == 0) {
        ABORT("Couldn't read file '%s'", FileName.c_str());
    }
    reserve(len);
    for(size_type i=0; i<len; ++i) {
        if( fread (&value, sizeof(value), 1, file) == 0 ) {
            ABORT("Couldn't read file '%s'", FileName.c_str());
        }
        insert(i, value);
    }
    fclose(file);
}

Chromosom::size_type Chromosom::GetNukleotidNumber(size_type nuckleotide_idx) const noexcept
{
    nucleotide_t nuckleotide_value=THIS[nuckleotide_idx];

    if( m_env.m_nucleotide_value_scale * (m_env.m_nucleotide_count-1) <= nuckleotide_value &&
        nuckleotide_value <= m_env.m_max_nucleotide_value )
    {
        // Trifft auch fuer Nukleotide gleich nullptr zu !!!
        return m_env.m_nucleotide_count;
    }

    for (size_type n=m_env.m_nucleotide_count-1; n > 0; --n) { // FIXME: Double '-1' here and on 'n'?
        if( m_env.m_nucleotide_value_scale * (n-1) <= nuckleotide_value &&
            nuckleotide_value < m_env.m_nucleotide_value_scale * n ) {
            return n;
        }
    }

    ABORT("Failed to determine nucleotide number of nucleotide[%zu]=%zu", (size_t)nuckleotide_idx, (size_t)nuckleotide_value);
    return 0;
}

nucleotide_t Chromosom::GetUserNukleotidValue(size_type i) const noexcept
{
    return m_env.m_nucleotide_value_scale*(i-1);
}

bool Chromosom::FindIntron(size_type &von, size_type &bis) const noexcept
{
    bool done = false;
    size_type i, i2, i3;

    if (bis-von+1 >= m_env.m_splice_code_ptr->Length)
    {
        // Spleissen..Anfang suchen
        i=von; i2=0;
        while (i <= bis && m_env.m_splice_code_ptr->SpliceCode[i2] != 0) {
            if (GetNukleotidNumber (i) == m_env.m_splice_code_ptr->SpliceCode[i2]) {
                ++i2;
                ++i;
            } else {
                i2=0;
                von=++i;
            }
        }
        if (i < bis) {
            assert (m_env.m_splice_code_ptr->SpliceCode[i2] == 0) ;
            // von gefunden...
            // Spleissen..Ende suchen
            i3=i;
            i=++i2;
            while (i3 <= bis && m_env.m_splice_code_ptr->SpliceCode[i2] != 0) {
                if (GetNukleotidNumber(i3) == m_env.m_splice_code_ptr->SpliceCode[i2]) {
                    i2++;
                } else {
                    i2=i;
                }
                i3++;
            }
            if (m_env.m_splice_code_ptr->SpliceCode[i2] == 0) {
                // bis gefunden...
                bis=i3-1;
                done=true;
            }
        }
    }
    return done;
}

Chromosom::size_type Chromosom::Splicing()
{
  size_type NNukleotide=0, IntronStart=0, IntronEnd=size()-1;
  #ifndef NDEBUG
      size_type len_old;
  #endif

  // Die Bedingung fuer den SpliceCode ist Nukleotide > 1 !
  // Nukleotide wird in Chromosomen::CalcParameter gesetzt !
  if (m_env.m_nucleotide_count <= 1) {
      // Kein Splicing , da kein SpliceCode !!!
      return 0;
  }

  while( FindIntron(IntronStart, IntronEnd) ) {
      #ifndef NDEBUG
          len_old=size();
      #endif
      assert( IntronStart <= IntronEnd );
      erase( IntronStart, IntronEnd+1 );
      assert ( len_old - ( IntronEnd - IntronStart + 1 ) == size() );
      NNukleotide+=IntronEnd-IntronStart+1;
      IntronEnd=size()-1;
  }
  return NNukleotide;
}

void Chromosom::InsertIntrons (size_type von, size_type length)
{
    size_type i, iRumpf;

    if (m_env.m_splice_code_ptr != nullptr &&
            m_env.m_splice_code_ptr->SpliceCode[0] != 0 &&
            von <= size()           &&
            length >= m_env.m_splice_code_ptr->Length)
    {
        // StartSequenz einfuegen.
        for (i=0; m_env.m_splice_code_ptr->SpliceCode[i] != 0; ++i, ++von, --length) {
            insert(von, GetUserNukleotidValue(m_env.m_splice_code_ptr->SpliceCode[i]));
        }
        // Stelle fuer Rumpf merken.
        iRumpf=von;
        // EndSequenz einfuegen.
        for (++i; m_env.m_splice_code_ptr->SpliceCode[i] != 0; ++i, ++von, --length) {
            insert(von, GetUserNukleotidValue (m_env.m_splice_code_ptr->SpliceCode[i]));
        }
        // Rumpf fuellen.
        while (length > 0) {
            insert(iRumpf, Random (m_env.m_min_nucleotide_value, m_env.m_max_nucleotide_value));
            length--;
        }
    }
}

void Chromosom::Inversion()
{
    const size_type start = Random<size_type>(0, size()-2);
    assert (start < size()-1);

    const size_type end = Random(start+1, size()-1) ;
    assert (end < size());
    assert (end > start);

    const size_type length = end-start+1;
    nucleotide_array_t save(length);

    /**
     *   S   E
     * 0123456789     01234
     * abcdefghij  -> cdefg
     *
     *   S   E
     * 0123456789     43210
     * abgfedchij  <- gfedc
     */
    for (size_type i=start; i <= end; ++i) {
        save.push_back( THIS[i] );
    }
    assert( save.size() == length );

    for (size_type i=start; i <= end; ++i)  {
        THIS[i]=save[end-i];
    }
}

void Chromosom::Translocation()
{
    const size_type start = Random<size_type>(0, size()-2);
    assert (start < size()-1);
    const size_type end = Random (start+1, size()-1) ;
    assert (end < size());
    assert (end > start);

    const size_type length = end-start+1;
    nucleotide_array_t save(length);

    for(size_type i=start; i <= end; ++i) {
        save.push_back(THIS[i]);
    }
    erase(start, end+1);
    assert( save.size() == length );

    const size_type dest = Random<size_type>( 0, size()-1 );
    for(size_type i=0; i<save.size(); ++i) {
        THIS.insert(dest+i, save[i]);
    }
}

// *******************************************************************
// *******************************************************************
//  C H R O M O S O M E N
// *******************************************************************
// *******************************************************************

// STATISCHE-KLASSEN-GLOBALE VARIABLEN !!!

const Chromosomen::size_type Chromosomen::m_mutation_freq_variance=1000;

Chromosomen::Chromosomen (nucleotide_t UserNukleoMinVal, 
                          nucleotide_t UserNukleoMaxVal,
                          size_type max_chromosom_count,
                          const std::string& StartGenFile,
                          size_type Nukleotide,
                          SpliceCodeInfo *PtrSpliceCodeInfo,
                          size_type InversionFreq,
                          size_type TranslocationFreq,
                          size_type AsymXOverFreq,
                          size_type CrossVal,
                          size_type MutationFreq,
                          size_type max_no_improving_gens)
: chromosom_array_t(max_chromosom_count),
  m_all_time_best_gen(1),
  m_all_time_best_avrg_fitness(0),
  m_all_time_best(*this),

  m_avrg_fitness(0),
  m_best_fitness(0),
  m_fitness_sum(0),

  m_max_no_improving_gens(max_no_improving_gens),
  m_no_improving_gens(0),

  m_generation(1),
  m_max_chromosom_count(max_chromosom_count),
  m_intro_code_len_sum(0),
  m_spliced_chromosom_count(0),

  m_file_ptk_ptr(nullptr),

  m_generation_start(0),
  m_generation_end(0),
  m_evolution_start(0),
  m_evolution_end(0),

  m_min_chromosom_len(0),
  m_max_chromosom_len(0),
  m_avrg_chromosom_len(0),
  m_chromosom_above_zero_count(0),

  m_mutations_this_gen(0),
  m_inversions_this_gen(0),
  m_translocations_this_gen(0),
  m_mutation_freq(MutationFreq),

  m_inversion_freq(InversionFreq),
  m_translocation_freq(TranslocationFreq),
  m_asymxover_freq(AsymXOverFreq),
  m_cross_val(CrossVal),
  m_xover_number(0), // FIXME

  m_splice_code_ptr(PtrSpliceCodeInfo),
  m_min_nucleotide_value(UserNukleoMinVal),
  m_max_nucleotide_value(UserNukleoMaxVal),
  m_nucleotide_value_scale(1),
  m_nucleotide_count(Nukleotide)
{
    // StartGene aus dem File holen ! ....
    FILE *file;

    CalcParameter();

    if ((file=fopen(StartGenFile.c_str(),"rb") ) == nullptr ) {
        ABORT("Couldn't open file '%s'", StartGenFile.c_str());
    }
    {
        // 4-7-94, 3:3 PM
        // Liest als erstes die Groesse des abgespeicherten NukleoTyps
        // aus dem File
        uint16_t NukleoLen;
        if (fread (&NukleoLen, sizeof(NukleoLen), 1, file)!=1) {
            ABORT("can't read file %s\n", StartGenFile.c_str());
        }
        if( NukleoLen != sizeof(nucleotide_t) ) {
            std::cerr << "!! ERROR !!\n"
                    << "   Laenge des Datentyps der gespeicherten Nuklotide\n"
                    << "   ist kleiner als das abgespeicherte Format\n";
            exit (0);
        }
        // 4-7-94, 3:3 PM
    }

    // int ChromLen, ChromQuantity, i, j;
    size_type ChromQuantity;
    if (fread(&ChromQuantity, sizeof(ChromQuantity), 1, file)==0) {
        ABORT("can't read file %s\n", StartGenFile.c_str());
    }

    // Puffer der richtigen Laenge bereitstellen
    nucleotide_t value;
    for(size_type j=0; j<ChromQuantity; ++j ) {
        Chromosom Gamma( *this, 0 );
        assert (Gamma.size()==0);

        size_type ChromLen;
        if (fread(&ChromLen, sizeof(ChromLen), 1, file) == 0) {
            ABORT("can't read file %s\n", StartGenFile.c_str());
        } // FIXME: Use fixed width type
        for(size_type i=0; i<ChromLen; ++i) {
            if (fread(&value, sizeof(value), 1, file) == 0) {
                ABORT("can't read file %s\n", StartGenFile.c_str());
            }
            Gamma.push_back(value);
        }
        assert (Gamma.size()==ChromLen);
        push_back(Gamma);
    }
    assert (size()==ChromQuantity);
    fclose (file);
}

Chromosomen::Chromosomen ( nucleotide_t UserNukleoMinVal, nucleotide_t UserNukleoMaxVal,
                           size_type max_chromosom_count,
                           size_type StartChromosomNumber,
                           size_type StartChromosomLength,
                           size_type Nukleotide,
                           SpliceCodeInfo *PtrSpliceCodeInfo,
                           size_type InversionFreq,
                           size_type TranslocationFreq,
                           size_type AsymXOverFreq,
                           size_type CrossVal,
                           size_type MutationFreq,
                           size_type max_no_improving_gens)
: chromosom_array_t(max_chromosom_count),
  m_all_time_best_gen(1),
  m_all_time_best_avrg_fitness(0),
  m_all_time_best(*this),

  m_avrg_fitness(0),
  m_best_fitness(0),
  m_fitness_sum(0),

  m_max_no_improving_gens(max_no_improving_gens),
  m_no_improving_gens(0),

  m_generation(1),
  m_max_chromosom_count(max_chromosom_count),
  m_intro_code_len_sum(0),
  m_spliced_chromosom_count(0),

  m_file_ptk_ptr(nullptr),

  m_generation_start(0),
  m_generation_end(0),
  m_evolution_start(0),
  m_evolution_end(0),

  m_min_chromosom_len(0),
  m_max_chromosom_len(0),
  m_avrg_chromosom_len(0),
  m_chromosom_above_zero_count(0),

  m_mutations_this_gen(0),
  m_inversions_this_gen(0),
  m_translocations_this_gen(0),
  m_mutation_freq(MutationFreq),

  m_inversion_freq(InversionFreq),
  m_translocation_freq(TranslocationFreq),
  m_asymxover_freq(AsymXOverFreq),
  m_cross_val(CrossVal),
  m_xover_number(0), // FIXME

  m_splice_code_ptr(PtrSpliceCodeInfo),
  m_min_nucleotide_value(UserNukleoMinVal),
  m_max_nucleotide_value(UserNukleoMaxVal),
  m_nucleotide_value_scale(1),
  m_nucleotide_count(Nukleotide)
{
    CalcParameter();

    // StartGene zufaellig setzen !
    for (size_type i=0; i<StartChromosomNumber ; ++i) {
        Chromosom Gamma (*this, StartChromosomLength);
        assert (Gamma.size() == StartChromosomLength);
        push_back(Gamma);
    }
    assert (size()==StartChromosomNumber);
}

void Chromosomen::Save (const std::string& FileName) const
{
    FILE *file;

    if((file=fopen(FileName.c_str(), "wb")) == nullptr) {
        ABORT("Couldn't open file '%s'", FileName.c_str());
    }
    {
        // 4-7-94, 2:51 PM
        // Speichert die Groesse des NukleoTyps als erstes im File ab
        const uint16_t NukleoLen=sizeof(nucleotide_t);
        if (fwrite (&NukleoLen, sizeof (NukleoLen), 1, file) != 1) {
            ABORT("can't write to file %s\n", FileName.c_str());
        }
        // 4-7-94, 2:51 PM
    }
    size_type ChromQuantity = size();
    if (fwrite (&ChromQuantity, sizeof (ChromQuantity), 1, file) != 1 ) {
        ABORT("can't write to file %s\n", FileName.c_str());
    }
    for (size_type j=0; j<ChromQuantity; j++ ) {
        const size_type ChromLen = THIS[j].size();
        if (fwrite (&ChromLen, sizeof (ChromLen), 1, file) != 1) {
            ABORT("can't write to file %s\n", FileName.c_str());
        }
        for(size_type i=0; i<ChromLen; ++i) {
            nucleotide_t value = THIS[j][i];
            if (fwrite (&value, sizeof (nucleotide_t), 1, file) != 1) {
                ABORT("can't write to file %s\n", FileName.c_str());
            }
        }
    }
    fclose(file);
}

Chromosomen::size_type Chromosomen::Evolution (double GoalFitness, const std::string& chrptrPtkFile,
			                                   double BirthRate, int Bigamie )
{
    double BestEverAverageFitness = -1,
           Cut = 0; // Der Cut beim Sterben
    bool stop = false;

    InitFitness() ;
    if( !chrptrPtkFile.empty() ) {
        if ((m_file_ptk_ptr=fopen (chrptrPtkFile.c_str(), "wt")) == nullptr) {
            ABORT("Couldn't open file '%s'", chrptrPtkFile.c_str());
        }
    } else {
        m_file_ptk_ptr = nullptr;
    }

    m_evolution_start=time(nullptr);
    m_generation=1;
    m_spliced_chromosom_count=0;
    m_intro_code_len_sum=0;
    m_no_improving_gens = 0;
    Protokoll();
    if( !Echo() ) { stop = true; }

    if( BirthRate <= 0.0 || BirthRate > 1.0 ) { stop = true; }

    while( m_best_fitness < GoalFitness &&
           m_no_improving_gens < m_max_no_improving_gens &&
           !stop )
    {
        m_generation++;
        m_spliced_chromosom_count=0;
        m_intro_code_len_sum=0;
        if( BirthRate < 1.0 ) {
            NewGeneration(BirthRate, Bigamie);
            // Fuer LetDie !!!
            Cut = GetXWorstFitness( size() - m_max_chromosom_count );
        } else {
            NewGeneration(Bigamie);
            Cut = -.5;	// Eltern Fitness auf -1 gesetzt
        }
        // Sterben !!!
        LetDie(Cut);
        // Die Mutation
        Mutation();
        // Fitness berechnen
        CalcWholeFitness();
        // Weitere Mutationen
        InversionsMutation();
        TranslocationsMutation();

        m_generation_end=time(nullptr);

        if( BestEverAverageFitness < m_avrg_fitness ) {
            m_no_improving_gens=0;
            BestEverAverageFitness = m_avrg_fitness ;
        } else {
            m_no_improving_gens++ ;
        }
        Protokoll();
        if(!Echo()) { stop = true; }
    }

    m_evolution_end=time(nullptr);
    Echo();
    Protokoll();

    if (m_file_ptk_ptr != nullptr) { fclose (m_file_ptk_ptr); }

    return m_generation;
}

void Chromosomen::InitFitness()
{
    for (size_type i = 0; i < size(); ++i) {
        Chromosom Neu = THIS[i];
        Neu.Splicing();
        THIS[i].SetFitness(Fitness(Neu));
    }
    CalcWholeFitness();
}

void Chromosomen::NewGeneration(double BirthRate, bool Bigamie)
{
    indexlist_t ElternPaare;

    size_type l = (size_type) ( (double)size() * BirthRate );
    size_type i;

    m_generation_start=time(nullptr);

    // Gerade Anzahl der ElternPaare !!!
    if( l%2 > 0 ) {
        --l;
    }

    // ooops, die letzten beiden Elternpaare sind nicht-bigamistisch SCHLECHT
    // zu finden !!!
    if( 2 <= m_chromosom_above_zero_count && m_chromosom_above_zero_count <= l ) {
        m_chromosom_above_zero_count -= 2;
    }

    // Neue Partner in die ElternPaare-Menge einfuegen
    // "Bigamistische Beziehung" werden, wenn moeglich, nicht erlaubt.
    while(l>0) {
        // Nur NEUE Partner !!!
        if (!Bigamie && ElternPaare.size() < m_chromosom_above_zero_count) {
            do {
                i=RouletteSelect();
            } while( jau::contains(ElternPaare, i) ) ;
            ElternPaare.push_back(i);
            l--;
        } else {
            if (!Bigamie)   {
                // the hard way ...
                do {
                    i = Random<size_type>(0, size()-1);
                } while( jau::contains(ElternPaare, i) ) ;
                ElternPaare.push_back(i);
                l--;
            } else {
                // Bigamistische Beziehung JA, aber sexuell !!!
                size_type w, m;
                ElternPaare.push_back((w = Random<size_type>(0, size()-1))) ;
                while (w == (m=Random<size_type>(0, size()-1))) ;
                ElternPaare.push_back(m) ;
                l-=2;
            }
        }
    };

    l=ElternPaare.size();

    #ifndef NDEBUG
        // alles ok ?? GERADE ELTERNPAARE ????
        assert( l%2==0 );
        assert( l== ( (size_type)( size() * BirthRate ) % 2 ) ?
                    (size_type)( size() * BirthRate ) - 1 :
                    (size_type)( size() * BirthRate )
        );
    #endif

    // Fortpflanzen !!!
    while (l > 1) {
        CrossingOver (ElternPaare[l-1], ElternPaare[l-2]);
        l -= 2 ;
    }
    assert (l == 0);
}


void Chromosomen::NewGeneration(bool Bigamie)
{
    indexlist_t ElternPaare;
    size_type l = m_max_chromosom_count;

    m_generation_start=time(nullptr);

    // Gerade Anzahl der ElternPaare !!!
    if( l%2 > 0 ) {
        --l;
    }

    // ooops, die letzten beiden Elternpaare sind nicht-bigamistisch SCHLECHT
    // zu finden !!!
    if( 2 <= m_chromosom_above_zero_count && m_chromosom_above_zero_count <= l ) {
        m_chromosom_above_zero_count -= 2;
    }

    // Neue Partner in die ElternPaare-Menge einfuegen
    // "Bigamistische Beziehung" werden, wenn moeglich, nicht erlaubt.
    while( l > 0 ) {
        // Nur NEUE Partner !!!
        if( !Bigamie && ElternPaare.size() < m_chromosom_above_zero_count ) {
            size_type i;
            do {
                i = RouletteSelect();
            } while ( jau::contains(ElternPaare, i) ) ;
            ElternPaare.push_back(i);
            l--;
        } else {
            if( !Bigamie ) {
                size_type i;
                do {
                    i = Random<size_type>(0, size()-1);
                } while( jau::contains(ElternPaare, i) ) ;
                ElternPaare.push_back(i);
                l--;
            } else { // Bigamistische Beziehung JA, aber sexuell !!!
                size_type w, m;
                ElternPaare.push_back( ( w = Random<size_type>(0, size()-1) ) );
                while( w == ( m = Random<size_type>(0, size()-1) ) ) ;
                ElternPaare.push_back(m);
                l-=2;
            }
        }
    };

    l=ElternPaare.size();

    // alles ok ?? GERADE ANZAHL DER ELTERN !!!
    assert (l%2==0);
    (void)l;

    // Neue Population...
    l=ElternPaare.size();
    while (l > 1) {
        // Beim Crossing Over werden die Nachkommen am Ende der Populations-Liste
        // eingefuegt.
        // Die Indizes fuer die ElternPaare bleiben somit aktuell !!!
        CrossingOver (ElternPaare[l-1], ElternPaare[l-2]);
        l-=2;
    }

    // Eltern markieren, damit sie "getoetet" werden koennen !!!
    // Das geschied in 'LetDie', Aufruf in Evolution
    l=ElternPaare.size()-1;
    while (l > 0) {
        const size_type w=ElternPaare[l-1];
        // markieren !!!
        THIS[w].SetFitness(-1);
        l--;
    }
}

void Chromosomen::LetDie(double cut)
{
    size_type i=0;

    while (i < size() && size() > m_max_chromosom_count) {
        if (THIS[i].GetFitness() <= cut) {
            Kill (i);
        } else {
            ++i;
        }
    }

    // Sicherheitsabfrage
    if (size() > m_max_chromosom_count) {
        cut = GetXWorstFitness (size()-m_max_chromosom_count);
        i = 0;
        while (i < size() && size() > m_max_chromosom_count) {
            if (THIS[i].GetFitness() <= cut) {
                Kill (i);
            } else {
                ++i;
            }
        }
    }
    assert (size() <= m_max_chromosom_count);
}

Chromosomen::size_type Chromosomen::RouletteSelect() const
// Chris: geaendert
{
    size_type i;

    double Sum       = 0.0;
    const double Threshold = Random (0.0, 1.0) * GetFitnessSum();

    // Aufgrund der Ungenauigkeit der Flieskommaoperatoren
    // muss auch das Ende der Liste abgefragt werden.
    for (i=0; Sum < Threshold && i<size(); ++i) {
        Sum += THIS[i].GetFitness();
    }
    return ( i < size() ) ? i : size()-1;
}

void Chromosomen::CreateNewSymChromosom ( Chromosom &dest, size_type m, size_type w,
                                          crosspoints_t &CrossPoints )
{
    // i          : Indize des Crosspoints
    // von, bis   : Zu Uebertragender Chromosomenabschnitt [von..bis[
    // ch         : Alternierendes Indize zwischen den beiden Chromosomen
    //              mit den Geschlechtern 'm' und 'w'
    // done       : Abbruch Indikator
    bool done = false;
    size_type i, von, bis, ch;

    // Startwerte !!
    i = bis = 0;
    ch = w;

    dest.reserve( THIS[ch].size() );

    do {
        // An dem letzten exklusiven Ende fortfahren
        von = bis;
        if ( i < CrossPoints.size() ) {
            // Chromosomenabschnitt-Ende holen.
            if( CrossPoints[i] < THIS[ch].size() ) bis = CrossPoints[i];
            else                                   bis = THIS[ch].size();
            ++i;
        } else {
            // ...und den Rest uebertragen
            bis = THIS[ch].size();
            done = true;
        }
        assert ( bis <= THIS[ch].size() );
        assert ( von == dest.size() );
        // Chromosomenabschnitt uebertragen [von..bis[
        for (size_type n=von; n<bis; n++) {
            dest.insert(n, THIS[ch][n]);
        }
        // Indize zwischen den Geschlechtern alternieren
        ch = ( ch==m ? w : m ) ;
    } while ( !done );

    // ...sonst Fehler !!!
    assert( dest.size() > 0 );
}

void Chromosomen::CrossingOver (size_type m, size_type w)
{
    if ( m_cross_val == 0 ) { return; }

    size_type shortest=( ( THIS[m].size() < THIS[w].size() ) ? m : w);

    if ( m_xover_number++ < m_asymxover_freq || m_asymxover_freq==0 ) {
        // Symmetrisches XOver !!!
        crosspoints_t CrossPoints;
        assert( CrossPoints.size()==0 );

        // Kreuzungspunkte sortiert eintragen.
        for ( size_type i = 0; i < m_cross_val; ++i ) {
            CrossPoints.insert( Random<size_type>(0 , THIS[shortest].size()) );
        }

        Chromosom NeuA (*this, 0);
        Chromosom NeuB (*this, 0);
        size_type SplicedCode;

        CreateNewSymChromosom ( NeuA, m, w, CrossPoints );
        push_back( NeuA );
        SplicedCode=NeuA.Splicing();
        if(SplicedCode>0) {
            m_spliced_chromosom_count++;
            m_intro_code_len_sum+=SplicedCode;
        }
        // Die Fitness des gespleissten Chromosomes in das ungespleisste
        // eingebundene Chromosom einsetzen !!!
        THIS[size()-1].SetFitness(Fitness(NeuA));

        CreateNewSymChromosom ( NeuB, w, m, CrossPoints );
        push_back( NeuB );
        SplicedCode=NeuB.Splicing();
        if(SplicedCode>0) {
            m_spliced_chromosom_count++;
            m_intro_code_len_sum+=SplicedCode;
        }
        // Die Fitness des gespleissten Chromosomes in das ungespleisste
        // eingebundene Chromosom einsetzen !!!
        THIS[size()-1].SetFitness(Fitness(NeuB));

    } else {

        m_xover_number = 0;
        // Assymetrisches XOver : Nur ein neues Element !
        Chromosom Neu (*this, 0);

        // Kopieren
        Neu=THIS[m];

        // anhaengen
        for (size_type n=0; n<THIS[w].size(); n++) {
            Neu.push_back( THIS[w][n] );
        }
        Neu.SetFitness(Fitness(Neu));
        push_back( Neu );

    }
}

void Chromosomen::Mutation()
{
    static size_type next_nukleotide_idx = m_mutation_freq + Random<size_type>( 0, (size_type)(m_mutation_freq/m_mutation_freq_variance) ) ;
    m_mutations_this_gen=0;

    if ( m_mutation_freq > 0 ) {
        size_type nukleotide_idx = next_nukleotide_idx;
        for(size_type chromosom_idx = 0; chromosom_idx < size(); ++chromosom_idx ) {
            bool mutated = false;
            const size_type chromosom_len = THIS[chromosom_idx].size();
            while( nukleotide_idx < chromosom_len ) {
                // Die Mutation.
                (THIS[chromosom_idx])[nukleotide_idx] = Random<size_type>( m_min_nucleotide_value, m_max_nucleotide_value);
                m_mutations_this_gen++;
                mutated=true;
                nukleotide_idx += m_mutation_freq + Random<size_type>( 0, (size_type)(m_mutation_freq/m_mutation_freq_variance) );
            }
            if( nukleotide_idx >= chromosom_len ) {
                nukleotide_idx -= chromosom_len;
            }
            if(mutated) {
                // Fitness neuberechnen ...
                // ... this is a social event - we are hard folks :-)
                Chromosom Neu (THIS[chromosom_idx]);
                Neu.Splicing();
                THIS[chromosom_idx].SetFitness(Fitness(Neu));
            }
        }
        next_nukleotide_idx = nukleotide_idx;
    }
}


void Chromosomen::InversionsMutation()
{
    static size_type next_chromosomen_idx = m_inversion_freq;
    m_inversions_this_gen=0;
    size_type chromosomen_idx = next_chromosomen_idx;
    while( chromosomen_idx < size() ) {
        m_inversions_this_gen++;
        THIS[chromosomen_idx].Inversion();
        // Fitness neuberechnen ...
        Chromosom Neu (THIS[chromosomen_idx]);
        Neu.Splicing();
        THIS[chromosomen_idx].SetFitness(Fitness(Neu));
        chromosomen_idx += m_inversion_freq;
    }
    if( chromosomen_idx >= size() ) {
        chromosomen_idx -= size();
    }
    next_chromosomen_idx = chromosomen_idx;
}

void Chromosomen::TranslocationsMutation()
{
    static size_type next_chromosomen_idx = m_translocation_freq;
    m_translocations_this_gen=0;
    size_type chromosomen_idx = next_chromosomen_idx;
    while( chromosomen_idx < size() ) {
        m_translocations_this_gen++;
        THIS[chromosomen_idx].Translocation();
        // Fitness neuberechnen ...
        Chromosom Neu (THIS[chromosomen_idx]);
        Neu.Splicing();
        THIS[chromosomen_idx].SetFitness(Fitness(Neu));
        chromosomen_idx += m_translocation_freq;
    }
    if( chromosomen_idx >= size() ) {
        chromosomen_idx -= size();
    }
    next_chromosomen_idx = chromosomen_idx;
}

void Chromosomen::CalcWholeFitness()
{
    double Total=0, TempFitness;
    size_type BestChrom=npos, ChromLen;
    size_type ChromLenSum=0;

    m_min_chromosom_len = std::numeric_limits<size_type>::max();
    m_max_chromosom_len = std::numeric_limits<size_type>::min();
    m_best_fitness = -1;
    m_chromosom_above_zero_count = 0;

    for (size_type i = 0; i < size(); ++i) {
        ChromLen = THIS[i].size();
        ChromLenSum += ChromLen;
        if( m_min_chromosom_len > ChromLen ) { m_min_chromosom_len=ChromLen; }
        if( m_max_chromosom_len < ChromLen ) { m_max_chromosom_len=ChromLen; }
        if( ( TempFitness = THIS[i].GetFitness() ) > 2*std::numeric_limits<double>::epsilon() ) {
            m_chromosom_above_zero_count++ ;
        }
        Total += TempFitness ;
        if( m_best_fitness < TempFitness ) {
            m_best_fitness = TempFitness;
            BestChrom = i;
        }
    }
    m_avrg_fitness = Total / (double)size();
    m_fitness_sum = Total;
    m_avrg_chromosom_len = (double)ChromLenSum/(double)size();
    if( m_all_time_best.GetFitness() < m_best_fitness && npos != BestChrom) {
        m_all_time_best=THIS[BestChrom];
        m_all_time_best.Splicing();
    }
    if (m_all_time_best_avrg_fitness < m_avrg_fitness) {
        m_all_time_best_avrg_fitness = m_avrg_fitness;
        m_all_time_best_gen = m_generation;
    }
}

Chromosomen::size_type Chromosomen::GetWorstChromosom() const
{
    double WorstFitness=2, TempFitness;
    size_type iw = npos;

    for (size_type i=0; i<size(); ++i) {
        TempFitness=THIS[i].GetFitness();
        if (WorstFitness > TempFitness) {
            WorstFitness = TempFitness ;
            iw=i;
        }
    }
    return iw;
}

double Chromosomen::GetXWorstFitness(size_type count) const
{
    doubles_sorted_t Worst;

    if (count == 0) { return -1; } // Falsche Anzahl uebergeben
    for (size_type i=0; i<size(); ++i) {
        if( Worst.size() < count ||
            THIS[i].GetFitness() < Worst[count-1] ) 
        {
            Worst.insert(THIS[i].GetFitness());
        }
    }
    if (Worst.size() >= count) {
        return Worst[count-1];
    } else {
        return Worst[Worst.size()-1];
    }
}

Chromosomen::size_type Chromosomen::GetBestChromosom() const
{
    double BestFitness=-1, TempFitness;
    size_type ib = npos;

    for (size_type i=0; i<size(); ++i) {
        TempFitness=THIS[i].GetFitness();
        if (BestFitness < TempFitness) {
            BestFitness = TempFitness ;
            ib=i;
        }
    }
    return ib;
}

const Chromosom &Chromosomen::GetTheBestEverChromosom()
// Hier kein kein Slicing mehr, da beim abspeichern des besten
// Chromosoms in der Variablen 'TheBestEver' der Code schon
// gesplict abgespeichert wird.
{
  return (const Chromosom&) m_all_time_best;
}

void Chromosomen::CalcParameter()
{
    if (m_splice_code_ptr==nullptr ||
        m_splice_code_ptr->SpliceCode==nullptr ||
        m_splice_code_ptr->SpliceCode[0]==0 )
    {
        // Ohne Splicing ....
        m_nucleotide_count=0;
        m_nucleotide_value_scale=1;
    } else {
        if (m_splice_code_ptr->Length ==0) {
            // Laenge berechnen ...
            int i;

            for (i=0; m_splice_code_ptr->SpliceCode[i]!=0; ++i) {
                m_splice_code_ptr->Length ++;
            }
            for (++i; m_splice_code_ptr->SpliceCode[i]!=0; ++i) {
                m_splice_code_ptr->Length ++;
            }
        }
        if(m_nucleotide_count>0) {
            m_nucleotide_value_scale = ( m_max_nucleotide_value - m_min_nucleotide_value + 1 ) / m_nucleotide_count ;
        } else {
            m_nucleotide_value_scale = 1;
        }
    }
}

void Chromosomen::Protokoll()
{
    double SpliceCodePerChrom ;

    SpliceCodePerChrom = ( m_spliced_chromosom_count > 0 ) ?
                         ( (double)m_intro_code_len_sum / (double)m_spliced_chromosom_count )
                         : 0.0;

    if (m_file_ptk_ptr != nullptr) {
        if (m_evolution_end == 0) {
            fprintf (m_file_ptk_ptr, "=======================================================\n\n\n");
            fprintf (m_file_ptk_ptr, "\nGeneration / Generierungsdauer        : %3zu  /  %zu s\n",
                    (size_t)m_generation, (size_t)(m_generation_end-m_generation_start) );
            fprintf (m_file_ptk_ptr, "Populationsgroesse                    : %3zu\n",
                    (size_t)size());
            fprintf (m_file_ptk_ptr, "Chromosomen Laenge Minimum            : %3zu\n",
                    (size_t)m_min_chromosom_len);
            fprintf (m_file_ptk_ptr, "Chromosomen Laenge Maximum            : %3zu\n",
                    (size_t)m_max_chromosom_len);
            fprintf (m_file_ptk_ptr, "Chromosomen Laenge Durchschnitt       : %10.6lf\n",
                    m_avrg_chromosom_len);
            fprintf (m_file_ptk_ptr, "Gespleisste Chromosomen               : %3zu\n",
                    (size_t)m_spliced_chromosom_count);
            fprintf (m_file_ptk_ptr, "Gespleisstes Code pro Chromosom       : %10.6lf\n",
                    SpliceCodePerChrom);

            fprintf (m_file_ptk_ptr, "\nBestFitness                           : %10.6lf\n",
                    m_best_fitness);
            fprintf (m_file_ptk_ptr, "Average Fitness                       : %10.6lf\n",
                    m_avrg_fitness);

            fprintf (m_file_ptk_ptr, "\nMutationen dieser Generation          : %3zu\n",
                    (size_t)m_mutations_this_gen);
            fprintf (m_file_ptk_ptr, "Inversionen dieser Generation         : %3zu\n",
                    (size_t)m_inversions_this_gen);
            fprintf (m_file_ptk_ptr, "Translokationen dieser Generation     : %3zu\n",
                    (size_t)m_translocations_this_gen);
            fprintf (m_file_ptk_ptr, "\nTheBestEverFitness                    : %10.6lf\n",
                    m_all_time_best.GetFitness());
            fprintf (m_file_ptk_ptr, "TheBestEversAverageFitness            : %10.6lf\n",
                    m_all_time_best_avrg_fitness);
            fprintf (m_file_ptk_ptr, "TheBestEversGeneration                : %3zu\n\n",
                    (size_t)m_all_time_best_gen);
        } else {
            if(m_generation>1) {
                fprintf (m_file_ptk_ptr, "Avrg. Generationsdauer                  : %f s / Generation\n",
                        ((double) (m_evolution_end-m_evolution_start))/((double)(GetGeneration()-1)) );
            }
            fprintf (m_file_ptk_ptr, "\nGenerationen / Evolutionsdauer        : %3zu /  %zu s\n",
                    (size_t)m_generation, (size_t)(m_evolution_end-m_evolution_start));
        }
    }
}

bool Chromosomen::Echo() const
{
    if (m_generation == 1) {
        printf(" Generation: BestGeneration: AverageFitness: BestFitness:"
                " TheBestLength:\n");
    }
    printf("\r%11zu%16zu%16.9lf%13.9lf%15zu",
            (size_t)GetGeneration(), (size_t)m_all_time_best_gen,
            GetAverageFitness(), GetBestFitness(),
            (size_t)m_all_time_best.size() );

    if (m_evolution_end > 0) {
        if (m_generation > 1) {
            printf ("\n\nAvrg. Generationsdauer                : %f s / Generation\n",
                    ((double)(m_evolution_end-m_evolution_start))/((double)(GetGeneration()-1)) );

        }
        printf ("\n\nGenerationen / Evolutionsdauer        : %3zu  /  %3zu s\n",
                (size_t)m_generation, (size_t)(m_evolution_end-m_evolution_start));
    }
    return true;
}

void Chromosom::Ausgabe (std::ostream& OS) const noexcept
{
    const int Zeilenlaenge = 10;

    OS << "( l " << size() << ", f "
       << ( ( GetFitness() < std::numeric_limits<double>::epsilon() ) ? (double)(0) : GetFitness() )
       << " ) < ";
    for (size_type i=0; i<size(); ++i) {
        if (i % Zeilenlaenge == 0 &&  i) {
            OS << "\n\t";
        }
        double wandler = THIS[i];
        OS << wandler;
        if (i % Zeilenlaenge != Zeilenlaenge-1 && i != size()-1) {
            OS << ", ";
        }
    }
    OS << " >";
}

void Chromosomen::Ausgabe (std::ostream& OS) const noexcept
{
    const int Zeilenlaenge = 7;

    OS << "( " << size() << " ) < ";
    for (size_type i=0; i<size(); ++i) {
        if (i % Zeilenlaenge == 0 &&  i) {
            OS << "\n\t";
        }
        OS << ( ( THIS[i].GetFitness() < std::numeric_limits<double>::epsilon() ) ? (double)(0) : THIS[i].GetFitness() );
        if (i % Zeilenlaenge != Zeilenlaenge-1 && i != size()-1) {
            OS << ", ";
        }
    }
    OS << " >";
}

/*
std::ostream& operator << (std::ostream& OS, Liste<NukleoTyp>& Ch)
{
  Ch.Ausgabe (OS);
  return OS;
}
*/
# ifdef TEST
    // Der Testteil
    # include "error.cpp"
    # include "maschine.cpp"

    int main()
    {
      Chromosom test1(0, 4, 5, nullptr, 50), test2(0, 4, 5, nullptr, 60);

      randomize();
      if (test1 == test1)
	cout << "\ntest2\n" << test2;
      else
	cerr << "\nopps: Operator \"==\" arbeitet nicht korekt !!";

      if (test1 != test2)
	cout << "\ntest1\n" << test1;
      else
	cerr << "\nopps: Operator \"!=\" arbeitet nicht korekt !!";

      test2 = test1;
      getch();

      if (test2 == test1)
	cout << "\ntest2 nach Zuweisung\n" << test2;
      else
	cerr << "\nopps: Operator \"==\" arbeitet nicht korekt !!";

      while (!kbhit())  {
	Chromosom test3(0, Random (0, 10), 5, nullptr, Random (30, 50));
	cout << endl << test3;
	getch();
      }
      return 0;
    }
# endif