<commit_before>/*
 * Copyright (c) 2013-2014, Christian Loose
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "terminalemulator.h"

#include <QDebug>
#include <QEvent>
#include <QTextCodec>

#include "displaybuffer.h"
#include "field.h"
#include "formattable.h"
#include "generaldatastream.h"
#include "terminaldisplay.h"

namespace q5250 {

TerminalEmulator::TerminalEmulator(QObject *parent) :
    QObject(parent)
{
    codec = QTextCodec::codecForName("IBM500");
}

void TerminalEmulator::setDisplayBuffer(DisplayBuffer *buffer)
{
    displayBuffer = buffer;
}

void TerminalEmulator::setFormatTable(FormatTable *table)
{
    formatTable = table;
}

void TerminalEmulator::setTerminalDisplay(TerminalDisplay *display)
{
    terminalDisplay = display;
}

Cursor TerminalEmulator::cursorPosition() const
{
    return cursor;
}

void TerminalEmulator::parseStreamData(const QByteArray &data)
{
    GeneralDataStream stream(data);

    while (!stream.atEnd()) {
        unsigned char byte = stream.readByte();

        if (byte == 0x04 /*ESC*/) {
            byte = stream.readByte();

            switch (byte) {
            case 0x11 /*WRITE TO DISPLAY*/:
                handleWriteToDisplayCommand(stream);
                break;
            case 0x40 /*CLEAR UNIT*/:
                handleClearUnitCommand();
                break;
            case 0xf3 /*WRITE STRUCTURED FIELD*/:
                handleWriteStructuredFieldCommand(stream);
                break;
            }
        }
    }
}

void TerminalEmulator::handleKeypress(int key, const QString &text)
{
    switch (key) {
    case Qt::Key_Up:
        cursor.moveUp();
        break;
    case Qt::Key_Down:
        cursor.moveDown();
        break;
    case Qt::Key_Left:
        cursor.moveLeft();
        break;
    case Qt::Key_Right:
        cursor.moveRight();
        break;
    case Qt::Key_Return:
        {
            GeneralDataStream stream;

            stream << cursor.row() << cursor.column() << 0xf1 /*AID*/;

            formatTable->map([&](Field* field) {
                stream << 0x11
                       << field->startRow
                       << field->startColumn;

                for (int i = 0; i < field->content.size(); ++i) {
                    stream << field->content.at(i);
                }
            });

            emit sendData(stream.toByteArray());
        }
        break;
    default:
        if (!text.isEmpty()) {
            Field *currentField = formatTable->fieldAt(cursor, displayBuffer->size().width());

            if (currentField && !currentField->isBypassField()) {
                QByteArray ebcdic = codec->fromUnicode(text);
                displayBuffer->setCharacterAt(cursor.column(), cursor.row(), ebcdic.at(0));
                currentField->setContent(cursor, displayBuffer->size().width(), ebcdic);
                cursor.moveRight();
            }
        }
        break;
    }
}

void TerminalEmulator::dataReceived(const QByteArray &data)
{
    parseStreamData(data);
    update();
}

void TerminalEmulator::update()
{
    QByteArray text;

    unsigned char startColumn = 0;
    unsigned char startRow = 0;

    int bufferWidth = displayBuffer->size().width();
    int bufferHeight = displayBuffer->size().height();

    terminalDisplay->clear();

    for (int row = 0; row < bufferHeight; ++row) {
        for (int column = 0; column < bufferWidth; ++column) {
            unsigned char character = displayBuffer->characterAt(column+1, row+1);
            if (character >= 0x20 && character <= 0x3f) {
                if (text.length() > 0) {
                    terminalDisplay->displayText(startColumn, startRow, codec->toUnicode(text));
                    text.clear();
                }
                terminalDisplay->displayAttribute(character);
            } else {
                if (text.isEmpty()) {
                    startColumn = column+1;
                    startRow = row+1;
                }
                text += (character == '\0' ? '\x40': character);
            }
        }

        if (text.length() > 0) {
            terminalDisplay->displayText(startColumn, startRow, codec->toUnicode(text));
            text.clear();
        }
    }

    terminalDisplay->displayCursor(cursor.column(), cursor.row());

    emit updateFinished();
}

void TerminalEmulator::keyPressed(int key, const QString &text)
{
    handleKeypress(key, text);
    update();
}

void TerminalEmulator::handleClearUnitCommand()
{
    displayBuffer->setSize(80, 25);
    formatTable->clear();
    cursor.setPosition(1, 1);
}

void TerminalEmulator::handleWriteToDisplayCommand(GeneralDataStream &stream)
{
    unsigned char cc1 = stream.readByte();
    unsigned char cc2 = stream.readByte();

    qDebug() << bin << showbase << cc1 << cc2;

    while (!stream.atEnd()) {
        unsigned char byte = stream.readByte();

        switch (byte) {
        case 0x01 /*START OF HEADER*/:
            {
                unsigned dataLength = stream.readByte();
                formatTable->clear();
            }
            break;
        case 0x02 /*REPEAT TO ADDRESS*/:
            {
                unsigned char row = stream.readByte();
                unsigned char column = stream.readByte();
                unsigned char character = stream.readByte();
                displayBuffer->repeatCharacterToAddress(column, row, character);
            }
            break;
        case 0x04 /*ESC*/:
            stream.seekToPreviousByte();
            return;
        case 0x11 /*SET BUFFER ADDRESS*/:
            {
                unsigned char row = stream.readByte();
                unsigned char column = stream.readByte();
                displayBuffer->setBufferAddress(column, row);
            }
            break;
        case 0x1d /*START OF FIELD*/:
            {
                Field *field = new Field();

                unsigned char byte = stream.readByte();
                if (byte & 0x40 /*is input field?*/) {
                    unsigned char ffw2 = stream.readByte();
                    field->format = (byte << 8) | ffw2;
                    field->attribute = stream.readByte();
                } else {
                    field->attribute = byte;
                }

                field->setLength(stream.readWord());

                displayBuffer->addField(field);

                if (field->isInputField()) {
                    formatTable->append(field);
                }
            }
            break;
        default:
            displayBuffer->setCharacter(byte);
            break;
        }
    }
}

