/*
 *
 *
 *
 */
#include "DjVuFileImplementation.h"

#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Directory.h"

#include "../PdfWriter/PdfRenderer.h"

#include "../DesktopEditor/graphics/pro/Fonts.h"
#include "../DesktopEditor/graphics/pro/Graphics.h"
#include "../DesktopEditor/graphics/pro/Image.h"

#include "../DesktopEditor/common/StringExt.h"

#define VER_DPI		96
#define HOR_DPI		96

#include <vector>

namespace NSDjvu
{
	static GUTF8String MakeUTF8String(const std::wstring& wsText)
	{
        std::string sText = NSFile::CUtf8Converter::GetUtf8StringFromUnicode(wsText);
        GUTF8String utf8String(sText.c_str());
        return utf8String;
	}
    static std::string MakeCString(GUTF8String& strText)
	{
		std::string sString(strText.getbuf());
        return sString;
	}
	static int     GetInteger(const std::wstring& wsString)
	{
		if (wsString.size() < 1)
			return 0;

		try
		{
            return std::stoi(wsString);
		}
		catch (...)
		{
		}

		try
		{
            return static_cast<int>(std::stol(wsString));
		}
		catch (...)
		{
			return 0;
		}
	}
}

CDjVuFileImplementation::CDjVuFileImplementation(NSFonts::IApplicationFonts* pFonts)
{
	m_pDoc = NULL;
	std::wstring wsTempPath = NSFile::CFileBinary::GetTempPath();
	wsTempPath += L"DJVU\\";
	m_wsTempDirectory = wsTempPath;
	NSDirectory::CreateDirectory(m_wsTempDirectory);

    m_pApplicationFonts = pFonts;
}
CDjVuFileImplementation::~CDjVuFileImplementation()
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);
}
bool               CDjVuFileImplementation::LoadFromFile(const std::wstring& wsSrcFileName, const std::wstring& wsXMLOptions)
{
	m_pDoc = NULL;
	try
	{
		GUTF8String utf8;
		GURL url = GURL::Filename::UTF8(NSDjvu::MakeUTF8String(wsSrcFileName));
		m_pDoc = DjVuDocument::create(url);
		m_pDoc->wait_get_pages_num();
	}
	catch (...)
	{
		return false;
	}

	return true;
}
void               CDjVuFileImplementation::Close()
{
}
std::wstring       CDjVuFileImplementation::GetTempDirectory() const
{
	return m_wsTempDirectory;
}
void               CDjVuFileImplementation::SetTempDirectory(const std::wstring& wsDirectory)
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);

	m_wsTempDirectory = wsDirectory;
	m_wsTempDirectory += L"\\DJVU\\";
	NSDirectory::CreateDirectory(m_wsTempDirectory);
}
int                CDjVuFileImplementation::GetPagesCount() const
{
	if (!m_pDoc)
		return 0;

	return m_pDoc->get_pages_num();
}
void               CDjVuFileImplementation::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY) const
{
	if (!m_pDoc)
	{
		*pdWidth  = 0;
		*pdHeight = 0;

		*pdDpiX = 96;
		*pdDpiY = 96;
	}

#if 0
	GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);

	pPage->wait_for_complete_decode();
	pPage->set_rotate(0);

	*pdWidth = pPage->get_real_width();
	*pdHeight = pPage->get_real_height();

	*pdDpiX = pPage->get_dpi();
	*pdDpiY = pPage->get_dpi();
