OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / core / NumberNode.h

178 lines · c ref:
/*
 * opencog/atoms/core/NumberNode.h
 *
 * Copyright (C) 2015 Linas Vepstas
 * All Rights Reserved
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#ifndef _OPENCOG_NUMBER_NODE_H
#define _OPENCOG_NUMBER_NODE_H

#include <charconv>
#include <opencog/util/exceptions.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/value/FloatValue.h>

// Ubunutu 20.04 LTS does not yet have this c++17 feature. Bummer.
#ifndef __cpp_lib_to_chars
#include <boost/lexical_cast.hpp>
#endif

namespace opencog
{
/** \addtogroup grp_atomspace
 *  @{
 *
 * NumberNode implementation. This is a vector of floats; thus, just
 * like a FloatValue, except that it's a Node. We don't want to actually
 * do multiple inheritance here, as that tends to blow up C++.
 */

class NumberNode : public Node
{
private:

	// It turns out that std::to_string(double x) is locale-dependent
	// See, for example, Martin B.'s comment here:
	// http://comp.lang.cpp.moderated.narkive.com/dWGdM0Od/std-to-string-int-deviates-from-iostreams-result-deliberate
	// Also this:
	// https://github.com/nlohmann/json/issues/51
	// And this:
	// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0067r0.html
	// This was a painful discovery that ate the chatbot's lunch.
	// So we perform a hack here.  The core issue is that the rest of the
	// system is explicitly defined to be locale-independent, including
	// the natural-language pipeline in guile/scheme. Thus, printing
	// the European comma as a decimal separator blows up the code.
	// Using boost::lexical_cast<> avoids this issue.
	//    #include <boost/lexical_cast.hpp>
	//    return boost::lexical_cast<std::string>(x);
	static std::string double_to_string(double x)
	{
// Ubunutu 20.04 LTS does not yet have this c++17 feature. Bummer.
#ifdef __cpp_lib_to_chars
		static const size_t buf_size = 30;
		char buf[buf_size]{};
		std::to_chars_result result = std::to_chars(buf, buf + buf_size,
			x, std::chars_format::general, 18);
		if (result.ec != std::errc())
			throw RuntimeException(TRACE_INFO,
				"Error: Failed double_to_string(%g): %s\n",
				x, std::make_error_code(result.ec).message().c_str());
		return std::string(buf);
#else // __cpp_lib_to_chars
		return boost::lexical_cast<std::string>(x);
#endif // __cpp_lib_to_chars
	}

protected:
	std::vector<double> _value;

public:
	// Please to NOT use this constructor!
	NumberNode(Type, const std::string&&);

public:
	NumberNode(const std::string&&);
	NumberNode(const std::vector<double>&);
	NumberNode(std::vector<double>&&);
	NumberNode(const FloatValuePtr&);
	NumberNode(const ValuePtr&);

	NumberNode(double vvv)
		: Node(NUMBER_NODE, double_to_string(vvv))
	{ _value.push_back(vvv); }

	NumberNode(NumberNode&) = delete;
	NumberNode& operator=(const NumberNode&) = delete;

	static std::vector<double> to_vector(const std::string&);
	static std::string vector_to_json(const std::vector<double>&);
	static std::string vector_to_plain(const std::vector<double>&);

	static std::string validate(const std::string& str)
	{
		return vector_to_plain(to_vector(str));
	}

	size_t size() const { return _value.size(); }
	const std::vector<double>& value(void) const { return _value; }
	double get_value(void) const { return _value[0]; }

	static Handle factory(const Handle&);
};

NODE_PTR_DECL(NumberNode)
#define createNumberNode CREATE_DECL(NumberNode)

// --------------------
// Scalar multiplication and addition
inline
ValuePtr plus(double f, const NumberNodePtr& fvp) {
	return createFloatValue(std::move(plus(f, fvp->value()))); }
inline
ValuePtr minus(double f, const NumberNodePtr& fvp) {
	return createFloatValue(std::move(minus(f, fvp->value()))); }
inline
ValuePtr times(double f, const NumberNodePtr& fvp) {
	return createFloatValue(std::move(times(f, fvp->value()))); }
inline
ValuePtr divide(double f, const NumberNodePtr& fvp) {
	return createFloatValue(std::move(divide(f, fvp->value()))); }

ValuePtr plus(double, const ValuePtr&, bool silent=false);
ValuePtr minus(double, const ValuePtr&, bool silent=false);
ValuePtr times(double, const ValuePtr&, bool silent=false);
ValuePtr divide(double, const ValuePtr&, bool silent=false);

// Vector multiplication and addition
inline
ValuePtr plus(const NumberNodePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(plus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr minus(const NumberNodePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(minus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr times(const NumberNodePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(times(fvpa->value(), fvpb->value()))); }
inline
ValuePtr divide(const NumberNodePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(divide(fvpa->value(), fvpb->value()))); }

inline
ValuePtr plus(const FloatValuePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(plus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr minus(const FloatValuePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(minus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr times(const FloatValuePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(times(fvpa->value(), fvpb->value()))); }
inline
ValuePtr divide(const FloatValuePtr& fvpa, const NumberNodePtr& fvpb) {
	return createFloatValue(std::move(divide(fvpa->value(), fvpb->value()))); }

inline
ValuePtr plus(const NumberNodePtr& fvpa, const FloatValuePtr& fvpb) {
	return createFloatValue(std::move(plus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr minus(const NumberNodePtr& fvpa, const FloatValuePtr& fvpb) {
	return createFloatValue(std::move(minus(fvpa->value(), fvpb->value()))); }
inline
ValuePtr times(const NumberNodePtr& fvpa, const FloatValuePtr& fvpb) {
	return createFloatValue(std::move(times(fvpa->value(), fvpb->value()))); }
inline
ValuePtr divide(const NumberNodePtr& fvpa, const FloatValuePtr& fvpb) {
	return createFloatValue(std::move(divide(fvpa->value(), fvpb->value()))); }

ValuePtr plus(const ValuePtr&, const ValuePtr&, bool silent=false);
ValuePtr minus(const ValuePtr&, const ValuePtr&, bool silent=false);
ValuePtr times(const ValuePtr&, const ValuePtr&, bool silent=false);
ValuePtr divide(const ValuePtr&, const ValuePtr&, bool silent=false);

/** @}*/
}

#endif // _OPENCOG_NUMBER_NODE_H