void TerminalEmulator::handleWriteStructuredFieldCommand(GeneralDataStream &stream)
{
    unsigned short length = stream.readWord();
    unsigned char commandClass = stream.readByte();
    unsigned char commandType = stream.readByte();
    unsigned char flags = stream.readByte();

    qDebug() << "[WSF] len =" << length
             << "class =" << hex << showbase << commandClass
             << "type =" << hex << showbase << commandType
             << "flags =" << bin << showbase << flags;

    // 5250 QUERY command
    if (commandClass == 0xd9 && commandType == 0x70) {
        GeneralDataStream stream;

        // [ROW] [COLUMN] [AID] [Structured Field]
        stream << 0x00                          // cursor row
               << 0x00                          // cursor column
               << 0x88;                         // 5250 QUERY reply

        // [LL] [C] [T] [F1] [Data Field]
        stream << 0x00 << 0x44                  // Total length of structured field
               << 0xd9                          // Command Class
               << 0x70                          // Command Type: 5250 QUERY
               << 0x80;                         // Fixed flag byte for QUERY response

        stream << 0x06 << 0x00                  // Workstation Control Unit
               << 0x01 << 0x01 << 0x00          // Code Level
               << 0x00 << 0x00 << 0x00 << 0x00  // Reserved (16 bytes)
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x01;                         // Workstation Type: Display

        QByteArray machineType = codec->fromUnicode(QStringLiteral("5251"));
        for (int i = 0; i < machineType.size(); ++i) {
            stream << machineType.at(i);
        }

        QByteArray modelNumber = codec->fromUnicode(QStringLiteral("011"));
        for (int i = 0; i < modelNumber.size(); ++i) {
            stream << modelNumber.at(i);
        }

        stream << 0x02                          // Keyboard ID: Standard
               << 0x00                          // Extended Keyboard ID
               << 0x00                          // Reserved
               << 0x00 << 0x00 << 0x00 << 0x00  // Serial Number: None
               << 0x01 << 0x00                  // Maximum Number of Input Fields: 256
               << 0x00                          // Control Unit Customization
                                                //  Bit 7   : host can send a 5250 WSC CUSTOMIZATION command
                                                //  Bit 6   : host can send a 5250 QUERY STATION STATE command
                                                //  Bit 5   : host can send a 5250 WORKSTATION CUSTOMIZATION
                                                //            command to select the SBA code returned in READ
                                                //            commands for displays with ideographic extended
                                                //            attributes
                                                //  Bit 4   : 5250 WORKSTATION CUSTOMIZATION command may
                                                //           be 6 bytes or greater than 8 bytes in length
                                                //  Bits 3-0: Reserved
               << 0x00 << 0x00                  // Reserved (2 bytes)
               // Device Capabilities (22 bytes)
               << 0x23                          // Byte 0 - Operating Capabilities:  0b00100011
                                                //  Bits 7-6: Row 1/Column 1 support (00=No, 01=Limited)
                                                //  Bit 5   : READ MDT ALTERNATE command is supported
                                                //  Bit 4   : Workstation has PA1 and PA2 support
                                                //  Bit 3   : Workstation has PA3 support
                                                //  Bit 2   : Workstation has cursor select support
                                                //  Bit 1   : Move Cursor order, Transparent Data order and
                                                //            Transparent entry field FCW support
                                                //  Bit 0   : READ MODIFIED IMMEDIATE ALTERNATE command
                                                //            is supported
               << 0x31                          // Byte 1 - Display Screen Capabilities: 0b00110001
                                                //  Bits 7-6: Reserved
                                                //  Bit 5   : 27 x 132 screen size is supported
                                                //  Bit 4   : 24 x 80 screen size is supported
                                                //  Bit 3   : SLP is supported
                                                //  Bit 2   : MSR is supported
                                                //  Bits 1-0: color support (00=Monochrome, 01=Color)
               << 0x00                          // Byte 2
               << 0x00                          // Byte 3
               << 0x00                          // Byte 4
               << 0x00                          // Byte 5
               << 0x00                          // Byte 6 - Reserved
               << 0x00                          // Byte 7 - 5250 Image/Fax Support
               << 0x00                          // Byte 8 - 5250 Image/Fax Support
               << 0x00                          // Byte 9
               << 0x00                          // Byte 10
               << 0x00                          // Byte 11 - Reserved
               << 0x00                          // Byte 12 - Number of Grid Line Buffers supported: None
               << 0x00                          // Byte 13 - Type of Grid Line Support: No Support
               << 0x00                          // Byte 14 - Reserved
               << 0x00                          // Byte 15 - Number of Fax or Images: No Support
               << 0x00                          // Byte 16 - Image/Fax Scaling Granularity: No Support
               << 0x00                          // Byte 17 - Image/Fax Rotating Granularity: No Support
               << 0x00                          // Byte 18 - 5250 Image/Fax Support: No Support
               << 0x00 << 0x00 << 0x00;         // Bytes 19-21 - Reserved

        emit sendData(stream.toByteArray());
    }
}

} // namespace q5250
<commit_msg>Add more debug output to find problem with SF order<commit_after>/*
 * Copyright (c) 2013-2014, Christian Loose
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "terminalemulator.h"

#include <QDebug>
#include <QEvent>
#include <QTextCodec>

#include "displaybuffer.h"
#include "field.h"
#include "formattable.h"
#include "generaldatastream.h"
#include "terminaldisplay.h"

namespace q5250 {

TerminalEmulator::TerminalEmulator(QObject *parent) :
    QObject(parent)
{
    codec = QTextCodec::codecForName("IBM500");
}

void TerminalEmulator::setDisplayBuffer(DisplayBuffer *buffer)
{
    displayBuffer = buffer;
}

void TerminalEmulator::setFormatTable(FormatTable *table)
{
    formatTable = table;
}

void TerminalEmulator::setTerminalDisplay(TerminalDisplay *display)
{
    terminalDisplay = display;
}

Cursor TerminalEmulator::cursorPosition() const
{
    return cursor;
}

void TerminalEmulator::parseStreamData(const QByteArray &data)
{
    GeneralDataStream stream(data);

    while (!stream.atEnd()) {
        unsigned char byte = stream.readByte();

        if (byte == 0x04 /*ESC*/) {
            byte = stream.readByte();

            switch (byte) {
            case 0x11 /*WRITE TO DISPLAY*/:
                handleWriteToDisplayCommand(stream);
                break;
            case 0x40 /*CLEAR UNIT*/:
                handleClearUnitCommand();
                break;
            case 0xf3 /*WRITE STRUCTURED FIELD*/:
                handleWriteStructuredFieldCommand(stream);
                break;
            }
        }
    }
}

void TerminalEmulator::handleKeypress(int key, const QString &text)
{
    switch (key) {
    case Qt::Key_Up:
        cursor.moveUp();
        break;
    case Qt::Key_Down:
        cursor.moveDown();
        break;
    case Qt::Key_Left:
        cursor.moveLeft();
        break;
    case Qt::Key_Right:
        cursor.moveRight();
        break;
    case Qt::Key_Return:
        {
            GeneralDataStream stream;

            stream << cursor.row() << cursor.column() << 0xf1 /*AID*/;

            formatTable->map([&](Field* field) {
                stream << 0x11
                       << field->startRow
                       << field->startColumn;

                for (int i = 0; i < field->content.size(); ++i) {
                    stream << field->content.at(i);
                }
            });

            emit sendData(stream.toByteArray());
        }
        break;
    default:
        if (!text.isEmpty()) {
            Field *currentField = formatTable->fieldAt(cursor, displayBuffer->size().width());

            if (currentField && !currentField->isBypassField()) {
                QByteArray ebcdic = codec->fromUnicode(text);
                displayBuffer->setCharacterAt(cursor.column(), cursor.row(), ebcdic.at(0));
                currentField->setContent(cursor, displayBuffer->size().width(), ebcdic);
                cursor.moveRight();
            }
        }
        break;
    }
}

void TerminalEmulator::dataReceived(const QByteArray &data)
{
    parseStreamData(data);
    update();
}

void TerminalEmulator::update()
{
    QByteArray text;

    unsigned char startColumn = 0;
    unsigned char startRow = 0;

    int bufferWidth = displayBuffer->size().width();
    int bufferHeight = displayBuffer->size().height();

    terminalDisplay->clear();

    for (int row = 0; row < bufferHeight; ++row) {
        for (int column = 0; column < bufferWidth; ++column) {
            unsigned char character = displayBuffer->characterAt(column+1, row+1);
            if (character >= 0x20 && character <= 0x3f) {
                if (text.length() > 0) {
                    terminalDisplay->displayText(startColumn, startRow, codec->toUnicode(text));
                    text.clear();
                }
                terminalDisplay->displayAttribute(character);
            } else {
                if (text.isEmpty()) {
                    startColumn = column+1;
                    startRow = row+1;
                }
                text += (character == '\0' ? '\x40': character);
            }
        }

        if (text.length() > 0) {
            terminalDisplay->displayText(startColumn, startRow, codec->toUnicode(text));
            text.clear();
        }
    }

    terminalDisplay->displayCursor(cursor.column(), cursor.row());

    emit updateFinished();
}

void TerminalEmulator::keyPressed(int key, const QString &text)
{
    handleKeypress(key, text);
    update();
}

void TerminalEmulator::handleClearUnitCommand()
{
    displayBuffer->setSize(80, 25);
    formatTable->clear();
    cursor.setPosition(1, 1);
}

void TerminalEmulator::handleWriteToDisplayCommand(GeneralDataStream &stream)
{
    unsigned char cc1 = stream.readByte();
    unsigned char cc2 = stream.readByte();

    qDebug() << "[WTD] cc1 =" << bin << showbase << cc1
             << "cc2 =" << bin << showbase << cc2;

    while (!stream.atEnd()) {
        unsigned char byte = stream.readByte();

        char ch = (char)byte;
        qDebug() << "[WTD] order/data =" << codec->toUnicode(&ch, 1) << hex << showbase << byte;

        switch (byte) {
        case 0x01 /*START OF HEADER*/:
            {
                unsigned dataLength = stream.readByte();
                formatTable->clear();
            }
            break;
        case 0x02 /*REPEAT TO ADDRESS*/:
            {
                unsigned char row = stream.readByte();
                unsigned char column = stream.readByte();
                unsigned char character = stream.readByte();
                displayBuffer->repeatCharacterToAddress(column, row, character);
            }
            break;
        case 0x04 /*ESC*/:
            stream.seekToPreviousByte();
            return;
        case 0x11 /*SET BUFFER ADDRESS*/:
            {
                unsigned char row = stream.readByte();
                unsigned char column = stream.readByte();
                displayBuffer->setBufferAddress(column, row);
                qDebug() << "[WTD:SBA] row =" << row << "column =" << column;
            }
            break;
        case 0x1d /*START OF FIELD*/:
            {
                Field *field = new Field();

                unsigned char byte = stream.readByte();
                if (byte & 0x40 /*is input field?*/) {
                    unsigned char ffw2 = stream.readByte();
                    field->format = (byte << 8) | ffw2;
                    field->attribute = stream.readByte();
                } else {
                    field->attribute = byte;
                }

                field->setLength(stream.readWord());

                displayBuffer->addField(field);

                if (field->isInputField()) {
                    formatTable->append(field);
                }
                qDebug() << "[WTD:SF ] format =" << hex << showbase << field->format
                         << "attribute =" << hex << showbase << field->attribute
                         << "length =" << dec << field->length;
            }
            break;
        default:
            displayBuffer->setCharacter(byte);
            break;
        }
    }
}