#endif
    int nW = 0;
    int nH = 0;
    int nDpi = 0;
    m_pDoc->ReadPageInfo(nPageIndex, nW, nH, nDpi);
    *pdWidth = nW;
    *pdHeight = nH;
    *pdDpiX = nDpi;
    *pdDpiY = nDpi;
}
void               CDjVuFileImplementation::DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak)
{
	if (!m_pDoc)
		return;

	try
	{
		GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);
		pPage->wait_for_complete_decode();
		pPage->set_rotate(0);

		long lRendererType = c_nUnknownRenderer;
		pRenderer->get_Type(&lRendererType);
		if (false)//c_nGrRenderer == lRendererType)
		{
			CreateGrFrame(pRenderer, pPage, pBreak);
		}
		else if (c_nPDFWriter == lRendererType)
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreatePdfFrame(pRenderer, pPage, nPageIndex, oText);
		}
		else
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreateFrame(pRenderer, pPage, nPageIndex, oText);
		}
	}
	catch (...)
	{
	}
}
void               CDjVuFileImplementation::ConvertToRaster(int nPageIndex, const std::wstring& wsDstPath, int nImageType, const int& nRasterW, const int& nRasterH)
{
    if (!m_pApplicationFonts)
        return;

    NSFonts::IFontManager *pFontManager = m_pApplicationFonts->GenerateFontManager();
    NSFonts::IFontsCache* pFontCache = NSFonts::NSFontCache::Create();
    pFontCache->SetStreams(m_pApplicationFonts->GetStreams());
	pFontManager->SetOwnerCache(pFontCache);

    NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create();
    pRenderer->SetFontManager(pFontManager);

	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiX);

    int nWidth  = (nRasterW > 0) ? nRasterW : ((int)dWidth * 96 / dPageDpiX);
    int nHeight = (nRasterH > 0) ? nRasterH : ((int)dHeight * 96 / dPageDpiX);

	BYTE* pBgraData = new BYTE[nWidth * nHeight * 4];
	if (!pBgraData)
		return;

	memset(pBgraData, 0xff, nWidth * nHeight * 4);
	CBgraFrame oFrame;
	oFrame.put_Data(pBgraData);
	oFrame.put_Width(nWidth);
	oFrame.put_Height(nHeight);
	oFrame.put_Stride(-4 * nWidth);

    pRenderer->CreateFromBgraFrame(&oFrame);
    pRenderer->SetSwapRGB(false);
    pRenderer->put_Width(dWidth);
    pRenderer->put_Height(dHeight);

	bool bBreak = false;
    DrawPageOnRenderer(pRenderer, nPageIndex, &bBreak);

	oFrame.SaveFile(wsDstPath, nImageType);
	RELEASEINTERFACE(pFontManager);
    RELEASEOBJECT(pRenderer);
}
void               CDjVuFileImplementation::ConvertToPdf(const std::wstring& wsDstPath)
{
    CPdfRenderer oPdf(m_pApplicationFonts);

	bool bBreak = false;
	for (int nPageIndex = 0, nPagesCount = GetPagesCount(); nPageIndex < nPagesCount; nPageIndex++)
	{
		oPdf.NewPage();

		double dPageDpiX, dPageDpiY;
		double dWidth, dHeight;
		GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
		dWidth  *= 25.4 / dPageDpiX;
		dHeight *= 25.4 / dPageDpiY;
		oPdf.put_Width(dWidth);
		oPdf.put_Height(dHeight);

		DrawPageOnRenderer(&oPdf, nPageIndex, &bBreak);
		printf("%d of %d pages\n", nPageIndex + 1, nPagesCount);
	}

	oPdf.SaveToFile(wsDstPath);
}
void               CDjVuFileImplementation::CreateFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPage, XmlUtils::CXmlNode& text)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	int nDpi = pPage->get_dpi();

	double dPixToMM = 25.4;

	double dRendDpiX = 0;
	double dRendDpiY = 0;

	pRenderer->get_DpiX(&dRendDpiX);
	pRenderer->get_DpiY(&dRendDpiY);

	if (0 >= dRendDpiX)
		dRendDpiX = 72.0;
	if (0 >= dRendDpiY)
		dRendDpiY = 72.0;

	double dRendWidth = 0;
	double dRendHeight = 0;

	pRenderer->get_Width(&dRendWidth);
	pRenderer->get_Height(&dRendHeight);

	if (0 >= dRendWidth)
		dRendWidth = 200;
	if (0 >= dRendHeight)
		dRendHeight = 300;

	LONG lImageWidth	= (LONG)(dRendDpiX * dRendWidth / dPixToMM);
	LONG lImageHeight	= (LONG)(dRendDpiY * dRendHeight / dPixToMM);

	long lRendererType = c_nUnknownRenderer;
	pRenderer->get_Type(&lRendererType);
	if (c_nPDFWriter == lRendererType)
	{
		lImageWidth	 = pPage->get_real_width();
		lImageHeight = pPage->get_real_height();
	}
	else if (c_nHtmlRendrerer == lRendererType)
	{
		//VARIANT var;
		//LONG lMaxWidth = var.lVal;
	}

	BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
	if (!pBufferDst)
		return;

	Aggplus::CImage oImage;
	oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);
	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			BYTE* pLine = pBitmap->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
			{

			}
		}

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = lImageHeight - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();

                unsigned int* palette = new unsigned int[nPaletteEntries];

				// Create palette for the bitmap
				int color = 0xff0000;
				int decrement = color / (nPaletteEntries - 1);
				for (int i = 0; i < nPaletteEntries; ++i)
				{
					BYTE level = (BYTE)(color >> 16);
					palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
					color -= decrement;
				}

                unsigned int* pBuffer = (unsigned int*)pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					BYTE* pLine = pBitmap->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
					{
						if (*pLine < nPaletteEntries)
						{
							*pBuffer = palette[*pLine];
						}
						else
						{
							*pBuffer = palette[0];
						}
					}
				}

				RELEASEARRAYOBJECTS(palette);
			}
		}
	}

	pRenderer->BeginCommand(c_nPageType);

	if (c_nGrRenderer != lRendererType && c_nHtmlRendrerer != lRendererType && c_nHtmlRendrerer2 != lRendererType)
	{
		TextToRenderer(pRenderer, text, dPixToMM / nDpi);
	}

	pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dRendWidth, dRendHeight);
	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreatePdfFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPageIndex, XmlUtils::CXmlNode& oText)
{
	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
	dWidth  *= 25.4 / dPageDpiX;
	dHeight *= 25.4 / dPageDpiY;

	pRenderer->BeginCommand(c_nPageType);

	TextToRenderer(pRenderer, oText, 25.4 / pPage->get_dpi());

	LONG lImageWidth  = pPage->get_real_width();
	LONG lImageHeight = pPage->get_real_height();

	CPdfRenderer* pPdf = (CPdfRenderer*)pRenderer;

	if (pPage->is_legal_photo())
	{
		BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
		if (!pBufferDst)
			return;

		Aggplus::CImage oImage;
		oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
		pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
	}
	else if (pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<IW44Image> pIW44Image = pPage->get_bg44();
		if (NULL != pIW44Image)
		{
			int nBgWidth  = pIW44Image->get_width();
			int nBgHeight = pIW44Image->get_height();

			GP<GPixmap> pBgImage = pIW44Image->get_pixmap();
			if (NULL != pBgImage)
			{
				BYTE* pBgBuffer = new BYTE[4 * nBgWidth * nBgHeight];
				if (!pBgBuffer)
					return;

				Aggplus::CImage oBgImage;
				oBgImage.Create(pBgBuffer, nBgWidth, nBgHeight, 4 * nBgWidth);

				BYTE* pBuffer = pBgBuffer;
				for (int j = nBgHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pBgImage->operator [](j);

					for (int i = 0; i < nBgWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oBgImage, 0, 0, dWidth, dHeight);
			}
		}

		GP<GPixmap> pImage = pPage->get_fgpm();
		if (NULL == pImage)
			pImage = pPage->get_fg_pixmap(oRectAll);

		if (NULL != pImage)
		{
			unsigned int unPixmapH = pImage->rows();
			unsigned int unPixmapW = pImage->columns();
			BYTE* pBufferDst = new BYTE[4 * unPixmapH * unPixmapW];
			if (!pBufferDst)
				return;

			Aggplus::CImage oImage;
			oImage.Create(pBufferDst, unPixmapW, unPixmapH, 4 * unPixmapW);

			BYTE* pBuffer = pBufferDst;
			for (int j = unPixmapH - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < unPixmapW; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

            NSImages::CPixJbig2 oPix;
            if (oPix.Create(lImageWidth, lImageHeight, 1))
			{
				for (int nY = 0; nY < lImageHeight; nY++)
				{
					BYTE* pLine = pBitmap->operator [](nY);
					for (int nX = 0; nX < lImageWidth; nX++, pLine++)
					{
                        oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
					}
				}

                pPdf->DrawImageWith1bppMask((IGrObject*)&oImage, &oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                oPix.Destroy();
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

        NSImages::CPixJbig2 oPix;
        if (oPix.Create(lImageWidth, lImageHeight, 1))
		{
			for (int nY = 0; nY < lImageHeight; nY++)
			{
				BYTE* pLine = pBitmap->operator [](nY);
				for (int nX = 0; nX < lImageWidth; nX++, pLine++)
				{
                    oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
				}
			}

            pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
            oPix.Destroy();
		}
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
			if (pBufferDst)
			{
				Aggplus::CImage oImage;
				oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

				BYTE* pBuffer = pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pImage->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();
				if (nPaletteEntries <= 2)
				{
                    NSImages::CPixJbig2 oPix;
                    if (oPix.Create(lImageWidth, lImageHeight, 1))
					{
						for (int nY = 0; nY < lImageHeight; nY++)
						{
							BYTE* pLine = pBitmap->operator [](nY);
							for (int nX = 0; nX < lImageWidth; nX++, pLine++)
							{
                                oPix.Create(nX, lImageHeight - 1 - nY, *pLine);
							}
						}

                        pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                        oPix.Destroy();
					}
				}
				else
				{
					BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
					if (!pBufferDst)
						return;

					Aggplus::CImage oImage;
					oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

                    unsigned int* palette = new unsigned int[nPaletteEntries];

					// Create palette for the bitmap
					int color = 0xff0000;
					int decrement = color / (nPaletteEntries - 1);
					for (int i = 0; i < nPaletteEntries; ++i)
					{
						BYTE level = (BYTE)(color >> 16);
						palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
						color -= decrement;
					}

                    unsigned int* pBuffer = (unsigned int*)pBufferDst;
					for (int j = lImageHeight - 1; j >= 0; --j)
					{
						BYTE* pLine = pBitmap->operator [](j);

						for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
						{
							if (*pLine < nPaletteEntries)
							{
								*pBuffer = palette[*pLine];
							}
							else
							{
								*pBuffer = palette[0];
							}
						}
					}

					RELEASEARRAYOBJECTS(palette);
					pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
				}
			}
		}
	}

	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreateGrFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, bool* pBreak)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	BYTE*	pBufferDst	 = NULL;
	LONG	lImageWidth	 = 0;
	LONG	lImageHeight = 0;

	//VARIANT var;

	volatile bool* pCancel = pBreak;

	if ((NULL != pCancel) && (TRUE == *pCancel))
		return;

	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = 0; j < lImageHeight; ++j)
		{
			GPixel* pLine = pImage->operator [](j);

			if ((NULL != pCancel) && (TRUE == *pCancel))
				return;

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;
		for (int j = 0; j < lImageHeight; ++j)
		{
			BYTE* pLine = pBitmap->operator [](j);

			if ((NULL != pCancel) && (TRUE == *pCancel))
				return;

			for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
			{
				if (*pLine < nPaletteEntries)
				{
					*pBuffer = palette[*pLine];
				}
				else
				{
					*pBuffer = palette[0];
				}
			}
		}

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				GPixel* pLine = pImage->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			return;
		}

		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

		if (NULL != pBitmap)
		{
			int nPaletteEntries = pBitmap->get_grays();

            unsigned int* palette = new unsigned int[nPaletteEntries];

			// Create palette for the bitmap
			int color = 0xff0000;
			int decrement = color / (nPaletteEntries - 1);
			for (int i = 0; i < nPaletteEntries; ++i)
			{
				BYTE level = (BYTE)(color >> 16);
				palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
				color -= decrement;
			}

            unsigned int* pBuffer = (unsigned int*)pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				BYTE* pLine = pBitmap->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
				{
					if (*pLine < nPaletteEntries)
					{
						*pBuffer = palette[*pLine];
					}
					else
					{
						*pBuffer = palette[0];
					}
				}
			}

			RELEASEARRAYOBJECTS(palette);
		}
	}
}
XmlUtils::CXmlNode CDjVuFileImplementation::ParseText(GP<DjVuImage> pPage)
{
	XmlUtils::CXmlNode paragraph;
	const GP<DjVuText> text(DjVuText::create());
	const GP<ByteStream> text_str(pPage->get_text());
	if (text_str)
	{
		text->decode(text_str);
		GUTF8String pageText = text->get_xmlText(pPage->get_height());
		XmlUtils::CXmlNode hiddenText;
		XmlUtils::CXmlNode pageColumn;
		XmlUtils::CXmlNode region;
        hiddenText.FromXmlStringA(NSDjvu::MakeCString(pageText));
        hiddenText.GetNode(L"PAGECOLUMN", pageColumn);
        pageColumn.GetNode(L"REGION", region);
        region.GetNode(L"PARAGRAPH", paragraph);
	}
	return paragraph;
}
void               CDjVuFileImplementation::TextToRenderer(IRenderer* pRenderer, XmlUtils::CXmlNode oTextNode, double dKoef, bool isView)
{
	pRenderer->put_FontName(L"DjvuEmptyFont");
	XmlUtils::CXmlNodes oLinesNodes;
	oTextNode.GetNodes(L"LINE", oLinesNodes);
	for (int nLineIndex = 0; nLineIndex < oLinesNodes.GetCount(); ++nLineIndex)
	{
		XmlUtils::CXmlNode oLineNode;
		oLinesNodes.GetAt(nLineIndex, oLineNode);
		XmlUtils::CXmlNodes oWordsNodes;
		oLineNode.GetNodes(L"WORD", oWordsNodes);
		for (int nWordIndex = 0; nWordIndex < oWordsNodes.GetCount(); ++nWordIndex)
		{
			XmlUtils::CXmlNode oWordNode;
			oWordsNodes.GetAt(nWordIndex, oWordNode);
            std::wstring csWord   = oWordNode.GetText();
            std::wstring csCoords = oWordNode.GetAttribute(L"coords");
			double arrCoords[4];
            ParseCoords(csCoords, arrCoords, dKoef);
            DrawPageText(pRenderer, arrCoords, csWord);
		}
	}
}
void               CDjVuFileImplementation::DrawPageText(IRenderer* pRenderer, double* pdCoords, const std::wstring& wsText)
{
	pRenderer->put_FontSize(pdCoords[1] - pdCoords[3]);
	pRenderer->CommandDrawText(wsText,
							   (float)(pdCoords[0]),
							   (float)(pdCoords[3]),
							   (float)(pdCoords[2] - pdCoords[0]),
							   (float)(pdCoords[1] - pdCoords[3]));
}
void               CDjVuFileImplementation::ParseCoords(const std::wstring& wsCoordsStr, double* pdCoords, double dKoef)
{
    std::vector<std::wstring> vCoords = NSStringExt::Split(wsCoordsStr, L',');
	if (vCoords.size() >= 4)
	{
		for (int nIndex = 0; nIndex < 4; nIndex++)
		{
			pdCoords[nIndex] = NSDjvu::GetInteger(vCoords.at(nIndex)) * dKoef;
		}
	}
}


