<commit_before>/*
	Reliability and Flow Control Example
	From "Networking for Game Programmers" - http://www.gaffer.org/networking-for-game-programmers
	Author: Glenn Fiedler <gaffer@gaffer.org>
*/

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

#include "Net.h"

//#define SHOW_ACKS

using namespace std;
using namespace net;

const int ServerPort = 30000;
const int ClientPort = 30001;
const int ProtocolId = 0x11223344;
const float DeltaTime = 1.0f / 30.0f;
const float SendRate = 1.0f / 30.0f;
const float TimeOut = 10.0f;
const int PacketSize = 256;

class FlowControl
{
public:

	FlowControl()
	{
		printf( "flow control initialized\n" );
		Reset();
	}

	void Reset()
	{
		mode = Bad;
		penalty_time = 4.0f;
		good_conditions_time = 0.0f;
		penalty_reduction_accumulator = 0.0f;
	}

	void Update( float deltaTime, float rtt )
	{
		const float RTT_Threshold = 250.0f;

		if ( mode == Good )
		{
			if ( rtt > RTT_Threshold )
			{
				printf( "*** dropping to bad mode ***\n" );
				mode = Bad;
				if ( good_conditions_time < 10.0f && penalty_time < 60.0f )
				{
					penalty_time *= 2.0f;
					if ( penalty_time > 60.0f )
						penalty_time = 60.0f;
					printf( "penalty time increased to %.1f\n", penalty_time );
				}
				good_conditions_time = 0.0f;
				penalty_reduction_accumulator = 0.0f;
				return;
			}

			good_conditions_time += deltaTime;
			penalty_reduction_accumulator += deltaTime;

			if ( penalty_reduction_accumulator > 10.0f && penalty_time > 1.0f )
			{
				penalty_time /= 2.0f;
				if ( penalty_time < 1.0f )
					penalty_time = 1.0f;
				printf( "penalty time reduced to %.1f\n", penalty_time );
				penalty_reduction_accumulator = 0.0f;
			}
		}

		if ( mode == Bad )
		{
			if ( rtt <= RTT_Threshold )
				good_conditions_time += deltaTime;
			else
				good_conditions_time = 0.0f;

			if ( good_conditions_time > penalty_time )
			{
				printf( "*** upgrading to good mode ***\n" );
				good_conditions_time = 0.0f;
				penalty_reduction_accumulator = 0.0f;
				mode = Good;
				return;
			}
		}
	}

	float GetSendRate()
	{
		return mode == Good ? 30.0f : 10.0f;
	}

private:

	enum Mode
	{
		Good,
		Bad
	};

	Mode mode;
	float penalty_time;
	float good_conditions_time;
	float penalty_reduction_accumulator;
};

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

int main( int argc, char * argv[] )
{
	// parse command line

	enum Mode
	{
		Client,
		Server
	};

	Mode mode = Server;
	Address address;

	if ( argc >= 2 )
	{
		int a,b,c,d;
		if ( sscanf( argv[1], "%d.%d.%d.%d", &a, &b, &c, &d ) )
		{
			mode = Client;
			address = Address(a,b,c,d,ServerPort);
		}
	}

	// initialize

	if ( !InitializeSockets() )
	{
		printf( "failed to initialize sockets\n" );
		return 1;
	}

	ReliableConnection connection( ProtocolId, TimeOut );

	const int port = mode == Server ? ServerPort : ClientPort;

	if ( !connection.Start( port ) )
	{
		printf( "could not start connection on port %d\n", port );
		return 1;
	}

	if ( mode == Client )
		connection.Connect( address );
	else
		connection.Listen();

	bool connected = false;
	float sendAccumulator = 0.0f;
	float statsAccumulator = 0.0f;

	FlowControl flowControl;

	while ( true )
	{
		// update flow control

		if ( connection.IsConnected() )
			flowControl.Update( DeltaTime, connection.GetReliabilitySystem().GetRoundTripTime() * 1000.0f );

		const float sendRate = flowControl.GetSendRate();

		// detect changes in connection state

		if ( mode == Server && connected && !connection.IsConnected() )
		{
			flowControl.Reset();
			printf( "reset flow control\n" );
			connected = false;
		}

		if ( !connected && connection.IsConnected() )
		{
			printf( "client connected to server\n" );
			connected = true;
		}

		if ( !connected && connection.ConnectFailed() )
		{
			printf( "connection failed\n" );
			break;
		}

		// send and receive packets

		sendAccumulator += DeltaTime;

		while ( sendAccumulator > 1.0f / sendRate )
		{
			unsigned char packet[PacketSize];
			memset( packet, 0, sizeof( packet ) );
			connection.SendPacket( packet, sizeof( packet ) );
			sendAccumulator -= 1.0f / sendRate;
		}

		while ( true )
		{
			unsigned char packet[256];
			int bytes_read = connection.ReceivePacket( packet, sizeof(packet) );
			if ( bytes_read == 0 )
				break;
		}

		// show packets that were acked this frame

		#ifdef SHOW_ACKS
		unsigned int * acks = NULL;
		int ack_count = 0;
		connection.GetReliabilitySystem().GetAcks( &acks, ack_count );
		if ( ack_count > 0 )
		{
			printf( "acks: %d", acks[0] );
			for ( int i = 1; i < ack_count; ++i )
				printf( ",%d", acks[i] );
			printf( "\n" );
		}
		#endif

		// update connection

		connection.Update( DeltaTime );

		// show connection stats

		statsAccumulator += DeltaTime;

		while ( statsAccumulator >= 0.25f && connection.IsConnected() )
		{
			float rtt = connection.GetReliabilitySystem().GetRoundTripTime();

			unsigned int sent_packets = connection.GetReliabilitySystem().GetSentPackets();
			unsigned int acked_packets = connection.GetReliabilitySystem().GetAckedPackets();
			unsigned int lost_packets = connection.GetReliabilitySystem().GetLostPackets();

			float sent_bandwidth = connection.GetReliabilitySystem().GetSentBandwidth();
			float acked_bandwidth = connection.GetReliabilitySystem().GetAckedBandwidth();

			printf( "rtt %.1fms, sent %d, acked %d, lost %d (%.1f%%), sent bandwidth = %.1fkbps, acked bandwidth = %.1fkbps\n",
				rtt * 1000.0f, sent_packets, acked_packets, lost_packets,
				sent_packets > 0.0f ? (float) lost_packets / (float) sent_packets * 100.0f : 0.0f,
				sent_bandwidth, acked_bandwidth );

			statsAccumulator -= 0.25f;
		}

		wait( DeltaTime );
	}

	ShutdownSockets();

	return 0;
}
<commit_msg>error fix<commit_after>/*
	Reliability and Flow Control Example
	From "Networking for Game Programmers" - http://www.gaffer.org/networking-for-game-programmers
	Author: Glenn Fiedler <gaffer@gaffer.org>
*/

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

#include "Net.h"

//#define SHOW_ACKS

using namespace std;
using namespace net;

const int ServerPort = 30000;
const int ClientPort = 30001;
const int ProtocolId = 0x11223344;
const float DeltaTime = 1.0f / 30.0f;
const float SendRate = 1.0f / 30.0f;
const float TimeOut = 10.0f;
const int PacketSize = 256;

class FlowControl
{
public:

	FlowControl()
	{
		printf( "flow control initialized\n" );
		Reset();
	}

	void Reset()
	{
		mode = Bad;
		penalty_time = 4.0f;
		good_conditions_time = 0.0f;
		penalty_reduction_accumulator = 0.0f;
	}

	void Update( float deltaTime, float rtt )
	{
		const float RTT_Threshold = 250.0f;

		if ( mode == Good )
		{
			if ( rtt > RTT_Threshold )
			{
				printf( "*** dropping to bad mode ***\n" );
				mode = Bad;
				if ( good_conditions_time < 10.0f && penalty_time < 60.0f )
				{
					penalty_time *= 2.0f;
					if ( penalty_time > 60.0f )
						penalty_time = 60.0f;
					printf( "penalty time increased to %.1f\n", penalty_time );
				}
				good_conditions_time = 0.0f;
				penalty_reduction_accumulator = 0.0f;
				return;
			}

			good_conditions_time += deltaTime;
			penalty_reduction_accumulator += deltaTime;

			if ( penalty_reduction_accumulator > 10.0f && penalty_time > 1.0f )
			{
				penalty_time /= 2.0f;
				if ( penalty_time < 1.0f )
					penalty_time = 1.0f;
				printf( "penalty time reduced to %.1f\n", penalty_time );
				penalty_reduction_accumulator = 0.0f;
			}
		}

		if ( mode == Bad )
		{
			if ( rtt <= RTT_Threshold )
				good_conditions_time += deltaTime;
			else
				good_conditions_time = 0.0f;

			if ( good_conditions_time > penalty_time )
			{
				printf( "*** upgrading to good mode ***\n" );
				good_conditions_time = 0.0f;
				penalty_reduction_accumulator = 0.0f;
				mode = Good;
				return;
			}
		}
	}

	float GetSendRate()
	{
		return mode == Good ? 30.0f : 10.0f;
	}

private:

	enum Mode
	{
		Good,
		Bad
	};

	Mode mode;
	float penalty_time;
	float good_conditions_time;
	float penalty_reduction_accumulator;
};

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