void TerminalEmulator::handleWriteStructuredFieldCommand(GeneralDataStream &stream)
{
    unsigned short length = stream.readWord();
    unsigned char commandClass = stream.readByte();
    unsigned char commandType = stream.readByte();
    unsigned char flags = stream.readByte();

    qDebug() << "[WSF] len =" << length
             << "class =" << hex << showbase << commandClass
             << "type =" << hex << showbase << commandType
             << "flags =" << bin << showbase << flags;

    // 5250 QUERY command
    if (commandClass == 0xd9 && commandType == 0x70) {
        GeneralDataStream stream;

        // [ROW] [COLUMN] [AID] [Structured Field]
        stream << 0x00                          // cursor row
               << 0x00                          // cursor column
               << 0x88;                         // 5250 QUERY reply

        // [LL] [C] [T] [F1] [Data Field]
        stream << 0x00 << 0x44                  // Total length of structured field
               << 0xd9                          // Command Class
               << 0x70                          // Command Type: 5250 QUERY
               << 0x80;                         // Fixed flag byte for QUERY response

        stream << 0x06 << 0x00                  // Workstation Control Unit
               << 0x01 << 0x01 << 0x00          // Code Level
               << 0x00 << 0x00 << 0x00 << 0x00  // Reserved (16 bytes)
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x00 << 0x00 << 0x00 << 0x00
               << 0x01;                         // Workstation Type: Display

        QByteArray machineType = codec->fromUnicode(QStringLiteral("5251"));
        for (int i = 0; i < machineType.size(); ++i) {
            stream << machineType.at(i);
        }

        QByteArray modelNumber = codec->fromUnicode(QStringLiteral("011"));
        for (int i = 0; i < modelNumber.size(); ++i) {
            stream << modelNumber.at(i);
        }

        stream << 0x02                          // Keyboard ID: Standard
               << 0x00                          // Extended Keyboard ID
               << 0x00                          // Reserved
               << 0x00 << 0x00 << 0x00 << 0x00  // Serial Number: None
               << 0x01 << 0x00                  // Maximum Number of Input Fields: 256
               << 0x00                          // Control Unit Customization
                                                //  Bit 7   : host can send a 5250 WSC CUSTOMIZATION command
                                                //  Bit 6   : host can send a 5250 QUERY STATION STATE command
                                                //  Bit 5   : host can send a 5250 WORKSTATION CUSTOMIZATION
                                                //            command to select the SBA code returned in READ
                                                //            commands for displays with ideographic extended
                                                //            attributes
                                                //  Bit 4   : 5250 WORKSTATION CUSTOMIZATION command may
                                                //           be 6 bytes or greater than 8 bytes in length
                                                //  Bits 3-0: Reserved
               << 0x00 << 0x00                  // Reserved (2 bytes)
               // Device Capabilities (22 bytes)
               << 0x23                          // Byte 0 - Operating Capabilities:  0b00100011
                                                //  Bits 7-6: Row 1/Column 1 support (00=No, 01=Limited)
                                                //  Bit 5   : READ MDT ALTERNATE command is supported
                                                //  Bit 4   : Workstation has PA1 and PA2 support
                                                //  Bit 3   : Workstation has PA3 support
                                                //  Bit 2   : Workstation has cursor select support
                                                //  Bit 1   : Move Cursor order, Transparent Data order and
                                                //            Transparent entry field FCW support
                                                //  Bit 0   : READ MODIFIED IMMEDIATE ALTERNATE command
                                                //            is supported
               << 0x31                          // Byte 1 - Display Screen Capabilities: 0b00110001
                                                //  Bits 7-6: Reserved
                                                //  Bit 5   : 27 x 132 screen size is supported
                                                //  Bit 4   : 24 x 80 screen size is supported
                                                //  Bit 3   : SLP is supported
                                                //  Bit 2   : MSR is supported
                                                //  Bits 1-0: color support (00=Monochrome, 01=Color)
               << 0x00                          // Byte 2
               << 0x00                          // Byte 3
               << 0x00                          // Byte 4
               << 0x00                          // Byte 5
               << 0x00                          // Byte 6 - Reserved
               << 0x00                          // Byte 7 - 5250 Image/Fax Support
               << 0x00                          // Byte 8 - 5250 Image/Fax Support
               << 0x00                          // Byte 9
               << 0x00                          // Byte 10
               << 0x00                          // Byte 11 - Reserved
               << 0x00                          // Byte 12 - Number of Grid Line Buffers supported: None
               << 0x00                          // Byte 13 - Type of Grid Line Support: No Support
               << 0x00                          // Byte 14 - Reserved
               << 0x00                          // Byte 15 - Number of Fax or Images: No Support
               << 0x00                          // Byte 16 - Image/Fax Scaling Granularity: No Support
               << 0x00                          // Byte 17 - Image/Fax Rotating Granularity: No Support
               << 0x00                          // Byte 18 - 5250 Image/Fax Support: No Support
               << 0x00 << 0x00 << 0x00;         // Bytes 19-21 - Reserved

        emit sendData(stream.toByteArray());
    }
}

} // namespace q5250
<|endoftext|>


<commit_before>
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE davidson_test

#include <boost/test/unit_test.hpp>
#include <iostream>

#include <votca/xtp/bseoperator_btda.h>
#include <votca/xtp/davidsonsolver.h>
#include <votca/xtp/eigen.h>
#include <votca/xtp/matrixfreeoperator.h>

using namespace votca::xtp;
using namespace std;

Eigen::MatrixXd symm_matrix(int N, double eps) {
  Eigen::MatrixXd matrix;
  matrix = eps * Eigen::MatrixXd::Random(N, N);
  Eigen::MatrixXd tmat = matrix.transpose();
  matrix = matrix + tmat;
  return matrix;
}

Eigen::MatrixXd init_matrix(int N, double eps) {
  Eigen::MatrixXd matrix = Eigen::MatrixXd::Zero(N, N);
  for (int i = 0; i < N; i++) {
    for (int j = i; j < N; j++)
      if (i == j) {
        matrix(i, i) = std::sqrt(static_cast<double>(1 + i));
      } else {
        matrix(i, j) = eps / std::pow(static_cast<double>(j - i), 2);
        matrix(j, i) = eps / std::pow(static_cast<double>(j - i), 2);
      }
  }
  return matrix;
}

BOOST_AUTO_TEST_SUITE(davidson_test)

BOOST_AUTO_TEST_CASE(davidson_full_matrix) {

  int size = 100;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);
  Logger log;
  DavidsonSolver DS(log);
  DS.set_ortho("QR");
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
}

BOOST_AUTO_TEST_CASE(davidson_full_matrix_large) {

  int size = 400;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);
  Logger log;
  DavidsonSolver DS(log);
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
}

BOOST_AUTO_TEST_CASE(davidson_full_matrix_fail) {

  int size = 100;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_iter_max(1);
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 0);
}

class TestOperator : public MatrixFreeOperator {
 public:
  TestOperator(){};
  Eigen::RowVectorXd OperatorRow(int index) const;

 private:
};

//  get a col of the operator
Eigen::RowVectorXd TestOperator::OperatorRow(int index) const {
  int lsize = this->size();
  Eigen::RowVectorXd row_out = Eigen::RowVectorXd::Zero(lsize);
  for (int j = 0; j < lsize; j++) {
    if (j == index) {
      row_out(j) = std::sqrt(static_cast<double>(index + 1));
    } else {
      row_out(j) = 0.01 / std::pow(static_cast<double>(j - index), 2);
    }
  }
  return row_out;
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free) {

  int size = 100;
  int neigen = 10;

  // Create Operator
  TestOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");
  DS.set_ortho("QR");
  DS.solve(Aop, neigen);

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_large) {

  int size = 400;
  int neigen = 10;

  // Create Operator
  TestOperator Aop;
  Aop.set_size(size);

  Logger log;
  log.setReportLevel(logDEBUG);
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");
  DS.solve(Aop, neigen);
  std::cout << log << std::endl;
  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

class BlockOperator : public MatrixFreeOperator {
 public:
  BlockOperator(){};
  Eigen::MatrixXd OperatorBlock(int row, int col) const override;

  bool useRow() const override { return false; }
  bool useBlock() const override { return true; }
  int getBlocksize() const override { return size() / 10; }

 private:
};

//  get a block of the operator
Eigen::MatrixXd BlockOperator::OperatorBlock(int row, int col) const {
  int blocksize = getBlocksize();
  Eigen::MatrixXd block = Eigen::MatrixXd::Zero(blocksize, blocksize);
  int blocdisttodiagonal = std::abs(row - col) * blocksize;
  for (int i_col = 0; i_col < blocksize; i_col++) {
    for (int i_row = 0; i_row < blocksize; i_row++) {
      block(i_row, i_col) =
          0.01 / std::pow(static_cast<double>(std::abs(i_row - i_col) +
                                              blocdisttodiagonal),
                          2);
    }
  }
  if (blocdisttodiagonal == 0) {
    for (int i = 0; i < blocksize; i++) {
      block(i, i) = std::sqrt(static_cast<double>(row * blocksize + i + 1));
    }
  }

  return block;
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_block) {

  int size = 100;
  int neigen = 10;

  // Create Operator
  BlockOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_ortho("QR");
  DS.set_size_update("safe");

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);
  DS.solve(Aop, neigen);
  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_block_large) {

  int size = 400;
  int neigen = 10;

  // Create Operator
  BlockOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);
  DS.solve(Aop, neigen);
  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

Eigen::ArrayXi index_eval(Eigen::VectorXd ev, int neigen) {

  int nev = ev.rows();
  int npos = nev / 2;

  Eigen::ArrayXi idx = Eigen::ArrayXi::Zero(npos);
  int nstored = 0;

  // get only positives
  for (int i = 0; i < nev; i++) {
    if (ev(i) > 0) {
      idx(nstored) = i;
      nstored++;
    }
  }

  // sort the epos eigenvalues
  std::sort(idx.data(), idx.data() + idx.size(),
            [&](int i1, int i2) { return ev[i1] < ev[i2]; });
  return idx.head(neigen);
}

Eigen::MatrixXd extract_eigenvectors(const Eigen::MatrixXd &V,
                                     const Eigen::ArrayXi &idx) {
  Eigen::MatrixXd W = Eigen::MatrixXd::Zero(V.rows(), idx.size());
  for (int i = 0; i < idx.size(); i++) {
    W.col(i) = V.col(idx(i));
  }
  return W;
}