/*
 *
 *
 *
 */
#include "DjVuFileImplementation.h"

#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Directory.h"

#include "../PdfWriter/PdfRenderer.h"

#include "../DesktopEditor/graphics/pro/Fonts.h"
#include "../DesktopEditor/graphics/pro/Graphics.h"
#include "../DesktopEditor/graphics/pro/Image.h"

#include "../DesktopEditor/common/StringExt.h"

#define VER_DPI		96
#define HOR_DPI		96

#include <vector>

namespace NSDjvu
{
	static GUTF8String MakeUTF8String(const std::wstring& wsText)
	{
        std::string sText = NSFile::CUtf8Converter::GetUtf8StringFromUnicode(wsText);
        GUTF8String utf8String(sText.c_str());
        return utf8String;
	}
    static std::string MakeCString(GUTF8String& strText)
	{
		std::string sString(strText.getbuf());
        return sString;
	}
	static int     GetInteger(const std::wstring& wsString)
	{
		if (wsString.size() < 1)
			return 0;

		try
		{
            return std::stoi(wsString);
		}
		catch (...)
		{
		}

		try
		{
            return static_cast<int>(std::stol(wsString));
		}
		catch (...)
		{
			return 0;
		}
	}
}

CDjVuFileImplementation::CDjVuFileImplementation(NSFonts::IApplicationFonts* pFonts)
{
	m_pDoc = NULL;
	std::wstring wsTempPath = NSFile::CFileBinary::GetTempPath();
	wsTempPath += L"DJVU\\";
	m_wsTempDirectory = wsTempPath;
	NSDirectory::CreateDirectory(m_wsTempDirectory);

    m_pApplicationFonts = pFonts;
}
CDjVuFileImplementation::~CDjVuFileImplementation()
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);
}
bool               CDjVuFileImplementation::LoadFromFile(const std::wstring& wsSrcFileName, const std::wstring& wsXMLOptions)
{
	m_pDoc = NULL;
	try
	{
		GUTF8String utf8;
		GURL url = GURL::Filename::UTF8(NSDjvu::MakeUTF8String(wsSrcFileName));
		m_pDoc = DjVuDocument::create(url);
		m_pDoc->wait_get_pages_num();
	}
	catch (...)
	{
		return false;
	}

	return true;
}
void               CDjVuFileImplementation::Close()
{
}
std::wstring       CDjVuFileImplementation::GetTempDirectory() const
{
	return m_wsTempDirectory;
}
void               CDjVuFileImplementation::SetTempDirectory(const std::wstring& wsDirectory)
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);

	m_wsTempDirectory = wsDirectory;
	m_wsTempDirectory += L"\\DJVU\\";
	NSDirectory::CreateDirectory(m_wsTempDirectory);
}
int                CDjVuFileImplementation::GetPagesCount() const
{
	if (!m_pDoc)
		return 0;

	return m_pDoc->get_pages_num();
}
void               CDjVuFileImplementation::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY) const
{
	if (!m_pDoc)
	{
		*pdWidth  = 0;
		*pdHeight = 0;

		*pdDpiX = 96;
		*pdDpiY = 96;
	}

#if 0
	GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);

	pPage->wait_for_complete_decode();
	pPage->set_rotate(0);

	*pdWidth = pPage->get_real_width();
	*pdHeight = pPage->get_real_height();

	*pdDpiX = pPage->get_dpi();
	*pdDpiY = pPage->get_dpi();
