OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / reduct / DivideLink.cc

137 lines · cc ref:
/*
 * opencog/atoms/reduct/DivideLink.cc
 *
 * Copyright (C) 2015, 2018 Linas Vepstas
 * All Rights Reserved
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <opencog/atoms/atom_types/atom_types.h>
#include <opencog/atoms/base/ClassServer.h>
#include <opencog/atoms/core/NumberNode.h>
#include "DivideLink.h"

using namespace opencog;

DivideLink::DivideLink(const HandleSeq&& oset, Type t)
    : TimesLink(std::move(oset), t)
{
	init();
}

DivideLink::DivideLink(const Handle& a, const Handle& b)
    : TimesLink({a, b}, DIVIDE_LINK)
{
	init();
}

void DivideLink::init(void)
{
	Type tscope = get_type();
	if (not nameserver().isA(tscope, DIVIDE_LINK))
		throw InvalidParamException(TRACE_INFO, "Expecting a DivideLink");

	_commutative = false;

	// Disallow unary Divide. This makes things easier, overall.
	if (1 == _outgoing.size())
		_outgoing.insert(_outgoing.begin(), one);
}

// No ExpLink or PowLink and so kons is very simple
ValuePtr DivideLink::kons(AtomSpace* as, bool silent,
                          const ValuePtr& fi, const ValuePtr& fj) const
{
	// Try to yank out values, if possible.
	ValuePtr vi(exec_for_value(as, silent, fi));
	Type vitype = vi->get_type();

	ValuePtr vj(exec_for_value(as, silent, fj));
	Type vjtype = vj->get_type();

	// Are they numbers? If so, perform vector (pointwise) subtraction.
	// Always lower the strength: Number+Number->Number
	// but FloatValue+Number->FloatValue
	try
	{
		if (NUMBER_NODE == vitype and NUMBER_NODE == vjtype)
			return createNumberNode(divide(vi, vj, true));

		return divide(vi, vj, true);
	}
	catch (const SilentException& ex)
	{
		// If we are here, they were not simple numbers.
	}

	// If vj is one, just drop it
	if (NUMBER_NODE == vjtype and content_eq(HandleCast(vj), one))
		return vi;

	// Collapse (3 / (5 * x)) and (3 / (x * 5))
	if (NUMBER_NODE == vitype and TIMES_LINK == vjtype)
	{
		Handle multiplier(HandleCast(vj)->getOutgoingAtom(0));
		Handle multiplicand(HandleCast(vj)->getOutgoingAtom(1));
		if (NUMBER_NODE == multiplier->get_type())
		{
			Handle hquot(createNumberNode(divide(vi, multiplier)));
			return createDivideLink(hquot, multiplicand);
		}
		if (NUMBER_NODE == multiplicand->get_type())
		{
			Handle hquot(createNumberNode(divide(vi, multiplicand)));
			return createDivideLink(hquot, multiplier);
		}
	}

	// Collapse ((x * 13) / 6) and ((13 * x) / 6)
	if (TIMES_LINK == vitype and NUMBER_NODE == vjtype)
	{
		Handle multiplier(HandleCast(vi)->getOutgoingAtom(0));
		Handle multiplicand(HandleCast(vi)->getOutgoingAtom(1));
		if (NUMBER_NODE == multiplier->get_type())
		{
			Handle hquot(createNumberNode(divide(multiplier, vj)));
			if (content_eq(hquot, one))
				return multiplicand;
			return createTimesLink(multiplicand, hquot);
		}
		if (NUMBER_NODE == multiplicand->get_type())
		{
			Handle hquot(createNumberNode(divide(multiplicand, vj)));
			if (content_eq(hquot, one))
				return multiplier;
			return createTimesLink(multiplier, hquot);
		}
	}

	// ------------------------------------------------------------
	// Values 
	try
	{
		if (NUMBER_NODE == vitype and NUMBER_NODE == vjtype)
			return createNumberNode(divide(vi, vj, true));

		return divide(vi, vj, true);
	}
	catch (const SilentException& ex)
	{
		// If we are here, they were not simple numbers.
	}

	Handle hi(HandleCast(vi));
	if (nullptr == hi) hi = HandleCast(fi);

	Handle hj(HandleCast(vj));
	if (nullptr == hj) hj = HandleCast(fj);

	// If we are here, we've been asked to take a ratio of two things,
	// but they are not of a type that we know how to divide.
	return createDivideLink(hi, hj);
}

DEFINE_LINK_FACTORY(DivideLink, DIVIDE_LINK)

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