OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-sensory / opencog / atoms / sensory-v0 / StreamEqualLink.cc

194 lines · cc ref:
/*
 * opencog/atoms/sensory-v0/StreamEqualLink.cc
 *
 * Copyright (C) 2022, 2024 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * 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 <opencog/atoms/signature/TypeNode.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/atoms/value/StringValue.h>
#include <opencog/util/exceptions.h>

#include "StreamEqualLink.h"

using namespace opencog;

StreamEqualLink::StreamEqualLink(const HandleSeq&& oset, Type t)
	: UnorderedLink(std::move(oset), t)
{
	if (not nameserver().isA(t, STREAM_EQUAL_LINK))
	{
		const std::string& tname = nameserver().getTypeName(t);
		throw InvalidParamException(TRACE_INFO,
			"Expecting an StreamEqualLink, got %s", tname.c_str());
	}
	init();
}

StreamEqualLink::StreamEqualLink(const Handle& ha, const Handle& hb)
	: UnorderedLink(HandleSeq({ha, hb}), STREAM_EQUAL_LINK)
{
	init();
}

void StreamEqualLink::init(void)
{
	if (_outgoing.size() > 2)
		throw RuntimeException(TRACE_INFO,
			"Compare of more than two stream items not implemented.");
}

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

/// When evaluated, compare the first item in a stream to an Atom,
/// of the first item in two different streams.  StringValue to
/// Node name compares succeed, if the actual strings match; the
/// Node type is discarded.
///
/// Only the first item in the stream is compared. This works great
/// for sampled streams (e.g. "is the door open, right now?") but
/// might be inappropriate for buffered streams. Depends on the
/// stream implementation. We're fast and loose here.
bool StreamEqualLink::bevaluate(AtomSpace* as, bool silent)
{
	// Self-equality is trivially true. Or we could throw an
	// exception here.
	if (_outgoing.size() < 2)
		return true;

	ValueSeq comps;
	for (const Handle& ho: _outgoing)
	{
		if (ho->is_executable())
		{
			ValuePtr vp = ho->execute(as, silent);

			// If its a stream, explicitly get only the first item
			// in the stream. (This may cause unintended data loss
			// in buffered streams).
			if (vp->is_type(STREAM_VALUE))
				vp = LinkValueCast(vp)->value()[0];
			comps.push_back(vp);
		}
		else
			comps.push_back(ho);
	}

	return compare(comps[0], comps[1]);
}

bool StreamEqualLink::compare(ValuePtr vpa, ValuePtr vpb)
{
	// Sort. Nodes come first.
	if (vpb->is_node()) vpb.swap(vpa);

	// Do string compares, ignoring node type.
	if (vpa->is_node())
	{
		// Anonymous type compare
		if (vpa->is_type(TYPE_NODE))
		{
			Type wtype = TypeNodeCast(HandleCast(vpa))->get_kind();
			return vpb->is_type(wtype);
		}

		// Node name compare; ignore node type
		if (vpb->is_node())
		{
			const std::string& na(HandleCast(vpa)->get_name());
			const std::string& nb(HandleCast(vpb)->get_name());
			return 0 == na.compare(nb);
		}
		if (not vpb->is_type(STRING_VALUE))
			return false;

		// String compare
		StringValuePtr svp(StringValueCast(vpb));
		if (1 < svp->size())
			return false;

		const std::string& na(HandleCast(vpa)->get_name());
		const std::string& nb(svp->value()[0]);
		return 0 == na.compare(nb);
	}

	// Do string vector compares.
	if (vpa->is_type(STRING_VALUE))
	{
		if (not vpb->is_type(STRING_VALUE))
			return false;
		if (vpa->size() != vpb->size())
			return false;

		const std::vector<std::string>& sva(StringValueCast(vpa)->value());
		const std::vector<std::string>& svb(StringValueCast(vpb)->value());
		for (size_t i=0; i<sva.size(); i++)
		{
			if (sva[i].compare(svb[i]))
				return false;
		}
		return true;
	}

	// Sort. Links come first.
	if (vpb->is_link()) vpb.swap(vpa);

	// Do link compares. Ignore link type, if the other side
	// is a LinkStream. Otherwise do strict link compares.
	if (vpa->is_link())
	{
		if (vpb->is_link())
			return *vpa == *vpb;
		if (not vpb->is_type(LINK_VALUE))
			return false;
		if (vpa->size() != vpb->size())
			return false;

		const HandleSeq& oset(HandleCast(vpa)->getOutgoingSet());
		const ValueSeq& vseq(LinkValueCast(vpb)->value());
		for (size_t i=0; i<oset.size(); i++)
		{
			if (not compare(oset[i], vseq[i]))
				return false;
		}
		return true;
	}

	// I don't know what's happening, if vpa isn't a link stream.
	if (not vpa->is_type(LINK_VALUE))
		return false;

	if (vpa->size() != vpb->size())
		return false;

	const ValueSeq& vqa(LinkValueCast(vpa)->value());
	const ValueSeq& vqb(LinkValueCast(vpb)->value());
	for (size_t i=0; i<vqa.size(); i++)
	{
		if (not compare(vqa[i], vqb[i]))
			return false;
	}
	return true;
}

DEFINE_LINK_FACTORY(StreamEqualLink, STREAM_EQUAL_LINK)

/* ===================== END OF FILE ===================== */