#endif
    int nW = 0;
    int nH = 0;
    int nDpi = 0;
    m_pDoc->ReadPageInfo(nPageIndex, nW, nH, nDpi);
    *pdWidth = nW;
    *pdHeight = nH;
    *pdDpiX = nDpi;
    *pdDpiY = nDpi;
}
void               CDjVuFileImplementation::DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak)
{
	if (!m_pDoc)
		return;

	try
	{
		GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);
		pPage->wait_for_complete_decode();
		pPage->set_rotate(0);

		long lRendererType = c_nUnknownRenderer;
		pRenderer->get_Type(&lRendererType);
		if (false)//c_nGrRenderer == lRendererType)
		{
			CreateGrFrame(pRenderer, pPage, pBreak);
		}
		else if (c_nPDFWriter == lRendererType)
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreatePdfFrame(pRenderer, pPage, nPageIndex, oText);
		}
		else
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreateFrame(pRenderer, pPage, nPageIndex, oText);
		}
	}
	catch (...)
	{
	}
}
void               CDjVuFileImplementation::ConvertToRaster(int nPageIndex, const std::wstring& wsDstPath, int nImageType, const int& nRasterW, const int& nRasterH)
{
    if (!m_pApplicationFonts)
        return;

    NSFonts::IFontManager *pFontManager = m_pApplicationFonts->GenerateFontManager();
    NSFonts::IFontsCache* pFontCache = NSFonts::NSFontCache::Create();
    pFontCache->SetStreams(m_pApplicationFonts->GetStreams());
	pFontManager->SetOwnerCache(pFontCache);

    NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create();
    pRenderer->SetFontManager(pFontManager);

	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiX);

    int nWidth  = (nRasterW > 0) ? nRasterW : ((int)dWidth * 96 / dPageDpiX);
    int nHeight = (nRasterH > 0) ? nRasterH : ((int)dHeight * 96 / dPageDpiX);

	BYTE* pBgraData = new BYTE[nWidth * nHeight * 4];
	if (!pBgraData)
		return;

	memset(pBgraData, 0xff, nWidth * nHeight * 4);
	CBgraFrame oFrame;
	oFrame.put_Data(pBgraData);
	oFrame.put_Width(nWidth);
	oFrame.put_Height(nHeight);
	oFrame.put_Stride(-4 * nWidth);

    pRenderer->CreateFromBgraFrame(&oFrame);
    pRenderer->SetSwapRGB(false);
    pRenderer->put_Width(dWidth);
    pRenderer->put_Height(dHeight);

	bool bBreak = false;
    DrawPageOnRenderer(pRenderer, nPageIndex, &bBreak);

	oFrame.SaveFile(wsDstPath, nImageType);
	RELEASEINTERFACE(pFontManager);
    RELEASEOBJECT(pRenderer);
}
void               CDjVuFileImplementation::ConvertToPdf(const std::wstring& wsDstPath)
{
    CPdfRenderer oPdf(m_pApplicationFonts);

	bool bBreak = false;
	for (int nPageIndex = 0, nPagesCount = GetPagesCount(); nPageIndex < nPagesCount; nPageIndex++)
	{
		oPdf.NewPage();

		double dPageDpiX, dPageDpiY;
		double dWidth, dHeight;
		GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
		dWidth  *= 25.4 / dPageDpiX;
		dHeight *= 25.4 / dPageDpiY;
		oPdf.put_Width(dWidth);
		oPdf.put_Height(dHeight);

		DrawPageOnRenderer(&oPdf, nPageIndex, &bBreak);
		printf("%d of %d pages\n", nPageIndex + 1, nPagesCount);
	}

	oPdf.SaveToFile(wsDstPath);
}
void               CDjVuFileImplementation::CreateFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPage, XmlUtils::CXmlNode& text)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	int nDpi = pPage->get_dpi();

	double dPixToMM = 25.4;

	double dRendDpiX = 0;
	double dRendDpiY = 0;

	pRenderer->get_DpiX(&dRendDpiX);
	pRenderer->get_DpiY(&dRendDpiY);

	if (0 >= dRendDpiX)
		dRendDpiX = 72.0;
	if (0 >= dRendDpiY)
		dRendDpiY = 72.0;

	double dRendWidth = 0;
	double dRendHeight = 0;

	pRenderer->get_Width(&dRendWidth);
	pRenderer->get_Height(&dRendHeight);

	if (0 >= dRendWidth)
		dRendWidth = 200;
	if (0 >= dRendHeight)
		dRendHeight = 300;

	LONG lImageWidth	= (LONG)(dRendDpiX * dRendWidth / dPixToMM);
	LONG lImageHeight	= (LONG)(dRendDpiY * dRendHeight / dPixToMM);

	long lRendererType = c_nUnknownRenderer;
	pRenderer->get_Type(&lRendererType);
	if (c_nPDFWriter == lRendererType)
	{
		lImageWidth	 = pPage->get_real_width();
		lImageHeight = pPage->get_real_height();
	}
	else if (c_nHtmlRendrerer == lRendererType)
	{
		//VARIANT var;
		//LONG lMaxWidth = var.lVal;
	}

	BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
	if (!pBufferDst)
		return;

	Aggplus::CImage oImage;
	oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);
	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			BYTE* pLine = pBitmap->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
			{
				if (*pLine < nPaletteEntries)
				{
					*pBuffer = palette[*pLine];
				}
				else
				{
					*pBuffer = palette[0];
				}
			}
		}

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = lImageHeight - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();

                unsigned int* palette = new unsigned int[nPaletteEntries];

				// Create palette for the bitmap
				int color = 0xff0000;
				int decrement = color / (nPaletteEntries - 1);
				for (int i = 0; i < nPaletteEntries; ++i)
				{
					BYTE level = (BYTE)(color >> 16);
					palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
					color -= decrement;
				}

                unsigned int* pBuffer = (unsigned int*)pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					BYTE* pLine = pBitmap->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
					{
						if (*pLine < nPaletteEntries)
						{
							*pBuffer = palette[*pLine];
						}
						else
						{
							*pBuffer = palette[0];
						}
					}
				}

				RELEASEARRAYOBJECTS(palette);
			}
		}
	}

	pRenderer->BeginCommand(c_nPageType);

	if (c_nGrRenderer != lRendererType && c_nHtmlRendrerer != lRendererType && c_nHtmlRendrerer2 != lRendererType)
	{
		TextToRenderer(pRenderer, text, dPixToMM / nDpi);
	}

	pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dRendWidth, dRendHeight);
	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreatePdfFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPageIndex, XmlUtils::CXmlNode& oText)
{
	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
	dWidth  *= 25.4 / dPageDpiX;
	dHeight *= 25.4 / dPageDpiY;

	pRenderer->BeginCommand(c_nPageType);

	TextToRenderer(pRenderer, oText, 25.4 / pPage->get_dpi());

	LONG lImageWidth  = pPage->get_real_width();
	LONG lImageHeight = pPage->get_real_height();

	CPdfRenderer* pPdf = (CPdfRenderer*)pRenderer;

	if (pPage->is_legal_photo())
	{
		BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
		if (!pBufferDst)
			return;

		Aggplus::CImage oImage;
		oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
		pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
	}
	else if (pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<IW44Image> pIW44Image = pPage->get_bg44();
		if (NULL != pIW44Image)
		{
			int nBgWidth  = pIW44Image->get_width();
			int nBgHeight = pIW44Image->get_height();

			GP<GPixmap> pBgImage = pIW44Image->get_pixmap();
			if (NULL != pBgImage)
			{
				BYTE* pBgBuffer = new BYTE[4 * nBgWidth * nBgHeight];
				if (!pBgBuffer)
					return;

				Aggplus::CImage oBgImage;
				oBgImage.Create(pBgBuffer, nBgWidth, nBgHeight, 4 * nBgWidth);

				BYTE* pBuffer = pBgBuffer;
				for (int j = nBgHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pBgImage->operator [](j);

					for (int i = 0; i < nBgWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oBgImage, 0, 0, dWidth, dHeight);
			}
		}

		GP<GPixmap> pImage = pPage->get_fgpm();
		if (NULL == pImage)
			pImage = pPage->get_fg_pixmap(oRectAll);

		if (NULL != pImage)
		{
			unsigned int unPixmapH = pImage->rows();
			unsigned int unPixmapW = pImage->columns();
			BYTE* pBufferDst = new BYTE[4 * unPixmapH * unPixmapW];
			if (!pBufferDst)
				return;

			Aggplus::CImage oImage;
			oImage.Create(pBufferDst, unPixmapW, unPixmapH, 4 * unPixmapW);

			BYTE* pBuffer = pBufferDst;
			for (int j = unPixmapH - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < unPixmapW; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

            NSImages::CPixJbig2 oPix;
            if (oPix.Create(lImageWidth, lImageHeight, 1))
			{
				for (int nY = 0; nY < lImageHeight; nY++)
				{
					BYTE* pLine = pBitmap->operator [](nY);
					for (int nX = 0; nX < lImageWidth; nX++, pLine++)
					{
                        oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
					}
				}

                pPdf->DrawImageWith1bppMask((IGrObject*)&oImage, &oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                oPix.Destroy();
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

        NSImages::CPixJbig2 oPix;
        if (oPix.Create(lImageWidth, lImageHeight, 1))
		{
			for (int nY = 0; nY < lImageHeight; nY++)
			{
				BYTE* pLine = pBitmap->operator [](nY);
				for (int nX = 0; nX < lImageWidth; nX++, pLine++)
				{
                    oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
				}
			}

            pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
            oPix.Destroy();
		}
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
			if (pBufferDst)
			{
				Aggplus::CImage oImage;
				oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

				BYTE* pBuffer = pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pImage->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();
				if (nPaletteEntries <= 2)
				{
                    NSImages::CPixJbig2 oPix;
                    if (oPix.Create(lImageWidth, lImageHeight, 1))
					{
						for (int nY = 0; nY < lImageHeight; nY++)
						{
							BYTE* pLine = pBitmap->operator [](nY);
							for (int nX = 0; nX < lImageWidth; nX++, pLine++)
							{
                                oPix.Create(nX, lImageHeight - 1 - nY, *pLine);
							}
						}

                        pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                        oPix.Destroy();
					}
				}
				else
				{
					BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
					if (!pBufferDst)
						return;

					Aggplus::CImage oImage;
					oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

                    unsigned int* palette = new unsigned int[nPaletteEntries];

					// Create palette for the bitmap
					int color = 0xff0000;
					int decrement = color / (nPaletteEntries - 1);
					for (int i = 0; i < nPaletteEntries; ++i)
					{
						BYTE level = (BYTE)(color >> 16);
						palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
						color -= decrement;
					}

                    unsigned int* pBuffer = (unsigned int*)pBufferDst;
					for (int j = lImageHeight - 1; j >= 0; --j)
					{
						BYTE* pLine = pBitmap->operator [](j);

						for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
						{
							if (*pLine < nPaletteEntries)
							{
								*pBuffer = palette[*pLine];
							}
							else
							{
								*pBuffer = palette[0];
							}
						}
					}

					RELEASEARRAYOBJECTS(palette);
					pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
				}
			}
		}
	}

	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreateGrFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, bool* pBreak)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	BYTE*	pBufferDst	 = NULL;
	LONG	lImageWidth	 = 0;
	LONG	lImageHeight = 0;

	//VARIANT var;

	volatile bool* pCancel = pBreak;

	if ((NULL != pCancel) && (TRUE == *pCancel))
		return;

	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = 0; j < lImageHeight; ++j)
		{
			GPixel* pLine = pImage->operator [](j);

			if ((NULL != pCancel) && (TRUE == *pCancel))
				return;

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				GPixel* pLine = pImage->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			return;
		}

		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

		if (NULL != pBitmap)
		{
			int nPaletteEntries = pBitmap->get_grays();

            unsigned int* palette = new unsigned int[nPaletteEntries];

			// Create palette for the bitmap
			int color = 0xff0000;
			int decrement = color / (nPaletteEntries - 1);
			for (int i = 0; i < nPaletteEntries; ++i)
			{
				BYTE level = (BYTE)(color >> 16);
				palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
				color -= decrement;
			}

            unsigned int* pBuffer = (unsigned int*)pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				BYTE* pLine = pBitmap->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
				{
					if (*pLine < nPaletteEntries)
					{
						*pBuffer = palette[*pLine];
					}
					else
					{
						*pBuffer = palette[0];
					}
				}
			}

			RELEASEARRAYOBJECTS(palette);
		}
	}
}
XmlUtils::CXmlNode CDjVuFileImplementation::ParseText(GP<DjVuImage> pPage)
{
	XmlUtils::CXmlNode paragraph;
	const GP<DjVuText> text(DjVuText::create());
	const GP<ByteStream> text_str(pPage->get_text());
	if (text_str)
	{
		text->decode(text_str);
		GUTF8String pageText = text->get_xmlText(pPage->get_height());
		XmlUtils::CXmlNode hiddenText;
		XmlUtils::CXmlNode pageColumn;
		XmlUtils::CXmlNode region;
        hiddenText.FromXmlStringA(NSDjvu::MakeCString(pageText));
        hiddenText.GetNode(L"PAGECOLUMN", pageColumn);
        pageColumn.GetNode(L"REGION", region);
        region.GetNode(L"PARAGRAPH", paragraph);
	}
	return paragraph;
}
void               CDjVuFileImplementation::TextToRenderer(IRenderer* pRenderer, XmlUtils::CXmlNode oTextNode, double dKoef, bool isView)
{
	pRenderer->put_FontName(L"DjvuEmptyFont");
	XmlUtils::CXmlNodes oLinesNodes;
	oTextNode.GetNodes(L"LINE", oLinesNodes);
	for (int nLineIndex = 0; nLineIndex < oLinesNodes.GetCount(); ++nLineIndex)
	{
		XmlUtils::CXmlNode oLineNode;
		oLinesNodes.GetAt(nLineIndex, oLineNode);
		XmlUtils::CXmlNodes oWordsNodes;
		oLineNode.GetNodes(L"WORD", oWordsNodes);
		for (int nWordIndex = 0; nWordIndex < oWordsNodes.GetCount(); ++nWordIndex)
		{
			XmlUtils::CXmlNode oWordNode;
			oWordsNodes.GetAt(nWordIndex, oWordNode);
            std::wstring csWord   = oWordNode.GetText();
            std::wstring csCoords = oWordNode.GetAttribute(L"coords");
			double arrCoords[4];
            ParseCoords(csCoords, arrCoords, dKoef);
            DrawPageText(pRenderer, arrCoords, csWord);
		}
	}
}
void               CDjVuFileImplementation::DrawPageText(IRenderer* pRenderer, double* pdCoords, const std::wstring& wsText)
{
	pRenderer->put_FontSize(pdCoords[1] - pdCoords[3]);
	pRenderer->CommandDrawText(wsText,
							   (float)(pdCoords[0]),
							   (float)(pdCoords[3]),
							   (float)(pdCoords[2] - pdCoords[0]),
							   (float)(pdCoords[1] - pdCoords[3]));
}
void               CDjVuFileImplementation::ParseCoords(const std::wstring& wsCoordsStr, double* pdCoords, double dKoef)
{
    std::vector<std::wstring> vCoords = NSStringExt::Split(wsCoordsStr, L',');
	if (vCoords.size() >= 4)
	{
		for (int nIndex = 0; nIndex < 4; nIndex++)
		{
			pdCoords[nIndex] = NSDjvu::GetInteger(vCoords.at(nIndex)) * dKoef;
		}
	}
}


