<commit_before>/*
 *    _aaaa,  _aa.  sa,  aaa              _aaaa,_  ac  .aa.   .aa.  .aa,  _a, sa
 *  .wWV!!!T  |Wm;  dQ[  $WF            _mWT!"?Y  ]QE  :Q#:   ]QW[  :WWk. ]Q[ dW
 * .jWf       :WW: .dQ[  dQ[           .mW(       )WE  :Q#:  .mSQh. :mWQa.]W[ dQ
 * |QW:       :Wm;  mQ[  dQ[           ]Qk        )Qmi_aQW:  <B:$Qc :WBWQ()W[ dQ
 * |W#:  .ww  ;WW;  dQ[  dQ[  .......  ]Qk        )QB?YYW#:  jf ]Qp.:mE)Qm]Q[ )W
 * +WQ;  :Wm  |Wm; .mQ[  dQ[ :qgggggga ]Qm.       ]WE  :Q# :=QasuQm;:Wk 3QQW[ )Y
 *  ]Wmi.:Wm  +$Q; .mW(  dQ[  !"!!"!!^ dQk,  ._   ]WE  :Q# :3D"!!$Qc.Wk -$WQ[
 *   "?????? ` "?!=m?!   ??'            -??????!  -?!  -?? -?'   "?"-?"  "??' "?
 *
 * Copyright (c) 2004 darkbits                              Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of darkbits nor the     :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/container.hpp"
#include "guichan/exception.hpp"

#include <iostream>

namespace gcn
{

	Container::Container()
	{
		mWidgetWithMouse = NULL;
		mOpaque = true;

	} // end Container

	Container::~Container()
	{
		clear();

	} // end ~Container

	void Container::logic()
	{
		logicChildren();

	} // end logic

	void Container::draw(Graphics* graphics)
	{
		if (mOpaque)
		{
			graphics->setColor(getBaseColor());
			graphics->fillRectangle(Rectangle(0, 0, getDimension().width, getDimension().height));
		}

		drawChildren(graphics);

	} // draw

	void Container::logicChildren()
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->logic();
		}

	} // end logicChildren

	void Container::drawChildren(Graphics* graphics)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			graphics->pushClipArea((*iter)->getDimension());
			(*iter)->draw(graphics);
			graphics->popClipArea();
		}

	} // end drawChildren

	void Container::setOpaque(bool opaque)
	{
		mOpaque = opaque;

	} // end setOpaque

	bool Container::isOpaque() const
	{
		return mOpaque;

	} // end isOpaque

	void Container::moveToTop(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_back(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToTop. There is no such widget in this container");

	} // end moveToTop

	void Container::moveToBottom(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_front(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToBottom. There is no such widget in this container");

	} // end moveToBottom

	void Container::_announceDeath(Widget *widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::_announceDeath. There is no such widget in this container");

	} // end _announceDeath

	void Container::getDrawSize(int& width, int& height, Widget* widget)
	{
		WidgetIterator iter;
		bool contains = false;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (widget == *iter)
			{
				contains = true;
				break;
			}
		}

		if (contains)
		{
			Rectangle widgetDim = widget->getDimension();
			Rectangle dim = getDimension();

			width = widgetDim.width;
			height = widgetDim.height;

			if (widgetDim.x < 0)
			{
				width += widgetDim.x;
			}

			if (widgetDim.y < 0)
			{
				height += widgetDim.y;
			}

			if (widgetDim.x + widgetDim.width > dim.width)
			{
				width -= (widgetDim.x + widgetDim.width) - dim.width;
			}

			if (widgetDim.y + widgetDim.height > dim.height)
			{
				height -= (widgetDim.y + widgetDim.height) - dim.height;
			}

			if (width < 0)
			{
				width = 0;
			}

			if (height < 0)
			{
				height = 0;
			}
		}
		else
		{
			throw GCN_EXCEPTION("Container::getDrawSize. Widget not in container");
		}

	} // end getDrawSize

	void Container::add(Widget* widget)
	{
		mWidgets.push_back(widget);
		widget->_setFocusHandler(_getFocusHandler());
		widget->_setParent(this);
		// TODO: Give it the mousetypehandler too!

	} // add

	void Container::add(Widget* widget, int x, int y)
	{
		widget->setPosition(x, y);
		add(widget);

	} // end add

	void Container::remove(Widget* widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				widget->_setFocusHandler(NULL);
				widget->_setParent(NULL);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::remove. There is no such widget in this container");

	} // end remove

	void Container::clear()
	{
		mWidgetWithMouse = NULL;

		WidgetIterator iter;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(NULL);
			(*iter)->_setParent(NULL);
		}

		mWidgets.clear();

	} // end clear

	void Container::_setFocusHandler(FocusHandler* focusHandler)
	{
		Widget::_setFocusHandler(focusHandler);

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(focusHandler);
		}

	} // end _setFocusHandler

	void Container::_mouseInputMessage(const MouseInput &mouseInput)
	{
		Widget* tempWidgetWithMouse = NULL;

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->getDimension().isPointInRect(mouseInput.x, mouseInput.y))
			{
				tempWidgetWithMouse = (*iter);
			}
		}

		if (tempWidgetWithMouse != mWidgetWithMouse)
		{
			if (mWidgetWithMouse)
			{
				mWidgetWithMouse->_mouseOutMessage();
			}

			if (tempWidgetWithMouse)
			{
				tempWidgetWithMouse->_mouseInMessage();
			}

			mWidgetWithMouse = tempWidgetWithMouse;
		}

		if (mWidgetWithMouse && !mWidgetWithMouse->hasFocus())
		{
			MouseInput mi = mouseInput;
			mi.x -= mWidgetWithMouse->getX();
			mi.y -= mWidgetWithMouse->getY();
			mWidgetWithMouse->_mouseInputMessage(mi);
		}

		if (mWidgetWithMouse == NULL)
		{
			Widget::_mouseInputMessage(mouseInput);
		}

	} //end _mouseInputMessage

	void Container::_mouseOutMessage()
	{
		if (mWidgetWithMouse)
		{
			mWidgetWithMouse->_mouseOutMessage();
			mWidgetWithMouse = NULL;
		}

		Widget::_mouseOutMessage();

	} // end _mouseOutMessage

} // end gcn
<commit_msg>Fixed include path and remove <iostream><commit_after>/*
 *    _aaaa,  _aa.  sa,  aaa              _aaaa,_  ac  .aa.   .aa.  .aa,  _a, sa
 *  .wWV!!!T  |Wm;  dQ[  $WF            _mWT!"?Y  ]QE  :Q#:   ]QW[  :WWk. ]Q[ dW
 * .jWf       :WW: .dQ[  dQ[           .mW(       )WE  :Q#:  .mSQh. :mWQa.]W[ dQ
 * |QW:       :Wm;  mQ[  dQ[           ]Qk        )Qmi_aQW:  <B:$Qc :WBWQ()W[ dQ
 * |W#:  .ww  ;WW;  dQ[  dQ[  .......  ]Qk        )QB?YYW#:  jf ]Qp.:mE)Qm]Q[ )W
 * +WQ;  :Wm  |Wm; .mQ[  dQ[ :qgggggga ]Qm.       ]WE  :Q# :=QasuQm;:Wk 3QQW[ )Y
 *  ]Wmi.:Wm  +$Q; .mW(  dQ[  !"!!"!!^ dQk,  ._   ]WE  :Q# :3D"!!$Qc.Wk -$WQ[
 *   "?????? ` "?!=m?!   ??'            -??????!  -?!  -?? -?'   "?"-?"  "??' "?
 *
 * Copyright (c) 2004 darkbits                              Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of darkbits nor the     :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/exception.hpp"
#include "guichan/widgets/container.hpp"

namespace gcn
{

	Container::Container()
	{
		mWidgetWithMouse = NULL;
		mOpaque = true;

	} // end Container

	Container::~Container()
	{
		clear();

	} // end ~Container

	void Container::logic()
	{
		logicChildren();

	} // end logic

	void Container::draw(Graphics* graphics)
	{
		if (mOpaque)
		{
			graphics->setColor(getBaseColor());
			graphics->fillRectangle(Rectangle(0, 0, getDimension().width, getDimension().height));
		}

		drawChildren(graphics);

	} // draw

	void Container::logicChildren()
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->logic();
		}

	} // end logicChildren

	void Container::drawChildren(Graphics* graphics)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			graphics->pushClipArea((*iter)->getDimension());
			(*iter)->draw(graphics);
			graphics->popClipArea();
		}

	} // end drawChildren

	void Container::setOpaque(bool opaque)
	{
		mOpaque = opaque;

	} // end setOpaque

	bool Container::isOpaque() const
	{
		return mOpaque;

	} // end isOpaque

	void Container::moveToTop(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_back(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToTop. There is no such widget in this container");

	} // end moveToTop

	void Container::moveToBottom(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_front(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToBottom. There is no such widget in this container");

	} // end moveToBottom

	void Container::_announceDeath(Widget *widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::_announceDeath. There is no such widget in this container");

	} // end _announceDeath

	void Container::getDrawSize(int& width, int& height, Widget* widget)
	{
		WidgetIterator iter;
		bool contains = false;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (widget == *iter)
			{
				contains = true;
				break;
			}
		}

		if (contains)
		{
			Rectangle widgetDim = widget->getDimension();
			Rectangle dim = getDimension();

			width = widgetDim.width;
			height = widgetDim.height;

			if (widgetDim.x < 0)
			{
				width += widgetDim.x;
			}

			if (widgetDim.y < 0)
			{
				height += widgetDim.y;
			}

			if (widgetDim.x + widgetDim.width > dim.width)
			{
				width -= (widgetDim.x + widgetDim.width) - dim.width;
			}

			if (widgetDim.y + widgetDim.height > dim.height)
			{
				height -= (widgetDim.y + widgetDim.height) - dim.height;
			}

			if (width < 0)
			{
				width = 0;
			}

			if (height < 0)
			{
				height = 0;
			}
		}
		else
		{
			throw GCN_EXCEPTION("Container::getDrawSize. Widget not in container");
		}

	} // end getDrawSize

	void Container::add(Widget* widget)
	{
		mWidgets.push_back(widget);
		widget->_setFocusHandler(_getFocusHandler());
		widget->_setParent(this);
		// TODO: Give it the mousetypehandler too!

	} // add

	void Container::add(Widget* widget, int x, int y)
	{
		widget->setPosition(x, y);
		add(widget);

	} // end add

	void Container::remove(Widget* widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				widget->_setFocusHandler(NULL);
				widget->_setParent(NULL);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::remove. There is no such widget in this container");

	} // end remove

	void Container::clear()
	{
		mWidgetWithMouse = NULL;

		WidgetIterator iter;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(NULL);
			(*iter)->_setParent(NULL);
		}

		mWidgets.clear();

	} // end clear

	void Container::_setFocusHandler(FocusHandler* focusHandler)
	{
		Widget::_setFocusHandler(focusHandler);

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(focusHandler);
		}

	} // end _setFocusHandler

	void Container::_mouseInputMessage(const MouseInput &mouseInput)
	{
		Widget* tempWidgetWithMouse = NULL;

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->getDimension().isPointInRect(mouseInput.x, mouseInput.y))
			{
				tempWidgetWithMouse = (*iter);
			}
		}

		if (tempWidgetWithMouse != mWidgetWithMouse)
		{
			if (mWidgetWithMouse)
			{
				mWidgetWithMouse->_mouseOutMessage();
			}

			if (tempWidgetWithMouse)
			{
				tempWidgetWithMouse->_mouseInMessage();
			}

			mWidgetWithMouse = tempWidgetWithMouse;
		}

		if (mWidgetWithMouse && !mWidgetWithMouse->hasFocus())
		{
			MouseInput mi = mouseInput;
			mi.x -= mWidgetWithMouse->getX();
			mi.y -= mWidgetWithMouse->getY();
			mWidgetWithMouse->_mouseInputMessage(mi);
		}

		if (mWidgetWithMouse == NULL)
		{
			Widget::_mouseInputMessage(mouseInput);
		}

	} //end _mouseInputMessage

	void Container::_mouseOutMessage()
	{
		if (mWidgetWithMouse)
		{
			mWidgetWithMouse->_mouseOutMessage();
			mWidgetWithMouse = NULL;
		}

		Widget::_mouseOutMessage();

	} // end _mouseOutMessage

} // end gcn
<|endoftext|>


<commit_before>/*
 * BIT.cc
 *
 * Copyright (C) 2016-2017 OpenCog Foundation
 * Author: Nil Geisweiller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <boost/range/algorithm/lower_bound.hpp>
#include <boost/range/algorithm/remove_if.hpp>
#include <boost/range/algorithm/binary_search.hpp>

#include <opencog/util/random.h>

#include <opencog/atomutils/Neighbors.h>
#include "BIT.h"
#include "BCLogger.h"

namespace opencog {

/////////////
// BITNode //
/////////////

BITNode::BITNode(const Handle& bd, const BITNodeFitness& fi)
	: body(bd), fitness(fi) {}

double BITNode::operator()() const
{
	// The probably is anti-proportional to the fitness. Indeed if the
	// fitness already is high, expanding the BIT-Node won't likely
	// add anything to the success of the inference.
	return (fitness.upper - fitness(*this)) / (fitness.upper - fitness.lower);
}

std::string	BITNode::to_string() const
{
	std::stringstream ss;
	ss << "body:" << std::endl << oc_to_string(body)
	   << "rules:" << std::endl << oc_to_string(rules);
	return ss.str();
}

////////////
// AndBIT //
////////////

AndBIT::AndBIT() {}

AndBIT::AndBIT(AtomSpace& as, const Handle& target, const Handle& vardecl,
               const BITNodeFitness& fitness)
{
	// Create initial FCS
	HandleSeq bl{target, target};
	if (vardecl.is_defined())
		bl.insert(bl.begin(), vardecl);
	fcs = as.add_link(BIND_LINK, bl);

	insert_bitnode(target, fitness);
}

AndBIT::~AndBIT() {}

AndBIT AndBIT::expand(const Handle& leaf, const Rule& rule) const
{
	AndBIT andbit;
	andbit.fcs = expand_fcs(leaf, rule);
	andbit.set_leaf2bitnode();  // TODO: might differ till needed
	return andbit;
}

BITNode& AndBIT::select_leaf()
{
	// TODO: optimize, do not remake the distribution each time
	LeafDistribution dist = get_distribution();
	size_t index = dist(randGen());
	return std::next(leaf2bitnode.begin(), index)->second;
}

bool AndBIT::operator==(const AndBIT& andbit) const
{
	return fcs == andbit.fcs;
}

bool AndBIT::operator<(const AndBIT& andbit) const
{
	size_t fcs_size = fcs->size();
	size_t other_size = andbit.fcs->size();
	return (fcs_size < other_size)
		or (fcs_size == other_size and fcs < andbit.fcs);
}

std::string AndBIT::to_string() const
{
	return oc_to_string(fcs);
}

Handle AndBIT::expand_fcs(const Handle& leaf, const Rule& rule) const
{
	// Unify the rule conclusion with the leaf, and substitute any
	// variables in it by the associated term.
	Handle nfcs = substitute_unified_variables(leaf, rule);

	BindLinkPtr nfcs_bl(BindLinkCast(nfcs));
	Handle nfcs_vardecl = nfcs_bl->get_vardecl();
	Handle nfcs_pattern = nfcs_bl->get_body();
	Handle nfcs_rewrite = nfcs_bl->get_implicand();
	Handle rule_vardecl = rule.get_vardecl();

	// Generate new pattern term
	Handle npattern = expand_fcs_pattern(nfcs_pattern, rule);

	// Generate new rewrite term
	Handle nrewrite = expand_fcs_rewrite(nfcs_rewrite, rule);

	// Generate new vardecl
	Handle nvardecl = filter_vardecl(merge_vardecl(nfcs_vardecl, rule_vardecl),
	                                 {npattern, nrewrite});

	// Generate new atomese forward chaining strategy
	HandleSeq noutgoings({npattern, nrewrite});
	if (nvardecl.is_defined())
		noutgoings.insert(noutgoings.begin(), nvardecl);
	nfcs = fcs->getAtomSpace()->add_link(BIND_LINK, noutgoings);

	LAZY_BC_LOG_DEBUG << "Expanded forward chainer strategy:" << std::endl
	                  << "from:" << std::endl << fcs
	                  << "to:" << std::endl << nfcs;

	return nfcs;
}

void AndBIT::set_leaf2bitnode()
{
	// For each leaf of fcs, associate a corresponding BITNode
	for (const Handle& leaf : get_leaves())
		insert_bitnode(leaf, BITNodeFitness());
}

void AndBIT::insert_bitnode(Handle leaf, const BITNodeFitness& fitness)
{
	if (leaf.is_undefined())
		return;

	if (leaf2bitnode.find(leaf) == leaf2bitnode.end())
		leaf2bitnode.emplace(leaf, BITNode(leaf, fitness));
}

OrderedHandleSet AndBIT::get_leaves() const
{
	return get_leaves(fcs);
}

OrderedHandleSet AndBIT::get_leaves(const Handle& h) const
{
	Type t = h->getType();
	if (t == BIND_LINK) {
		BindLinkPtr hsc = BindLinkCast(h);
		Handle rewrite = hsc->get_implicand();
		return get_leaves(rewrite);
	} else if (t == EXECUTION_OUTPUT_LINK) {
		// All arguments except the first one are potential target leaves
		Handle args = h->getOutgoingAtom(1);
		OrderedHandleSet leaves;
		if (args->getType() == LIST_LINK) {
			OC_ASSERT(args->getArity() > 0);
			for (Arity i = 1; i < args->getArity(); i++) {
				OrderedHandleSet aleaves = get_leaves(args->getOutgoingAtom(i));
				leaves.insert(aleaves.begin(), aleaves.end());
			}
		} else
			leaves.insert(args);
		return leaves;
	} else if (t == SET_LINK) {
		// All atoms wrapped in a SetLink are potential target leaves
		OrderedHandleSet leaves;
		for (const Handle& el : h->getOutgoingSet()) {
			OrderedHandleSet el_leaves = get_leaves(el);
			leaves.insert(el_leaves.begin(), el_leaves.end());
		}
		return leaves;
	} else
		// It is probably a leaf so return it
		return OrderedHandleSet{h};
}

Handle AndBIT::substitute_unified_variables(const Handle& leaf,
                                            const Rule& rule) const
{
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// If the conclusion is already equal to leaf no need to unify
	if (content_eq(conclusion, leaf))
		return fcs;

	BindLinkPtr fcs_bl(BindLinkCast(fcs));
	Handle leaf_vardecl = filter_vardecl(fcs_bl->get_vardecl(), leaf),
		conclusion_vardecl = rule.get_vardecl();
	UnificationSolutionSet sol =
		unify(leaf, conclusion, leaf_vardecl, conclusion_vardecl);

	OC_ASSERT(sol.satisfiable); // If the rule has been selected it
                                // has to be satisfiable
	TypedSubstitutions tss =
		typed_substitutions(sol, leaf, leaf, conclusion,
		                    fcs_bl->get_vardecl(), conclusion_vardecl);
	OC_ASSERT(not tss.empty());
	auto ts = *tss.begin();
	return Handle(substitute(fcs_bl, ts));
}

Handle AndBIT::expand_fcs_pattern(const Handle& fcs_pattern,
                                  const Rule& rule) const
{
	HandleSeq clauses = rule.get_clauses();
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// Simple case where the fcs contains the conclusion itself
	if (content_eq(fcs_pattern, conclusion))
		return fcs->getAtomSpace()->add_link(AND_LINK,
		                                     HandleSeq(clauses.begin(),
		                                               clauses.end()));

	// The fcs contains a conjunction of clauses. Remove any clause
	// that is equal to the rule conclusion, or precondition that uses
	// that conclusion as argument.
	OC_ASSERT(fcs_pattern->getType() == AND_LINK);
	HandleSeq fcs_clauses = fcs_pattern->getOutgoingSet();
	auto to_remove = [&](const Handle& h) {
		return (is_locally_quoted_eq(h, conclusion)
		        or is_argument_of(h, conclusion));
	};
	fcs_clauses.erase(boost::remove_if(fcs_clauses, to_remove),
	                  fcs_clauses.end());

	// Add the patterns and preconditions associated to the rule
	fcs_clauses.insert(fcs_clauses.end(), clauses.begin(), clauses.end());

	return fcs->getAtomSpace()->add_link(AND_LINK, fcs_clauses);
}

Handle AndBIT::expand_fcs_rewrite(const Handle& fcs_rewrite,
                                  const Rule& rule) const
{
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// Base cases

	// Replace the fcs rewrite atoms by the rule rewrite if equal to
	// the rule conclusion
	if (content_eq(fcs_rewrite, conclusion))
		return rule.get_implicand();
	// If node and isn't equal to conclusion leave alone
	if (fcs_rewrite->isNode())
		return fcs_rewrite;

	// Recursive case

	Type t = fcs_rewrite->getType();
	HandleSeq outgoings;
	for (const Handle& h : fcs_rewrite->getOutgoingSet())
		outgoings.push_back(expand_fcs_rewrite(h, rule));
	return fcs->getAtomSpace()->add_link(t, outgoings);
}

bool AndBIT::is_argument_of(const Handle& eval, const Handle& atom) const
{
	if (eval->getType() == EVALUATION_LINK) {
		Handle args = eval->getOutgoingAtom(1);
		if (content_eq(args, atom))
			return true;
		if (args->getType() == LIST_LINK)
			for (Arity i = 0; i+1 < args->getArity(); i++)
				if (content_eq(args->getOutgoingAtom(i), atom))
					return true;
	}
	return false;
}

bool AndBIT::is_locally_quoted_eq(const Handle& lhs, const Handle& rhs) const
{
	if (content_eq(lhs, rhs))
		return true;
	Type lhs_t = lhs->getType();
	Type rhs_t = rhs->getType();
	if (lhs_t == LOCAL_QUOTE_LINK and rhs_t != LOCAL_QUOTE_LINK)
		return content_eq(lhs->getOutgoingAtom(0), rhs);
	if (lhs_t != LOCAL_QUOTE_LINK and rhs_t == LOCAL_QUOTE_LINK)
		return content_eq(lhs, rhs->getOutgoingAtom(0));
	return false;
}

AndBIT::LeafDistribution AndBIT::get_distribution()
{
	std::vector<double> weights;
	for (const auto& lb : leaf2bitnode)
		weights.push_back(lb.second());
	return LeafDistribution(weights.begin(), weights.end());
}

/////////
// BIT //
/////////

BIT::BIT(AtomSpace& as,
         const Handle& target,
         const Handle& vardecl,
         const BITNodeFitness& fitness)
	: bit_as(&as),
	  _init_target(target), _init_vardecl(vardecl), _init_fitness(fitness)
{}

BIT::~BIT() {}

bool BIT::empty() const
{
	return andbits.empty();
}

AndBIT* BIT::init()
{
	andbits.emplace_back(bit_as, _init_target, _init_vardecl, _init_fitness);

	LAZY_BC_LOG_DEBUG << "Initialize BIT with:" << std::endl
	                  << andbits.begin()->to_string();

	return &*andbits.begin();
}

AndBIT* BIT::expand(AndBIT& andbit, BITNode& bitleaf, const Rule& rule)
{
	// Make sure that the rule is not already an or-child of bitleaf.
	if (is_in(rule, bitleaf)) {
		bc_logger().debug() << "An equivalent rule has already expanded "
		                    << "that BIT-node, abort expansion";
		return nullptr;
	}

	// Insert the rule as or-branch of this bitleaf
	bitleaf.rules.insert(rule);

	// Expand the and-BIT and insert it in the BIT
	return insert(andbit.expand(bitleaf.body, rule));
}

AndBIT* BIT::insert(const AndBIT& andbit)
{
	// Check that it isn't already in the BIT
	if (boost::binary_search(andbits, andbit)) {
		LAZY_BC_LOG_DEBUG << "The following and-BIT is already in the BIT:"
		                  << std::endl << andbit.to_string();
		return nullptr;
	} else {
		// Check that it is not alpha-equivalent either
		for (const AndBIT& ab : andbits) {
			if (ScopeLinkCast(andbit.fcs)->is_equal(ab.fcs)) {
				LAZY_BC_LOG_DEBUG << "The following and-BIT is alpha-equivalent "
				                  << "to another one already in the BIT:"
				                  << std::endl << andbit.to_string();
				return nullptr;
			}
		}
	}

	// Insert while keeping the order
	auto it = andbits.insert(boost::lower_bound(andbits, andbit), andbit);

	// Return andbit pointer
	return &*it;
}

BIT::AndBITs::iterator BIT::erase(BIT::AndBITs::iterator from,
                                  BIT::AndBITs::iterator to)
{
	return andbits.erase(from, to);
}

bool BIT::is_in(const Rule& rule, const BITNode& bitnode)
{
	for (const Rule& bnr : bitnode.rules)
		if (rule.is_alpha_equivalent(bnr))
			return true;
	return false;
}

std::string oc_to_string(const BITNode& bitnode)
{
	return bitnode.to_string();
}

std::string oc_to_string(const AndBIT& andbit)
{
	return andbit.to_string();
}

// std::string oc_to_string(const BIT& bit)
// {
// 	return bit.to_string();
// }

} // ~namespace opencog
<commit_msg>Simplify AndBIT::select_leaf using improved rand_element<commit_after>/*
 * BIT.cc
 *
 * Copyright (C) 2016-2017 OpenCog Foundation
 * Author: Nil Geisweiller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <boost/range/algorithm/lower_bound.hpp>
#include <boost/range/algorithm/remove_if.hpp>
#include <boost/range/algorithm/binary_search.hpp>

#include <opencog/util/random.h>

#include <opencog/atomutils/Neighbors.h>
#include "BIT.h"
#include "BCLogger.h"

namespace opencog {

/////////////
// BITNode //
/////////////

BITNode::BITNode(const Handle& bd, const BITNodeFitness& fi)
	: body(bd), fitness(fi) {}

double BITNode::operator()() const
{
	// The probably is anti-proportional to the fitness. Indeed if the
	// fitness already is high, expanding the BIT-Node won't likely
	// add anything to the success of the inference.
	return (fitness.upper - fitness(*this)) / (fitness.upper - fitness.lower);
}

std::string	BITNode::to_string() const
{
	std::stringstream ss;
	ss << "body:" << std::endl << oc_to_string(body)
	   << "rules:" << std::endl << oc_to_string(rules);
	return ss.str();
}

////////////
// AndBIT //
////////////

AndBIT::AndBIT() {}

AndBIT::AndBIT(AtomSpace& as, const Handle& target, const Handle& vardecl,
               const BITNodeFitness& fitness)
{
	// Create initial FCS
	HandleSeq bl{target, target};
	if (vardecl.is_defined())
		bl.insert(bl.begin(), vardecl);
	fcs = as.add_link(BIND_LINK, bl);

	insert_bitnode(target, fitness);
}

AndBIT::~AndBIT() {}

AndBIT AndBIT::expand(const Handle& leaf, const Rule& rule) const
{
	AndBIT andbit;
	andbit.fcs = expand_fcs(leaf, rule);
	andbit.set_leaf2bitnode();  // TODO: might differ till needed
	return andbit;
}

BITNode& AndBIT::select_leaf()
{
	// TODO: optimize, do not remake the distribution each time
	LeafDistribution dist = get_distribution();
	return rand_element(leaf2bitnode, dist).second;
}

bool AndBIT::operator==(const AndBIT& andbit) const
{
	return fcs == andbit.fcs;
}

bool AndBIT::operator<(const AndBIT& andbit) const
{
	size_t fcs_size = fcs->size();
	size_t other_size = andbit.fcs->size();
	return (fcs_size < other_size)
		or (fcs_size == other_size and fcs < andbit.fcs);
}

std::string AndBIT::to_string() const
{
	return oc_to_string(fcs);
}

Handle AndBIT::expand_fcs(const Handle& leaf, const Rule& rule) const
{
	// Unify the rule conclusion with the leaf, and substitute any
	// variables in it by the associated term.
	Handle nfcs = substitute_unified_variables(leaf, rule);

	BindLinkPtr nfcs_bl(BindLinkCast(nfcs));
	Handle nfcs_vardecl = nfcs_bl->get_vardecl();
	Handle nfcs_pattern = nfcs_bl->get_body();
	Handle nfcs_rewrite = nfcs_bl->get_implicand();
	Handle rule_vardecl = rule.get_vardecl();

	// Generate new pattern term
	Handle npattern = expand_fcs_pattern(nfcs_pattern, rule);

	// Generate new rewrite term
	Handle nrewrite = expand_fcs_rewrite(nfcs_rewrite, rule);

	// Generate new vardecl
	Handle nvardecl = filter_vardecl(merge_vardecl(nfcs_vardecl, rule_vardecl),
	                                 {npattern, nrewrite});

	// Generate new atomese forward chaining strategy
	HandleSeq noutgoings({npattern, nrewrite});
	if (nvardecl.is_defined())
		noutgoings.insert(noutgoings.begin(), nvardecl);
	nfcs = fcs->getAtomSpace()->add_link(BIND_LINK, noutgoings);

	LAZY_BC_LOG_DEBUG << "Expanded forward chainer strategy:" << std::endl
	                  << "from:" << std::endl << fcs
	                  << "to:" << std::endl << nfcs;

	return nfcs;
}

void AndBIT::set_leaf2bitnode()
{
	// For each leaf of fcs, associate a corresponding BITNode
	for (const Handle& leaf : get_leaves())
		insert_bitnode(leaf, BITNodeFitness());
}

void AndBIT::insert_bitnode(Handle leaf, const BITNodeFitness& fitness)
{
	if (leaf.is_undefined())
		return;

	if (leaf2bitnode.find(leaf) == leaf2bitnode.end())
		leaf2bitnode.emplace(leaf, BITNode(leaf, fitness));
}

OrderedHandleSet AndBIT::get_leaves() const
{
	return get_leaves(fcs);
}

OrderedHandleSet AndBIT::get_leaves(const Handle& h) const
{
	Type t = h->getType();
	if (t == BIND_LINK) {
		BindLinkPtr hsc = BindLinkCast(h);
		Handle rewrite = hsc->get_implicand();
		return get_leaves(rewrite);
	} else if (t == EXECUTION_OUTPUT_LINK) {
		// All arguments except the first one are potential target leaves
		Handle args = h->getOutgoingAtom(1);
		OrderedHandleSet leaves;
		if (args->getType() == LIST_LINK) {
			OC_ASSERT(args->getArity() > 0);
			for (Arity i = 1; i < args->getArity(); i++) {
				OrderedHandleSet aleaves = get_leaves(args->getOutgoingAtom(i));
				leaves.insert(aleaves.begin(), aleaves.end());
			}
		} else
			leaves.insert(args);
		return leaves;
	} else if (t == SET_LINK) {
		// All atoms wrapped in a SetLink are potential target leaves
		OrderedHandleSet leaves;
		for (const Handle& el : h->getOutgoingSet()) {
			OrderedHandleSet el_leaves = get_leaves(el);
			leaves.insert(el_leaves.begin(), el_leaves.end());
		}
		return leaves;
	} else
		// It is probably a leaf so return it
		return OrderedHandleSet{h};
}

Handle AndBIT::substitute_unified_variables(const Handle& leaf,
                                            const Rule& rule) const
{
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// If the conclusion is already equal to leaf no need to unify
	if (content_eq(conclusion, leaf))
		return fcs;

	BindLinkPtr fcs_bl(BindLinkCast(fcs));
	Handle leaf_vardecl = filter_vardecl(fcs_bl->get_vardecl(), leaf),
		conclusion_vardecl = rule.get_vardecl();
	UnificationSolutionSet sol =
		unify(leaf, conclusion, leaf_vardecl, conclusion_vardecl);

	OC_ASSERT(sol.satisfiable); // If the rule has been selected it
                                // has to be satisfiable
	TypedSubstitutions tss =
		typed_substitutions(sol, leaf, leaf, conclusion,
		                    fcs_bl->get_vardecl(), conclusion_vardecl);
	OC_ASSERT(not tss.empty());
	auto ts = *tss.begin();
	return Handle(substitute(fcs_bl, ts));
}

Handle AndBIT::expand_fcs_pattern(const Handle& fcs_pattern,
                                  const Rule& rule) const
{
	HandleSeq clauses = rule.get_clauses();
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// Simple case where the fcs contains the conclusion itself
	if (content_eq(fcs_pattern, conclusion))
		return fcs->getAtomSpace()->add_link(AND_LINK,
		                                     HandleSeq(clauses.begin(),
		                                               clauses.end()));

	// The fcs contains a conjunction of clauses. Remove any clause
	// that is equal to the rule conclusion, or precondition that uses
	// that conclusion as argument.
	OC_ASSERT(fcs_pattern->getType() == AND_LINK);
	HandleSeq fcs_clauses = fcs_pattern->getOutgoingSet();
	auto to_remove = [&](const Handle& h) {
		return (is_locally_quoted_eq(h, conclusion)
		        or is_argument_of(h, conclusion));
	};
	fcs_clauses.erase(boost::remove_if(fcs_clauses, to_remove),
	                  fcs_clauses.end());

	// Add the patterns and preconditions associated to the rule
	fcs_clauses.insert(fcs_clauses.end(), clauses.begin(), clauses.end());

	return fcs->getAtomSpace()->add_link(AND_LINK, fcs_clauses);
}

Handle AndBIT::expand_fcs_rewrite(const Handle& fcs_rewrite,
                                  const Rule& rule) const
{
	HandlePairSeq conclusions = rule.get_conclusions();
	OC_ASSERT(conclusions.size() == 1);
	Handle conclusion = conclusions[0].second;

	// Base cases

	// Replace the fcs rewrite atoms by the rule rewrite if equal to
	// the rule conclusion
	if (content_eq(fcs_rewrite, conclusion))
		return rule.get_implicand();
	// If node and isn't equal to conclusion leave alone
	if (fcs_rewrite->isNode())
		return fcs_rewrite;

	// Recursive case

	Type t = fcs_rewrite->getType();
	HandleSeq outgoings;
	for (const Handle& h : fcs_rewrite->getOutgoingSet())
		outgoings.push_back(expand_fcs_rewrite(h, rule));
	return fcs->getAtomSpace()->add_link(t, outgoings);
}

bool AndBIT::is_argument_of(const Handle& eval, const Handle& atom) const
{
	if (eval->getType() == EVALUATION_LINK) {
		Handle args = eval->getOutgoingAtom(1);
		if (content_eq(args, atom))
			return true;
		if (args->getType() == LIST_LINK)
			for (Arity i = 0; i+1 < args->getArity(); i++)
				if (content_eq(args->getOutgoingAtom(i), atom))
					return true;
	}
	return false;
}

bool AndBIT::is_locally_quoted_eq(const Handle& lhs, const Handle& rhs) const
{
	if (content_eq(lhs, rhs))
		return true;
	Type lhs_t = lhs->getType();
	Type rhs_t = rhs->getType();
	if (lhs_t == LOCAL_QUOTE_LINK and rhs_t != LOCAL_QUOTE_LINK)
		return content_eq(lhs->getOutgoingAtom(0), rhs);
	if (lhs_t != LOCAL_QUOTE_LINK and rhs_t == LOCAL_QUOTE_LINK)
		return content_eq(lhs, rhs->getOutgoingAtom(0));
	return false;
}

AndBIT::LeafDistribution AndBIT::get_distribution()
{
	std::vector<double> weights;
	for (const auto& lb : leaf2bitnode)
		weights.push_back(lb.second());
	return LeafDistribution(weights.begin(), weights.end());
}

/////////
// BIT //
/////////

BIT::BIT(AtomSpace& as,
         const Handle& target,
         const Handle& vardecl,
         const BITNodeFitness& fitness)
	: bit_as(&as),
	  _init_target(target), _init_vardecl(vardecl), _init_fitness(fitness)
{}

BIT::~BIT() {}

bool BIT::empty() const
{
	return andbits.empty();
}

AndBIT* BIT::init()
{
	andbits.emplace_back(bit_as, _init_target, _init_vardecl, _init_fitness);

	LAZY_BC_LOG_DEBUG << "Initialize BIT with:" << std::endl
	                  << andbits.begin()->to_string();

	return &*andbits.begin();
}

AndBIT* BIT::expand(AndBIT& andbit, BITNode& bitleaf, const Rule& rule)
{
	// Make sure that the rule is not already an or-child of bitleaf.
	if (is_in(rule, bitleaf)) {
		bc_logger().debug() << "An equivalent rule has already expanded "
		                    << "that BIT-node, abort expansion";
		return nullptr;
	}

	// Insert the rule as or-branch of this bitleaf
	bitleaf.rules.insert(rule);

	// Expand the and-BIT and insert it in the BIT
	return insert(andbit.expand(bitleaf.body, rule));
}

AndBIT* BIT::insert(const AndBIT& andbit)
{
	// Check that it isn't already in the BIT
	if (boost::binary_search(andbits, andbit)) {
		LAZY_BC_LOG_DEBUG << "The following and-BIT is already in the BIT:"
		                  << std::endl << andbit.to_string();
		return nullptr;
	} else {
		// Check that it is not alpha-equivalent either
		for (const AndBIT& ab : andbits) {
			if (ScopeLinkCast(andbit.fcs)->is_equal(ab.fcs)) {
				LAZY_BC_LOG_DEBUG << "The following and-BIT is alpha-equivalent "
				                  << "to another one already in the BIT:"
				                  << std::endl << andbit.to_string();
				return nullptr;
			}
		}
	}

	// Insert while keeping the order
	auto it = andbits.insert(boost::lower_bound(andbits, andbit), andbit);

	// Return andbit pointer
	return &*it;
}

BIT::AndBITs::iterator BIT::erase(BIT::AndBITs::iterator from,
                                  BIT::AndBITs::iterator to)
{
	return andbits.erase(from, to);
}

bool BIT::is_in(const Rule& rule, const BITNode& bitnode)
{
	for (const Rule& bnr : bitnode.rules)
		if (rule.is_alpha_equivalent(bnr))
			return true;
	return false;
}

std::string oc_to_string(const BITNode& bitnode)
{
	return bitnode.to_string();
}

std::string oc_to_string(const AndBIT& andbit)
{
	return andbit.to_string();
}

// std::string oc_to_string(const BIT& bit)
// {
// 	return bit.to_string();
// }

} // ~namespace opencog
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPythonInterpreter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPython.h" // this must be the first include.
#include "vtkPythonInterpreter.h"

#include "vtkCommand.h"
#include "vtkObjectFactory.h"
#include "vtkWeakPointer.h"
#include "vtkPythonStdStreamCaptureHelper.h"

#include <vtksys/SystemTools.hxx>

#include <algorithm>
#include <string>
#include <vector>
#include <signal.h>

namespace
{
  class StringPool
    {
  public:
    std::vector<char*> Strings;
    ~StringPool()
      {
      for (size_t cc=0; cc < this->Strings.size(); cc++)
        {
        delete [] this->Strings[cc];
        }
      }
    };

  static std::vector<vtkWeakPointer<vtkPythonInterpreter> > GlobalInterpreters;
  static std::vector<std::string> PythonPaths;

  void NotifyInterpreters(unsigned long eventid, void *calldata=NULL)
    {
    std::vector<vtkWeakPointer<vtkPythonInterpreter> >::iterator iter;
    for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end();
      ++iter)
      {
      if (iter->GetPointer())
        {
        iter->GetPointer()->InvokeEvent(eventid, calldata);
        }
      }
    }

  inline void vtkPrependPythonPath(const char* pathtoadd)
    {
    PyObject* path = PySys_GetObject(const_cast<char*>("path"));
    PyObject* newpath = PyString_FromString(pathtoadd);
    PyList_Insert(path, 0, newpath);
    Py_DECREF(newpath);
    }
}

bool vtkPythonInterpreter::InitializedOnce = false;
bool vtkPythonInterpreter::CaptureStdin = false;

vtkStandardNewMacro(vtkPythonInterpreter);
//----------------------------------------------------------------------------
vtkPythonInterpreter::vtkPythonInterpreter()
{
  GlobalInterpreters.push_back(this);
}

//----------------------------------------------------------------------------
vtkPythonInterpreter::~vtkPythonInterpreter()
{
  std::vector<vtkWeakPointer<vtkPythonInterpreter> >::iterator iter;
  for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end();
    ++iter)
    {
    if (*iter == this)
      {
      GlobalInterpreters.erase(iter);
      break;
      }
    }
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::IsInitialized()
{
  return (Py_IsInitialized() != 0);
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::Initialize(int initsigs /*=0*/)
{
  vtkPythonInterpreter::InitializedOnce = true;
  if (Py_IsInitialized() == 0)
    {
#if (VTK_PYTHON_MAJOR_VERSION > 2) ||\
    (VTK_PYTHON_MAJOR_VERSION == 2 && VTK_PYTHON_MINOR_VERSION >= 4)
    Py_InitializeEx(initsigs);
#else
    (void)initsigs;
    Py_Initialize();
#endif

#ifdef SIGINT
    // Put default SIGINT handler back after Py_Initialize/Py_InitializeEx.
    signal(SIGINT, SIG_DFL);
#endif
    // HACK: Calling PyRun_SimpleString for the first time for some reason results in
    // a "\n" message being generated which is causing the error dialog to
    // popup. So we flush that message out of the system before setting up the
    // callbacks.
    vtkPythonInterpreter::RunSimpleString("");

    // Setup handlers for stdout/stdin/stderr.
    vtkPythonStdStreamCaptureHelper* wrapperOut =
      NewPythonStdStreamCaptureHelper(false);
    vtkPythonStdStreamCaptureHelper* wrapperErr =
      NewPythonStdStreamCaptureHelper(true);

    // Redirect Python's stdout and stderr and stdin
    PySys_SetObject(const_cast<char*>("stdout"),
      reinterpret_cast<PyObject*>(wrapperOut));
    PySys_SetObject(const_cast<char*>("stderr"),
      reinterpret_cast<PyObject*>(wrapperErr));
    PySys_SetObject(const_cast<char*>("stdin"),
      reinterpret_cast<PyObject*>(wrapperOut));
    Py_DECREF(wrapperOut);
    Py_DECREF(wrapperErr);

    for (size_t cc=0; cc < PythonPaths.size(); cc++)
      {
      vtkPrependPythonPath(PythonPaths[cc].c_str());
      }

    NotifyInterpreters(vtkCommand::EnterEvent);
    return true;
    }

  return false;
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::Finalize()
{
  if (Py_IsInitialized() != 0)
    {
    NotifyInterpreters(vtkCommand::ExitEvent);
    Py_Finalize();
    }
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::SetProgramName(const char* programname)
{
  if (vtkPythonInterpreter::InitializedOnce ||
    Py_IsInitialized() != 0)
    {
    vtkGenericWarningMacro("Calling SetProgramName() after Python was "
      "initialized once. This indicates a bug.");
    return;
    }

  if (programname)
    {
    static StringPool pool;
    pool.Strings.push_back(vtksys::SystemTools::DuplicateString(programname));

    // From Python Docs: The argument should point to a zero-terminated character
    // string in static storage whose contents will not change for the duration of
    // the program's execution. No code in the Python interpreter will change the
    // contents of this storage.
    Py_SetProgramName(pool.Strings.back());
    }
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::PrependPythonPath(const char* dir)
{
  if (!dir) { return; }

  std::string out_dir = dir;

#if defined(_WIN32) && !defined(__CYGWIN__)
  // Convert slashes for this platform.
  std::replace(out_dir.begin(), out_dir.end(), '/', '\\');
#endif

  // save path for future use.
  PythonPaths.push_back(out_dir);

  if (Py_IsInitialized() == 0)
    {
    return;
    }

  // Append the path to the python sys.path object.
  vtkPrependPythonPath(out_dir.c_str());
}

//----------------------------------------------------------------------------
int vtkPythonInterpreter::PyMain(int argc, char** argv)
{
  if (!vtkPythonInterpreter::InitializedOnce && Py_IsInitialized() == 0 &&
    argc > 0)
    {
    vtkPythonInterpreter::SetProgramName(argv[0]);
    }
  vtkPythonInterpreter::Initialize(1);
  return Py_Main(argc, argv);
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::RunSimpleString(const char* script)
{
  vtkPythonInterpreter::Initialize(1);

  // The embedded python interpreter cannot handle DOS line-endings, see
  // http://sourceforge.net/tracker/?group_id=5470&atid=105470&func=detail&aid=1167922
  std::string buffer = script ? script : "";
  buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());

  // The cast is necessary because PyRun_SimpleString() hasn't always been const-correct
  PyRun_SimpleString(const_cast<char*>(buffer.c_str()));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::SetCaptureStdin(bool val)
{
  vtkPythonInterpreter::CaptureStdin = val;
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::GetCaptureStdin()
{
  return vtkPythonInterpreter::CaptureStdin;
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::WriteStdOut(const char* txt)
{
  cout << txt;
  NotifyInterpreters(vtkCommand::SetOutputEvent, const_cast<char*>(txt));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::FlushStdOut()
{
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::WriteStdErr(const char* txt)
{
  cerr << txt;
  NotifyInterpreters(vtkCommand::ErrorEvent, const_cast<char*>(txt));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::FlushStdErr()
{
}

//----------------------------------------------------------------------------
vtkStdString vtkPythonInterpreter::ReadStdin()
{
  if (!vtkPythonInterpreter::CaptureStdin)
    {
    vtkStdString string;
    cin >> string;
    return string;
    }
  vtkStdString string;
  NotifyInterpreters(vtkCommand::UpdateEvent, &string);
  return string;
}
<commit_msg>Get rid of warning when using VTK externally through Python.<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPythonInterpreter.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPython.h" // this must be the first include.
#include "vtkPythonInterpreter.h"

#include "vtkCommand.h"
#include "vtkObjectFactory.h"
#include "vtkWeakPointer.h"
#include "vtkPythonStdStreamCaptureHelper.h"

#include <vtksys/SystemTools.hxx>

#include <algorithm>
#include <string>
#include <vector>
#include <signal.h>

namespace
{
  class StringPool
    {
  public:
    std::vector<char*> Strings;
    ~StringPool()
      {
      for (size_t cc=0; cc < this->Strings.size(); cc++)
        {
        delete [] this->Strings[cc];
        }
      }
    };

  static std::vector<vtkWeakPointer<vtkPythonInterpreter> > GlobalInterpreters;
  static std::vector<std::string> PythonPaths;

  void NotifyInterpreters(unsigned long eventid, void *calldata=NULL)
    {
    std::vector<vtkWeakPointer<vtkPythonInterpreter> >::iterator iter;
    for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end();
      ++iter)
      {
      if (iter->GetPointer())
        {
        iter->GetPointer()->InvokeEvent(eventid, calldata);
        }
      }
    }

  inline void vtkPrependPythonPath(const char* pathtoadd)
    {
    PyObject* path = PySys_GetObject(const_cast<char*>("path"));
    PyObject* newpath = PyString_FromString(pathtoadd);
    PyList_Insert(path, 0, newpath);
    Py_DECREF(newpath);
    }
}

bool vtkPythonInterpreter::InitializedOnce = false;
bool vtkPythonInterpreter::CaptureStdin = false;

vtkStandardNewMacro(vtkPythonInterpreter);
//----------------------------------------------------------------------------
vtkPythonInterpreter::vtkPythonInterpreter()
{
  GlobalInterpreters.push_back(this);
}

//----------------------------------------------------------------------------
vtkPythonInterpreter::~vtkPythonInterpreter()
{
  std::vector<vtkWeakPointer<vtkPythonInterpreter> >::iterator iter;
  for (iter = GlobalInterpreters.begin(); iter != GlobalInterpreters.end();
    ++iter)
    {
    if (*iter == this)
      {
      GlobalInterpreters.erase(iter);
      break;
      }
    }
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::IsInitialized()
{
  return (Py_IsInitialized() != 0);
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::Initialize(int initsigs /*=0*/)
{
  vtkPythonInterpreter::InitializedOnce = true;
  if (Py_IsInitialized() == 0)
    {
#if (VTK_PYTHON_MAJOR_VERSION > 2) ||\
    (VTK_PYTHON_MAJOR_VERSION == 2 && VTK_PYTHON_MINOR_VERSION >= 4)
    Py_InitializeEx(initsigs);
#else
    (void)initsigs;
    Py_Initialize();
#endif

#ifdef SIGINT
    // Put default SIGINT handler back after Py_Initialize/Py_InitializeEx.
    signal(SIGINT, SIG_DFL);
#endif
    // HACK: Calling PyRun_SimpleString for the first time for some reason results in
    // a "\n" message being generated which is causing the error dialog to
    // popup. So we flush that message out of the system before setting up the
    // callbacks.
    vtkPythonInterpreter::RunSimpleString("");

    // Setup handlers for stdout/stdin/stderr.
    vtkPythonStdStreamCaptureHelper* wrapperOut =
      NewPythonStdStreamCaptureHelper(false);
    vtkPythonStdStreamCaptureHelper* wrapperErr =
      NewPythonStdStreamCaptureHelper(true);

    // Redirect Python's stdout and stderr and stdin
    PySys_SetObject(const_cast<char*>("stdout"),
      reinterpret_cast<PyObject*>(wrapperOut));
    PySys_SetObject(const_cast<char*>("stderr"),
      reinterpret_cast<PyObject*>(wrapperErr));
    PySys_SetObject(const_cast<char*>("stdin"),
      reinterpret_cast<PyObject*>(wrapperOut));
    Py_DECREF(wrapperOut);
    Py_DECREF(wrapperErr);

    for (size_t cc=0; cc < PythonPaths.size(); cc++)
      {
      vtkPrependPythonPath(PythonPaths[cc].c_str());
      }

    NotifyInterpreters(vtkCommand::EnterEvent);
    return true;
    }

  return false;
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::Finalize()
{
  if (Py_IsInitialized() != 0)
    {
    NotifyInterpreters(vtkCommand::ExitEvent);
    Py_Finalize();
    }
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::SetProgramName(const char* programname)
{
  if (vtkPythonInterpreter::InitializedOnce ||
    Py_IsInitialized() != 0)
    {
    return;
    }

  if (programname)
    {
    static StringPool pool;
    pool.Strings.push_back(vtksys::SystemTools::DuplicateString(programname));

    // From Python Docs: The argument should point to a zero-terminated character
    // string in static storage whose contents will not change for the duration of
    // the program's execution. No code in the Python interpreter will change the
    // contents of this storage.
    Py_SetProgramName(pool.Strings.back());
    }
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::PrependPythonPath(const char* dir)
{
  if (!dir) { return; }

  std::string out_dir = dir;

#if defined(_WIN32) && !defined(__CYGWIN__)
  // Convert slashes for this platform.
  std::replace(out_dir.begin(), out_dir.end(), '/', '\\');
#endif

  // save path for future use.
  PythonPaths.push_back(out_dir);

  if (Py_IsInitialized() == 0)
    {
    return;
    }

  // Append the path to the python sys.path object.
  vtkPrependPythonPath(out_dir.c_str());
}

//----------------------------------------------------------------------------
int vtkPythonInterpreter::PyMain(int argc, char** argv)
{
  if (!vtkPythonInterpreter::InitializedOnce && Py_IsInitialized() == 0 &&
    argc > 0)
    {
    vtkPythonInterpreter::SetProgramName(argv[0]);
    }
  vtkPythonInterpreter::Initialize(1);
  return Py_Main(argc, argv);
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::RunSimpleString(const char* script)
{
  vtkPythonInterpreter::Initialize(1);

  // The embedded python interpreter cannot handle DOS line-endings, see
  // http://sourceforge.net/tracker/?group_id=5470&atid=105470&func=detail&aid=1167922
  std::string buffer = script ? script : "";
  buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());

  // The cast is necessary because PyRun_SimpleString() hasn't always been const-correct
  PyRun_SimpleString(const_cast<char*>(buffer.c_str()));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::SetCaptureStdin(bool val)
{
  vtkPythonInterpreter::CaptureStdin = val;
}

//----------------------------------------------------------------------------
bool vtkPythonInterpreter::GetCaptureStdin()
{
  return vtkPythonInterpreter::CaptureStdin;
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::WriteStdOut(const char* txt)
{
  cout << txt;
  NotifyInterpreters(vtkCommand::SetOutputEvent, const_cast<char*>(txt));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::FlushStdOut()
{
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::WriteStdErr(const char* txt)
{
  cerr << txt;
  NotifyInterpreters(vtkCommand::ErrorEvent, const_cast<char*>(txt));
}

//----------------------------------------------------------------------------
void vtkPythonInterpreter::FlushStdErr()
{
}

//----------------------------------------------------------------------------
vtkStdString vtkPythonInterpreter::ReadStdin()
{
  if (!vtkPythonInterpreter::CaptureStdin)
    {
    vtkStdString string;
    cin >> string;
    return string;
    }
  vtkStdString string;
  NotifyInterpreters(vtkCommand::UpdateEvent, &string);
  return string;
}
<|endoftext|>


<commit_before>/* <x0/ternary_search.hpp>
 *
 * This file is part of the x0 web server, released under GPLv3.
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */
#ifndef x0_trie_h
#define x0_trie_H

#include <stdexcept>
#include <string>
#include <cstring>

namespace x0 {

/**
 * \ingroup common
 * \brief a generic ternary search trie data structure
 *
 * \see http://en.wikipedia.org/wiki/Ternary_search
 * \see http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm
 * \see http://en.wikipedia.org/wiki/Trie
 * \see http://www.cs.bu.edu/teaching/c/tree/ternary_search/
 */
template<typename _Key, typename _Value>
class ternary_search
{
public:
	typedef _Key key_type;
	typedef typename key_type::value_type keyelem_type;
	typedef _Value value_type;

private:
	static const keyelem_type EOS = 0;

	struct node // {{{
	{
		keyelem_type value;
		value_type data;

		node *left;
		node *middle;
		node *right;

		node() :
			value(), data(), left(0), middle(0), right(0)
		{
		}

		~node()
		{
			left = middle = right = 0;
		}
	}; // }}}

	struct nodelines // {{{
	{
		node *line;
		nodelines *next;

		nodelines() :
			line(0), next(0)
		{
		}

		~nodelines()
		{
		}
	}; // }}}

	class node_iterator // {{{
	{
	private:
		node *np_;

	public:
		node_iterator() :
			np_(0)
		{
		}

		node_iterator(node *np) :
			np_(np)
		{
		}

		operator bool () const
		{
			return np_ != NULL;
		}

		const value_type& operator*() const
		{
			if (!np_)
				throw std::runtime_error("ternary_search's iterator value is NULL");

			return np_->data;
		}

		friend bool operator!=(const node_iterator& a, const node_iterator& b)
		{
			return a.np_ != b.np_;
		}

		friend bool operator==(const node_iterator& a, const node_iterator& b)
		{
			return a.np_ == b.np_;
		}

		node_iterator& operator++()
		{
			throw std::runtime_error("ternary_search::iterator::operator++() not yet implemented");

			return *this;
		}
	};
	// }}}

public:
	typedef node_iterator iterator;

public:
	explicit ternary_search(int width = 30) :
		width_(width),
		lines_(0),
		freelist_(),
		size_(0)
	{
		std::memset(head_, 0, sizeof(head_));

		lines_ = new nodelines;
		lines_->line = new node[width_];
		lines_->next = NULL;

		node *current_node = lines_->line;
		freelist_ = current_node;

		for (int i = 1; i < width_; ++i)
		{
			current_node->middle = &(lines_->line[i]);
			current_node = current_node->middle;
		}
		current_node->middle = NULL;

		/* lines->line now points to an array of nodes of width_ size.
		 * the freelist_ field points to the first entry at lines->line.
		 * each node's middle attribute within this "line" points to its right neighbour
		 * and the last points to nullptr.
		 */
	}

	~ternary_search()
	{
		nodelines *next_line = lines_;

		do
		{
			nodelines *current_line = next_line;
			next_line = current_line->next;
			delete[] current_line->line;
			delete current_line;
		}
		while (next_line != NULL);
	}

	/**
	 * clears out all datasets in this container.
	 * \see insert(), remove()
	 */
	void clear()
	{
		throw std::runtime_error("ternary_search::clear() not yet implemented");
	}

	/**
	 * adds a new dataset item pair into this container.
	 *
	 * \param key the key to use.
	 * \param value the value data to use.
	 * \param replace true if possible already existing match is to be overwritten, false (default) otherwise.
	 * \see find(), erase(), remove(), clear()
	 */
	void insert(const key_type& key, const value_type& value, bool replace = false) // {{{
	{
		node *current_node;
		node *new_node_tree_begin = NULL;
		bool perform_loop = true;

		if (key.empty())
			throw std::runtime_error("ternary_search: key may not be empty");

		if (head_[key[0]] == NULL)
		{
			current_node = head_[key[0]] = acquire_free_node(key[1]);

			if (key[1] == EOS)
			{
				current_node->data = value;
				++size_;
				return;
			}
			else
			{
				perform_loop = false;
			}
		}

		current_node = head_[key[0]];
		int key_index = 1;

		while (perform_loop)
		{
			if (key[key_index] == current_node->value)
			{
				if (key[key_index] == EOS)
				{
					if (!replace) throw std::runtime_error("ternary_search: duplicate key");

					current_node->data = value;
					++size_;
					return;
				}
				else
				{
					if (current_node->middle == NULL)
					{
						current_node->middle = acquire_free_node(key[key_index]);
						new_node_tree_begin = current_node;
						current_node = current_node->middle;
						break;
					}
					else
					{
						current_node = current_node->middle;
						++key_index;
						continue;
					}
				}
			}

			if ((current_node->value == 0 && key[key_index] < 64) ||
				(current_node->value != 0 && key[key_index] < current_node->value))
			{
				if (current_node->left == NULL)
				{
					current_node->left = acquire_free_node(key[key_index]);
					new_node_tree_begin = current_node;
					current_node = current_node->left;

					if (key[key_index] == EOS)
					{
						current_node->data = value;
						++size_;
						return;
					}
					break;
				}
				else
				{
					current_node = current_node->left;
					continue;
				}
			}
			else
			{
				if (current_node->right == NULL)
				{
					current_node->right = acquire_free_node(key[key_index]);
					new_node_tree_begin = current_node;
					current_node = current_node->right;
					break;
				}
				else
				{
					current_node = current_node->right;
					continue;
				}
			}
		}

		do
		{
			++key_index;

			try
			{
				current_node->middle = acquire_free_node(key[key_index]);
				current_node = current_node->middle;
			}
			catch (...) // XXX ideally only memory allocation error
			{
				// put allocated nodes back to freelist and rethrow.
				current_node = new_node_tree_begin->middle;

				while (current_node->middle != NULL)
					current_node = current_node->middle;

				current_node->middle = freelist_;
				freelist_ = new_node_tree_begin->middle;
				new_node_tree_begin->middle = NULL;

				throw;
			}
		}
		while (key[key_index] != EOS);

		current_node->data = value;
		++size_;
	}
	// }}}

	/**
	 * searches for a given key in this container.
	 *
	 * \param key the pattern to lookup and retrieve its data value for.
	 * \param prefix_len an optional out arg which will contain the number of prefix elements having traversed.
	 * \returns an iterator referencing the resulting data value or end() if not found.
	 * \see begin(), end()
	 */
	node_iterator find(const key_type& key, int *prefix_len = NULL) const // {{{
	{
		node *longest_match = NULL;

		if (key.empty())
			return end();

		if (head_[key[0]] == 0)
			return end();

		if (prefix_len) *prefix_len = 0;
		node *current_node = head_[key[0]];
		int key_index = 1;

		while (current_node != NULL)
		{
			if (key[key_index] == current_node->value)
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					return node_iterator(current_node);
				}
				else
				{
					current_node = current_node->middle;
					if (current_node && current_node->value == 0)
					{
						if (prefix_len) *prefix_len = key_index + 1;
						longest_match = current_node;
					}
					++key_index;
					continue;
				}
			}
			else if ((current_node->value == 0 && key[key_index] < 64) ||
					 (current_node->value != 0 && key[key_index] < current_node->value))
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					longest_match = current_node;
				}
				current_node = current_node->left;
				continue;
			}
			else
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					longest_match = current_node;
				}
				current_node = current_node->right;
				continue;
			}
		}
		return node_iterator(longest_match);
	}
	// }}}

	/**
	 * removes a dataset item at given iterator location.
	 *
	 * after this operation, the passed iterator points automatically to the next dataset if available, or end().
	 *
	 * \see find(), end(), remove(), remove_all()
	 */
	void erase(iterator iter)
	{
		throw std::runtime_error("ternary_search::erase() not yet implemented");
	}

	/** removes a dataset item from this container */
	void remove(const key_type& key)
	{
		// TODO
		throw std::runtime_error("ternary_search::remove() not yet implemented");
	}

	/** removes all dataset items that match given key (as found by lookup) */
	void remove_all(const key_type& key)
	{
		while (iterator i = find(key))
			erase(i);
	}

	/**
	 * retrieves the beginning of the data items being stored in this container.
	 */
	node_iterator begin() const
	{
		throw std::runtime_error("ternary_search::begin() not yet implemented");
	}

	/**
	 * retrieves the end-marker that denotes the end of iteration for this container.
	 */
	node_iterator end() const
	{
		return node_iterator();
	}

	/** tests wether given key is stored in this container. */
	inline bool contains(const key_type& key) const
	{
		return find(key) != end();
	}

	/** retrieves the number of stored items in this container. */
	inline std::size_t size() const
	{
		return size_;
	}

	/** tests wether given dataset container is empty or not. */
	inline bool empty() const
	{
		return !size_;
	}

	const value_type& operator[](const key_type& key) const
	{
		return get(key, NULL);
	}

	value_type& operator[](const key_type& key)
	{
		if (!contains(key))
			const_cast<ternary_search *>(this)->insert(key, value_type());

		return *const_cast<value_type *>(&get(key, NULL));
	}

private:
	int width_;
	nodelines *lines_;
	node *freelist_;
	node *head_[127];

	std::size_t size_;

private:
	inline node *acquire_free_node(const keyelem_type& value)
	{
		if (freelist_ == NULL)
			grow_node_free_list();

		node *n = freelist_;
		freelist_ = freelist_->middle;

		n->middle = NULL;
		n->value = value;

		return n;
	}

	inline void grow_node_free_list()
	{
		node *current_node;
		nodelines *new_line;
		int i;

		new_line = new nodelines;
		new_line->line = new node[width_];
		new_line->next = lines_;
		lines_ = new_line;

		current_node = lines_->line;
		freelist_ = current_node;

		for (i = 1; i < width_; ++i)
		{
			current_node->middle = &(lines_->line[i]);
			current_node = current_node->middle;
		}
		current_node->middle = NULL;
	}
};

} // namespace x0

#endif
<commit_msg>silence gcc 4.4 warnings<commit_after>/* <x0/ternary_search.hpp>
 *
 * This file is part of the x0 web server, released under GPLv3.
 * (c) 2009 Chrisitan Parpart <trapni@gentoo.org>
 */
#ifndef x0_trie_h
#define x0_trie_H

#include <stdexcept>
#include <string>
#include <cstring>

namespace x0 {

/**
 * \ingroup common
 * \brief a generic ternary search trie data structure
 *
 * \see http://en.wikipedia.org/wiki/Ternary_search
 * \see http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm
 * \see http://en.wikipedia.org/wiki/Trie
 * \see http://www.cs.bu.edu/teaching/c/tree/ternary_search/
 */
template<typename _Key, typename _Value>
class ternary_search
{
public:
	typedef _Key key_type;
	typedef typename key_type::value_type keyelem_type;
	typedef _Value value_type;

private:
	static const keyelem_type EOS = 0;

	struct node // {{{
	{
		keyelem_type value;
		value_type data;

		node *left;
		node *middle;
		node *right;

		node() :
			value(), data(), left(0), middle(0), right(0)
		{
		}

		~node()
		{
			left = middle = right = 0;
		}
	}; // }}}

	struct nodelines // {{{
	{
		node *line;
		nodelines *next;

		nodelines() :
			line(0), next(0)
		{
		}

		~nodelines()
		{
		}
	}; // }}}

	class node_iterator // {{{
	{
	private:
		node *np_;

	public:
		node_iterator() :
			np_(0)
		{
		}

		node_iterator(node *np) :
			np_(np)
		{
		}

		operator bool () const
		{
			return np_ != NULL;
		}

		const value_type& operator*() const
		{
			if (!np_)
				throw std::runtime_error("ternary_search's iterator value is NULL");

			return np_->data;
		}

		friend bool operator!=(const node_iterator& a, const node_iterator& b)
		{
			return a.np_ != b.np_;
		}

		friend bool operator==(const node_iterator& a, const node_iterator& b)
		{
			return a.np_ == b.np_;
		}

		node_iterator& operator++()
		{
			throw std::runtime_error("ternary_search::iterator::operator++() not yet implemented");

			return *this;
		}
	};
	// }}}

public:
	typedef node_iterator iterator;

public:
	explicit ternary_search(int width = 30) :
		width_(width),
		lines_(0),
		freelist_(),
		size_(0)
	{
		std::memset(head_, 0, sizeof(head_));

		lines_ = new nodelines;
		lines_->line = new node[width_];
		lines_->next = NULL;

		node *current_node = lines_->line;
		freelist_ = current_node;

		for (int i = 1; i < width_; ++i)
		{
			current_node->middle = &(lines_->line[i]);
			current_node = current_node->middle;
		}
		current_node->middle = NULL;

		/* lines->line now points to an array of nodes of width_ size.
		 * the freelist_ field points to the first entry at lines->line.
		 * each node's middle attribute within this "line" points to its right neighbour
		 * and the last points to nullptr.
		 */
	}

	~ternary_search()
	{
		nodelines *next_line = lines_;

		do
		{
			nodelines *current_line = next_line;
			next_line = current_line->next;
			delete[] current_line->line;
			delete current_line;
		}
		while (next_line != NULL);
	}

	/**
	 * clears out all datasets in this container.
	 * \see insert(), remove()
	 */
	void clear()
	{
		throw std::runtime_error("ternary_search::clear() not yet implemented");
	}

	/**
	 * adds a new dataset item pair into this container.
	 *
	 * \param key the key to use.
	 * \param value the value data to use.
	 * \param replace true if possible already existing match is to be overwritten, false (default) otherwise.
	 * \see find(), erase(), remove(), clear()
	 */
	void insert(const key_type& key, const value_type& value, bool replace = false) // {{{
	{
		node *current_node;
		node *new_node_tree_begin = NULL;
		bool perform_loop = true;

		if (key.empty())
			throw std::runtime_error("ternary_search: key may not be empty");

		if (head_[int(key[0])] == NULL)
		{
			current_node = head_[int(key[0])] = acquire_free_node(key[1]);

			if (key[1] == EOS)
			{
				current_node->data = value;
				++size_;
				return;
			}
			else
			{
				perform_loop = false;
			}
		}

		current_node = head_[int(key[0])];
		int key_index = 1;

		while (perform_loop)
		{
			if (key[key_index] == current_node->value)
			{
				if (key[key_index] == EOS)
				{
					if (!replace) throw std::runtime_error("ternary_search: duplicate key");

					current_node->data = value;
					++size_;
					return;
				}
				else
				{
					if (current_node->middle == NULL)
					{
						current_node->middle = acquire_free_node(key[key_index]);
						new_node_tree_begin = current_node;
						current_node = current_node->middle;
						break;
					}
					else
					{
						current_node = current_node->middle;
						++key_index;
						continue;
					}
				}
			}

			if ((current_node->value == 0 && key[key_index] < 64) ||
				(current_node->value != 0 && key[key_index] < current_node->value))
			{
				if (current_node->left == NULL)
				{
					current_node->left = acquire_free_node(key[key_index]);
					new_node_tree_begin = current_node;
					current_node = current_node->left;

					if (key[key_index] == EOS)
					{
						current_node->data = value;
						++size_;
						return;
					}
					break;
				}
				else
				{
					current_node = current_node->left;
					continue;
				}
			}
			else
			{
				if (current_node->right == NULL)
				{
					current_node->right = acquire_free_node(key[key_index]);
					new_node_tree_begin = current_node;
					current_node = current_node->right;
					break;
				}
				else
				{
					current_node = current_node->right;
					continue;
				}
			}
		}

		do
		{
			++key_index;

			try
			{
				current_node->middle = acquire_free_node(key[key_index]);
				current_node = current_node->middle;
			}
			catch (...) // XXX ideally only memory allocation error
			{
				// put allocated nodes back to freelist and rethrow.
				current_node = new_node_tree_begin->middle;

				while (current_node->middle != NULL)
					current_node = current_node->middle;

				current_node->middle = freelist_;
				freelist_ = new_node_tree_begin->middle;
				new_node_tree_begin->middle = NULL;

				throw;
			}
		}
		while (key[key_index] != EOS);

		current_node->data = value;
		++size_;
	}
	// }}}

	/**
	 * searches for a given key in this container.
	 *
	 * \param key the pattern to lookup and retrieve its data value for.
	 * \param prefix_len an optional out arg which will contain the number of prefix elements having traversed.
	 * \returns an iterator referencing the resulting data value or end() if not found.
	 * \see begin(), end()
	 */
	node_iterator find(const key_type& key, int *prefix_len = NULL) const // {{{
	{
		node *longest_match = NULL;

		if (key.empty())
			return end();

		if (head_[int(key[0])] == 0)
			return end();

		if (prefix_len) *prefix_len = 0;
		node *current_node = head_[int(key[0])];
		int key_index = 1;

		while (current_node != NULL)
		{
			if (key[key_index] == current_node->value)
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					return node_iterator(current_node);
				}
				else
				{
					current_node = current_node->middle;
					if (current_node && current_node->value == 0)
					{
						if (prefix_len) *prefix_len = key_index + 1;
						longest_match = current_node;
					}
					++key_index;
					continue;
				}
			}
			else if ((current_node->value == 0 && key[key_index] < 64) ||
					 (current_node->value != 0 && key[key_index] < current_node->value))
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					longest_match = current_node;
				}
				current_node = current_node->left;
				continue;
			}
			else
			{
				if (current_node->value == 0)
				{
					if (prefix_len) *prefix_len = key_index;
					longest_match = current_node;
				}
				current_node = current_node->right;
				continue;
			}
		}
		return node_iterator(longest_match);
	}
	// }}}

	/**
	 * removes a dataset item at given iterator location.
	 *
	 * after this operation, the passed iterator points automatically to the next dataset if available, or end().
	 *
	 * \see find(), end(), remove(), remove_all()
	 */
	void erase(iterator iter)
	{
		throw std::runtime_error("ternary_search::erase() not yet implemented");
	}

	/** removes a dataset item from this container */
	void remove(const key_type& key)
	{
		// TODO
		throw std::runtime_error("ternary_search::remove() not yet implemented");
	}

	/** removes all dataset items that match given key (as found by lookup) */
	void remove_all(const key_type& key)
	{
		while (iterator i = find(key))
			erase(i);
	}

	/**
	 * retrieves the beginning of the data items being stored in this container.
	 */
	node_iterator begin() const
	{
		throw std::runtime_error("ternary_search::begin() not yet implemented");
	}

	/**
	 * retrieves the end-marker that denotes the end of iteration for this container.
	 */
	node_iterator end() const
	{
		return node_iterator();
	}

	/** tests wether given key is stored in this container. */
	inline bool contains(const key_type& key) const
	{
		return find(key) != end();
	}

	/** retrieves the number of stored items in this container. */
	inline std::size_t size() const
	{
		return size_;
	}

	/** tests wether given dataset container is empty or not. */
	inline bool empty() const
	{
		return !size_;
	}

	const value_type& operator[](const key_type& key) const
	{
		return get(key, NULL);
	}

	value_type& operator[](const key_type& key)
	{
		if (!contains(key))
			const_cast<ternary_search *>(this)->insert(key, value_type());

		return *const_cast<value_type *>(&get(key, NULL));
	}

private:
	int width_;
	nodelines *lines_;
	node *freelist_;
	node *head_[127];

	std::size_t size_;

private:
	inline node *acquire_free_node(const keyelem_type& value)
	{
		if (freelist_ == NULL)
			grow_node_free_list();

		node *n = freelist_;
		freelist_ = freelist_->middle;

		n->middle = NULL;
		n->value = value;

		return n;
	}

	inline void grow_node_free_list()
	{
		node *current_node;
		nodelines *new_line;
		int i;

		new_line = new nodelines;
		new_line->line = new node[width_];
		new_line->next = lines_;
		lines_ = new_line;

		current_node = lines_->line;
		freelist_ = current_node;

		for (i = 1; i < width_; ++i)
		{
			current_node->middle = &(lines_->line[i]);
			current_node = current_node->middle;
		}
		current_node->middle = NULL;
	}
};

} // namespace x0

#endif
<|endoftext|>