class HermitianBlockOperator : public MatrixFreeOperator {
 public:
  HermitianBlockOperator(){};

  void attach_matrix(const Eigen::MatrixXd &mat);
  Eigen::RowVectorXd OperatorRow(int index) const;
  void set_diag(int diag);
  Eigen::VectorXd diag_el;

 private:
  Eigen::MatrixXd _mat;
  int _diag;
};

void HermitianBlockOperator::attach_matrix(const Eigen::MatrixXd &mat) {
  _mat = mat;
}

//  get a col of the operator
Eigen::RowVectorXd HermitianBlockOperator::OperatorRow(int index) const {
  return _mat.row(index);
}

BOOST_AUTO_TEST_CASE(davidson_hamiltonian_matrix_free) {

  int size = 60;
  int neigen = 5;
  Logger log;

  // Create Operator
  HermitianBlockOperator Rop;
  Rop.set_size(size);
  Eigen::MatrixXd rmat = init_matrix(size, 0.01);
  Rop.attach_matrix(rmat);

  HermitianBlockOperator Cop;
  Cop.set_size(size);
  Eigen::MatrixXd cmat = symm_matrix(size, 0.01);
  Cop.attach_matrix(cmat);

  // create Hamiltonian operator
  HamiltonianOperator<HermitianBlockOperator, HermitianBlockOperator> Hop(Rop,
                                                                          Cop);

  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("max");
  DS.set_ortho("QR");
  DS.set_matrix_type("HAM");
  DS.solve(Hop, neigen);

  auto lambda = DS.eigenvalues().real();
  std::sort(lambda.data(), lambda.data() + lambda.size());
  Eigen::MatrixXd H = Hop.get_full_matrix();

  Eigen::EigenSolver<Eigen::MatrixXd> es(H);
  Eigen::ArrayXi idx = index_eval(es.eigenvalues().real(), neigen);
  Eigen::VectorXd lambda_ref = idx.unaryExpr(es.eigenvalues().real());

  bool check_eigenvalues = lambda.isApprox(lambda_ref.head(neigen), 1E-6);
  if (!check_eigenvalues) {
    cout << "Davidson not converged after " << DS.num_iterations()
         << " iterations" << endl;
    cout << "Reference eigenvalues" << endl;
    cout << lambda_ref.head(neigen) << endl;
    cout << "Davidson eigenvalues" << endl;
    cout << lambda << endl;
    cout << "Residue norms" << endl;
    cout << DS.residues() << endl;
  }
  BOOST_CHECK_EQUAL(check_eigenvalues, 1);

  Eigen::MatrixXd evect_dav = DS.eigenvectors().real();
  Eigen::MatrixXd evect = es.eigenvectors().real();
  Eigen::MatrixXd evect_ref = extract_eigenvectors(evect, idx);

  bool check_eigenvectors =
      evect_ref.cwiseAbs2().isApprox(evect_dav.cwiseAbs2(), 0.001);
  BOOST_CHECK_EQUAL(check_eigenvectors, 1);
}

BOOST_AUTO_TEST_SUITE_END()<commit_msg>removed int _diag<commit_after>
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE davidson_test

#include <boost/test/unit_test.hpp>
#include <iostream>

#include <votca/xtp/bseoperator_btda.h>
#include <votca/xtp/davidsonsolver.h>
#include <votca/xtp/eigen.h>
#include <votca/xtp/matrixfreeoperator.h>

using namespace votca::xtp;
using namespace std;

Eigen::MatrixXd symm_matrix(int N, double eps) {
  Eigen::MatrixXd matrix;
  matrix = eps * Eigen::MatrixXd::Random(N, N);
  Eigen::MatrixXd tmat = matrix.transpose();
  matrix = matrix + tmat;
  return matrix;
}

Eigen::MatrixXd init_matrix(int N, double eps) {
  Eigen::MatrixXd matrix = Eigen::MatrixXd::Zero(N, N);
  for (int i = 0; i < N; i++) {
    for (int j = i; j < N; j++)
      if (i == j) {
        matrix(i, i) = std::sqrt(static_cast<double>(1 + i));
      } else {
        matrix(i, j) = eps / std::pow(static_cast<double>(j - i), 2);
        matrix(j, i) = eps / std::pow(static_cast<double>(j - i), 2);
      }
  }
  return matrix;
}

BOOST_AUTO_TEST_SUITE(davidson_test)

BOOST_AUTO_TEST_CASE(davidson_full_matrix) {

  int size = 100;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);
  Logger log;
  DavidsonSolver DS(log);
  DS.set_ortho("QR");
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
}

BOOST_AUTO_TEST_CASE(davidson_full_matrix_large) {

  int size = 400;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);
  Logger log;
  DavidsonSolver DS(log);
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
}

BOOST_AUTO_TEST_CASE(davidson_full_matrix_fail) {

  int size = 100;
  int neigen = 10;
  double eps = 0.01;
  Eigen::MatrixXd A = init_matrix(size, eps);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_iter_max(1);
  DS.solve(A, neigen);
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 0);
}

class TestOperator : public MatrixFreeOperator {
 public:
  TestOperator(){};
  Eigen::RowVectorXd OperatorRow(int index) const;

 private:
};

//  get a col of the operator
Eigen::RowVectorXd TestOperator::OperatorRow(int index) const {
  int lsize = this->size();
  Eigen::RowVectorXd row_out = Eigen::RowVectorXd::Zero(lsize);
  for (int j = 0; j < lsize; j++) {
    if (j == index) {
      row_out(j) = std::sqrt(static_cast<double>(index + 1));
    } else {
      row_out(j) = 0.01 / std::pow(static_cast<double>(j - index), 2);
    }
  }
  return row_out;
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free) {

  int size = 100;
  int neigen = 10;

  // Create Operator
  TestOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");
  DS.set_ortho("QR");
  DS.solve(Aop, neigen);

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_large) {

  int size = 400;
  int neigen = 10;

  // Create Operator
  TestOperator Aop;
  Aop.set_size(size);

  Logger log;
  log.setReportLevel(logDEBUG);
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");
  DS.solve(Aop, neigen);
  std::cout << log << std::endl;
  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);

  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

class BlockOperator : public MatrixFreeOperator {
 public:
  BlockOperator(){};
  Eigen::MatrixXd OperatorBlock(int row, int col) const override;

  bool useRow() const override { return false; }
  bool useBlock() const override { return true; }
  int getBlocksize() const override { return size() / 10; }

 private:
};

//  get a block of the operator
Eigen::MatrixXd BlockOperator::OperatorBlock(int row, int col) const {
  int blocksize = getBlocksize();
  Eigen::MatrixXd block = Eigen::MatrixXd::Zero(blocksize, blocksize);
  int blocdisttodiagonal = std::abs(row - col) * blocksize;
  for (int i_col = 0; i_col < blocksize; i_col++) {
    for (int i_row = 0; i_row < blocksize; i_row++) {
      block(i_row, i_col) =
          0.01 / std::pow(static_cast<double>(std::abs(i_row - i_col) +
                                              blocdisttodiagonal),
                          2);
    }
  }
  if (blocdisttodiagonal == 0) {
    for (int i = 0; i < blocksize; i++) {
      block(i, i) = std::sqrt(static_cast<double>(row * blocksize + i + 1));
    }
  }

  return block;
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_block) {

  int size = 100;
  int neigen = 10;

  // Create Operator
  BlockOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_ortho("QR");
  DS.set_size_update("safe");

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);
  DS.solve(Aop, neigen);
  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

BOOST_AUTO_TEST_CASE(davidson_matrix_free_block_large) {

  int size = 400;
  int neigen = 10;

  // Create Operator
  BlockOperator Aop;
  Aop.set_size(size);

  Logger log;
  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("safe");

  Eigen::MatrixXd A = Aop.get_full_matrix();
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(A);
  DS.solve(Aop, neigen);
  auto lambda = DS.eigenvalues();
  auto lambda_ref = es.eigenvalues().head(neigen);
  bool check_eigenvalues = lambda.isApprox(lambda_ref, 1E-6);

  BOOST_CHECK_EQUAL(check_eigenvalues, 1);
  if (!check_eigenvalues) {
    std::cout << "ref" << std::endl;
    std::cout << es.eigenvalues().head(neigen).transpose() << std::endl;
    std::cout << "result" << std::endl;
    std::cout << DS.eigenvalues().transpose() << std::endl;
  }
}

Eigen::ArrayXi index_eval(Eigen::VectorXd ev, int neigen) {

  int nev = ev.rows();
  int npos = nev / 2;

  Eigen::ArrayXi idx = Eigen::ArrayXi::Zero(npos);
  int nstored = 0;

  // get only positives
  for (int i = 0; i < nev; i++) {
    if (ev(i) > 0) {
      idx(nstored) = i;
      nstored++;
    }
  }

  // sort the epos eigenvalues
  std::sort(idx.data(), idx.data() + idx.size(),
            [&](int i1, int i2) { return ev[i1] < ev[i2]; });
  return idx.head(neigen);
}

Eigen::MatrixXd extract_eigenvectors(const Eigen::MatrixXd &V,
                                     const Eigen::ArrayXi &idx) {
  Eigen::MatrixXd W = Eigen::MatrixXd::Zero(V.rows(), idx.size());
  for (int i = 0; i < idx.size(); i++) {
    W.col(i) = V.col(idx(i));
  }
  return W;
}