try
		{
            return std::stoi(wsString);
		}
		catch (...)
		{
		}

/*
 *
 *
 *
 */
#include "DjVuFileImplementation.h"

#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Directory.h"

#include "../PdfWriter/PdfRenderer.h"

#include "../DesktopEditor/graphics/pro/Fonts.h"
#include "../DesktopEditor/graphics/pro/Graphics.h"
#include "../DesktopEditor/graphics/pro/Image.h"

#include "../DesktopEditor/common/StringExt.h"

#define VER_DPI		96
#define HOR_DPI		96

#include <vector>

namespace NSDjvu
{
	static GUTF8String MakeUTF8String(const std::wstring& wsText)
	{
        std::string sText = NSFile::CUtf8Converter::GetUtf8StringFromUnicode(wsText);
        GUTF8String utf8String(sText.c_str());
        return utf8String;
	}
    static std::string MakeCString(GUTF8String& strText)
	{
		std::string sString(strText.getbuf());
        return sString;
	}
	static int     GetInteger(const std::wstring& wsString)
	{
		if (wsString.size() < 1)
			return 0;

		try
		{
            return static_cast<int>(std::stol(wsString));
		}
		catch (...)
		{
			return 0;
		}
	}
}

CDjVuFileImplementation::CDjVuFileImplementation(NSFonts::IApplicationFonts* pFonts)
{
	m_pDoc = NULL;
	std::wstring wsTempPath = NSFile::CFileBinary::GetTempPath();
	wsTempPath += L"DJVU\\";
	m_wsTempDirectory = wsTempPath;
	NSDirectory::CreateDirectory(m_wsTempDirectory);

    m_pApplicationFonts = pFonts;
}
CDjVuFileImplementation::~CDjVuFileImplementation()
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);
}
bool               CDjVuFileImplementation::LoadFromFile(const std::wstring& wsSrcFileName, const std::wstring& wsXMLOptions)
{
	m_pDoc = NULL;
	try
	{
		GUTF8String utf8;
		GURL url = GURL::Filename::UTF8(NSDjvu::MakeUTF8String(wsSrcFileName));
		m_pDoc = DjVuDocument::create(url);
		m_pDoc->wait_get_pages_num();
	}
	catch (...)
	{
		return false;
	}

	return true;
}
void               CDjVuFileImplementation::Close()
{
}
std::wstring       CDjVuFileImplementation::GetTempDirectory() const
{
	return m_wsTempDirectory;
}
void               CDjVuFileImplementation::SetTempDirectory(const std::wstring& wsDirectory)
{
	NSDirectory::DeleteDirectory(m_wsTempDirectory);

	m_wsTempDirectory = wsDirectory;
	m_wsTempDirectory += L"\\DJVU\\";
	NSDirectory::CreateDirectory(m_wsTempDirectory);
}
int                CDjVuFileImplementation::GetPagesCount() const
{
	if (!m_pDoc)
		return 0;

	return m_pDoc->get_pages_num();
}
void               CDjVuFileImplementation::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY) const
{
	if (!m_pDoc)
	{
		*pdWidth  = 0;
		*pdHeight = 0;

		*pdDpiX = 96;
		*pdDpiY = 96;
	}

#if 0
	GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);

	pPage->wait_for_complete_decode();
	pPage->set_rotate(0);

	*pdWidth = pPage->get_real_width();
	*pdHeight = pPage->get_real_height();

	*pdDpiX = pPage->get_dpi();
	*pdDpiY = pPage->get_dpi();