int main( int argc, char * argv[] )
{
	// parse command line

	enum Mode
	{
		Client,
		Server
	};

	Mode mode = Server;
	Address address;

	if ( argc >= 2 )
	{
		int a,b,c,d;
		if ( sscanf( argv[1], "%d.%d.%d.%d", &a, &b, &c, &d ) )
		{
			mode = Client;
			address = Address(a,b,c,d,ServerPort);
		}
	}

	// initialize

	if ( !InitializeSockets() )
	{
		printf( "failed to initialize sockets\n" );
		return 1;
	}

	ReliableConnection connection( ProtocolId, TimeOut );

	const int port = mode == Server ? ServerPort : ClientPort;

	if ( !connection.Start( port ) )
	{
		printf( "could not start connection on port %d\n", port );
		return 1;
	}

	if ( mode == Client )
		connection.Connect( address );
	else
		connection.Listen();

	bool connected = false;
	float sendAccumulator = 0.0f;
	float statsAccumulator = 0.0f;

	FlowControl flowControl;

	while ( true )
	{
		// update flow control

		if ( connection.IsConnected() )
			flowControl.Update( DeltaTime, connection.GetReliabilitySystem().GetRoundTripTime() * 1000.0f );

		const float sendRate = flowControl.GetSendRate();

		// detect changes in connection state

		if ( mode == Server && connected && !connection.IsConnected() )
		{
			flowControl.Reset();
			printf( "reset flow control\n" );
			connected = false;
		}

		if ( !connected && connection.IsConnected() )
		{
			printf( "client connected to server\n" );
			connected = true;
		}

		if ( !connected && connection.ConnectFailed() )
		{
			printf( "connection failed\n" );
			break;
		}

		// send and receive packets

		sendAccumulator += DeltaTime;

		while ( sendAccumulator > 1.0f / sendRate )
		{
			unsigned char packet[PacketSize];
			memset( packet, 0, sizeof( packet ) );
			connection.SendPacket( packet, sizeof( packet ) );
			sendAccumulator -= 1.0f / sendRate;
		}

		while ( true )
		{
			unsigned char packet[256];
			int bytes_read = connection.ReceivePacket( packet, sizeof(packet) );
			if ( bytes_read == 0 )
				break;
		}

		// show packets that were acked this frame

		#ifdef SHOW_ACKS
		unsigned int * acks = NULL;
		int ack_count = 0;
		connection.GetReliabilitySystem().GetAcks( &acks, ack_count );
		if ( ack_count > 0 )
		{
			printf( "acks: %d", acks[0] );
			for ( int i = 1; i < ack_count; ++i )
				printf( ",%d", acks[i] );
			printf( "\n" );
		}
		#endif

		// update connection

		connection.Update( DeltaTime );

		// show connection stats

		statsAccumulator += DeltaTime;

		while ( statsAccumulator >= 0.25f && connection.IsConnected() )
		{
			float rtt = connection.GetReliabilitySystem().GetRoundTripTime();

			unsigned int sent_packets = connection.GetReliabilitySystem().GetSentPackets();
			unsigned int acked_packets = connection.GetReliabilitySystem().GetAckedPackets();
			unsigned int lost_packets = connection.GetReliabilitySystem().GetLostPackets();

			float sent_bandwidth = connection.GetReliabilitySystem().GetSentBandwidth();
			float acked_bandwidth = connection.GetReliabilitySystem().GetAckedBandwidth();

			printf( "rtt %.1fms, sent %d, acked %d, lost %d (%.1f%%), sent bandwidth = %.1fkbps, acked bandwidth = %.1fkbps\n",
				rtt * 1000.0f, sent_packets, acked_packets, lost_packets,
				sent_packets > 0.0f ? (float) lost_packets / (float) sent_packets * 100.0f : 0.0f,
				sent_bandwidth, acked_bandwidth );

			statsAccumulator -= 0.25f;
		}

		net::wait( DeltaTime );
	}

	ShutdownSockets();

	return 0;
}
<|endoftext|>


<commit_before>#include "HexView.h"
#include "Log.h"
#include <stdio.h>
#include <assert.h>

HexView::HexView(const char* filename) : Window(filename)
{
    m_fp = 0;
    m_buffer = 0;
    m_topLine = 0;
    m_selected = 0;
    fopen_s(&m_fp, filename, "rb");
    if (m_fp)
    {
        fseek(m_fp, 0, SEEK_END);
        m_fileSize = ftell(m_fp);
        fseek(m_fp, 0, SEEK_SET);
    }
}

HexView::~HexView()
{
    if (m_fp)
        fclose(m_fp);
}