class HermitianBlockOperator : public MatrixFreeOperator {
 public:
  HermitianBlockOperator(){};

  void attach_matrix(const Eigen::MatrixXd &mat);
  Eigen::RowVectorXd OperatorRow(int index) const;
  void set_diag(int diag);
  Eigen::VectorXd diag_el;

 private:
  Eigen::MatrixXd _mat;
};

void HermitianBlockOperator::attach_matrix(const Eigen::MatrixXd &mat) {
  _mat = mat;
}

//  get a col of the operator
Eigen::RowVectorXd HermitianBlockOperator::OperatorRow(int index) const {
  return _mat.row(index);
}

BOOST_AUTO_TEST_CASE(davidson_hamiltonian_matrix_free) {

  int size = 60;
  int neigen = 5;
  Logger log;

  // Create Operator
  HermitianBlockOperator Rop;
  Rop.set_size(size);
  Eigen::MatrixXd rmat = init_matrix(size, 0.01);
  Rop.attach_matrix(rmat);

  HermitianBlockOperator Cop;
  Cop.set_size(size);
  Eigen::MatrixXd cmat = symm_matrix(size, 0.01);
  Cop.attach_matrix(cmat);

  // create Hamiltonian operator
  HamiltonianOperator<HermitianBlockOperator, HermitianBlockOperator> Hop(Rop,
                                                                          Cop);

  DavidsonSolver DS(log);
  DS.set_tolerance("normal");
  DS.set_size_update("max");
  DS.set_ortho("QR");
  DS.set_matrix_type("HAM");
  DS.solve(Hop, neigen);

  auto lambda = DS.eigenvalues().real();
  std::sort(lambda.data(), lambda.data() + lambda.size());
  Eigen::MatrixXd H = Hop.get_full_matrix();

  Eigen::EigenSolver<Eigen::MatrixXd> es(H);
  Eigen::ArrayXi idx = index_eval(es.eigenvalues().real(), neigen);
  Eigen::VectorXd lambda_ref = idx.unaryExpr(es.eigenvalues().real());

  bool check_eigenvalues = lambda.isApprox(lambda_ref.head(neigen), 1E-6);
  if (!check_eigenvalues) {
    cout << "Davidson not converged after " << DS.num_iterations()
         << " iterations" << endl;
    cout << "Reference eigenvalues" << endl;
    cout << lambda_ref.head(neigen) << endl;
    cout << "Davidson eigenvalues" << endl;
    cout << lambda << endl;
    cout << "Residue norms" << endl;
    cout << DS.residues() << endl;
  }
  BOOST_CHECK_EQUAL(check_eigenvalues, 1);

  Eigen::MatrixXd evect_dav = DS.eigenvectors().real();
  Eigen::MatrixXd evect = es.eigenvectors().real();
  Eigen::MatrixXd evect_ref = extract_eigenvectors(evect, idx);

  bool check_eigenvectors =
      evect_ref.cwiseAbs2().isApprox(evect_dav.cwiseAbs2(), 0.001);
  BOOST_CHECK_EQUAL(check_eigenvectors, 1);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/* Copyright (c) 2017 Benjamin Jack All Rights Reserved. */

#include <catch.hpp>

#include <iostream>
#include <string>
#include <vector>

#include "choices.hpp"
#include "feature.hpp"
#include "polymer.hpp"

namespace Helper {
bool termination_fired = false;
bool transcript_fired = false;
Transcript::Ptr my_transcript;
void ShiftMaskN(Polymer *polymer, int n) {
  for (int i = 0; i < n; i++) {
    polymer->ShiftMask();
  }
}
void MovePolymeraseN(Polymer *polymer, Polymerase::Ptr pol, int n) {
  for (int i = 0; i < n; i++) {
    polymer->Move(pol);
  }
}
void EmitTermination(int index, const std::string &pol_name,
                     const std::string &last_gene) {
  termination_fired = true;
}
void EmitTranscript(const Transcript::Ptr &transcript) {
  transcript_fired = true;
  my_transcript = transcript;
}
}

TEST_CASE("Polymer methods", "[Polymer]") {
  std::vector<std::string> interactions = {"ecolipol", "rnapol"};
  Promoter::Ptr prom;
  Terminator::Ptr term;
  prom = std::make_shared<Promoter>("p1", 5, 15, interactions);
  std::map<std::string, double> efficiency;
  efficiency["ecolipol"] = 0.6;
  efficiency["rnapol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 50, 55, interactions, efficiency);

  std::vector<Element::Ptr> elements;
  elements.push_back(prom);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 10, 100, mask_interactions);

  Polymer polymer = Polymer("test_polymer", 1, 100, elements, mask);
  polymer.termination_signal_.Connect(Helper::EmitTermination);

  auto pol = std::make_shared<Polymerase>("ecolipol", 10, 30);
  auto pol2 = std::make_shared<Polymerase>("rnapol", 10, 30);
  auto pol3 = std::make_shared<Polymerase>("rnapol", 10, 30);

  SECTION("Construction") {
    REQUIRE(polymer.index() == 0);
    REQUIRE(term->IsCovered());
    REQUIRE(!term->WasCovered());
    REQUIRE(!term->WasUncovered());
    REQUIRE(prom->IsCovered());
    REQUIRE(!prom->WasCovered());
    REQUIRE(!prom->WasUncovered());
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(polymer.uncovered("t1") == 0);
  }

  SECTION("Polymerase binding") {
    Random::seed(22);
    // Promoter should be covered an inaccessible
    REQUIRE_THROWS(polymer.Bind(pol, "p1"));
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(polymer.prop_sum() == 30);
  }

  SECTION("Mask shifting") {
    polymer.ShiftMask();
    REQUIRE(prom->IsCovered());
    REQUIRE(term->IsCovered());
    // Shift mask 10 spaces
    Helper::ShiftMaskN(&polymer, 10);
    REQUIRE(!prom->IsCovered());
    REQUIRE(term->IsCovered());
    // Make sure mask can't shift past end of polymer
    Helper::ShiftMaskN(&polymer, 1000);
    REQUIRE(!term->IsCovered());
  }

  SECTION("Test termination") {
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    // Record old propensity
    double old_prop_sum = polymer.prop_sum();
    polymer.Terminate(pol, "gene1");
    // Make sure propensity has changed and termination signal fired
    REQUIRE(polymer.prop_sum() != old_prop_sum);
    REQUIRE(Helper::termination_fired);
  }

  SECTION("Test coverings") {
    polymer.UncoverElement("p1");
    REQUIRE(polymer.uncovered("p1") == 1);
    polymer.CoverElement("p1");
    REQUIRE(polymer.uncovered("p1") == 0);
  }

  SECTION("Moving polymerase") {
    // Shift mask back to expose promoter
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    // Make sure promoter is recorded as covered
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(prom->IsCovered());
    // Move polymerase 10 spaces and re-expose promoter
    Helper::MovePolymeraseN(&polymer, pol, 11);
    REQUIRE(polymer.uncovered("p1") == 1);
    REQUIRE(!prom->IsCovered());
    // Check for collisions between polymerases
    polymer.Bind(pol2, "p1");
    Helper::MovePolymeraseN(&polymer, pol2, 15);
    REQUIRE(pol2->stop() + 1 == pol->start());
    // Remove pol and make sure pol2 can't shift mask'
    polymer.Terminate(pol, "gene1");
    int old_stop = pol2->stop();
    Helper::MovePolymeraseN(&polymer, pol2, 20);
    // Pol should only be able to move 10 spaces
    REQUIRE(pol2->stop() == old_stop + 10);
    // Expose terminator
    REQUIRE(polymer.uncovered("t1") == 0);
    Helper::ShiftMaskN(&polymer, 60);
    REQUIRE(polymer.uncovered("t1") == 1);
    // Test termination, pol should detach as soon as it hits terminator
    Helper::MovePolymeraseN(&polymer, pol2, 25);
    REQUIRE_THROWS(polymer.Move(pol2));

    // Now check for readthrough
    polymer.Bind(pol, "p1");
    Random::seed(55);
    Helper::MovePolymeraseN(&polymer, pol, 42);
    REQUIRE(term->readthrough());
    Helper::MovePolymeraseN(&polymer, pol, 10);
    // Now run polymerase off of the end of the transcript
    Helper::MovePolymeraseN(&polymer, pol, 35);
    REQUIRE_THROWS(polymer.Move(pol));
    // Run polymerase through terminator again, this time it should terminate
    Random::seed(19);
    polymer.Bind(pol, "p1");
    Helper::MovePolymeraseN(&polymer, pol, 36);
    REQUIRE(!term->readthrough());
    REQUIRE_THROWS(polymer.Move(pol));
  }
}

TEST_CASE("Polymer methods with multipromoter", "[Polymer]") {
  std::vector<std::string> interactions = {"ecolipol"};
  Promoter::Ptr prom1, prom2, prom3;
  Terminator::Ptr term;
  prom1 = std::make_shared<Promoter>("p1", 5, 15, interactions);
  prom2 = std::make_shared<Promoter>("p2", 16, 20, interactions);
  prom3 = std::make_shared<Promoter>("p3", 21, 30, interactions);
  std::map<std::string, double> efficiency;
  efficiency["ecolipol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 31, 33, interactions, efficiency);

  std::vector<Element::Ptr> elements;
  elements.push_back(prom1);
  elements.push_back(prom2);
  elements.push_back(prom3);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 100, 100, mask_interactions);

  Polymer polymer = Polymer("test_polymer", 1, 100, elements, mask);
  polymer.termination_signal_.Connect(Helper::EmitTermination);

  auto pol = std::make_shared<Polymerase>("ecolipol", 10, 30);
  auto pol2 = std::make_shared<Polymerase>("rnapol", 10, 30);
  auto pol3 = std::make_shared<Polymerase>("rnapol", 10, 30);

  SECTION("Moving polymerase") {
    polymer.Bind(pol, "p1");
    Helper::MovePolymeraseN(&polymer, pol, 7);
    REQUIRE(prom1->IsCovered());
    REQUIRE(prom2->IsCovered());
    REQUIRE(prom3->IsCovered());

    Helper::MovePolymeraseN(&polymer, pol, 4);
    REQUIRE(!prom1->IsCovered());
    REQUIRE(prom2->IsCovered());
    REQUIRE(prom3->IsCovered());
  }
}

TEST_CASE("Genome methods", "[Polymer]") {
  // Set up transcript template
  Element::VecPtr transcript_template;
  std::vector<std::string> interactions = {"ribosome"};
  std::map<std::string, double> efficiency;
  efficiency["ribosome"] = 1.0;
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs1", 0, 10, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 209, 210, interactions, efficiency));
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs", 215, 230, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 269, 270, interactions, efficiency));
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs", 285, 300, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 599, 600, interactions, efficiency));
  // Set up genome
  interactions = {"ecolipol", "rnapol"};
  Promoter::Ptr prom;
  Terminator::Ptr term;
  prom = std::make_shared<Promoter>("p1", 5, 15, interactions);
  efficiency["ecolipol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 601, 605, interactions, efficiency);
  Element::VecPtr elements;
  elements.push_back(prom);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 10, 700, mask_interactions);
  Genome genome =
      Genome("test_genome", 700, elements, transcript_template, mask);
  genome.termination_signal_.Connect(Helper::EmitTermination);
  genome.transcript_signal_.Connect(Helper::EmitTranscript);

  auto pol = std::make_shared<Polymerase>(Polymerase("ecolipol", 10, 30));

  SECTION("Bind polymerase to genome") {
    REQUIRE(!Helper::transcript_fired);
    Helper::ShiftMaskN(&genome, 20);
    genome.Bind(pol, "p1");
    REQUIRE(Helper::transcript_fired);
    REQUIRE(Helper::my_transcript->stop() == genome.stop());
  }

  SECTION("Polymerase movement and transcript unmasking") {
    Helper::ShiftMaskN(&genome, 20);
    genome.Bind(pol, "p1");
    Helper::MovePolymeraseN(&genome, pol, 500);
    genome.Move(pol);
    REQUIRE(Helper::my_transcript->uncovered("stop") == 2);
  }
}<commit_msg>more make_shared clean up<commit_after>/* Copyright (c) 2017 Benjamin Jack All Rights Reserved. */

#include <catch.hpp>

#include <iostream>
#include <string>
#include <vector>

#include "choices.hpp"
#include "feature.hpp"
#include "polymer.hpp"

namespace Helper {
bool termination_fired = false;
bool transcript_fired = false;
Transcript::Ptr my_transcript;
void ShiftMaskN(Polymer *polymer, int n) {
  for (int i = 0; i < n; i++) {
    polymer->ShiftMask();
  }
}
void MovePolymeraseN(Polymer *polymer, Polymerase::Ptr pol, int n) {
  for (int i = 0; i < n; i++) {
    polymer->Move(pol);
  }
}
void EmitTermination(int index, const std::string &pol_name,
                     const std::string &last_gene) {
  termination_fired = true;
}
void EmitTranscript(const Transcript::Ptr &transcript) {
  transcript_fired = true;
  my_transcript = transcript;
}
}

TEST_CASE("Polymer methods", "[Polymer]") {
  std::vector<std::string> interactions = {"ecolipol", "rnapol"};
  Promoter::Ptr prom;
  Terminator::Ptr term;
  prom = std::make_shared<Promoter>("p1", 5, 15, interactions);
  std::map<std::string, double> efficiency;
  efficiency["ecolipol"] = 0.6;
  efficiency["rnapol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 50, 55, interactions, efficiency);

  std::vector<Element::Ptr> elements;
  elements.push_back(prom);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 10, 100, mask_interactions);

  Polymer polymer = Polymer("test_polymer", 1, 100, elements, mask);
  polymer.termination_signal_.Connect(Helper::EmitTermination);

  auto pol = std::make_shared<Polymerase>("ecolipol", 10, 30);
  auto pol2 = std::make_shared<Polymerase>("rnapol", 10, 30);
  auto pol3 = std::make_shared<Polymerase>("rnapol", 10, 30);

  SECTION("Construction") {
    REQUIRE(polymer.index() == 0);
    REQUIRE(term->IsCovered());
    REQUIRE(!term->WasCovered());
    REQUIRE(!term->WasUncovered());
    REQUIRE(prom->IsCovered());
    REQUIRE(!prom->WasCovered());
    REQUIRE(!prom->WasUncovered());
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(polymer.uncovered("t1") == 0);
  }

  SECTION("Polymerase binding") {
    Random::seed(22);
    // Promoter should be covered an inaccessible
    REQUIRE_THROWS(polymer.Bind(pol, "p1"));
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(polymer.prop_sum() == 30);
  }

  SECTION("Mask shifting") {
    polymer.ShiftMask();
    REQUIRE(prom->IsCovered());
    REQUIRE(term->IsCovered());
    // Shift mask 10 spaces
    Helper::ShiftMaskN(&polymer, 10);
    REQUIRE(!prom->IsCovered());
    REQUIRE(term->IsCovered());
    // Make sure mask can't shift past end of polymer
    Helper::ShiftMaskN(&polymer, 1000);
    REQUIRE(!term->IsCovered());
  }

  SECTION("Test termination") {
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    // Record old propensity
    double old_prop_sum = polymer.prop_sum();
    polymer.Terminate(pol, "gene1");
    // Make sure propensity has changed and termination signal fired
    REQUIRE(polymer.prop_sum() != old_prop_sum);
    REQUIRE(Helper::termination_fired);
  }

  SECTION("Test coverings") {
    polymer.UncoverElement("p1");
    REQUIRE(polymer.uncovered("p1") == 1);
    polymer.CoverElement("p1");
    REQUIRE(polymer.uncovered("p1") == 0);
  }

  SECTION("Moving polymerase") {
    // Shift mask back to expose promoter
    Helper::ShiftMaskN(&polymer, 10);
    polymer.Bind(pol, "p1");
    // Make sure promoter is recorded as covered
    REQUIRE(polymer.uncovered("p1") == 0);
    REQUIRE(prom->IsCovered());
    // Move polymerase 10 spaces and re-expose promoter
    Helper::MovePolymeraseN(&polymer, pol, 11);
    REQUIRE(polymer.uncovered("p1") == 1);
    REQUIRE(!prom->IsCovered());
    // Check for collisions between polymerases
    polymer.Bind(pol2, "p1");
    Helper::MovePolymeraseN(&polymer, pol2, 15);
    REQUIRE(pol2->stop() + 1 == pol->start());
    // Remove pol and make sure pol2 can't shift mask'
    polymer.Terminate(pol, "gene1");
    int old_stop = pol2->stop();
    Helper::MovePolymeraseN(&polymer, pol2, 20);
    // Pol should only be able to move 10 spaces
    REQUIRE(pol2->stop() == old_stop + 10);
    // Expose terminator
    REQUIRE(polymer.uncovered("t1") == 0);
    Helper::ShiftMaskN(&polymer, 60);
    REQUIRE(polymer.uncovered("t1") == 1);
    // Test termination, pol should detach as soon as it hits terminator
    Helper::MovePolymeraseN(&polymer, pol2, 25);
    REQUIRE_THROWS(polymer.Move(pol2));

    // Now check for readthrough
    polymer.Bind(pol, "p1");
    Random::seed(55);
    Helper::MovePolymeraseN(&polymer, pol, 42);
    REQUIRE(term->readthrough());
    Helper::MovePolymeraseN(&polymer, pol, 10);
    // Now run polymerase off of the end of the transcript
    Helper::MovePolymeraseN(&polymer, pol, 35);
    REQUIRE_THROWS(polymer.Move(pol));
    // Run polymerase through terminator again, this time it should terminate
    Random::seed(19);
    polymer.Bind(pol, "p1");
    Helper::MovePolymeraseN(&polymer, pol, 36);
    REQUIRE(!term->readthrough());
    REQUIRE_THROWS(polymer.Move(pol));
  }
}

TEST_CASE("Polymer methods with multipromoter", "[Polymer]") {
  std::vector<std::string> interactions = {"ecolipol"};
  Promoter::Ptr prom1, prom2, prom3;
  Terminator::Ptr term;
  prom1 = std::make_shared<Promoter>("p1", 5, 15, interactions);
  prom2 = std::make_shared<Promoter>("p2", 16, 20, interactions);
  prom3 = std::make_shared<Promoter>("p3", 21, 30, interactions);
  std::map<std::string, double> efficiency;
  efficiency["ecolipol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 31, 33, interactions, efficiency);

  std::vector<Element::Ptr> elements;
  elements.push_back(prom1);
  elements.push_back(prom2);
  elements.push_back(prom3);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 100, 100, mask_interactions);

  Polymer polymer = Polymer("test_polymer", 1, 100, elements, mask);
  polymer.termination_signal_.Connect(Helper::EmitTermination);

  auto pol = std::make_shared<Polymerase>("ecolipol", 10, 30);
  auto pol2 = std::make_shared<Polymerase>("rnapol", 10, 30);
  auto pol3 = std::make_shared<Polymerase>("rnapol", 10, 30);

  SECTION("Moving polymerase") {
    polymer.Bind(pol, "p1");
    Helper::MovePolymeraseN(&polymer, pol, 7);
    REQUIRE(prom1->IsCovered());
    REQUIRE(prom2->IsCovered());
    REQUIRE(prom3->IsCovered());

    Helper::MovePolymeraseN(&polymer, pol, 4);
    REQUIRE(!prom1->IsCovered());
    REQUIRE(prom2->IsCovered());
    REQUIRE(prom3->IsCovered());
  }
}

TEST_CASE("Genome methods", "[Polymer]") {
  // Set up transcript template
  Element::VecPtr transcript_template;
  std::vector<std::string> interactions = {"ribosome"};
  std::map<std::string, double> efficiency;
  efficiency["ribosome"] = 1.0;
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs1", 0, 10, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 209, 210, interactions, efficiency));
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs", 215, 230, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 269, 270, interactions, efficiency));
  transcript_template.push_back(
      std::make_shared<Promoter>("rbs", 285, 300, interactions));
  transcript_template.push_back(
      std::make_shared<Terminator>("stop", 599, 600, interactions, efficiency));
  // Set up genome
  interactions = {"ecolipol", "rnapol"};
  Promoter::Ptr prom;
  Terminator::Ptr term;
  prom = std::make_shared<Promoter>("p1", 5, 15, interactions);
  efficiency["ecolipol"] = 1.0;
  term = std::make_shared<Terminator>("t1", 601, 605, interactions, efficiency);
  Element::VecPtr elements;
  elements.push_back(prom);
  elements.push_back(term);
  std::vector<std::string> mask_interactions = {"ecolipol"};
  Mask mask = Mask("test_mask", 10, 700, mask_interactions);
  Genome genome =
      Genome("test_genome", 700, elements, transcript_template, mask);
  genome.termination_signal_.Connect(Helper::EmitTermination);
  genome.transcript_signal_.Connect(Helper::EmitTranscript);

  auto pol = std::make_shared<Polymerase>("ecolipol", 10, 30);

  SECTION("Bind polymerase to genome") {
    REQUIRE(!Helper::transcript_fired);
    Helper::ShiftMaskN(&genome, 20);
    genome.Bind(pol, "p1");
    REQUIRE(Helper::transcript_fired);
    REQUIRE(Helper::my_transcript->stop() == genome.stop());
  }

  SECTION("Polymerase movement and transcript unmasking") {
    Helper::ShiftMaskN(&genome, 20);
    genome.Bind(pol, "p1");
    Helper::MovePolymeraseN(&genome, pol, 500);
    genome.Move(pol);
    REQUIRE(Helper::my_transcript->uncovered("stop") == 2);
  }
}<|endoftext|>


<commit_before>#include <stdexcept>
#include <iostream>
#include <string>
#include <sstream>
#include <typeinfo>

template <typename T>
class linked_list {
    class Node {
        const T* object;
        Node* previous;
        Node* next;

     public:
        Node(const T& data, Node* tempPrevious = 0, Node* tempNext = 0) : object(new T(data)), previous(tempPrevious), next(tempNext) {}

        ~Node() {
            delete object;
            previous = NULL;
            next = NULL;
        }

        const T& getData() const {
            return *object;
        }

        Node* getNext() const {
            return next;
        }

        void setNext(Node* tempNext) {
            next = tempNext;
        }

        Node* getPrevious() const {
            return previous;
        }

        void setPrevious(Node* tempPrevious) {
            previous = tempPrevious;
        }

        void print(std::ostream& stream) const {
            stream << getData();
        }

        bool operator== (const Node& rhs) const {
            if (object == rhs.getData() && previous == rhs.getPrevious() && next == rhs.getNext()) {
                return true;
            }

            return false;
        }

        bool operator!= (const Node& rhs) const {
            return !(*this == rhs);
        }

        const T& operator*() const {
            return getData();
        }

        const T& operator->() const {
            return getData();
        }
    };

 public:
    class iterator : public std::iterator<std::forward_iterator_tag, Node*> {
        linked_list<T>& list;
        Node* itr;

     public:
        iterator(linked_list<T>& tempList, const Node* temp = NULL) : list(tempList), itr(temp) {}

        iterator(linked_list<T>& tempList, Node* temp = NULL) : list(tempList), itr(temp) {}

        iterator(const iterator& newItr) : list(newItr.list), itr(newItr.itr) {}

        T remove() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            Node* nodeToDelete = itr;
            operator++();
            T val = list.remove(nodeToDelete);
            return val;
        }

        iterator& operator++() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            itr = itr->getNext();
            return *this;
        }

        iterator operator++(int) {
            iterator result(*this);
            ++(*this);
            return result;
        }

        iterator& operator--() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            itr = itr->getPrevious();
            return *this;
        }

        iterator operator--(int) {
            iterator result(*this);
            ++(*this);
            return result;
        }

        iterator& operator=(const Node* newItr) {
            itr = newItr;
            return *this;
        }

        iterator& operator=(Node* newItr) {
            itr = newItr;
            return *this;
        }

        bool operator==(const iterator& rhs) const {
            return itr == rhs.itr;
        }

        bool operator==(const Node* rhs) const {
            return itr == rhs;
        }

        bool operator!=(const iterator& rhs) const {
            return !(*this == rhs);
        }

        bool operator!=(const Node* rhs) const {
            return !(*this == rhs);
        }

        const T& operator*() const {
            return itr->getData();
        }

        const T& operator->() const {
            return itr->getData();
        }
    };

    Node* head;
    Node* tail;
    int internalSize;

    Node* getNode(const int& position) const {
        if (position > (size() - 1)) {
            // Throw error
            std::ostringstream stream;
            stream << "linked_list getNode failed (position = " << position << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* currentNode;
        if (position < size()/2) {
            currentNode = head;
            int i = 0;

            while (i < position) {
                currentNode = currentNode->getNext();
                i++;
            }
        } else {
            currentNode = tail;
            int i = size() - 1;

            while (i > position) {
                currentNode = currentNode->getPrevious();
                i--;
            }
        }

        return currentNode;
    }

    T remove(Node* node) {
        int index = getIndex(node);
        return remove(index);
    }

    const int getIndex(Node* node) {
        int index = 0;
        iterator it = iterator(*this, begin());

        for (; it != NULL; it++) {
            if (it == node) {
                return index;
            }

            index++;
        }

        return -1;
    }

 public:
    linked_list() : head(NULL), tail(NULL), internalSize(0) {}

    ~linked_list() { empty(); }

    bool insert(const int& position, const T& object) {
        if (position > size() || position < 0) {
            // Throw error, out of bounds
            std::ostringstream stream;
            stream << "linked_list insert failed (position = " << position << ", object = " << object << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* newNode = new Node(object);

        if (!size()) {
            head = newNode;
            tail = newNode;
        } else if (!position) {
            newNode->setNext(head);
            head->setPrevious(newNode);
            head = newNode;
        } else if (position  == size()) {
            newNode->setPrevious(tail);
            tail->setNext(newNode);
            tail = newNode;
        } else {
            Node* current = getNode(position);
            Node* previous = current->getPrevious();
            Node* next = current->getNext();
            newNode->setNext(current);
            newNode->setPrevious(previous);
            previous->setNext(newNode);
            current->setPrevious(newNode);
        }

        internalSize++;
        return true;
    }

    bool insert(const int& position, const T* object) {
        return insert(position, *object);
    }

    bool replace(const int& position, const T& data) {
        Node* newNode = new Node(data);
        Node* oldNode = getNode(position);
        Node* previous = oldNode->getPrevious();
        Node* next = oldNode->getNext();
        newNode->setPrevious(previous);
        previous->setNext(newNode);
        newNode->setNext(next);
        next->setPrevious(newNode);
        delete oldNode;
        return true;

    }

    const T& item_at(const int& position) const {
        if (position >= size() || position < 0) {
            // Throw error
            std::cerr << "linked_list item_at failed (position = " << position << ", internalSize = " << size() << ")" << std::endl;
            throw std::out_of_range("Position is not valid inside this linked_list");
        }

        return getNode(position)->getData();
    }

    bool contains(const T& object) {
        iterator it = getIterator(begin());
        for (; it != NULL; it++) {
            if (*it == object) {
                return true;
            }
        }

        return false;
    }

    Node* begin() const {
        return head;
    }

    Node* end() const {
        return tail;
    }

    iterator getIterator(Node* node) {
        return iterator(*this, node);
    }

    bool push_front(const T& object) {
        return insert(0, object);
    }

    bool push_back(const T& object) {
        return insert(size(), object);
    }

    T pop_front() {
        return remove(0);
    }

    T pop_back() {
        return remove(size() - 1);
    }

    T remove(const int& position) {
        if (position >= size() || position < 0) {
            // Throw error, out of bounds
            std::ostringstream stream;
            stream << "linked_list remove failed (position = " << position << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* nodeToDelete = getNode(position);
        T val = nodeToDelete->getData();

        if (position == 0 && size() == 1) {   // Head, Size == 1
            head = NULL;
            tail = NULL;
        } else if (position == 0 && size() > 1) {    // Head, Size > 1
            head = getNode(1);
            head->setPrevious(NULL);
        } else if (position < (size() - 1) && position != 0) {   // Middle
            getNode(position - 1)->setNext(getNode(position + 1));
            getNode(position)->setPrevious(getNode(position - 1));
        } else if (position == (size() - 1)) {   // Tail
            tail = getNode(position - 1);
            tail->setNext(NULL);
        }

        internalSize--;
        delete nodeToDelete;
        return val;
    }

    const int size() const {
        return internalSize;
    }

    void empty() {
        while (size()) {
            pop_front();
        }
    }

    bool isEmpty() {
        return !size();
    }
};

template <typename T>
std::ostream& operator<< (std::ostream& os, linked_list<T>& list) {
    std::stringstream ss;
    std::stringstream newSS;
    typename linked_list<T>::iterator it = list.getIterator(list.begin());
    ss << "[";
    for (; it != NULL; it++) {
        ss << *it << ", ";
    }

    std::string str = ss.str();
    str = str.substr(0, str.size() - 2);
    str += "]";
    newSS << str;
    os << newSS.str();
    return os;
}
<commit_msg>One more bug...<commit_after>#include <stdexcept>
#include <iostream>
#include <string>
#include <sstream>
#include <typeinfo>

template <typename T>
class linked_list {
    class Node {
        const T* object;
        Node* previous;
        Node* next;

     public:
        Node(const T& data, Node* tempPrevious = 0, Node* tempNext = 0) : object(new T(data)), previous(tempPrevious), next(tempNext) {}

        ~Node() {
            delete object;
            previous = NULL;
            next = NULL;
        }

        const T& getData() const {
            return *object;
        }

        Node* getNext() const {
            return next;
        }

        void setNext(Node* tempNext) {
            next = tempNext;
        }

        Node* getPrevious() const {
            return previous;
        }

        void setPrevious(Node* tempPrevious) {
            previous = tempPrevious;
        }

        void print(std::ostream& stream) const {
            stream << getData();
        }

        bool operator== (const Node& rhs) const {
            if (object == rhs.getData() && previous == rhs.getPrevious() && next == rhs.getNext()) {
                return true;
            }

            return false;
        }

        bool operator!= (const Node& rhs) const {
            return !(*this == rhs);
        }

        const T& operator*() const {
            return getData();
        }

        const T& operator->() const {
            return getData();
        }
    };

 public:
    class iterator : public std::iterator<std::forward_iterator_tag, Node*> {
        linked_list<T>& list;
        Node* itr;

     public:
        iterator(linked_list<T>& tempList, const Node* temp = NULL) : list(tempList), itr(temp) {}

        iterator(linked_list<T>& tempList, Node* temp = NULL) : list(tempList), itr(temp) {}

        iterator(const iterator& newItr) : list(newItr.list), itr(newItr.itr) {}

        T remove() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            Node* nodeToDelete = itr;
            operator++();
            T val = list.remove(nodeToDelete);
            return val;
        }

        iterator& operator++() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            itr = itr->getNext();
            return *this;
        }

        iterator operator++(int) {
            iterator result(*this);
            ++(*this);
            return result;
        }

        iterator& operator--() {
            if (itr == NULL) { throw std::invalid_argument("Iterator refers to a NULL pointer");}

            itr = itr->getPrevious();
            return *this;
        }

        iterator operator--(int) {
            iterator result(*this);
            ++(*this);
            return result;
        }

        iterator& operator=(const Node* newItr) {
            itr = newItr;
            return *this;
        }

        iterator& operator=(Node* newItr) {
            itr = newItr;
            return *this;
        }

        bool operator==(const iterator& rhs) const {
            return itr == rhs.itr;
        }

        bool operator==(const Node* rhs) const {
            return itr == rhs;
        }

        bool operator!=(const iterator& rhs) const {
            return !(*this == rhs);
        }

        bool operator!=(const Node* rhs) const {
            return !(*this == rhs);
        }

        const T& operator*() const {
            return itr->getData();
        }

        const T& operator->() const {
            return itr->getData();
        }
    };

    Node* head;
    Node* tail;
    int internalSize;

    Node* getNode(const int& position) const {
        if (position > (size() - 1)) {
            // Throw error
            std::ostringstream stream;
            stream << "linked_list getNode failed (position = " << position << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* currentNode;
        if (position < size()/2) {
            currentNode = head;
            int i = 0;

            while (i < position) {
                currentNode = currentNode->getNext();
                i++;
            }
        } else {
            currentNode = tail;
            int i = size() - 1;

            while (i > position) {
                currentNode = currentNode->getPrevious();
                i--;
            }
        }

        return currentNode;
    }

    T remove(Node* node) {
        int index = getIndex(node);
        return remove(index);
    }

    const int getIndex(Node* node) {
        int index = 0;
        iterator it = iterator(*this, begin());

        for (; it != NULL; it++) {
            if (it == node) {
                return index;
            }

            index++;
        }

        return -1;
    }

 public:
    linked_list() : head(NULL), tail(NULL), internalSize(0) {}

    ~linked_list() { empty(); }

    bool insert(const int& position, const T& object) {
        if (position > size() || position < 0) {
            // Throw error, out of bounds
            std::ostringstream stream;
            stream << "linked_list insert failed (position = " << position << ", object = " << object << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* newNode = new Node(object);

        if (!size()) {
            head = newNode;
            tail = newNode;
        } else if (!position) {
            newNode->setNext(head);
            head->setPrevious(newNode);
            head = newNode;
        } else if (position  == size()) {
            newNode->setPrevious(tail);
            tail->setNext(newNode);
            tail = newNode;
        } else {
            Node* current = getNode(position);
            Node* previous = current->getPrevious();
            newNode->setNext(current);
            newNode->setPrevious(previous);
            previous->setNext(newNode);
            current->setPrevious(newNode);
        }

        internalSize++;
        return true;
    }

    bool insert(const int& position, const T* object) {
        return insert(position, *object);
    }

    bool replace(const int& position, const T& data) {
        Node* newNode = new Node(data);
        Node* oldNode = getNode(position);
        Node* previous = oldNode->getPrevious();
        Node* next = oldNode->getNext();
        newNode->setPrevious(previous);
        previous->setNext(newNode);
        newNode->setNext(next);
        next->setPrevious(newNode);
        delete oldNode;
        return true;

    }

    const T& item_at(const int& position) const {
        if (position >= size() || position < 0) {
            // Throw error
            std::cerr << "linked_list item_at failed (position = " << position << ", internalSize = " << size() << ")" << std::endl;
            throw std::out_of_range("Position is not valid inside this linked_list");
        }

        return getNode(position)->getData();
    }

    bool contains(const T& object) {
        iterator it = getIterator(begin());
        for (; it != NULL; it++) {
            if (*it == object) {
                return true;
            }
        }

        return false;
    }

    Node* begin() const {
        return head;
    }

    Node* end() const {
        return tail;
    }

    iterator getIterator(Node* node) {
        return iterator(*this, node);
    }

    bool push_front(const T& object) {
        return insert(0, object);
    }

    bool push_back(const T& object) {
        return insert(size(), object);
    }

    T pop_front() {
        return remove(0);
    }

    T pop_back() {
        return remove(size() - 1);
    }

    T remove(const int& position) {
        if (position >= size() || position < 0) {
            // Throw error, out of bounds
            std::ostringstream stream;
            stream << "linked_list remove failed (position = " << position << ", internalSize = " << size() << ")";
            std::cerr << stream.str() << std::endl;
            throw std::out_of_range(stream.str());
        }

        Node* nodeToDelete = getNode(position);
        T val = nodeToDelete->getData();

        if (position == 0 && size() == 1) {   // Head, Size == 1
            head = NULL;
            tail = NULL;
        } else if (position == 0 && size() > 1) {    // Head, Size > 1
            head = getNode(1);
            head->setPrevious(NULL);
        } else if (position < (size() - 1) && position != 0) {   // Middle
            getNode(position - 1)->setNext(getNode(position + 1));
            getNode(position)->setPrevious(getNode(position - 1));
        } else if (position == (size() - 1)) {   // Tail
            tail = getNode(position - 1);
            tail->setNext(NULL);
        }

        internalSize--;
        delete nodeToDelete;
        return val;
    }

    const int size() const {
        return internalSize;
    }

    void empty() {
        while (size()) {
            pop_front();
        }
    }

    bool isEmpty() {
        return !size();
    }
};

template <typename T>
std::ostream& operator<< (std::ostream& os, linked_list<T>& list) {
    std::stringstream ss;
    std::stringstream newSS;
    typename linked_list<T>::iterator it = list.getIterator(list.begin());
    ss << "[";
    for (; it != NULL; it++) {
        ss << *it << ", ";
    }

    std::string str = ss.str();
    str = str.substr(0, str.size() - 2);
    str += "]";
    newSS << str;
    os << newSS.str();
    return os;
}
<|endoftext|>