#endif
    int nW = 0;
    int nH = 0;
    int nDpi = 0;
    m_pDoc->ReadPageInfo(nPageIndex, nW, nH, nDpi);
    *pdWidth = nW;
    *pdHeight = nH;
    *pdDpiX = nDpi;
    *pdDpiY = nDpi;
}
void               CDjVuFileImplementation::DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak)
{
	if (!m_pDoc)
		return;

	try
	{
		GP<DjVuImage> pPage = m_pDoc->get_page(nPageIndex);
		pPage->wait_for_complete_decode();
		pPage->set_rotate(0);

		long lRendererType = c_nUnknownRenderer;
		pRenderer->get_Type(&lRendererType);
		if (false)//c_nGrRenderer == lRendererType)
		{
			CreateGrFrame(pRenderer, pPage, pBreak);
		}
		else if (c_nPDFWriter == lRendererType)
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreatePdfFrame(pRenderer, pPage, nPageIndex, oText);
		}
		else
		{
			XmlUtils::CXmlNode oText = ParseText(pPage);
			CreateFrame(pRenderer, pPage, nPageIndex, oText);
		}
	}
	catch (...)
	{
	}
}
void               CDjVuFileImplementation::ConvertToRaster(int nPageIndex, const std::wstring& wsDstPath, int nImageType, const int& nRasterW, const int& nRasterH)
{
    if (!m_pApplicationFonts)
        return;

    NSFonts::IFontManager *pFontManager = m_pApplicationFonts->GenerateFontManager();
    NSFonts::IFontsCache* pFontCache = NSFonts::NSFontCache::Create();
    pFontCache->SetStreams(m_pApplicationFonts->GetStreams());
	pFontManager->SetOwnerCache(pFontCache);

    NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create();
    pRenderer->SetFontManager(pFontManager);

	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiX);

    int nWidth  = (nRasterW > 0) ? nRasterW : ((int)dWidth * 96 / dPageDpiX);
    int nHeight = (nRasterH > 0) ? nRasterH : ((int)dHeight * 96 / dPageDpiX);

	BYTE* pBgraData = new BYTE[nWidth * nHeight * 4];
	if (!pBgraData)
		return;

	memset(pBgraData, 0xff, nWidth * nHeight * 4);
	CBgraFrame oFrame;
	oFrame.put_Data(pBgraData);
	oFrame.put_Width(nWidth);
	oFrame.put_Height(nHeight);
	oFrame.put_Stride(-4 * nWidth);

    pRenderer->CreateFromBgraFrame(&oFrame);
    pRenderer->SetSwapRGB(false);
    pRenderer->put_Width(dWidth);
    pRenderer->put_Height(dHeight);

	bool bBreak = false;
    DrawPageOnRenderer(pRenderer, nPageIndex, &bBreak);

	oFrame.SaveFile(wsDstPath, nImageType);
	RELEASEINTERFACE(pFontManager);
    RELEASEOBJECT(pRenderer);
}
void               CDjVuFileImplementation::ConvertToPdf(const std::wstring& wsDstPath)
{
    CPdfRenderer oPdf(m_pApplicationFonts);

	bool bBreak = false;
	for (int nPageIndex = 0, nPagesCount = GetPagesCount(); nPageIndex < nPagesCount; nPageIndex++)
	{
		oPdf.NewPage();

		double dPageDpiX, dPageDpiY;
		double dWidth, dHeight;
		GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
		dWidth  *= 25.4 / dPageDpiX;
		dHeight *= 25.4 / dPageDpiY;
		oPdf.put_Width(dWidth);
		oPdf.put_Height(dHeight);

		DrawPageOnRenderer(&oPdf, nPageIndex, &bBreak);
		printf("%d of %d pages\n", nPageIndex + 1, nPagesCount);
	}

	oPdf.SaveToFile(wsDstPath);
}
void               CDjVuFileImplementation::CreateFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPage, XmlUtils::CXmlNode& text)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	int nDpi = pPage->get_dpi();

	double dPixToMM = 25.4;

	double dRendDpiX = 0;
	double dRendDpiY = 0;

	pRenderer->get_DpiX(&dRendDpiX);
	pRenderer->get_DpiY(&dRendDpiY);

	if (0 >= dRendDpiX)
		dRendDpiX = 72.0;
	if (0 >= dRendDpiY)
		dRendDpiY = 72.0;

	double dRendWidth = 0;
	double dRendHeight = 0;

	pRenderer->get_Width(&dRendWidth);
	pRenderer->get_Height(&dRendHeight);

	if (0 >= dRendWidth)
		dRendWidth = 200;
	if (0 >= dRendHeight)
		dRendHeight = 300;

	LONG lImageWidth	= (LONG)(dRendDpiX * dRendWidth / dPixToMM);
	LONG lImageHeight	= (LONG)(dRendDpiY * dRendHeight / dPixToMM);

	long lRendererType = c_nUnknownRenderer;
	pRenderer->get_Type(&lRendererType);
	if (c_nPDFWriter == lRendererType)
	{
		lImageWidth	 = pPage->get_real_width();
		lImageHeight = pPage->get_real_height();
	}
	else if (c_nHtmlRendrerer == lRendererType)
	{
		//VARIANT var;
		//LONG lMaxWidth = var.lVal;
	}

	BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
	if (!pBufferDst)
		return;

	Aggplus::CImage oImage;
	oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);
	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			BYTE* pLine = pBitmap->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
			{
				if (*pLine < nPaletteEntries)
				{
					*pBuffer = palette[*pLine];
				}
				else
				{
					*pBuffer = palette[0];
				}
			}
		}

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = lImageHeight - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();

                unsigned int* palette = new unsigned int[nPaletteEntries];

				// Create palette for the bitmap
				int color = 0xff0000;
				int decrement = color / (nPaletteEntries - 1);
				for (int i = 0; i < nPaletteEntries; ++i)
				{
					BYTE level = (BYTE)(color >> 16);
					palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
					color -= decrement;
				}

                unsigned int* pBuffer = (unsigned int*)pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					BYTE* pLine = pBitmap->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
					{
						if (*pLine < nPaletteEntries)
						{
							*pBuffer = palette[*pLine];
						}
						else
						{
							*pBuffer = palette[0];
						}
					}
				}

				RELEASEARRAYOBJECTS(palette);
			}
		}
	}

	pRenderer->BeginCommand(c_nPageType);

	if (c_nGrRenderer != lRendererType && c_nHtmlRendrerer != lRendererType && c_nHtmlRendrerer2 != lRendererType)
	{
		TextToRenderer(pRenderer, text, dPixToMM / nDpi);
	}

	pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dRendWidth, dRendHeight);
	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreatePdfFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, int nPageIndex, XmlUtils::CXmlNode& oText)
{
	double dPageDpiX, dPageDpiY;
	double dWidth, dHeight;
	GetPageInfo(nPageIndex, &dWidth, &dHeight, &dPageDpiX, &dPageDpiY);
	dWidth  *= 25.4 / dPageDpiX;
	dHeight *= 25.4 / dPageDpiY;

	pRenderer->BeginCommand(c_nPageType);

	TextToRenderer(pRenderer, oText, 25.4 / pPage->get_dpi());

	LONG lImageWidth  = pPage->get_real_width();
	LONG lImageHeight = pPage->get_real_height();

	CPdfRenderer* pPdf = (CPdfRenderer*)pRenderer;

	if (pPage->is_legal_photo())
	{
		BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
		if (!pBufferDst)
			return;

		Aggplus::CImage oImage;
		oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = lImageHeight - 1; j >= 0; --j)
		{
			GPixel* pLine = pImage->operator [](j);

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
		pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
	}
	else if (pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<IW44Image> pIW44Image = pPage->get_bg44();
		if (NULL != pIW44Image)
		{
			int nBgWidth  = pIW44Image->get_width();
			int nBgHeight = pIW44Image->get_height();

			GP<GPixmap> pBgImage = pIW44Image->get_pixmap();
			if (NULL != pBgImage)
			{
				BYTE* pBgBuffer = new BYTE[4 * nBgWidth * nBgHeight];
				if (!pBgBuffer)
					return;

				Aggplus::CImage oBgImage;
				oBgImage.Create(pBgBuffer, nBgWidth, nBgHeight, 4 * nBgWidth);

				BYTE* pBuffer = pBgBuffer;
				for (int j = nBgHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pBgImage->operator [](j);

					for (int i = 0; i < nBgWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oBgImage, 0, 0, dWidth, dHeight);
			}
		}

		GP<GPixmap> pImage = pPage->get_fgpm();
		if (NULL == pImage)
			pImage = pPage->get_fg_pixmap(oRectAll);

		if (NULL != pImage)
		{
			unsigned int unPixmapH = pImage->rows();
			unsigned int unPixmapW = pImage->columns();
			BYTE* pBufferDst = new BYTE[4 * unPixmapH * unPixmapW];
			if (!pBufferDst)
				return;

			Aggplus::CImage oImage;
			oImage.Create(pBufferDst, unPixmapW, unPixmapH, 4 * unPixmapW);

			BYTE* pBuffer = pBufferDst;
			for (int j = unPixmapH - 1; j >= 0; --j)
			{
				GPixel* pLine = pImage->operator [](j);

				for (int i = 0; i < unPixmapW; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

            NSImages::CPixJbig2 oPix;
            if (oPix.Create(lImageWidth, lImageHeight, 1))
			{
				for (int nY = 0; nY < lImageHeight; nY++)
				{
					BYTE* pLine = pBitmap->operator [](nY);
					for (int nX = 0; nX < lImageWidth; nX++, pLine++)
					{
                        oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
					}
				}

                pPdf->DrawImageWith1bppMask((IGrObject*)&oImage, &oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                oPix.Destroy();
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

        NSImages::CPixJbig2 oPix;
        if (oPix.Create(lImageWidth, lImageHeight, 1))
		{
			for (int nY = 0; nY < lImageHeight; nY++)
			{
				BYTE* pLine = pBitmap->operator [](nY);
				for (int nX = 0; nX < lImageWidth; nX++, pLine++)
				{
                    oPix.SetPixel(nX, lImageHeight - 1 - nY, *pLine);
				}
			}

            pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
            oPix.Destroy();
		}
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
			if (pBufferDst)
			{
				Aggplus::CImage oImage;
				oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

				BYTE* pBuffer = pBufferDst;
				for (int j = lImageHeight - 1; j >= 0; --j)
				{
					GPixel* pLine = pImage->operator [](j);

					for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
					{
						pBuffer[0] = pLine->b;
						pBuffer[1] = pLine->g;
						pBuffer[2] = pLine->r;
						pBuffer[3] = 255;
					}
				}
				pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
			}
		}
		else
		{
			GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
			if (NULL != pBitmap)
			{
				int nPaletteEntries = pBitmap->get_grays();
				if (nPaletteEntries <= 2)
				{
                    NSImages::CPixJbig2 oPix;
                    if (oPix.Create(lImageWidth, lImageHeight, 1))
					{
						for (int nY = 0; nY < lImageHeight; nY++)
						{
							BYTE* pLine = pBitmap->operator [](nY);
							for (int nX = 0; nX < lImageWidth; nX++, pLine++)
							{
                                oPix.Create(nX, lImageHeight - 1 - nY, *pLine);
							}
						}

                        pPdf->DrawImage1bpp(&oPix, lImageWidth, lImageHeight, 0, 0, dWidth, dHeight);
                        oPix.Destroy();
					}
				}
				else
				{
					BYTE* pBufferDst = new BYTE[4 * lImageHeight * lImageWidth];
					if (!pBufferDst)
						return;

					Aggplus::CImage oImage;
					oImage.Create(pBufferDst, lImageWidth, lImageHeight, 4 * lImageWidth);

                    unsigned int* palette = new unsigned int[nPaletteEntries];

					// Create palette for the bitmap
					int color = 0xff0000;
					int decrement = color / (nPaletteEntries - 1);
					for (int i = 0; i < nPaletteEntries; ++i)
					{
						BYTE level = (BYTE)(color >> 16);
						palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
						color -= decrement;
					}

                    unsigned int* pBuffer = (unsigned int*)pBufferDst;
					for (int j = lImageHeight - 1; j >= 0; --j)
					{
						BYTE* pLine = pBitmap->operator [](j);

						for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
						{
							if (*pLine < nPaletteEntries)
							{
								*pBuffer = palette[*pLine];
							}
							else
							{
								*pBuffer = palette[0];
							}
						}
					}

					RELEASEARRAYOBJECTS(palette);
					pRenderer->DrawImage((IGrObject*)&oImage, 0, 0, dWidth, dHeight);
				}
			}
		}
	}

	pRenderer->EndCommand(c_nPageType);
}
void               CDjVuFileImplementation::CreateGrFrame(IRenderer* pRenderer, GP<DjVuImage>& pPage, bool* pBreak)
{
	int nWidth	= pPage->get_real_width();
	int nHeight	= pPage->get_real_height();

	BYTE*	pBufferDst	 = NULL;
	LONG	lImageWidth	 = 0;
	LONG	lImageHeight = 0;

	//VARIANT var;

	volatile bool* pCancel = pBreak;

	if ((NULL != pCancel) && (TRUE == *pCancel))
		return;

	if (pPage->is_legal_photo() || pPage->is_legal_compound())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		BYTE* pBuffer = pBufferDst;
		for (int j = 0; j < lImageHeight; ++j)
		{
			GPixel* pLine = pImage->operator [](j);

			if ((NULL != pCancel) && (TRUE == *pCancel))
				return;

			for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
			{
				pBuffer[0] = pLine->b;
				pBuffer[1] = pLine->g;
				pBuffer[2] = pLine->r;
				pBuffer[3] = 255;
			}
		}
	}
	else if (pPage->is_legal_bilevel())
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);
		int nPaletteEntries = pBitmap->get_grays();

        unsigned int* palette = new unsigned int[nPaletteEntries];

		// Create palette for the bitmap
		int color = 0xff0000;
		int decrement = color / (nPaletteEntries - 1);
		for (int i = 0; i < nPaletteEntries; ++i)
		{
			BYTE level = (BYTE)(color >> 16);
			palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
			color -= decrement;
		}

        unsigned int* pBuffer = (unsigned int*)pBufferDst;
		for (int j = 0; j < lImageHeight; ++j)
		{
			BYTE* pLine = pBitmap->operator [](j);

			if ((NULL != pCancel) && (TRUE == *pCancel))
				return;

			for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
			{
				if (*pLine < nPaletteEntries)
				{
					*pBuffer = palette[*pLine];
				}
				else
				{
					*pBuffer = palette[0];
				}
			}
		}

		RELEASEARRAYOBJECTS(palette);
	}
	else
	{
		GRect oRectAll(0, 0, lImageWidth, lImageHeight);
		GP<GPixmap> pImage = pPage->get_pixmap(oRectAll, oRectAll);

		if (NULL != pImage)
		{
			BYTE* pBuffer = pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				GPixel* pLine = pImage->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, pBuffer += 4, ++pLine)
				{
					pBuffer[0] = pLine->b;
					pBuffer[1] = pLine->g;
					pBuffer[2] = pLine->r;
					pBuffer[3] = 255;
				}
			}

			return;
		}

		GP<GBitmap> pBitmap = pPage->get_bitmap(oRectAll, oRectAll, 4);

		if (NULL != pBitmap)
		{
			int nPaletteEntries = pBitmap->get_grays();

            unsigned int* palette = new unsigned int[nPaletteEntries];

			// Create palette for the bitmap
			int color = 0xff0000;
			int decrement = color / (nPaletteEntries - 1);
			for (int i = 0; i < nPaletteEntries; ++i)
			{
				BYTE level = (BYTE)(color >> 16);
				palette[i] = (0xFF000000 | level << 16 | level << 8 | level);
				color -= decrement;
			}

            unsigned int* pBuffer = (unsigned int*)pBufferDst;
			for (int j = 0; j < lImageHeight; ++j)
			{
				BYTE* pLine = pBitmap->operator [](j);

				if ((NULL != pCancel) && (TRUE == *pCancel))
					return;

				for (int i = 0; i < lImageWidth; ++i, ++pBuffer, ++pLine)
				{
					if (*pLine < nPaletteEntries)
					{
						*pBuffer = palette[*pLine];
					}
					else
					{
						*pBuffer = palette[0];
					}
				}
			}

			RELEASEARRAYOBJECTS(palette);
		}
	}
}
XmlUtils::CXmlNode CDjVuFileImplementation::ParseText(GP<DjVuImage> pPage)
{
	XmlUtils::CXmlNode paragraph;
	const GP<DjVuText> text(DjVuText::create());
	const GP<ByteStream> text_str(pPage->get_text());
	if (text_str)
	{
		text->decode(text_str);
		GUTF8String pageText = text->get_xmlText(pPage->get_height());
		XmlUtils::CXmlNode hiddenText;
		XmlUtils::CXmlNode pageColumn;
		XmlUtils::CXmlNode region;
        hiddenText.FromXmlStringA(NSDjvu::MakeCString(pageText));
        hiddenText.GetNode(L"PAGECOLUMN", pageColumn);
        pageColumn.GetNode(L"REGION", region);
        region.GetNode(L"PARAGRAPH", paragraph);
	}
	return paragraph;
}
void               CDjVuFileImplementation::TextToRenderer(IRenderer* pRenderer, XmlUtils::CXmlNode oTextNode, double dKoef, bool isView)
{
	pRenderer->put_FontName(L"DjvuEmptyFont");
	XmlUtils::CXmlNodes oLinesNodes;
	oTextNode.GetNodes(L"LINE", oLinesNodes);
	for (int nLineIndex = 0; nLineIndex < oLinesNodes.GetCount(); ++nLineIndex)
	{
		XmlUtils::CXmlNode oLineNode;
		oLinesNodes.GetAt(nLineIndex, oLineNode);
		XmlUtils::CXmlNodes oWordsNodes;
		oLineNode.GetNodes(L"WORD", oWordsNodes);
		for (int nWordIndex = 0; nWordIndex < oWordsNodes.GetCount(); ++nWordIndex)
		{
			XmlUtils::CXmlNode oWordNode;
			oWordsNodes.GetAt(nWordIndex, oWordNode);
            std::wstring csWord   = oWordNode.GetText();
            std::wstring csCoords = oWordNode.GetAttribute(L"coords");
			double arrCoords[4];
            ParseCoords(csCoords, arrCoords, dKoef);
            DrawPageText(pRenderer, arrCoords, csWord);
		}
	}
}
void               CDjVuFileImplementation::DrawPageText(IRenderer* pRenderer, double* pdCoords, const std::wstring& wsText)
{
	pRenderer->put_FontSize(pdCoords[1] - pdCoords[3]);
	pRenderer->CommandDrawText(wsText,
							   (float)(pdCoords[0]),
							   (float)(pdCoords[3]),
							   (float)(pdCoords[2] - pdCoords[0]),
							   (float)(pdCoords[1] - pdCoords[3]));
}
void               CDjVuFileImplementation::ParseCoords(const std::wstring& wsCoordsStr, double* pdCoords, double dKoef)
{
    std::vector<std::wstring> vCoords = NSStringExt::Split(wsCoordsStr, L',');
	if (vCoords.size() >= 4)
	{
		for (int nIndex = 0; nIndex < 4; nIndex++)
		{
			pdCoords[nIndex] = NSDjvu::GetInteger(vCoords.at(nIndex)) * dKoef;
		}
	}
}