void HexView::OnWindowRefreshed()
{
    s_consoleBuffer->FillRect(0, 1, m_width, m_height, ' ', FOREGROUND_RED);
    s_consoleBuffer->FillLine(m_height + 1, ' ', BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
    s_consoleBuffer->Write(2, m_height + 1, BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED, "%08X / %08X", m_selected, m_fileSize - 1);

    if (!m_fp)
        return;

    assert(m_buffer);
    int offset = m_topLine << 4;
    int selectedLine = GetSelectedLine();
    bool done = false;

    for (int j = 0; j < m_height && !done; j++)
    {
        int y = 1 + j;
        int x = 1;
        WORD colour = 0;

        if ((offset >> 4) == selectedLine)
        {
            // Highlight the selected line's offset text.
            colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY | COMMON_LVB_REVERSE_VIDEO;
        }
        else
        {
            colour = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        }

        s_consoleBuffer->Write(1, y, colour, "%08X", offset);
        x += 9;

        for (int i = 0; i < 16; i++, offset++)
        {
            int bufferIndex = offset - (m_topLine << 4);
            if (offset >= m_fileSize)
            {
                done = true;
                break;
            }

            assert(bufferIndex >= 0 && bufferIndex < m_fileSize);
            unsigned char c = m_buffer[bufferIndex];

            if (offset == m_selected)
            {
                // Highlight the selected byte.
                colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
            }
            else
            {
                colour = FOREGROUND_GREEN;
            }

            int xx = x + (i * 3);
            s_consoleBuffer->Write(xx, y, colour, "%02X", c);

            xx = x + (16 * 3) + i;
            if (c < ' ')
                c = '.';

            if (offset == m_selected)
            {
                // Highlight the selected character.
                colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
            }
            else
            {
                colour = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
            }
            s_consoleBuffer->Write(xx, y, colour, "%c", c);
        }
    }
}

void HexView::OnWindowResized(int width, int height)
{
    height -= 2;
    Window::OnWindowResized(width, height);
    CacheFile(true);
}

void HexView::CacheFile(bool resizeBuffer)
{
    if (!m_fp)
        return;

    assert(m_topLine >= 0);
    int offset = m_topLine << 4;
    assert(offset >= 0 && offset < m_fileSize);

    if (resizeBuffer)
    {
        assert(m_height >= 0);
        int screenSize = m_height << 4;

        delete[] m_buffer;
        m_bufferSize = offset + screenSize >= m_fileSize ? m_fileSize - offset : screenSize;
        m_buffer = new unsigned char[m_bufferSize];
        memset(m_buffer, 0, m_bufferSize);
    }

    fseek(m_fp, offset, SEEK_SET);
    fread_s(m_buffer, m_bufferSize, 1, m_bufferSize, m_fp);
}

void HexView::OnKeyEvent(const KEY_EVENT_RECORD& ker)
{
    if (!ker.bKeyDown)
        return;

    switch (ker.wVirtualKeyCode)
    {
        case VK_LEFT:
        {
            m_selected = max(m_selected - 1, 0);
            int selectedLine = GetSelectedLine();
            if (selectedLine < m_topLine)
            {
                m_topLine--;
                assert(m_topLine >= 0);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_RIGHT:
        {
            m_selected = min(m_selected + 1, m_fileSize - 1);
            int selectedLine = GetSelectedLine();
            int bottomLine = GetBottomLine();
            if (selectedLine > bottomLine)
            {
                m_topLine++;
                assert((m_topLine << 4) < m_fileSize);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_UP:
        {
            int selectedLine = GetSelectedLine();
            if (selectedLine == 0)
                break;

            m_selected = max(m_selected - 16, 0);
            selectedLine = GetSelectedLine();
            if (selectedLine < m_topLine)
            {
                m_topLine--;
                assert(m_topLine >= 0);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_DOWN:
        {
            int selectedLine = GetSelectedLine();
            int lastLine = GetLastLine();

            // If on the last line, don't move anywhere.
            if (selectedLine == lastLine)
                break;

            m_selected = min(m_selected + 16, m_fileSize - 1);
            selectedLine = GetSelectedLine();

            int bottomLine = GetBottomLine();
            if (selectedLine > bottomLine)
            {
                m_topLine++;
                assert((m_topLine << 4) < m_fileSize);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_HOME:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                m_selected = 0;
                m_topLine = 0;
                CacheFile();
            }
            else
            {
                m_selected &= ~15;
            }

            Window::Refresh();
            break;
        }

        case VK_END:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                m_selected = m_fileSize - 1;
                int selectedLine = GetSelectedLine();
                m_topLine = max(selectedLine - m_height + 1, 0);
                CacheFile();
            }
            else
            {
                m_selected = min(m_selected | 15, m_fileSize - 1);
            }

            Window::Refresh();
            break;
        }

        // Page down.
        case VK_NEXT:
        {
            // Current selection column in the last line.
            int lastLineSelected = min(((m_fileSize - 1) & ~0xf) | (m_selected & 0xf), m_fileSize - 1);

            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                // Control is down. Go to the bottom of the current page.
                int bottomLine = GetBottomLine();

                // Select the offset at the bottom of the current page.
                m_selected = min((bottomLine << 4) | (m_selected & 0xf), lastLineSelected);
            }
            else
            {
                int selectedLine = GetSelectedLine();

                // Get the current distance from the selection to the top line.
                int delta = selectedLine - m_topLine;

                // Select the offset at one page down from the current.
                m_selected = min(m_selected + (m_height << 4), lastLineSelected);

                // Determine if we need to update the top line.
                selectedLine = GetSelectedLine();
                int bottomLine = GetBottomLine();
                if (selectedLine > bottomLine)
                {
                    // Update the top line, but maintain the current selection distance so the cursor
                    // never moves.
                    m_topLine = max(selectedLine - delta, 0);
                    CacheFile();
                }
            }

            Window::Refresh();
            break;
        }

        // Page up.
        case VK_PRIOR:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                // Control is down. Go to the top of the current page.
                // Select the offset at the top of the current page.
                m_selected = (m_topLine << 4) | m_selected & 0xf;
            }
            else
            {
                int selectedLine = GetSelectedLine();

                // Get the current distance from the selection to the top line.
                int delta = selectedLine - m_topLine;

                // Select the offset at one page up from the current.
                m_selected = max(m_selected - (m_height << 4), (m_selected & 0xf));

                // Determine if we need to update the top line.
                selectedLine = GetSelectedLine();
                if (selectedLine < m_topLine)
                {
                    // Update the top line, but maintain the current selection distance so the cursor
                    // never moves.
                    m_topLine = max(selectedLine - delta, 0);
                    CacheFile();
                }
            }

            Window::Refresh();
            break;
        }
    }
}
<commit_msg>Draw a progress bar to show current position in file.<commit_after>#include "HexView.h"
#include "Log.h"
#include <stdio.h>
#include <assert.h>

HexView::HexView(const char* filename) : Window(filename)
{
    m_fp = 0;
    m_buffer = 0;
    m_topLine = 0;
    m_selected = 0;
    fopen_s(&m_fp, filename, "rb");
    if (m_fp)
    {
        fseek(m_fp, 0, SEEK_END);
        m_fileSize = ftell(m_fp);
        fseek(m_fp, 0, SEEK_SET);
    }
}

HexView::~HexView()
{
    if (m_fp)
        fclose(m_fp);
}

void HexView::OnWindowRefreshed()
{
    s_consoleBuffer->FillRect(0, 1, m_width, m_height, ' ', FOREGROUND_RED);
    s_consoleBuffer->FillLine(m_height + 1, ' ', BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);
    s_consoleBuffer->Write(2, m_height + 1, BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED, "%08X / %08X", m_selected, m_fileSize - 1);

    if (!m_fp)
        return;

    assert(m_buffer);
    int offset = m_topLine << 4;
    int selectedLine = GetSelectedLine();
    bool done = false;

    for (int j = 0; j < m_height; j++)
    {
        int y = 1 + j;
        int x = 2;
        WORD colour = 0;

        int curr = (m_selected >> 4) * (m_height - 1) / (m_fileSize >> 4);
        char c = j == curr ? 178 : 176;
        s_consoleBuffer->Write(0, y, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, "%c", c);

        if (done)
            continue;

        if ((offset >> 4) == selectedLine)
        {
            // Highlight the selected line's offset text.
            colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
        }
        else
        {
            colour = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        }

        s_consoleBuffer->Write(x, y, colour, "%08X", offset);
        x += 9;

        for (int i = 0; i < 16; i++, offset++)
        {
            int bufferIndex = offset - (m_topLine << 4);
            if (offset >= m_fileSize)
            {
                done = true;
                break;
            }

            assert(bufferIndex >= 0 && bufferIndex < m_fileSize);
            unsigned char c = m_buffer[bufferIndex];

            if (offset == m_selected)
            {
                // Highlight the selected byte.
                colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
            }
            else
            {
                colour = FOREGROUND_GREEN;
            }

            int xx = x + (i * 3);
            s_consoleBuffer->Write(xx, y, colour, "%02X", c);

            xx = x + (16 * 3) + i;
            if (c < ' ')
                c = '.';

            if (offset == m_selected)
            {
                // Highlight the selected character.
                colour = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
            }
            else
            {
                colour = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
            }
            s_consoleBuffer->Write(xx, y, colour, "%c", c);
        }
    }
}

void HexView::OnWindowResized(int width, int height)
{
    height -= 2;
    Window::OnWindowResized(width, height);
    CacheFile(true);
}

void HexView::CacheFile(bool resizeBuffer)
{
    if (!m_fp)
        return;

    assert(m_topLine >= 0);
    int offset = m_topLine << 4;
    assert(offset >= 0 && offset < m_fileSize);

    if (resizeBuffer)
    {
        assert(m_height >= 0);
        int screenSize = m_height << 4;

        delete[] m_buffer;
        m_bufferSize = offset + screenSize >= m_fileSize ? m_fileSize - offset : screenSize;
        m_buffer = new unsigned char[m_bufferSize];
        memset(m_buffer, 0, m_bufferSize);
    }

    fseek(m_fp, offset, SEEK_SET);
    fread_s(m_buffer, m_bufferSize, 1, m_bufferSize, m_fp);
}

void HexView::OnKeyEvent(const KEY_EVENT_RECORD& ker)
{
    if (!ker.bKeyDown)
        return;

    switch (ker.wVirtualKeyCode)
    {
        case VK_LEFT:
        {
            m_selected = max(m_selected - 1, 0);
            int selectedLine = GetSelectedLine();
            if (selectedLine < m_topLine)
            {
                m_topLine--;
                assert(m_topLine >= 0);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_RIGHT:
        {
            m_selected = min(m_selected + 1, m_fileSize - 1);
            int selectedLine = GetSelectedLine();
            int bottomLine = GetBottomLine();
            if (selectedLine > bottomLine)
            {
                m_topLine++;
                assert((m_topLine << 4) < m_fileSize);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_UP:
        {
            int selectedLine = GetSelectedLine();
            if (selectedLine == 0)
                break;

            m_selected = max(m_selected - 16, 0);
            selectedLine = GetSelectedLine();
            if (selectedLine < m_topLine)
            {
                m_topLine--;
                assert(m_topLine >= 0);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_DOWN:
        {
            int selectedLine = GetSelectedLine();
            int lastLine = GetLastLine();

            // If on the last line, don't move anywhere.
            if (selectedLine == lastLine)
                break;

            m_selected = min(m_selected + 16, m_fileSize - 1);
            selectedLine = GetSelectedLine();

            int bottomLine = GetBottomLine();
            if (selectedLine > bottomLine)
            {
                m_topLine++;
                assert((m_topLine << 4) < m_fileSize);
                CacheFile();
            }

            Window::Refresh();
            break;
        }

        case VK_HOME:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                m_selected = 0;
                m_topLine = 0;
                CacheFile();
            }
            else
            {
                m_selected &= ~15;
            }

            Window::Refresh();
            break;
        }

        case VK_END:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                m_selected = m_fileSize - 1;
                int selectedLine = GetSelectedLine();
                m_topLine = max(selectedLine - m_height + 1, 0);
                CacheFile();
            }
            else
            {
                m_selected = min(m_selected | 15, m_fileSize - 1);
            }

            Window::Refresh();
            break;
        }

        // Page down.
        case VK_NEXT:
        {
            // Current selection column in the last line.
            int lastLineSelected = min(((m_fileSize - 1) & ~0xf) | (m_selected & 0xf), m_fileSize - 1);

            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                // Control is down. Go to the bottom of the current page.
                int bottomLine = GetBottomLine();

                // Select the offset at the bottom of the current page.
                m_selected = min((bottomLine << 4) | (m_selected & 0xf), lastLineSelected);
            }
            else
            {
                int selectedLine = GetSelectedLine();

                // Get the current distance from the selection to the top line.
                int delta = selectedLine - m_topLine;

                // Select the offset at one page down from the current.
                m_selected = min(m_selected + (m_height << 4), lastLineSelected);

                // Determine if we need to update the top line.
                selectedLine = GetSelectedLine();
                int bottomLine = GetBottomLine();
                if (selectedLine > bottomLine)
                {
                    // Update the top line, but maintain the current selection distance so the cursor
                    // never moves.
                    m_topLine = max(selectedLine - delta, 0);
                    CacheFile();
                }
            }

            Window::Refresh();
            break;
        }

        // Page up.
        case VK_PRIOR:
        {
            DWORD ctrl = ker.dwControlKeyState;
            if ((ctrl & LEFT_CTRL_PRESSED) || (ctrl & RIGHT_CTRL_PRESSED))
            {
                // Control is down. Go to the top of the current page.
                // Select the offset at the top of the current page.
                m_selected = (m_topLine << 4) | m_selected & 0xf;
            }
            else
            {
                int selectedLine = GetSelectedLine();

                // Get the current distance from the selection to the top line.
                int delta = selectedLine - m_topLine;

                // Select the offset at one page up from the current.
                m_selected = max(m_selected - (m_height << 4), (m_selected & 0xf));

                // Determine if we need to update the top line.
                selectedLine = GetSelectedLine();
                if (selectedLine < m_topLine)
                {
                    // Update the top line, but maintain the current selection distance so the cursor
                    // never moves.
                    m_topLine = max(selectedLine - delta, 0);
                    CacheFile();
                }
            }

            Window::Refresh();
            break;
        }
    }
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>

#include <glob.h>   // TODO

#include <pdal/StageFactory.hpp>

#include <entwine/third/arbiter/arbiter.hpp>
#include <entwine/reader/query.hpp>
#include <entwine/reader/reader.hpp>
#include <entwine/third/json/json.hpp>
#include <entwine/tree/clipper.hpp>
#include <entwine/types/bbox.hpp>
#include <entwine/types/schema.hpp>
#include <entwine/types/structure.hpp>
#include <entwine/util/executor.hpp>

#include "read-queries/entwine.hpp"
#include "read-queries/unindexed.hpp"
#include "util/buffer-pool.hpp"

#include "session.hpp"

namespace
{
    // TODO Put this somewhere else - platform dependent code.
    std::vector<std::string> resolve(
            const std::vector<std::string>& dirs,
            const std::string& name)
    {
        glob_t buffer;
        for (std::size_t i(0); i < dirs.size(); ++i)
        {
            auto flags(GLOB_NOSORT | GLOB_TILDE);
            if (i) flags |= GLOB_APPEND;

            glob((dirs[i] + "/" + name + ".*").c_str(), flags, 0, &buffer);
        }

        std::vector<std::string> results;
        for (std::size_t i(0); i < buffer.gl_pathc; ++i)
        {
            results.push_back(buffer.gl_pathv[i]);
        }

        globfree(&buffer);

        return results;
    }

    std::string getTypeString(const entwine::Structure& structure)
    {
        if (structure.dimensions() == 2)
        {
            if (structure.tubular()) return "hybrid";
            else return "quadtree";
        }
        else if (structure.dimensions() == 3 && !structure.tubular())
        {
            return "octree";
        }
        else
        {
            throw std::runtime_error("Invalid structure");
        }
    }
}

Session::Session(
        pdal::StageFactory& stageFactory,
        std::mutex& factoryMutex)
    : m_stageFactory(stageFactory)
    , m_factoryMutex(factoryMutex)
    , m_initOnce()
    , m_source()
    , m_entwine()
{ }

Session::~Session()
{ }

bool Session::initialize(
        const std::string& name,
        std::vector<std::string> paths,
        std::shared_ptr<arbiter::Arbiter> arbiter,
        std::shared_ptr<entwine::Cache> cache)
{
    m_initOnce.ensure([this, &name, &paths, cache, arbiter]()
    {
        paths.push_back("s3://");

        std::cout << "Discovering " << name << std::endl;

        if (resolveIndex(name, paths, *arbiter, cache))
        {
            std::cout << "\tIndex for " << name << " found" << std::endl;

            Json::Value json;
            const std::size_t numPoints(m_entwine->numPoints());

            json["type"] = getTypeString(m_entwine->structure());
            json["numPoints"] = static_cast<Json::UInt64>(numPoints);
            json["schema"] = m_entwine->schema().toJson();
            json["bounds"] = m_entwine->bbox().toJson()["bounds"];
            json["srs"] = m_entwine->srs();

            m_info = json.toStyledString();
        }
        else if (resolveSource(name, paths))
        {
            std::cout << "\tSource for " << name << " found" << std::endl;
            const std::size_t numPoints(m_source->numPoints());

            Json::Value json;
            json["type"] = "unindexed";
            json["numPoints"] = static_cast<Json::UInt64>(numPoints);
            json["schema"] = m_source->schema().toJson();
            json["bounds"] = m_source->bbox().toJson()["bounds"];
            json["srs"] = m_source->srs();

            m_info = json.toStyledString();
        }
        else
        {
            std::cout << "\tBacking for " << name << " NOT found" << std::endl;
        }
    });

    return sourced() || indexed();
}

std::string Session::info() const
{
    check();
    return m_info;
}

std::shared_ptr<ReadQuery> Session::query(
        const entwine::Schema& schema,
        const bool compress)
{
    if (sourced())
    {
        return std::shared_ptr<ReadQuery>(
                new UnindexedReadQuery(
                    schema,
                    compress,
                    *m_source));
    }
    else
    {
        throw WrongQueryType();
    }
}

std::shared_ptr<ReadQuery> Session::query(
        const entwine::Schema& schema,
        const bool compress,
        const entwine::BBox& bbox,
        const std::size_t depthBegin,
        const std::size_t depthEnd)
{
    if (indexed())
    {
        return std::shared_ptr<ReadQuery>(
                new EntwineReadQuery(
                    schema,
                    compress,
                    m_entwine->query(
                        schema,
                        bbox.exists() ? bbox : m_entwine->bbox(),
                        depthBegin,
                        depthEnd)));
    }
    else
    {
        throw WrongQueryType();
    }
}

const entwine::Schema& Session::schema() const
{
    check();

    if (indexed()) return m_entwine->schema();
    else return m_source->schema();
}

bool Session::resolveIndex(
        const std::string& name,
        const std::vector<std::string>& paths,
        arbiter::Arbiter& arbiter,
        std::shared_ptr<entwine::Cache> cache)
{
    for (std::string path : paths)
    {
        try
        {
            if (path.size() && path.back() != '/') path.push_back('/');
            arbiter::Endpoint endpoint(arbiter.getEndpoint(path + name));
            m_entwine.reset(new entwine::Reader(endpoint, arbiter, *cache));
        }
        catch (...)
        {
            m_entwine.reset(0);
        }

        std::cout << "\tTried resolving index at " << path << ": ";
        if (m_entwine)
        {
            std::cout << "SUCCESS" << std::endl;
            break;
        }
        else
        {
            std::cout << "fail" << std::endl;
        }
    }

    return indexed();
}

bool Session::resolveSource(
        const std::string& name,
        const std::vector<std::string>& paths)
{
    const auto sources(resolve(paths, name));

    if (sources.size() > 1)
    {
        std::cout << "\tFound competing unindexed sources for " << name <<
            std::endl;
    }

    if (sources.size())
    {
        std::size_t i(0);

        while (!m_source && i < sources.size())
        {
            const std::string path(sources[i++]);

            std::unique_lock<std::mutex> lock(m_factoryMutex);
            const std::string driver(
                    m_stageFactory.inferReaderDriver(path));
            lock.unlock();

            if (driver.size())
            {
                try
                {
                    m_source.reset(
                            new SourceManager(
                                m_stageFactory,
                                m_factoryMutex,
                                path,
                                driver));
                }
                catch (...)
                {
                    m_source.reset(0);
                }
            }

            std::cout << "\tTried resolving unindexed at " << path << ": ";
            if (m_source) std::cout << "SUCCESS" << std::endl;
            else std::cout << "failed" << std::endl;
        }
    }

    return sourced();
}

<commit_msg>Expose hybrid trees as octrees, since their hybridness is transparent to the client.<commit_after>#include <fstream>
#include <iostream>

#include <glob.h>   // TODO

#include <pdal/StageFactory.hpp>

#include <entwine/third/arbiter/arbiter.hpp>
#include <entwine/reader/query.hpp>
#include <entwine/reader/reader.hpp>
#include <entwine/third/json/json.hpp>
#include <entwine/tree/clipper.hpp>
#include <entwine/types/bbox.hpp>
#include <entwine/types/schema.hpp>
#include <entwine/types/structure.hpp>
#include <entwine/util/executor.hpp>

#include "read-queries/entwine.hpp"
#include "read-queries/unindexed.hpp"
#include "util/buffer-pool.hpp"

#include "session.hpp"

namespace
{
    // TODO Put this somewhere else - platform dependent code.
    std::vector<std::string> resolve(
            const std::vector<std::string>& dirs,
            const std::string& name)
    {
        glob_t buffer;
        for (std::size_t i(0); i < dirs.size(); ++i)
        {
            auto flags(GLOB_NOSORT | GLOB_TILDE);
            if (i) flags |= GLOB_APPEND;

            glob((dirs[i] + "/" + name + ".*").c_str(), flags, 0, &buffer);
        }

        std::vector<std::string> results;
        for (std::size_t i(0); i < buffer.gl_pathc; ++i)
        {
            results.push_back(buffer.gl_pathv[i]);
        }

        globfree(&buffer);

        return results;
    }

    std::string getTypeString(const entwine::Structure& structure)
    {
        if (structure.dimensions() == 2)
        {
            if (structure.tubular()) return "octree";
            else return "quadtree";
        }
        else if (structure.dimensions() == 3 && !structure.tubular())
        {
            return "octree";
        }
        else
        {
            throw std::runtime_error("Invalid structure");
        }
    }
}

Session::Session(
        pdal::StageFactory& stageFactory,
        std::mutex& factoryMutex)
    : m_stageFactory(stageFactory)
    , m_factoryMutex(factoryMutex)
    , m_initOnce()
    , m_source()
    , m_entwine()
{ }

Session::~Session()
{ }

bool Session::initialize(
        const std::string& name,
        std::vector<std::string> paths,
        std::shared_ptr<arbiter::Arbiter> arbiter,
        std::shared_ptr<entwine::Cache> cache)
{
    m_initOnce.ensure([this, &name, &paths, cache, arbiter]()
    {
        paths.push_back("s3://");

        std::cout << "Discovering " << name << std::endl;

        if (resolveIndex(name, paths, *arbiter, cache))
        {
            std::cout << "\tIndex for " << name << " found" << std::endl;

            Json::Value json;
            const std::size_t numPoints(m_entwine->numPoints());

            json["type"] = getTypeString(m_entwine->structure());
            json["numPoints"] = static_cast<Json::UInt64>(numPoints);
            json["schema"] = m_entwine->schema().toJson();
            json["bounds"] = m_entwine->bbox().toJson()["bounds"];
            json["srs"] = m_entwine->srs();

            m_info = json.toStyledString();
        }
        else if (resolveSource(name, paths))
        {
            std::cout << "\tSource for " << name << " found" << std::endl;
            const std::size_t numPoints(m_source->numPoints());

            Json::Value json;
            json["type"] = "unindexed";
            json["numPoints"] = static_cast<Json::UInt64>(numPoints);
            json["schema"] = m_source->schema().toJson();
            json["bounds"] = m_source->bbox().toJson()["bounds"];
            json["srs"] = m_source->srs();

            m_info = json.toStyledString();
        }
        else
        {
            std::cout << "\tBacking for " << name << " NOT found" << std::endl;
        }
    });

    return sourced() || indexed();
}

std::string Session::info() const
{
    check();
    return m_info;
}

std::shared_ptr<ReadQuery> Session::query(
        const entwine::Schema& schema,
        const bool compress)
{
    if (sourced())
    {
        return std::shared_ptr<ReadQuery>(
                new UnindexedReadQuery(
                    schema,
                    compress,
                    *m_source));
    }
    else
    {
        throw WrongQueryType();
    }
}

std::shared_ptr<ReadQuery> Session::query(
        const entwine::Schema& schema,
        const bool compress,
        const entwine::BBox& bbox,
        const std::size_t depthBegin,
        const std::size_t depthEnd)
{
    if (indexed())
    {
        return std::shared_ptr<ReadQuery>(
                new EntwineReadQuery(
                    schema,
                    compress,
                    m_entwine->query(
                        schema,
                        bbox.exists() ? bbox : m_entwine->bbox(),
                        depthBegin,
                        depthEnd)));
    }
    else
    {
        throw WrongQueryType();
    }
}

const entwine::Schema& Session::schema() const
{
    check();

    if (indexed()) return m_entwine->schema();
    else return m_source->schema();
}

bool Session::resolveIndex(
        const std::string& name,
        const std::vector<std::string>& paths,
        arbiter::Arbiter& arbiter,
        std::shared_ptr<entwine::Cache> cache)
{
    for (std::string path : paths)
    {
        try
        {
            if (path.size() && path.back() != '/') path.push_back('/');
            arbiter::Endpoint endpoint(arbiter.getEndpoint(path + name));
            m_entwine.reset(new entwine::Reader(endpoint, arbiter, *cache));
        }
        catch (...)
        {
            m_entwine.reset(0);
        }

        std::cout << "\tTried resolving index at " << path << ": ";
        if (m_entwine)
        {
            std::cout << "SUCCESS" << std::endl;
            break;
        }
        else
        {
            std::cout << "fail" << std::endl;
        }
    }

    return indexed();
}

bool Session::resolveSource(
        const std::string& name,
        const std::vector<std::string>& paths)
{
    const auto sources(resolve(paths, name));

    if (sources.size() > 1)
    {
        std::cout << "\tFound competing unindexed sources for " << name <<
            std::endl;
    }

    if (sources.size())
    {
        std::size_t i(0);

        while (!m_source && i < sources.size())
        {
            const std::string path(sources[i++]);

            std::unique_lock<std::mutex> lock(m_factoryMutex);
            const std::string driver(
                    m_stageFactory.inferReaderDriver(path));
            lock.unlock();

            if (driver.size())
            {
                try
                {
                    m_source.reset(
                            new SourceManager(
                                m_stageFactory,
                                m_factoryMutex,
                                path,
                                driver));
                }
                catch (...)
                {
                    m_source.reset(0);
                }
            }

            std::cout << "\tTried resolving unindexed at " << path << ": ";
            if (m_source) std::cout << "SUCCESS" << std::endl;
            else std::cout << "failed" << std::endl;
        }
    }

    return sourced();
}

<|endoftext|>


<commit_before>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/model/CModelMerging.cpp,v $
//   $Revision: 1.2 $
//   $Name:  $
//   $Author: nsimus $
//   $Date: 2009/07/07 09:44:07 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

#include "CModelMerging.h"

#include "CModel.h"
#include "function/CExpression.h"
#include "report/CCopasiObject.h"

CModelMerging::CModelMerging(CModel* pModel, CModel* mModel)
    : mpModel(pModel), mmModel(mModel)
{
}

void CModelMerging::setModel(CModel* pModel, CModel* mModel)
{
  mpModel = pModel;
  mmModel = mModel;
}

void CModelMerging::simpleCall()
{
  if (!mpModel) return;

  if (!mmModel) return;

  keyMap[mmModel->getKey()] = mpModel->getKey();
  std::string name = "model_2";

  if (!addCompartments(name)) return; // TODO error message

  if (!addMetabolites(name)) return; // TODO error message

  if (!addModelValues(name)) return; // TODO error message

  if (!addReactions(name)) return; // TODO error message

  mpModel->compileIfNecessary(NULL);
}

bool CModelMerging::copyExpression(const CModelEntity * sourceEntity, CModelEntity * newEntity)
{

  bool info;

  // if (mmModel)
  // mmModel->setCompileFlag(true);

  const CExpression* pExpression = sourceEntity->getExpressionPtr();
  assert(pExpression);

  CExpression* tmp;
  tmp = new CExpression(*pExpression, mmModel);

  const std::vector<CEvaluationNode*>& objectNodes = tmp->getNodeList();
  unsigned j, jmax = objectNodes.size();

  for (j = 0; j < jmax; ++j)
    {
      if (CEvaluationNode::type(objectNodes[j]->getType()) == CEvaluationNode::OBJECT)
        {
          CEvaluationNodeObject* pObjectNode = dynamic_cast<CEvaluationNodeObject*>(objectNodes[j]);
          assert(pObjectNode);
          CCopasiObjectName cn = pObjectNode->getObjectCN();

          std::cout << cn << std::endl;

          const CCopasiObject* pObject = mmModel->getObject(cn);
          assert(pObject);

          // the last part is in CModelMerging.cpp_06.07
        }
    }

  // the last part is in CModelMerging.cpp_06.07

  return true;
}

bool CModelMerging::addCompartments(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  if (!mmModel->getCompartments().size()) return info;

  unsigned C_INT32 i, imax = mmModel->getCompartments().size();

  for (i = 0; i < imax; ++i)
    {
      const CCompartment* sourceComp = mmModel->getCompartments()[i];

      if (!sourceComp) return info;

      //create new compartment

      std::string newName = sourceComp->getObjectName() + "_" + name;

      CCompartment* newComp = mpModel->createCompartment(newName, sourceComp->getInitialValue());

      if (!newComp) return info;

      switch (sourceComp ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            break;

          case CModelEntity::ODE:

            break;

          default:

            return info;

            break;
        }

      newComp->setDimensionality(sourceComp->getDimensionality());

      keyMap[sourceComp->getKey()] = newComp->getKey();
      nameMap[sourceComp->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addMetabolites(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  if (!mmModel->getMetabolites().size()) return info;

  unsigned C_INT32 i, imax = mmModel->getMetabolites().size();

  for (i = 0; i < imax; ++i)
    {
      const CMetab* sourceMetab = mmModel->getMetabolites()[i];
      const CCompartment* sourceComp =  sourceMetab->getCompartment();

      if (!sourceMetab) return info;

      if (!sourceComp) return info;

      //create new metabolite

      std::string newName = sourceMetab->getObjectName() + "_" + name;

      CMetab* newMetab = mpModel->createMetabolite(sourceMetab->getObjectName(), nameMap[sourceComp->getObjectName()], sourceMetab->getInitialConcentration());

      if (!newMetab) return info;

      newMetab->setStatus(sourceMetab->getStatus());

      switch (sourceMetab ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            if (!copyExpression(sourceMetab, newMetab)) return info;

            break;

          case CModelEntity::ODE:

            break;

          case CModelEntity::REACTIONS:

            break;
          default:

            return info;

            break;
        }

      keyMap[sourceMetab->getKey()] = newMetab->getKey();
      nameMap[sourceMetab->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addModelValues(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  unsigned C_INT32 i, imax = mmModel->getModelValues().size();

  for (i = 0; i < imax; ++i)
    {
      const CModelValue* sourceModVal = mmModel->getModelValues()[i];

      if (!sourceModVal) return info;

      //create new model value

      std::string newName = sourceModVal->getObjectName() + "_" + name;

      CModelValue* newModVal = mpModel->createModelValue(newName, sourceModVal->getInitialValue());

      if (!newModVal) return info;

      newModVal->setStatus(sourceModVal->getStatus());

      switch (sourceModVal ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            break;

          case CModelEntity::ODE:

            break;

          case CModelEntity::REACTIONS:

            break;
          default:

            return info;

            break;
        }

      keyMap[sourceModVal->getKey()] = newModVal->getKey();
      nameMap[sourceModVal->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addReactions(std::string name)
{

  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  //create copies of the relevant reactions

  unsigned C_INT32 i, imax = mmModel->getReactions().size();

  if (!mmModel->getCompartments().size()) return info;

  unsigned C_INT32 ic, icmax = mmModel->getCompartments().size();

  for (ic = 0; ic < icmax; ++ic)
    {
      const CCompartment* sourceComp = mmModel->getCompartments()[ic];

      if (!sourceComp) return info;

      for (i = 0; i < imax; ++i)
        {
          CReaction * sourceReac = mmModel->getReactions()[i];

          if (reactionInvolvesCompartment(sourceReac, sourceComp))
            {

              std::string newName = sourceReac->getObjectName() + "_" + name;

              CReaction* newReac = mpModel->createReaction(newName);

              if (!newReac)
                continue; //should not happen TODO: error handling

              //copy the chemical equation. If the involved metabs are among those that
              //were copied with the compartment, replace them. Otherwise keep the original metab
              newReac->setReversible(sourceReac->isReversible());
              std::map<std::string, std::string>::const_iterator mapIt;
              std::string targetKey;
              unsigned C_INT32 j, jmax = sourceReac->getChemEq().getSubstrates().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getSubstrates()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.
                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addSubstrate(targetKey, sourceElement->getMultiplicity());
                }

              jmax = sourceReac->getChemEq().getProducts().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getProducts()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.
                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addProduct(targetKey, sourceElement->getMultiplicity());
                }

              jmax = sourceReac->getChemEq().getModifiers().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getModifiers()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.

                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addModifier(targetKey);
                }

              //set the kinetic function
              newReac->setFunction(const_cast<CFunction*>(sourceReac->getFunction()));

              //mapping and local parameters
              for (j = 0; j < newReac->getFunctionParameters().size(); ++j)
                {
                  switch (newReac->getFunctionParameters()[j]->getUsage())
                    {
                      case CFunctionParameter::SUBSTRATE:
                      case CFunctionParameter::PRODUCT:
                      case CFunctionParameter::MODIFIER:
                        //translate the metab keys
                      {
                        bool isVector = (newReac->getFunctionParameters()[j]->getType() == CFunctionParameter::VFLOAT64);

                        //we assume that only SUBSTRATE, PRODUCT, MODIFIER can be vectors
                        if (isVector)
                          newReac->clearParameterMapping(j);

                        unsigned C_INT32 k;

                        for (k = 0; k < sourceReac->getParameterMappings()[j].size(); ++k)
                          {
                            mapIt = keyMap.find(sourceReac->getParameterMappings()[j][k]);

                            if (mapIt == keyMap.end())
                              {
                                targetKey = sourceReac->getParameterMappings()[j][k];
                              }
                            else
                              targetKey = mapIt->second;

                            if (isVector)
                              newReac->addParameterMapping(j, targetKey);
                            else
                              newReac->setParameterMapping(j, targetKey);
                          }
                      }
                      break;

                      case CFunctionParameter::TIME:
                        //just copy the key
                        newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);
                        break;

                      case CFunctionParameter::VOLUME:

                        //translate the compartment key if necessary
                        if (sourceReac->getParameterMappings()[j][0] == sourceComp->getKey())
                          newReac->setParameterMapping(j, keyMap[sourceComp->getKey()]);
                        else
                          newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);

                        //TODO: this needs to be adapted when sets of compartments will be copied
                        break;

                      case CFunctionParameter::PARAMETER:

                        if (sourceReac->isLocalParameter(j))
                          newReac->setParameterValue(newReac->getFunctionParameters()[j]->getObjectName(),
                                                     sourceReac->getParameterValue(newReac->getFunctionParameters()[j]->getObjectName()));
                        else
                          newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);

                        break;

                      default:
                        //TODO: error handling
                        break;
                    }
                }
            }
        }
    }

  return true;
}

//static
bool CModelMerging::reactionInvolvesCompartment(const CReaction * reac, const CCompartment* comp)
{
  if (!reac) return false;

  if (!comp) return false;

  unsigned C_INT32 i, imax = reac->getChemEq().getSubstrates().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getSubstrates()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  imax = reac->getChemEq().getProducts().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getProducts()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  imax = reac->getChemEq().getModifiers().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getModifiers()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  return false;
}
<commit_msg>development of models merging<commit_after>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/model/CModelMerging.cpp,v $
//   $Revision: 1.3 $
//   $Name:  $
//   $Author: nsimus $
//   $Date: 2009/07/09 11:31:43 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

#include "CModelMerging.h"

#include "CModel.h"
#include "function/CExpression.h"
#include "report/CCopasiObject.h"
#include "CopasiDataModel/CCopasiDataModel.h"
#include "report/CCopasiRootContainer.h"

CModelMerging::CModelMerging(CModel* pModel, CModel* mModel)
    : mpModel(pModel), mmModel(mModel)
{
}

void CModelMerging::setModel(CModel* pModel, CModel* mModel)
{
  mpModel = pModel;
  mmModel = mModel;
}

void CModelMerging::simpleCall()
{
  if (!mpModel) return;

  if (!mmModel) return;

  keyMap[mmModel->getKey()] = mpModel->getKey();
  std::string name = "model_2";

  if (!addCompartments(name)) return; // TODO error message

  if (!addMetabolites(name)) return; // TODO error message

  if (!addModelValues(name)) return; // TODO error message

  if (!addReactions(name)) return; // TODO error message

  mpModel->compileIfNecessary(NULL);
}

bool CModelMerging::copyExpression(const CModelEntity * sourceEntity, CModelEntity * newEntity)
{

  bool info;

  const CExpression* pExpression = sourceEntity->getExpressionPtr();
  assert(pExpression);

  CExpression* tmp;
  tmp = new CExpression(*pExpression, mmModel);

  //std::cout << tmp->getRoot()->getDisplayString(tmp).c_str() << std::endl;

  const std::vector<CEvaluationNode*>& objectNodes = tmp->getNodeList();
  unsigned j, jmax = objectNodes.size();

  for (j = 0; j < jmax; ++j)
    {
      if (CEvaluationNode::type(objectNodes[j]->getType()) == CEvaluationNode::OBJECT)
        {
          CEvaluationNodeObject* pObjectNode = dynamic_cast<CEvaluationNodeObject*>(objectNodes[j]);
          assert(pObjectNode);
          CCopasiObjectName cn = pObjectNode->getObjectCN();

          //std::cout << cn << std::endl;

          const CCopasiObject* mObject = mmModel->getObjectDataModel()->getObject(cn);
          assert(mObject);
          std::string host = "";

          if (mObject->isReference())
            {
              host = ",Reference=" + mObject->getObjectName();
              mObject = mObject->getObjectParent();
            }

          assert(mObject);

          std::string key = keyMap[(dynamic_cast<const CModelEntity * >(mObject))->getKey()];
          CCopasiObject*  pObject = (CCopasiRootContainer::getKeyFactory()->get(key));

          cn = pObject->getCN() + host;

          //std::cout << cn << std::endl;

          pObjectNode->setData("<" + cn + ">");
        }
    }

  tmp->updateTree();

  //std::cout << tmp->getRoot()->getDisplayString(tmp).c_str() << std::endl;

  newEntity->setExpression(tmp->getInfix().c_str());

  return true;
}

bool CModelMerging::addCompartments(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  if (!mmModel->getCompartments().size()) return info;

  unsigned C_INT32 i, imax = mmModel->getCompartments().size();

  for (i = 0; i < imax; ++i)
    {
      const CCompartment* sourceComp = mmModel->getCompartments()[i];

      if (!sourceComp) return info;

      //create new compartment

      std::string newName = sourceComp->getObjectName() + "_" + name;

      CCompartment* newComp = mpModel->createCompartment(newName, sourceComp->getInitialValue());

      if (!newComp) return info;

      switch (sourceComp ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            if (!copyExpression(sourceComp, newComp)) return info;

            break;

          case CModelEntity::ODE:

            if (!copyExpression(sourceComp, newComp)) return info;

            break;

          default:

            return info;

            break;
        }

      newComp->setDimensionality(sourceComp->getDimensionality());

      keyMap[sourceComp->getKey()] = newComp->getKey();
      nameMap[sourceComp->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addMetabolites(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  if (!mmModel->getMetabolites().size()) return info;

  unsigned C_INT32 i, imax = mmModel->getMetabolites().size();

  for (i = 0; i < imax; ++i)
    {
      const CMetab* sourceMetab = mmModel->getMetabolites()[i];
      const CCompartment* sourceComp =  sourceMetab->getCompartment();

      if (!sourceMetab) return info;

      if (!sourceComp) return info;

      //create new metabolite

      std::string newName = sourceMetab->getObjectName() + "_" + name;

      CMetab* newMetab = mpModel->createMetabolite(sourceMetab->getObjectName(), nameMap[sourceComp->getObjectName()], sourceMetab->getInitialConcentration());

      if (!newMetab) return info;

      newMetab->setStatus(sourceMetab->getStatus());

      switch (sourceMetab ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            if (!copyExpression(sourceMetab, newMetab)) return info;

            break;

          case CModelEntity::ODE:

            if (!copyExpression(sourceMetab, newMetab)) return info;

            break;

          case CModelEntity::REACTIONS:

            break;
          default:

            return info;

            break;
        }

      keyMap[sourceMetab->getKey()] = newMetab->getKey();
      nameMap[sourceMetab->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addModelValues(std::string name)
{
  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  unsigned C_INT32 i, imax = mmModel->getModelValues().size();

  for (i = 0; i < imax; ++i)
    {
      const CModelValue* sourceModVal = mmModel->getModelValues()[i];

      if (!sourceModVal) return info;

      //create new model value

      std::string newName = sourceModVal->getObjectName() + "_" + name;

      CModelValue* newModVal = mpModel->createModelValue(newName, sourceModVal->getInitialValue());

      if (!newModVal) return info;

      newModVal->setStatus(sourceModVal->getStatus());

      switch (sourceModVal ->getStatus())
        {
          case CModelEntity::FIXED:

            break;
          case CModelEntity::ASSIGNMENT:

            if (!copyExpression(sourceModVal, newModVal)) return info;

            break;

          case CModelEntity::ODE:

            if (!copyExpression(sourceModVal, newModVal)) return info;

            break;

          default:

            return info;

            break;
        }

      keyMap[sourceModVal->getKey()] = newModVal->getKey();
      nameMap[sourceModVal->getObjectName()] = newName;
    }

  return true;
}

bool CModelMerging::addReactions(std::string name)
{

  bool info;

  if (!mpModel) return info;

  if (!mmModel) return info;

  //create copies of the relevant reactions

  unsigned C_INT32 i, imax = mmModel->getReactions().size();

  if (!mmModel->getCompartments().size()) return info;

  unsigned C_INT32 ic, icmax = mmModel->getCompartments().size();

  for (ic = 0; ic < icmax; ++ic)
    {
      const CCompartment* sourceComp = mmModel->getCompartments()[ic];

      if (!sourceComp) return info;

      for (i = 0; i < imax; ++i)
        {
          CReaction * sourceReac = mmModel->getReactions()[i];

          if (reactionInvolvesCompartment(sourceReac, sourceComp))
            {

              std::string newName = sourceReac->getObjectName() + "_" + name;

              CReaction* newReac = mpModel->createReaction(newName);

              if (!newReac)
                continue; //should not happen TODO: error handling

              //copy the chemical equation. If the involved metabs are among those that
              //were copied with the compartment, replace them. Otherwise keep the original metab
              newReac->setReversible(sourceReac->isReversible());
              std::map<std::string, std::string>::const_iterator mapIt;
              std::string targetKey;
              unsigned C_INT32 j, jmax = sourceReac->getChemEq().getSubstrates().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getSubstrates()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.
                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addSubstrate(targetKey, sourceElement->getMultiplicity());
                }

              jmax = sourceReac->getChemEq().getProducts().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getProducts()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.
                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addProduct(targetKey, sourceElement->getMultiplicity());
                }

              jmax = sourceReac->getChemEq().getModifiers().size();

              for (j = 0; j < jmax; ++j)
                {
                  const CChemEqElement * sourceElement = sourceReac->getChemEq().getModifiers()[j];
                  //check if the metab is in the map. If yes, translate it, otherwise not.

                  mapIt = keyMap.find(sourceElement->getMetaboliteKey());

                  if (mapIt == keyMap.end())
                    {
                      targetKey = sourceElement->getMetaboliteKey();
                    }
                  else
                    targetKey = mapIt->second;

                  newReac->addModifier(targetKey);
                }

              //set the kinetic function
              newReac->setFunction(const_cast<CFunction*>(sourceReac->getFunction()));

              //mapping and local parameters
              for (j = 0; j < newReac->getFunctionParameters().size(); ++j)
                {
                  switch (newReac->getFunctionParameters()[j]->getUsage())
                    {
                      case CFunctionParameter::SUBSTRATE:
                      case CFunctionParameter::PRODUCT:
                      case CFunctionParameter::MODIFIER:
                        //translate the metab keys
                      {
                        bool isVector = (newReac->getFunctionParameters()[j]->getType() == CFunctionParameter::VFLOAT64);

                        //we assume that only SUBSTRATE, PRODUCT, MODIFIER can be vectors
                        if (isVector)
                          newReac->clearParameterMapping(j);

                        unsigned C_INT32 k;

                        for (k = 0; k < sourceReac->getParameterMappings()[j].size(); ++k)
                          {
                            mapIt = keyMap.find(sourceReac->getParameterMappings()[j][k]);

                            if (mapIt == keyMap.end())
                              {
                                targetKey = sourceReac->getParameterMappings()[j][k];
                              }
                            else
                              targetKey = mapIt->second;

                            if (isVector)
                              newReac->addParameterMapping(j, targetKey);
                            else
                              newReac->setParameterMapping(j, targetKey);
                          }
                      }
                      break;

                      case CFunctionParameter::TIME:
                        //just copy the key
                        newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);
                        break;

                      case CFunctionParameter::VOLUME:

                        //translate the compartment key if necessary
                        if (sourceReac->getParameterMappings()[j][0] == sourceComp->getKey())
                          newReac->setParameterMapping(j, keyMap[sourceComp->getKey()]);
                        else
                          newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);

                        //TODO: this needs to be adapted when sets of compartments will be copied
                        break;

                      case CFunctionParameter::PARAMETER:

                        if (sourceReac->isLocalParameter(j))
                          newReac->setParameterValue(newReac->getFunctionParameters()[j]->getObjectName(),
                                                     sourceReac->getParameterValue(newReac->getFunctionParameters()[j]->getObjectName()));
                        else
                          newReac->setParameterMapping(j, sourceReac->getParameterMappings()[j][0]);

                        break;

                      default:
                        //TODO: error handling
                        break;
                    }
                }
            }
        }
    }

  return true;
}

//static
bool CModelMerging::reactionInvolvesCompartment(const CReaction * reac, const CCompartment* comp)
{
  if (!reac) return false;

  if (!comp) return false;

  unsigned C_INT32 i, imax = reac->getChemEq().getSubstrates().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getSubstrates()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  imax = reac->getChemEq().getProducts().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getProducts()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  imax = reac->getChemEq().getModifiers().size();

  for (i = 0; i < imax; ++i)
    if (reac->getChemEq().getModifiers()[i]->getMetabolite()->getCompartment() == comp)
      return true;

  return false;
}
<|endoftext|>


<commit_before>#include "routing/opening_hours_serdes.hpp"

#include "coding/writer.hpp"

#include "base/assert.hpp"
#include "base/scope_guard.hpp"
#include "base/stl_helpers.hpp"

#include <chrono>

namespace
{
uint16_t GetCurrentYear()
{
  auto const now = std::chrono::system_clock::now();
  std::time_t nowCType = std::chrono::system_clock::to_time_t(now);
  auto parts = std::localtime(&nowCType);
  return 1900 + parts->tm_year;
}

osmoh::RuleSequence GetTwentyFourHourRule()
{
  osmoh::Timespan range;

  auto hm = range.GetStart().GetHourMinutes();
  hm.SetHours(osmoh::HourMinutes::THours(0));
  range.GetStart().SetHourMinutes(hm);

  hm = range.GetStart().GetHourMinutes();
  hm.SetHours(osmoh::HourMinutes::THours(24));
  range.GetEnd().SetHourMinutes(hm);

  osmoh::RuleSequence result;
  result.SetTimes({range});
  return result;
}

bool ShouldSkipYear(osmoh::YearRange const & range, uint16_t currentYear)
{
  if (range.GetStart() > range.GetEnd())
    return false;

  return range.GetStart() < currentYear && range.GetEnd() < currentYear;
}

bool ShouldSkipYear(osmoh::MonthdayRange const & range, uint16_t currentYear)
{
  auto const hasYear = range.GetStart().HasYear() && range.GetEnd().HasYear();
  if (!hasYear)
    return false;

  auto const startYear = range.GetStart().GetYear();
  auto const endYear = range.GetEnd().GetYear();
  if (startYear > endYear)
    return false;

  return hasYear && startYear < currentYear && endYear < currentYear;
}

bool UselessModifier(osmoh::RuleSequence const & rule)
{
  if (rule.GetModifier() == osmoh::RuleSequence::Modifier::Closed ||
      rule.GetModifier() == osmoh::RuleSequence::Modifier::Open)
  {
    return false;
  }
  return true;
}
}  // namespace

namespace routing
{
// OpeningHoursSerDes::Header ----------------------------------------------------------------------
bool OpeningHoursSerDes::Header::IsBinaryFeature(OpeningHoursSerDes::Header::Bits feature)
{
  switch (feature)
  {
  case Header::Bits::Off:
  case Header::Bits::Holiday: return true;
  default: return false;
  }
  UNREACHABLE();
}

// OpeningHoursSerDes ------------------------------------------------------------------------------
OpeningHoursSerDes::OpeningHoursSerDes() : m_currentYear(GetCurrentYear())
{
  uint32_t bit = 1;
  while (static_cast<Header::Bits>(bit) != Header::Bits::Max)
  {
    m_unsupportedFeatures.emplace_back(static_cast<Header::Bits>(bit));
    bit <<= 1U;
  }
}

void OpeningHoursSerDes::Enable(OpeningHoursSerDes::Header::Bits bit)
{
  m_supportedFeatures.emplace_back(bit);
  auto const it = std::find(m_unsupportedFeatures.cbegin(), m_unsupportedFeatures.cend(), bit);
  if (it != m_unsupportedFeatures.cend())
    m_unsupportedFeatures.erase(it);
  base::SortUnique(m_supportedFeatures);
}

bool OpeningHoursSerDes::IsEnabled(OpeningHoursSerDes::Header::Bits bit) const
{
  auto const supIt = std::find(m_supportedFeatures.cbegin(), m_supportedFeatures.cend(), bit);
  auto const unsupIt = std::find(m_unsupportedFeatures.cbegin(), m_unsupportedFeatures.cend(), bit);

  CHECK((supIt == m_supportedFeatures.cend() && unsupIt != m_unsupportedFeatures.cend()) ||
        (supIt != m_supportedFeatures.cend() && unsupIt == m_unsupportedFeatures.cend()), ());

  return supIt != m_supportedFeatures.cend();
}

OpeningHoursSerDes::Header OpeningHoursSerDes::CreateHeader(osmoh::RuleSequence const & rule) const
{
  Header header;
  for (auto const & supportedFeature : m_supportedFeatures)
  {
    if (ExistsFeatureInOpeningHours(supportedFeature, rule))
    {
      header.Set(supportedFeature);
      // We always store minutes if we have hours and vice versa. We divided them to have more
      // atomic serialization and deserialization.
      if (supportedFeature == Header::Bits::Hours)
        header.Set(Header::Bits::Minutes);
      if (supportedFeature == Header::Bits::Minutes)
        header.Set(Header::Bits::Hours);
    }
  }

  return header;
}

bool OpeningHoursSerDes::NotSupported(osmoh::RuleSequence const & rule)
{
  for (auto const unsupportedFeature : m_unsupportedFeatures)
  {
    if (ExistsFeatureInOpeningHours(unsupportedFeature, rule))
      return true;
  }

  return false;
}

bool OpeningHoursSerDes::ExistsFeatureInOpeningHours(Header::Bits feature,
                                                     osmoh::RuleSequence const & rule) const
{
  switch (feature)
  {
  case Header::Bits::Year:
  {
    // 2019-2020 it is just |rule.HasYears()|
    // 2019 Apr - 2020 May - it is year in |osmoh::MonthDay| and |rule.HasYear()| == false.
    if (rule.HasYears())
      return true;

    for (auto const & monthRange : rule.GetMonths())
    {
      if (monthRange.GetStart().HasYear() && monthRange.GetEnd().HasYear())
        return true;
    }

    return false;
  }
  case Header::Bits::Month: return rule.HasMonths();
  case Header::Bits::MonthDay: return rule.HasMonthDay();
  case Header::Bits::WeekDay: return rule.GetWeekdays().HasWeekday();
  case Header::Bits::Hours:
  case Header::Bits::Minutes: return rule.HasTimes() || rule.IsTwentyFourHours();
  case Header::Bits::Off: return rule.GetModifier() == osmoh::RuleSequence::Modifier::Closed;
  case Header::Bits::Holiday: return rule.GetWeekdays().HasHolidays();
  case Header::Bits::Max: CHECK(false, ());
  }
  UNREACHABLE();
}

bool OpeningHoursSerDes::CheckSupportedFeatures() const
{
  if (IsEnabled(Header::Bits::MonthDay) && !IsEnabled(Header::Bits::Month))
    CHECK(false, ("Cannot use MonthDay without Month."));

  if (IsEnabled(Header::Bits::Hours) != !IsEnabled(Header::Bits::Minutes))
    CHECK(false, ("Cannot use Hours without Minutes and vice versa."));

  return true;
}

std::vector<osmoh::RuleSequence> OpeningHoursSerDes::DecomposeOh(osmoh::OpeningHours const & oh)
{
  auto const apply = [&](auto & rules, auto const & ranges, auto const & rangeSetter) {
    if (ranges.empty())
      return;

    std::vector<osmoh::RuleSequence> originalRules = std::move(rules);
    if (originalRules.empty())
      originalRules.emplace_back();

    rules.clear();
    for (auto const & range : ranges)
    {
      auto const toDo = [&range, &rangeSetter](auto & rule) { rangeSetter(range, rule); };
      for (auto const & originalRule : originalRules)
      {
        auto rule = originalRule;
        toDo(rule);
        rules.emplace_back(std::move(rule));
      }
    }
  };

  std::vector<osmoh::RuleSequence> finalRules;
  for (auto const & rule : oh.GetRule())
  {
    std::vector<osmoh::RuleSequence> rules;

    if (rule.IsTwentyFourHours())
    {
      static auto const kTwentyFourHourRule = GetTwentyFourHourRule();
      finalRules.emplace_back(kTwentyFourHourRule);
      break;
    }

    // Ir rule has just modifier (opening hours: "closed") it is empty but has a useful modifier.
    if (rule.IsEmpty() && UselessModifier(rule))
      continue;

    bool badRule = false;
    apply(rules, rule.GetYears(),
          [&](osmoh::YearRange const & range, osmoh::RuleSequence & item) {
            if (ShouldSkipYear(range, m_currentYear))
              badRule = true;

            item.SetYears({range});
          });

    apply(rules, rule.GetMonths(),
          [&](osmoh::MonthdayRange const & range, osmoh::RuleSequence & item) {
            if (ShouldSkipYear(range, m_currentYear))
              badRule = true;

            item.SetMonths({range});
          });

    if (badRule)
      continue;

    apply(rules, rule.GetWeekdays().GetWeekdayRanges(),
          [](osmoh::WeekdayRange const & range, osmoh::RuleSequence & item) {
            osmoh::Weekdays weekdays;
            weekdays.SetWeekdayRanges({range});
            item.SetWeekdays(weekdays);
          });

    apply(rules, rule.GetWeekdays().GetHolidays(),
          [](osmoh::Holiday const & holiday, osmoh::RuleSequence & item) {
            auto weekdays = item.GetWeekdays();
            weekdays.SetHolidays({holiday});
            item.SetWeekdays(weekdays);
          });

    apply(rules, rule.GetTimes(),
          [](osmoh::Timespan const & range, osmoh::RuleSequence & item) {
            item.SetTimes({range});
          });

    apply(rules, std::vector<osmoh::RuleSequence::Modifier>{rule.GetModifier()},
          [](osmoh::RuleSequence::Modifier modifier, osmoh::RuleSequence & item) {
            item.SetModifier(modifier);
          });

    finalRules.insert(finalRules.end(), rules.begin(), rules.end());
  }

  std::vector<osmoh::RuleSequence> filteredRules;
  for (auto rule : finalRules)
  {
    if (NotSupported(rule))
      continue;

    filteredRules.emplace_back(std::move(rule));
  }

  return filteredRules;
}

bool OpeningHoursSerDes::HaveSameHeaders(
    std::vector<osmoh::RuleSequence> const & decomposedOhs) const
{
  CHECK(!decomposedOhs.empty(), ());
  Header const header = CreateHeader(decomposedOhs.front());
  for (auto const & oh : decomposedOhs)
  {
    if (header != CreateHeader(oh))
      return false;
  }

  return true;
}

uint8_t OpeningHoursSerDes::GetBitsNumber(Header::Bits type) const
{
  switch (type)
  {
  case Header::Bits::Year: return 8;  // store value such that 2000 + value equals to real year
  case Header::Bits::Month: return 4;
  case Header::Bits::MonthDay: return 5;
  case Header::Bits::WeekDay: return 3;
  case Header::Bits::Hours: return 5;
  case Header::Bits::Minutes: return 6;
  case Header::Bits::Off: return 1;
  case Header::Bits::Holiday: return 1;
  case Header::Bits::Max: UNREACHABLE();
  }
  UNREACHABLE();
}

bool OpeningHoursSerDes::CheckYearRange(osmoh::MonthDay::TYear start,
                                        osmoh::MonthDay::TYear end) const
{
  if (start < kYearBias || end < kYearBias)
    return false;

  // Should be filtered after |DecomposeOh| method.
  CHECK(start <= end && end >= m_currentYear, (start, end, m_currentYear));
  return true;
}

bool OpeningHoursSerDes::IsTwentyFourHourRule(osmoh::RuleSequence const & rule) const
{
  static auto const kTwentyFourHourStart = osmoh::HourMinutes(osmoh::HourMinutes::THours(0));
  static auto const kTwentyFourHourEnd = osmoh::HourMinutes(osmoh::HourMinutes::THours(24));

  return rule.GetModifier() != osmoh::RuleSequence::Modifier::Closed &&
         rule.GetYears().empty() && rule.GetWeekdays().GetWeekdayRanges().empty() &&
         rule.GetMonths().empty() && rule.GetTimes().size() == 1 &&
         rule.GetTimes().back().GetStart() == kTwentyFourHourStart &&
         rule.GetTimes().back().GetEnd() == kTwentyFourHourEnd;
}

bool OpeningHoursSerDes::IsSerializable(osmoh::OpeningHours const & openingHours)
{
  std::vector<uint8_t> buffer;
  MemWriter memWriter(buffer);
  BitWriter tmpBitWriter(memWriter);

  return SerializeImpl(tmpBitWriter, openingHours);
}
}  // namespace routing
<commit_msg>[routing] Minor fix after PVS-Studio report fixes.<commit_after>#include "routing/opening_hours_serdes.hpp"

#include "coding/writer.hpp"

#include "base/assert.hpp"
#include "base/scope_guard.hpp"
#include "base/stl_helpers.hpp"

#include <chrono>

namespace
{
uint16_t GetCurrentYear()
{
  auto const now = std::chrono::system_clock::now();
  std::time_t nowCType = std::chrono::system_clock::to_time_t(now);
  auto parts = std::localtime(&nowCType);
  return 1900 + parts->tm_year;
}

osmoh::RuleSequence GetTwentyFourHourRule()
{
  osmoh::Timespan range;

  auto hm = range.GetStart().GetHourMinutes();
  hm.SetHours(osmoh::HourMinutes::THours(0));
  range.GetStart().SetHourMinutes(hm);

  hm = range.GetStart().GetHourMinutes();
  hm.SetHours(osmoh::HourMinutes::THours(24));
  range.GetEnd().SetHourMinutes(hm);

  osmoh::RuleSequence result;
  result.SetTimes({range});
  return result;
}

bool ShouldSkipYear(osmoh::YearRange const & range, uint16_t currentYear)
{
  if (range.GetStart() > range.GetEnd())
    return false;

  return range.GetStart() < currentYear && range.GetEnd() < currentYear;
}

bool ShouldSkipYear(osmoh::MonthdayRange const & range, uint16_t currentYear)
{
  auto const hasYear = range.GetStart().HasYear() && range.GetEnd().HasYear();
  if (!hasYear)
    return false;

  auto const startYear = range.GetStart().GetYear();
  auto const endYear = range.GetEnd().GetYear();
  if (startYear > endYear)
    return false;

  return hasYear && startYear < currentYear && endYear < currentYear;
}

bool UselessModifier(osmoh::RuleSequence const & rule)
{
  if (rule.GetModifier() == osmoh::RuleSequence::Modifier::Closed ||
      rule.GetModifier() == osmoh::RuleSequence::Modifier::Open)
  {
    return false;
  }
  return true;
}
}  // namespace

namespace routing
{
// OpeningHoursSerDes::Header ----------------------------------------------------------------------
bool OpeningHoursSerDes::Header::IsBinaryFeature(OpeningHoursSerDes::Header::Bits feature)
{
  switch (feature)
  {
  case Header::Bits::Off:
  case Header::Bits::Holiday: return true;
  default: return false;
  }
  UNREACHABLE();
}

// OpeningHoursSerDes ------------------------------------------------------------------------------
OpeningHoursSerDes::OpeningHoursSerDes() : m_currentYear(GetCurrentYear())
{
  uint32_t bit = 1;
  while (static_cast<Header::Bits>(bit) != Header::Bits::Max)
  {
    m_unsupportedFeatures.emplace_back(static_cast<Header::Bits>(bit));
    bit <<= 1U;
  }
}

void OpeningHoursSerDes::Enable(OpeningHoursSerDes::Header::Bits bit)
{
  m_supportedFeatures.emplace_back(bit);
  auto const it = std::find(m_unsupportedFeatures.cbegin(), m_unsupportedFeatures.cend(), bit);
  if (it != m_unsupportedFeatures.cend())
    m_unsupportedFeatures.erase(it);
  base::SortUnique(m_supportedFeatures);
}

bool OpeningHoursSerDes::IsEnabled(OpeningHoursSerDes::Header::Bits bit) const
{
  auto const supIt = std::find(m_supportedFeatures.cbegin(), m_supportedFeatures.cend(), bit);
  auto const unsupIt = std::find(m_unsupportedFeatures.cbegin(), m_unsupportedFeatures.cend(), bit);

  CHECK((supIt == m_supportedFeatures.cend() && unsupIt != m_unsupportedFeatures.cend()) ||
        (supIt != m_supportedFeatures.cend() && unsupIt == m_unsupportedFeatures.cend()), ());

  return supIt != m_supportedFeatures.cend();
}

OpeningHoursSerDes::Header OpeningHoursSerDes::CreateHeader(osmoh::RuleSequence const & rule) const
{
  Header header;
  for (auto const & supportedFeature : m_supportedFeatures)
  {
    if (ExistsFeatureInOpeningHours(supportedFeature, rule))
    {
      header.Set(supportedFeature);
      // We always store minutes if we have hours and vice versa. We divided them to have more
      // atomic serialization and deserialization.
      if (supportedFeature == Header::Bits::Hours)
        header.Set(Header::Bits::Minutes);
      if (supportedFeature == Header::Bits::Minutes)
        header.Set(Header::Bits::Hours);
    }
  }

  return header;
}

bool OpeningHoursSerDes::NotSupported(osmoh::RuleSequence const & rule)
{
  for (auto const unsupportedFeature : m_unsupportedFeatures)
  {
    if (ExistsFeatureInOpeningHours(unsupportedFeature, rule))
      return true;
  }

  return false;
}

bool OpeningHoursSerDes::ExistsFeatureInOpeningHours(Header::Bits feature,
                                                     osmoh::RuleSequence const & rule) const
{
  switch (feature)
  {
  case Header::Bits::Year:
  {
    // 2019-2020 it is just |rule.HasYears()|
    // 2019 Apr - 2020 May - it is year in |osmoh::MonthDay| and |rule.HasYear()| == false.
    if (rule.HasYears())
      return true;

    for (auto const & monthRange : rule.GetMonths())
    {
      if (monthRange.GetStart().HasYear() && monthRange.GetEnd().HasYear())
        return true;
    }

    return false;
  }
  case Header::Bits::Month: return rule.HasMonths();
  case Header::Bits::MonthDay: return rule.HasMonthDay();
  case Header::Bits::WeekDay: return rule.GetWeekdays().HasWeekday();
  case Header::Bits::Hours:
  case Header::Bits::Minutes: return rule.HasTimes() || rule.IsTwentyFourHours();
  case Header::Bits::Off: return rule.GetModifier() == osmoh::RuleSequence::Modifier::Closed;
  case Header::Bits::Holiday: return rule.GetWeekdays().HasHolidays();
  case Header::Bits::Max: CHECK(false, ());
  }
  UNREACHABLE();
}

bool OpeningHoursSerDes::CheckSupportedFeatures() const
{
  if (IsEnabled(Header::Bits::MonthDay) && !IsEnabled(Header::Bits::Month))
    CHECK(false, ("Cannot use MonthDay without Month."));

  if (IsEnabled(Header::Bits::Hours) != IsEnabled(Header::Bits::Minutes))
    CHECK(false, ("Cannot use Hours without Minutes and vice versa."));

  return true;
}

std::vector<osmoh::RuleSequence> OpeningHoursSerDes::DecomposeOh(osmoh::OpeningHours const & oh)
{
  auto const apply = [&](auto & rules, auto const & ranges, auto const & rangeSetter) {
    if (ranges.empty())
      return;

    std::vector<osmoh::RuleSequence> originalRules = std::move(rules);
    if (originalRules.empty())
      originalRules.emplace_back();

    rules.clear();
    for (auto const & range : ranges)
    {
      auto const toDo = [&range, &rangeSetter](auto & rule) { rangeSetter(range, rule); };
      for (auto const & originalRule : originalRules)
      {
        auto rule = originalRule;
        toDo(rule);
        rules.emplace_back(std::move(rule));
      }
    }
  };

  std::vector<osmoh::RuleSequence> finalRules;
  for (auto const & rule : oh.GetRule())
  {
    std::vector<osmoh::RuleSequence> rules;

    if (rule.IsTwentyFourHours())
    {
      static auto const kTwentyFourHourRule = GetTwentyFourHourRule();
      finalRules.emplace_back(kTwentyFourHourRule);
      break;
    }

    // Ir rule has just modifier (opening hours: "closed") it is empty but has a useful modifier.
    if (rule.IsEmpty() && UselessModifier(rule))
      continue;

    bool badRule = false;
    apply(rules, rule.GetYears(),
          [&](osmoh::YearRange const & range, osmoh::RuleSequence & item) {
            if (ShouldSkipYear(range, m_currentYear))
              badRule = true;

            item.SetYears({range});
          });

    apply(rules, rule.GetMonths(),
          [&](osmoh::MonthdayRange const & range, osmoh::RuleSequence & item) {
            if (ShouldSkipYear(range, m_currentYear))
              badRule = true;

            item.SetMonths({range});
          });

    if (badRule)
      continue;

    apply(rules, rule.GetWeekdays().GetWeekdayRanges(),
          [](osmoh::WeekdayRange const & range, osmoh::RuleSequence & item) {
            osmoh::Weekdays weekdays;
            weekdays.SetWeekdayRanges({range});
            item.SetWeekdays(weekdays);
          });

    apply(rules, rule.GetWeekdays().GetHolidays(),
          [](osmoh::Holiday const & holiday, osmoh::RuleSequence & item) {
            auto weekdays = item.GetWeekdays();
            weekdays.SetHolidays({holiday});
            item.SetWeekdays(weekdays);
          });

    apply(rules, rule.GetTimes(),
          [](osmoh::Timespan const & range, osmoh::RuleSequence & item) {
            item.SetTimes({range});
          });

    apply(rules, std::vector<osmoh::RuleSequence::Modifier>{rule.GetModifier()},
          [](osmoh::RuleSequence::Modifier modifier, osmoh::RuleSequence & item) {
            item.SetModifier(modifier);
          });

    finalRules.insert(finalRules.end(), rules.begin(), rules.end());
  }

  std::vector<osmoh::RuleSequence> filteredRules;
  for (auto rule : finalRules)
  {
    if (NotSupported(rule))
      continue;

    filteredRules.emplace_back(std::move(rule));
  }

  return filteredRules;
}

bool OpeningHoursSerDes::HaveSameHeaders(
    std::vector<osmoh::RuleSequence> const & decomposedOhs) const
{
  CHECK(!decomposedOhs.empty(), ());
  Header const header = CreateHeader(decomposedOhs.front());
  for (auto const & oh : decomposedOhs)
  {
    if (header != CreateHeader(oh))
      return false;
  }

  return true;
}

uint8_t OpeningHoursSerDes::GetBitsNumber(Header::Bits type) const
{
  switch (type)
  {
  case Header::Bits::Year: return 8;  // store value such that 2000 + value equals to real year
  case Header::Bits::Month: return 4;
  case Header::Bits::MonthDay: return 5;
  case Header::Bits::WeekDay: return 3;
  case Header::Bits::Hours: return 5;
  case Header::Bits::Minutes: return 6;
  case Header::Bits::Off: return 1;
  case Header::Bits::Holiday: return 1;
  case Header::Bits::Max: UNREACHABLE();
  }
  UNREACHABLE();
}

bool OpeningHoursSerDes::CheckYearRange(osmoh::MonthDay::TYear start,
                                        osmoh::MonthDay::TYear end) const
{
  if (start < kYearBias || end < kYearBias)
    return false;

  // Should be filtered after |DecomposeOh| method.
  CHECK(start <= end && end >= m_currentYear, (start, end, m_currentYear));
  return true;
}

bool OpeningHoursSerDes::IsTwentyFourHourRule(osmoh::RuleSequence const & rule) const
{
  static auto const kTwentyFourHourStart = osmoh::HourMinutes(osmoh::HourMinutes::THours(0));
  static auto const kTwentyFourHourEnd = osmoh::HourMinutes(osmoh::HourMinutes::THours(24));

  return rule.GetModifier() != osmoh::RuleSequence::Modifier::Closed &&
         rule.GetYears().empty() && rule.GetWeekdays().GetWeekdayRanges().empty() &&
         rule.GetMonths().empty() && rule.GetTimes().size() == 1 &&
         rule.GetTimes().back().GetStart() == kTwentyFourHourStart &&
         rule.GetTimes().back().GetEnd() == kTwentyFourHourEnd;
}

bool OpeningHoursSerDes::IsSerializable(osmoh::OpeningHours const & openingHours)
{
  std::vector<uint8_t> buffer;
  MemWriter memWriter(buffer);
  BitWriter tmpBitWriter(memWriter);

  return SerializeImpl(tmpBitWriter, openingHours);
}
}  // namespace routing
<|endoftext|>


<commit_before><commit_msg>[localization] fix missing string<commit_after><|endoftext|>