OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / value / FormulaStream.cc

184 lines · cc ref:
/*
 * opencog/atoms/value/FormulaStream.cc
 *
 * Copyright (C) 2020 Linas Vepstas
 * All Rights Reserved
 *
 * 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 <stdlib.h>
#include <opencog/atoms/value/FormulaStream.h>
#include <opencog/atoms/value/ValueFactory.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/core/NumberNode.h>
#include <opencog/atomspace/AtomSpace.h>

using namespace opencog;

// ==============================================================

FormulaStream::FormulaStream(const Handle& h) :
	FloatValue(FORMULA_STREAM), _formula({h}), _as(h->getAtomSpace())
{
	init();
}

FormulaStream::FormulaStream(const HandleSeq&& oset) :
	FloatValue(FORMULA_STREAM), _formula(std::move(oset))
{
	if (0 == _formula.size())
		throw SyntaxException(TRACE_INFO,
			"Expecting at least one atom!");

	_as = _formula[0]->getAtomSpace();

	init();
}

// Same as above, but Handles as a ValueSeq. The Sexper decoder
// will create these when it deserializes FormulaStreams.
FormulaStream::FormulaStream(const ValueSeq& voset) :
	FloatValue(FORMULA_STREAM)
{
	for (const ValuePtr& v : voset)
	{
		Handle h(HandleCast(v));
		if (h) _formula.emplace_back(h);
	}

	if (0 == _formula.size())
		throw SyntaxException(TRACE_INFO,
			"Expecting at least one atom!");

	_as = _formula[0]->getAtomSpace();

	init();
}

void FormulaStream::init(void)
{
	// If the single argument is a ListLink, unwrap it.
	if (1 == _formula.size() and LIST_LINK == _formula[0]->get_type())
	{
		_formula = _formula[0]->getOutgoingSet();
	}

	if (1 == _formula.size())
	{
		if (not _formula[0]->is_executable())
			throw SyntaxException(TRACE_INFO,
				"Expecting an executable atom, got %s",
				_formula[0]->to_string().c_str());
		return;
	}

	for (const Handle& h: _formula)
	{
		if (not h->is_executable())
			throw SyntaxException(TRACE_INFO,
				"Expecting an executable atom, got %s",
				h->to_string().c_str());
	}
}

// ==============================================================

// XXX FIXME The update here is not thread-safe...
void FormulaStream::update() const
{
	if (1 == _formula.size())
	{
		if (_formula[0]->is_executable())
		{
			ValuePtr vp = _formula[0]->execute(_as);

			if (NUMBER_NODE == vp->get_type())
			{
				_value = NumberNodeCast(vp)->value();
				return;
			}

			if (vp->is_type(FLOAT_VALUE))
			{
				_value = FloatValueCast(vp)->value();
				return;
			}

			throw SyntaxException(TRACE_INFO,
				"Expecting formula to return a Number or FloatValue, got %s",
				vp->to_string().c_str());
		}
		return;
	}

	// If there are multiple arguments, assume that each one
	// returns a single Float. Just concatenate all of them.
	std::vector<double> newval;
	for (const Handle& h :_formula)
	{
		ValuePtr vp = h->execute(_as);

		if (not vp->is_type(FLOAT_VALUE))
			throw SyntaxException(TRACE_INFO,
				"Expecting formula to return a FloatValue; instead %s\n"
				"returned %s",
				h->to_string().c_str(),
				vp->to_string().c_str());

		newval.push_back(FloatValueCast(vp)->value()[0]);
	}

	_value.swap(newval);
}

// ==============================================================

std::string FormulaStream::to_string(const std::string& indent) const
{
	std::string rv = indent + "(" + nameserver().getTypeName(_type);
	for (const Handle& h : _formula)
		rv += "\n" + h->to_short_string(indent + "   ");
	rv += "\n" + indent + "   ; Current sample:\n";
	rv += indent + "   ; " + FloatValue::to_string("", FLOAT_VALUE);
	rv += "\n)";
	return rv;
}

// ==============================================================

bool FormulaStream::operator==(const Value& other) const
{
	// If they are both FormulaStream's, then we're good.
	if (FORMULA_STREAM == other.get_type())
	{
		const FormulaStream* eso = (const FormulaStream*) &other;
		return eso->_formula == _formula;
	}

	if (not other.is_type(FLOAT_VALUE)) return false;

	// Value-compare
	return FloatValue::operator==(other);
}

// ==============================================================

// Adds factor when library is loaded.
DEFINE_VALUE_FACTORY(FORMULA_STREAM, createFormulaStream, const Handle&)
DEFINE_VALUE_FACTORY(FORMULA_STREAM, createFormulaStream, const HandleSeq&&)
DEFINE_VALUE_FACTORY(FORMULA_STREAM, createFormulaStream, const ValueSeq&)