#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()
{
	TheButcher* instance = new TheButcher();

	instance->autorelease();

	return instance;
}

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_TheButcher;
}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()
{
	return TheButcherPlans::create();
}


#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()
{
	TheButcher* instance = new TheButcher();

	instance->autorelease();

	return instance;
}

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

TheButcher::~TheButcher()
{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_TheButcher;
}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()


{
	TheButcher* instance = new TheButcher();

#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()

	instance->autorelease();

	return instance;
}

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

TheButcher::~TheButcher()
{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_TheButcher;
}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()
{
	return TheButcherPlans::create();
}


#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()
{
	TheButcher* instance = new TheButcher();

	return instance;
}

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

TheButcher::~TheButcher()
{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_TheButcher;
}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()
{
	return TheButcherPlans::create();
}


#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()
{
	TheButcher* instance = new TheButcher();

	instance->autorelease();

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

TheButcher::~TheButcher()
{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_TheButcher;
}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()
{
	return TheButcherPlans::create();
}


#include "TheButcher.h"

#include "Engine/Inventory/CurrencyInventory.h"
#include "Scenes/Platformer/Inventory/Currencies/IOU.h"
#include "Scenes/Platformer/Inventory/Items/Recipes/Weapons/Swords/TheButcherPlans.h"

#include "Resources/ItemResources.h"

#include "Strings/Strings.h"

using namespace cocos2d;

const std::string TheButcher::SaveKey = "the-butcher";

TheButcher* TheButcher::create()
{
	TheButcher* instance = new TheButcher();

	instance->autorelease();

	return instance;
}

TheButcher::TheButcher() : super(CurrencyInventory::create({{ IOU::getIOUIdentifier(), 2 }}), 24, 32, ItemStats(
	// Health
	0,
	// Mana
	0,
	// Attack
	0,
	// Magic Attack
	0,
	// Armor
	0,
	// Speed
	0.15f
))
{
}

TheButcher::~TheButcher()
{
}

Item* TheButcher::clone()
{
	return TheButcher::create();
}

LocalizedString* TheButcher::getString()
{
	return Strings::Items_Equipment_Weapons_Swords_TheButcher::create();
}

const std::string& TheButcher::getIconResource()
{
	return ItemResources::Equipment_Weapons_Special_Th

}

const std::string& TheButcher::getIdentifier()
{
	return TheButcher::SaveKey;
}

Recipe* TheButcher::getRecipe()
{
	return TheButcherPlans::create();
}


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];

