/*
* opencog/atoms/reduct/ElementOfLink.cc
*
* Copyright (C) 2020, 2022 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/Link.h>
#include <opencog/atoms/core/NumberNode.h>
#include <opencog/atoms/value/BoolValue.h>
#include <opencog/atoms/value/FloatValue.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/atoms/value/StringValue.h>
#include "ElementOfLink.h"
using namespace opencog;
ElementOfLink::ElementOfLink(const HandleSeq&& oset, Type t)
: Link(std::move(oset), t)
{
init();
}
ElementOfLink::ElementOfLink(const Handle& a, const Handle& b)
: Link({a, b}, ELEMENT_OF_LINK)
{
init();
}
void ElementOfLink::init(void)
{
Type tscope = get_type();
if (not nameserver().isA(tscope, ELEMENT_OF_LINK))
throw InvalidParamException(TRACE_INFO, "Expecting a ElementOfLink");
size_t nargs = _outgoing.size();
if (2 != nargs)
throw InvalidParamException(TRACE_INFO,
"ElementOfLink expects two arguments, got %s",
to_string().c_str());
}
// ============================================================
ValuePtr ElementOfLink::execute(AtomSpace* as, bool silent)
{
// The vector we're planning on cutting down.
ValuePtr vi(_outgoing[1]->execute(as, silent));
// Lets see what kind of index list this is. We accept two kinds:
// A NumberNode and a FloatValue.
// get_value() causes execution to happen on the arguments
ValuePtr vm(_outgoing[0]->execute(as, silent));
Type mtype = vm->get_type();
// Perhaps its a NumberNode?
if (NUMBER_NODE == mtype)
{
NumberNodePtr nnp = NumberNodeCast(vm);
return do_execute(nnp->value(), vi);
}
if (FLOAT_VALUE == mtype)
{
FloatValuePtr fvp = FloatValueCast(vm);
return do_execute(fvp->value(), vi);
}
throw SyntaxException(TRACE_INFO,
"Index list must be a list of numbers! Got %s",
vm->to_string().c_str());
}
ValuePtr ElementOfLink::do_execute(const std::vector<double>& vindex,
const ValuePtr& vi)
{
Type vitype = vi->get_type();
// Handle the various value types. Try the most likely ones first.
// Is it a Link?
if (vi->is_link())
{
const HandleSeq& oset(HandleCast(vi)->getOutgoingSet());
HandleSeq chopped;
for (double d : vindex)
chopped.push_back(oset.at((int)(d+0.5)));
// If user asked for just one thing, hand them that one thing.
if (1 == chopped.size())
return chopped[0];
return createLink(std::move(chopped), vitype);
}
// A vector of values or atoms.
if (nameserver().isA(vitype, LINK_VALUE))
{
const std::vector<ValuePtr>& lvec(LinkValueCast(vi)->value());
std::vector<ValuePtr> chopped;
for (double d : vindex)
chopped.push_back(lvec.at((int)(d+0.5)));
// Special case "hack". This feels both ugly, and natural.
// Ugly, because it breaks with the rhythm below, where
// everything gets wrapped. Natural, because if the user
// asked for just one thing, we should give them ... just
// one thing. Re-wrapping it forces the user to then take
// another step to unwrap it. Anyway, this is an important
// enough design decision, that I write it up, here.
if (1 == chopped.size())
return chopped[0];
return createLinkValue(std::move(chopped));
}
// If its a float value, it's a vector.
if (nameserver().isA(vitype, FLOAT_VALUE))
{
const std::vector<double>& dvec(FloatValueCast(vi)->value());
std::vector<double> chopped;
for (double d : vindex)
chopped.push_back(dvec.at((int)(d+0.5)));
return createFloatValue(std::move(chopped));
}
// If its a plain number, assume it's a vector.
if (NUMBER_NODE == vitype)
{
const std::vector<double>& dvec(NumberNodeCast(vi)->value());
std::vector<double> chopped;
for (double d : vindex)
chopped.push_back(dvec.at((int)(d+0.5)));
return createNumberNode(std::move(chopped));
}
// A vector of strings
if (nameserver().isA(vitype, STRING_VALUE))
{
const std::vector<std::string>& svec(StringValueCast(vi)->value());
std::vector<std::string> chopped;
for (double d : vindex)
chopped.push_back(svec.at((int)(d+0.5)));
return createStringValue(std::move(chopped));
}
// A vector of bools.
if (nameserver().isA(vitype, BOOL_VALUE))
{
const std::vector<bool>& bvec(BoolValueCast(vi)->value());
std::vector<bool> chopped;
for (double d : vindex)
chopped.push_back(bvec.at((int)(d+0.5)));
return createBoolValue(std::move(chopped));
}
if (VOID_VALUE == vitype)
return vi;
// WTF. Should never be reached.
return get_handle();
}
DEFINE_LINK_FACTORY(ElementOfLink, ELEMENT_OF_LINK);
// ============================================================