int main()


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];

int main() {


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];

int main() {
    while(cin >> N >> K >> P >> M) {
        for(int i = 0; i < N; ++i)
            scanf("%d", A + i);
        memset(DP, 0, sizeof(DP));
        int ans = 0;
        for(int i = 0; i < N; ++i) {
            DP[i][0] = 1;
            if(K == 1) {
                ans += 1;
                ans %= M;
            }
            for(int j = i + 1; j < N; ++j) {
                if(A[j] - A[i] <= P && A[i] - A[j] <= P) {
                    for(int k = 0; k <= i; ++k) {
                        if(DP[i][k]) {
                            DP[j][k + 1] += DP[i][k];
                            DP[j][k + 1] %= M;
                            if(k + 2 >= K) {
                                ans += DP[i][k];
                                ans %= M;
                            }
                        }
                    }
                }
            }
        }
        cout << ans << endl;
    }


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];

int main() {
    while(cin >> N >> K >> P >> M) {
        for(int i = 0; i < N; ++i)
            scanf("%d", A + i);
        memset(DP, 0, sizeof(DP));
        int ans = 0;
        for(int i = 0; i < N; ++i) {
            DP[i][0] = 1;
            if(K == 1) {
                ans += 1;
                ans %= M;
            }
            for(int j = i + 1; j < N; ++j) {
                if(A[j] - A[i] <= P && A[i] - A[j] <= P) {
                    for(int k = 0; k <= i; ++k) {
                        if(DP[i][k]) {
                            DP[j][k + 1] += DP[i][k];
                            DP[j][k + 1] %= M;


#include <iostream>
using namespace std;

int N, A[2000];
int K, P, M;

int DP[2000][2000];

int main() {
    while(cin >> N >> K >> P >> M) {
        for(int i = 0; i < N; ++i)
            scanf("%d", A + i);
        memset(DP, 0, sizeof(DP));
        int ans = 0;
        for(int i = 0; i < N; ++i) {
            DP[i][0] = 1;
            if(K == 1) {
                ans += 1;
                ans %= M;
            }
            for(int j = i + 1; j < N; ++j) {
                if(A[j] - A[i] <= P && A[i] - A[j] <= P) {

                }
            }
        }
        cout << ans << endl;
    }
}


/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }


/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true

}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


bool CLabel::EventProcess(const Event &event)
{
    return true;
}

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.


if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;

    switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

    m_engine->GetText()->DrawText(std::string(m_name), m_fontType, m_fontSize, pos, m_dim.x, m_textAlign, 0);
}

}


switch (m_textAlign)
    {
        case Gfx::TEXT_ALIGN_LEFT: pos.x = m_pos.x; break;
        case Gfx::TEXT_ALIGN_CENTER: pos.x = m_pos.x + m_dim.x / 2.0f; break;
        case Gfx::TEXT_ALIGN_RIGHT: pos.x = m_pos.x + m_dim.x; break;
    }

/*
 * This file is part of the Colobot: Gold Edition source code
 *
 */

#include "ui/controls/label.h"

#include "graphics/engine/engine.h"

namespace Ui
{

CLabel::CLabel() : CControl()
{
}

// Object's destructor.

CLabel::~CLabel()
{
}

// Creates a new button.

bool CLabel::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg)
{
    if (eventMsg == EVENT_NULL)
        eventMsg = GetUniqueEventType();

    CControl::Create(pos, dim, icon, eventMsg);
    return true;
}

// Management of an event.

bool CLabel::EventProcess(const Event &event)
{
    return true;
}

// Draws button.

void CLabel::Draw()
{
    Math::Point pos;

    if ( (m_state & STATE_VISIBLE) == 0 )
        return;

    pos.y = m_pos.y + m_dim.y / 2.0f;


/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

{
    El::Environment env( argc, argv );
    El::mpi::Comm comm = El::mpi::COMM_WORLD;

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }
        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }

    return 0;
}


{
    El::Environment env( argc, argv );

/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

int
main( int argc, char* argv[] )

    El::mpi::Comm comm = El::mpi::COMM_WORLD;

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }
        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }

    return 0;
}


El::mpi::Comm comm = El::mpi::COMM_WORLD;

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }
        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }

/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

int
main( int argc, char* argv[] )
{
    El::Environment env( argc, argv );


/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

int
main( int argc, char* argv[] )
{
    El::Environment env( argc, argv );
    El::mpi::Comm comm = El::mpi::COMM_WORLD;

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }
        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }


/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

int
main( int argc, char* argv[] )
{
    El::Environment env( argc, argv );
    El::mpi::Comm comm = El::mpi::CO

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }
        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }

    return 0;
}


if( display )
        {
            El::Display( F, "Egorov with Fourier phase" );
            El::Display( G, "Egorov with more general phase" );
        }

/*
   All rights reserved.

   which can be found in the LICENSE file in the root directory, or at
*/
#include <El.hpp>

int
main( int argc, char* argv[] )
{
    El::Environment env( argc, argv );
    El::mpi::Comm comm = El::mpi::COMM_WORLD;

    try
    {
        const El::Int n = El::Input("--size","size of matrix",10);
        const bool display = El::Input("--display","display matrix?",false);
        const bool print = El::Input("--print","print matrix?",true);
        El::ProcessInput();
        El::PrintInputReport();

        const El::Grid grid( comm );

        const double& pi = El::Pi<double>();
        auto fourier = [&]( El::Int i, El::Int j ) { return (-2*pi*i*j)/n; };
        auto phase = [&]( El::Int i, El::Int j )
          {
            return fourier(i,j) +
              El::Sqrt(double(i)*double(i) + double(j)*double(j));
          };

        El::DistMatrix<El::Complex<double>> F(grid), G(grid);
        El::Egorov( F, El::MakeFunction(fourier), n );
        El::Egorov( G, El::MakeFunction(phase),   n );

        if( print )
        {
            El::Print( F, "Egorov with Fourier phase:" );
            El::Print( G, "Egorov with more general phase:" );
        }
    }
    catch( std::exception& e ) { El::ReportException(e); }

    return 0;
}


#if COMPILATION_INSTRUCTIONS
$CXX `mpic++ -showme:compile|sed 's/-pthread/ /g'` $0 -o $0x `mpic++ -showme:link|sed 's/-pthread/ /g'`&&
#(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no                                 --suppressions=$0.openmpi.supp $0x)&&rm $0x;exit
mpic++    -O3 -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0.openmpi.supp    )&&
mpic++ -g -O3 -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0-g.openmpi.supp  )&&
mpic++ -g     -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0-O0.openmpi.supp  )&&
cat $0-g.openmpi.supp $0-O0.openmpi.supp >> $0.openmpi.supp&&rm $0x;exit
#endif
#include "../../mpi3/environment.hpp"

namespace mpi3 = boost::mpi3;

{

	MPI_Init(&argc, &argv);
	MPI_Comm dup_comm_world;
	MPI_Comm_dup( MPI_COMM_WORLD, &dup_comm_world );
	MPI_Comm_free(&dup_comm_world);
	MPI_Finalize();

}


{

	MPI_Init(&argc, &argv);
	MPI_Comm dup_comm_world;
	MPI_Comm_dup( MPI_COMM_WORLD, &dup_comm_world );
	MPI_Comm_free(&dup_comm_world);
	MPI_Finalize();

}

#if COMPILATION_INSTRUCTIONS
$CXX `mpic++ -showme:compile|sed 's/-pthread/ /g'` $0 -o $0x `mpic++ -showme:link|sed 's/-pthread/ /g'`&&
#(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no                                 --suppressions=$0.openmpi.supp $0x)&&rm $0x;exit
mpic++    -O3 -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0.openmpi.supp    )&&
mpic++ -g -O3 -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0-g.openmpi.supp  )&&
mpic++ -g     -Wall -Wextra $0 -o $0x&&(mpirun -n 3 valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all $0x 2>&1|grep -v '==' > $0-O0.openmpi.supp  )&&
cat $0-g.openmpi.supp $0-O0.openmpi.supp >> $0.openmpi.supp&&rm $0x;exit
#endif
#include "../../mpi3/environment.hpp"

namespace mpi3 = boost::mpi3;

int main(int argc, char* argv[])


{

	MPI_Init(&argc, &argv);
	MPI_Comm dup_comm_world;

	MPI_Comm_dup( MPI_COMM_WORLD, &dup_comm_world );
	MPI_Comm_free(&dup_comm_world);
	MPI_Finalize();

}