#pragma once

#ifndef _NOGSL
#include <gsl/gsl_vector.h>
#else
#include "CustomSolver.h"
#endif
#include "ValueGrad.h"
#include "BooleanNodes.h"
#include "BooleanDAG.h"
#include "NodeVisitor.h"
#include "VarStore.h"
#include <map>
#include <set>
#include "SymbolicEvaluator.h"
#include "Interface.h"

#include <iostream>

using namespace std;

class Path {
public:
	map<int, int> nodeToVal;

	Path() { }
	void add(map<int, int>& initMap) {
		nodeToVal.insert(initMap.begin(), initMap.end());
	}
	void addCond(int nodeid, int val) {
		Assert(nodeToVal.find(nodeid) == nodeToVal.end(), "Node already has value");
		nodeToVal[nodeid] = val;
	}

	string toString() {
		stringstream s;
		for (auto it = nodeToVal.begin(); it != nodeToVal.end(); it++) {
			s << "(" << it->first << "," << it->second << ");";
		}
		return s.str();
	}

	int getVal(int nodeid) {
		if (nodeToVal.find(nodeid) == nodeToVal.end()) {
			return -1;
		} else {
			return nodeToVal[nodeid];
		}
	}

	void empty() {
		nodeToVal.clear();
	}

	int length() {
		return nodeToVal.size();
	}

	int lowestId() {
		if (nodeToVal.size() == 0) {
			return -1;
		}
		return nodeToVal.begin()->first;
	}

	static bool isCompatible(Path* p1, Path* p2) {
		int size1 = p1->nodeToVal.size();
		int size2 = p2->nodeToVal.size();
		if (size1 == 0 || size2 == 0) {
			return true;
		}
		Path* pl;
		Path* ps;
		if (size1 > size2) {
			pl = p1;
			ps = p2;
		} else {
			pl = p2;
			ps = p1;
		}
		for (auto it = pl->nodeToVal.begin(); it != pl->nodeToVal.end(); it++) {
			int nodeid = it->first;
			int val = it->second;
			int val1 = ps->getVal(nodeid);
			if (val1 != -1 && val != val1) {
				return false;
			}
		}
		return true;
	}

	static void combinePaths(Path* p1, Path* p2, Path* p) {
		// assumes that paths are compatible
		p->nodeToVal.clear();
		p->add(p1->nodeToVal);
		p->add(p2->nodeToVal);
	}

	static void combinePath(Path* p1, Path* p) {
		p->nodeToVal.clear();
		p->add(p1->nodeToVal);
	}

	static void combinePaths(vector<Path*>& paths, Path* p2, Path* p) {
		p->nodeToVal.clear();
		p->add(p2->nodeToVal);
		// TODO: need to add common conditions in paths to p
	}

	static Path* pIntersect(Path* p1, Path* p2) {
		// assumes that the paths are compatible
		Path* p = new Path();
		p->add(p1->nodeToVal);
		p->add(p2->nodeToVal);
		return p;
	}

	// only one cond  should differ
	static int isConjugate(Path* p1, Path* p2) {
		//cout << "Conjugate check: " << p1->toString() << " | " << p2->toString() << endl;
		if (p1->length() != p2->length()) {
			return -1;
		}
		int conjIdx = -1;
		int ctr = 0;
		for (auto it = p1->nodeToVal.begin(); it != p1->nodeToVal.end(); it++) {
			if (p2->getVal(it->first) == -1) {
				return -1;
			}
			if (p2->getVal(it->first) != it->second) {
				if (conjIdx == -1) {
					conjIdx = ctr;
				} else {
					return -1;
				}
			}
			ctr++;
		}
		//cout << conjIdx << endl;
		return conjIdx;
	}

	static Path* pUnion(Path* p1, Path* p2) {
		// assumes that the paths are conjugates
		Path* p = new Path();
		for (auto it = p1->nodeToVal.begin(); it != p1->nodeToVal.end(); it++) {
			if (p2->getVal(it->first) == it->second) {
				p->addCond(it->first, it->second);
			}

		}
		return p;

	}

	static bool isSubset(Path* p1, Path* p2) {
		if (p1->length() == 0) return true;
		for (auto it = p1->nodeToVal.begin(); it != p1->nodeToVal.end(); it++) {
			if (p2->getVal(it->first) != it->second) {
				return false;
			}
		}
		return true;

	}

	// no common condition
	static bool isDisjoint(Path* p1, Path* p2) {
		if (p1->length() == 0 || p2->length() == 0) return false;
		for (auto it = p1->nodeToVal.begin(); it != p1->nodeToVal.end(); it++) {
			if (p2->getVal(it->first) != -1) {
				return false;
			}
		}
		return true;
	}
};

class MergeNodesList {
	vector<tuple<int, int, int, int>> nodesToMerge;

public:
	void add(int node1, int idx1, int node2, int idx2) {
		nodesToMerge.push_back(make_tuple(node1, idx1, node2, idx2));
	}

	string print() {
		stringstream s;
		for (int i = 0; i < nodesToMerge.size(); i++) {
			auto& t = nodesToMerge[i];
			s << "(" << get<0>(t) << "," << get<1>(t) << "," << get<2>(t) << "," << get<3>(t) << ") ";
		}
		return s.str();
	}

	int size() {
		return nodesToMerge.size();
	}

	tuple<int, int, int, int>& getTuple(int idx) {
		return nodesToMerge[idx];
	}
};

class KLocalityAutoDiff: public NodeVisitor, public SymbolicEvaluator
{
	BooleanDAG& bdag;
	map<string, int>& floatCtrls; // Maps float ctrl names to indices within grad vector
	int nctrls; // number of float ctrls
	gsl_vector* ctrls; // ctrl values
	vector<vector<ValueGrad*>> values; // Keeps track of values along with gradients for each node
	vector<vector<DistanceGrad*>> distances; // Keeps track of distance metric for boolean nodes
	vector<vector<Path*>> paths;
	vector<int> sizes;
    Interface* inputValues;

    set<string> pathStrings;

    int MAX_REGIONS = 16 + 1; // 0th idx is for final merging

    vector<vector<MergeNodesList*>> mergeNodes;

public:
    int DEFAULT_INP = -32;
    KLocalityAutoDiff(BooleanDAG& bdag_p, map<string, int>& floatCtrls_p);

	~KLocalityAutoDiff(void);

	virtual void visit( SRC_node& node );
	virtual void visit( DST_node& node );
	virtual void visit( CTRL_node& node );
	virtual void visit( PLUS_node& node );
	virtual void visit( TIMES_node& node );
	virtual void visit( ARRACC_node& node );
	virtual void visit( DIV_node& node );
	virtual void visit( MOD_node& node );
	virtual void visit( NEG_node& node );
	virtual void visit( CONST_node& node );
	virtual void visit( LT_node& node );
	virtual void visit( EQ_node& node );
	virtual void visit( AND_node& node );
	virtual void visit( OR_node& node );
	virtual void visit( NOT_node& node );
	virtual void visit( ARRASS_node& node );
	virtual void visit( UFUN_node& node );
	virtual void visit( TUPLE_R_node& node );
	virtual void visit( ASSERT_node& node );

    virtual void setInputs(Interface* inputValues_p);

	virtual void run(const gsl_vector* ctrls_p, const set<int>& nodesSubset) {
		Assert(false, "Not yet supported");
	}
    virtual void run(const gsl_vector* ctrls_p);

    virtual double getErrorOnConstraint(int nodeid, gsl_vector* grad);
    double getSqrtError(bool_node* node, gsl_vector* grad);
    double getAssertError(bool_node* node, gsl_vector* grad);
    double getBoolCtrlError(bool_node* node, gsl_vector* grad);
    double getBoolExprError(bool_node* node, gsl_vector* grad);

    virtual double getErrorOnConstraint(int nodeid);
    double getSqrtError(bool_node* node);
    double getAssertError(bool_node* node);
    double getBoolCtrlError(bool_node* node);
    double getBoolExprError(bool_node* node);

    virtual double getErrorForAsserts(const set<int>& assertIds, gsl_vector* grad) {
    	Assert(false, "TODO");
    }

	virtual double getErrorForAssert(int assertId, gsl_vector* grad) {
		Assert(false, "TODO");
	}

	void setvalue(bool_node& bn, int idx, ValueGrad* v) {
		values[bn.id][idx] = v;
	}

	ValueGrad* v(bool_node& bn, int idx) {
		ValueGrad* val = values[bn.id][idx];
		if (val == NULL) {
			gsl_vector* g = gsl_vector_alloc(nctrls);
			GradUtil::default_grad(g);
			val = new ValueGrad(0, g);
			setvalue(bn, idx, val);
		}
		return val;
	}

	ValueGrad* v(bool_node* bn, int idx) {
		return v(*bn, idx);
	}

	void setdistance(bool_node& bn, int idx, DistanceGrad* d) {
		distances[bn.id][idx] = d;
	}

	DistanceGrad* d(bool_node& bn, int idx) {
		DistanceGrad* dist = distances[bn.id][idx];
		if (dist == NULL) {
			gsl_vector* g = gsl_vector_alloc(nctrls);
			GradUtil::default_grad(g);
			dist = new DistanceGrad(0, g);
			setdistance(bn, idx, dist);
		}
		return dist;
	}

	DistanceGrad* d(bool_node* bn, int idx) {
		return d(*bn, idx);
	}

	int size(bool_node& bn) {
		return sizes[bn.id];
	}

	int size(bool_node* bn) {
		return size(*bn);
	}

	void setsize(bool_node& bn, int sz) {
		sizes[bn.id] = sz;
	}

	void setsize(bool_node* n, int sz) {
		setsize(*n, sz);
	}

	void setpath(bool_node& bn, int idx, Path* p) {
		paths[bn.id][idx] = p;
	}

	Path* path(bool_node& bn, int idx) {
		Path* p = paths[bn.id][idx];
		if (p == NULL) {
			p = new Path();
			setpath(bn, idx, p);
		}
		return p;
	}

	Path* path(bool_node* bn, int idx) {
		return path(*bn, idx);
	}

	virtual double dist(int nid) {
		ValueGrad* val = v(bdag[nid], 0);
		return val->getVal();
	}

	virtual double dist(int nid, gsl_vector* grad) {
		ValueGrad* val = v(bdag[nid], 0);
		gsl_vector_memcpy(grad, val->getGrad());
		return val->getVal();
	}

	virtual void print() {
		/*for (int i = 0; i < bdag.size(); i++) {
			if (bdag[i]->type == bool_node::ASSERT) {
				DistanceGrad* dist = d(bdag[i]->mother);
				if (dist->set) {
				double gmag = gsl_blas_dnrm2(dist->grad);
				if (dist->dist < 0.1) {
					cout << bdag[i]->mother->lprint() << endl;
					cout << dist->printFull() << endl;
				}
				}
			}
		}
		return;*/
		for (int i = 0; i < bdag.size(); i++) {
			cout << bdag[i]->lprint() << " ";
			if (bdag[i]->getOtype() == OutType::FLOAT) {
				if (v(bdag[i], 0)->set) {
					cout << v(bdag[i], 0)->print() << endl;
				} else {
					cout << "UNSET" << endl;
				}
			} else {
				if (d(bdag[i], 0)->set) {
					cout << d(bdag[i], 0)->print() << endl;
				} else {
					cout << "UNSET" << endl;
				}
			}
		}
	}
	virtual void printFull() {
		for (int i = 0; i < bdag.size(); i++) {
			cout << bdag[i]->lprint() << endl;
			if (bdag[i]->getOtype() == OutType::FLOAT) {
				if (v(bdag[i],0)->set) {
					cout << v(bdag[i],0)->printFull() << endl;
				} else {
					cout << "UNSET" <<endl;
				}
			} else {
				if(d(bdag[i],0)->set) {
					cout << d(bdag[i],0)->printFull() << endl;
				} else {
					cout << "UNSET" << endl;
				}
			}
			if (bdag[i]->type == bool_node::PLUS) {
				cout << v(bdag[i]->mother(),0)->getVal() << " + " << v(bdag[i]->father(), 0)->getVal() << endl;
			}
		}
	}

	bool isFloat(bool_node& bn) {
		return (bn.getOtype() == OutType::FLOAT);
	}

	bool isFloat(bool_node* bn) {
		return (bn->getOtype() == OutType::FLOAT);
	}

	int getInputValue(bool_node& bn) {
		if (inputValues->hasValue(bn.id)) {
			int val = inputValues->getValue(bn.id);
			return val;
		} else {
			return DEFAULT_INP;
		}
	}

	int getInputValue(bool_node* bn) {
		return getInputValue(*bn);
	}

	void doUfun(UFUN_node& node, ValueGrad* mval, ValueGrad* val);

	void copyNodes(bool_node& node, bool_node* m);
	double merge(bool_node* n, gsl_vector* grad);

	void mergePaths(vector<tuple<double, vector<tuple<int, int, int>>, Path*>>& mergedPaths);
	void getMergeNodesBinop(bool_node* n);
	void getMergeNodesUnop(bool_node* n, bool_node* m);
	void getMergeNodesArracc(bool_node* node);
	void getMergeNodes();

	void combineDistance(bool_node& node, int nodeid1, int idx1, int nodeid2, int idx2, double& dist, gsl_vector* dg_grad);
	void doPair(bool_node& node, int nodeid1, int idx1, int nodeid2, int idx2, double& val, gsl_vector* val_grad);
	void pairNodes(bool_node& node);
	void normalize(bool_node* n);

};


/*
   Copyright [2017-2021] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef _MCAS_HSTORE_POOL_MANAGER_H
#define _MCAS_HSTORE_POOL_MANAGER_H

#include <api/kvstore_itf.h> /* status_t */

#include "alloc_key.h" /* AK_FORMAL */
#include "pool_error.h"

#include <common/logging.h> /* log_source */
#include <common/string_view.h>
#include <nupm/region_descriptor.h>
#include <gsl/pointers>
#include <sys/uio.h>
#include <cstddef>
#include <functional>
#include <string>
#include <system_error>

struct pool_path;

struct dax_manager;

template <typename Pool>
  struct pool_manager
    : protected common::log_source
  {
    using string_view = common::string_view;
    pool_manager(unsigned debug_level_) : common::log_source(debug_level_) {}
    virtual ~pool_manager() {}

    virtual void pool_create_check(const std::size_t size_) = 0;

    virtual void pool_close_check(const string_view) = 0;

    virtual nupm::region_descriptor pool_get_regions(const Pool &) const = 0;

    /*
     * throws pool_error if create_region fails
     */
    virtual auto pool_create_1(
      const pool_path &path_
      , std::size_t size_
    ) -> nupm::region_descriptor = 0;

    virtual auto pool_create_2(
      AK_FORMAL
      const nupm::region_descriptor & rac
      , component::IKVStore::flags_t flags
      , std::size_t expected_obj_count
    ) -> std::unique_ptr<Pool> = 0;

    virtual auto pool_open_1(
      const pool_path &path_
    ) -> nupm::region_descriptor = 0;

    virtual auto pool_open_2(
      AK_FORMAL
      const nupm::region_descriptor & access_
      , component::IKVStore::flags_t flags_
    ) -> std::unique_ptr<Pool> = 0;

    virtual void pool_delete(const pool_path &path) = 0;
    virtual const std::unique_ptr<dax_manager> & get_dax_manager() const = 0;
  };

#endif


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef fsysdwrite_c5ab9cfc_e66a_40b6_8ea1_29b3bde4f5e3_h
#define fsysdwrite_c5ab9cfc_e66a_40b6_8ea1_29b3bde4f5e3_h

#include <d3d11.h>
#include <d3d10_1.h>
#include <dxgi.h>
#include <d2d1.h>
#include <dwrite.h>
#include <gslib/std.h>
#include <ariel/sysop.h>
#include <ariel/rendersysd3d11.h>

__ariel_begin__

typedef unordered_map<font, IDWriteTextFormat*> dwrite_font_map;

class fsys_dwrite:
    public fontsys
{
public:
    fsys_dwrite();
    virtual ~fsys_dwrite();
    virtual void initialize() override;
    virtual void set_font(const font& f) override;
    virtual bool query_size(const gchar* str, int& w, int& h, int len = -1) override;
    virtual bool create_text_image(image& img, const gchar* str, int x, int y, const color& cr, int len = -1) override;
    virtual bool create_text_texture(texture2d** tex, const gchar* str, int margin, const color& cr, int len = -1) override;
    virtual void draw(image& img, const gchar* str, int x, int y, const color& cr, int len = -1) override;

protected:
    dwrite_font_map                     _font_map;
    IDWriteTextFormat*                  _current_font;
    com_ptr<ID3D11Device>               _dev11;
    com_ptr<ID3D10Device1>              _dev101;
    com_ptr<IDWriteFactory>             _dwfactory;
    com_ptr<ID2D1Factory>               _d2dfactory;

protected:
    void destroy_font_map();
};

__ariel_end__

#endif


#pragma once

// View structures
// STATUS: prototype, NSC

#include <vector>

#include <gsl.h>

namespace duck {
/* span<T> represents a reference to a segment of a T array.
 * Equivalent to a (T* base, int len).
 */
using gsl::span;

/* string_view: Const reference (pointer) to a sequence of char.
 * Does not own the data, only points to it.
 * The char sequence may not be null terminated.
 *
 * std::string_view exists in C++17, but not before sadly.
 */
using string_view = gsl::cstring_span;

using gsl::to_string; // Create new std::string from string_view

bool is_prefix_of (string_view prefix, string_view str);
bool is_prefix_of (char prefix, string_view str);

// Split at separator. Does not remove empty parts, does not trim whitespace.
std::vector<string_view> split (char separator, string_view text);
} // namespace duck


/*  Copyright (C) 2006 Imperial College London and others.

    Please see the AUTHORS file in the main source directory for a full list
    of copyright holders.

    Prof. C Pain
    Applied Modelling and Computation Group
    Department of Earth Science and Engineering
    Imperial College London

    amcgsoftware@imperial.ac.uk

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation,
    version 2.1 of the License.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
    USA
*/

#ifndef COMMAND_LINE_OPTIONS_H
#define COMMAND_LINE_OPTIONS_H
#include "confdefs.h"
#include "version.h"

#include "Tokenize.h"

#ifdef _AIX
#include <unistd.h>
#else
#include <getopt.h>
#endif

#ifdef HAVE_MPI
#include <mpi.h>
#endif

#ifdef HAVE_PETSC
#include <petsc.h>
#endif

#include <signal.h>

#include <cstdlib>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include "fmangle.h"

extern std::map<std::string, std::string> fl_command_line_options;

void ParseArguments(int argc, char** argv);
void PetscInit(int argc, char** argv);
void print_version(std::ostream& stream = std::cerr);
void qg_usage(char *cmd);

extern "C" {
  void set_global_debug_level_fc(int *val);
}

#endif


/*
   Copyright [2017-2021] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef _MCAS_HSTORE_NUPM_H
#define _MCAS_HSTORE_NUPM_H

#include "pool_manager.h"

#include "alloc_key.h" /* AK_FORMAL */
#include "hstore_nupm_types.h"
#include "hstore_open_pool.h"
#include "persister_nupm.h"

#include <common/string_view.h>
#include <gsl/pointers>

#include <cstring> /* strerror */

#include <cinttypes> /* PRIx64 */
#include <cstdlib> /* getenv */

struct dax_manager;

template <typename PersistData, typename Heap>
  struct region;

#pragma GCC diagnostic push
/* Note: making enable_shared_from_this private avoids the non-virtual-dtor error but
 * generates a different error with no error text (G++ 5.4.0)
 */
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"

/* Region is region<persist_data_t, heap_rc>, Table is hstore::table_t Allocator is table_t::allocator_type, LockType is hstore::locK_type_t */
template <typename Region, typename Table, typename Allocator, typename LockType>
  struct hstore_nupm
    : public pool_manager<::open_pool<non_owner<Region>>>
  {
    using region_type = Region;
  private:
    using table_t = Table;
    using allocator_t = Allocator;
    using lock_type_t = LockType;
    using string_view = common::string_view;
  public:
    using open_pool_handle = ::open_pool<non_owner<region_type>>;
    using base = pool_manager<open_pool_handle>;
  private:
    std::unique_ptr<dax_manager> _dax_manager;
    unsigned _numa_node;

    static unsigned name_to_numa_node(const string_view name);
  public:
    hstore_nupm(unsigned debug_level_, const string_view, const string_view name_, std::unique_ptr<dax_manager> mgr_);

    virtual ~hstore_nupm();

    const std::unique_ptr<dax_manager> & get_dax_manager() const override { return _dax_manager; }
    void pool_create_check(std::size_t) override;

    auto pool_create_1(
      const pool_path &path_
      , std::size_t size_
    ) -> nupm::region_descriptor override;

    auto pool_create_2(
      AK_FORMAL
      const nupm::region_descriptor &rac
      , component::IKVStore::flags_t flags
      , std::size_t expected_obj_count
    ) -> std::unique_ptr<open_pool_handle> override;

    nupm::region_descriptor pool_open_1(
      const pool_path &path_
    ) override;

    auto pool_open_2(
      AK_FORMAL
      const nupm::region_descriptor & v_
      , component::IKVStore::flags_t flags_
    ) -> std::unique_ptr<open_pool_handle> override;

    void pool_close_check(const string_view) override;

    void pool_delete(const pool_path &path_) override;

    /* ERROR: want get_pool_regions(<proper type>, std::vector<::iovec>&) */
    nupm::region_descriptor pool_get_regions(const open_pool_handle &) const override;
  };
#pragma GCC diagnostic pop

#include "hstore_nupm.tcc"

#endif


#pragma once

#include "internal/Buffer.h"
#include <gsl/span>

namespace ge::gl4
{

using namespace gl45;

class BufferView
{
public:
	BufferView(Buffer &buffer, GLenum type, gsl::span<uint8_t> range);

	// Mapping Functions
	auto map(BufferAccessMask accessFlags) -> void *;
	void flush();
	void unmap();

	void bindToIndex(GLuint index);
	static void unbind(GLenum target, GLuint index);

	auto getBuffer() const -> Buffer &;

	const GLenum type;
	const gsl::span<uint8_t> range;
private:
	Buffer *buffer_;
};

}


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include <gsl/gsl>

namespace onnxruntime {
// Inspired by Fekir's Blog https://fekir.info/post/span-the-missing-constructor/
// Used under MIT license

// Use AsSpan for less typing on any container including initializer list to create a span
// (unnamed, untyped initializer list does not automatically convert to gsl::span).
// {1, 2, 3} as such does not have a type
// (see https://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html)
//
//   Example: AsSpan({1, 2, 3}) results in gsl::span<const int>
//
// The above would deduce to std::initializer_list<int> and the result is gsl::span<const int>
//
// AsSpan<int64_t>({1, 2, 3}) produces gsl::span<const int64_t>
//
// We can also do std::array<int64_t, 3>{1, 2, 3} that can be automatically converted to span
// without memory allocation.
//
// If type conversion is not required, then for C++17 std::array template parameters are
// auto-deduced. Example: std::array{1, 2, 3}.
// We are aiming at not allocating memory dynamically.

namespace details {
template <class P>
constexpr auto AsSpanImpl(P* p, size_t s) {
  return gsl::span<P>(p, s);
}
}  // namespace details

template <class C>
constexpr auto AsSpan(C& c) {
  return details::AsSpanImpl(c.data(), c.size());
}

template <class C>
constexpr auto AsSpan(const C& c) {
  return details::AsSpanImpl(c.data(), c.size());
}

template <class C>
constexpr auto AsSpan(C&& c) {
  return details::AsSpanImpl(c.data(), c.size());
}

template <class T>
constexpr auto AsSpan(std::initializer_list<T> c) {
  return details::AsSpanImpl(c.begin(), c.size());
}

template <class T, size_t N>
constexpr auto AsSpan(T (&arr)[N]) {
  return details::AsSpanImpl(arr, N);
}

template <class T, size_t N>
constexpr auto AsSpan(const T (&arr)[N]) {
  return details::AsSpanImpl(arr, N);
}

}


/* Author:  Lisandro Dalcin   */
/* Contact: dalcinl@gmail.com */

#ifndef PETSC4PY_H
#define PETSC4PY_H

#include <Python.h>
#include <petsc.h>
#include "petsc4py.PETSc_api.h"

static int import_petsc4py(void) {
  if (import_petsc4py__PETSc() < 0) goto bad;
  return 0;
 bad:
  return -1;
}

#endif /* !PETSC4PY_H */


#if !defined(MAIN_H_INCLUDED)
#define MAIN_H_INCLUDED

#include "Exceptions.h"

#include <cstdlib>
#include <filesystem>
#include <gsl/gsl>
#include <iostream>
#include <stdexcept>

template <typename T>
int commonMain(size_t argCount, const char*const*const argList)
{
	using ::std::filesystem::path;
	using ::gsl::make_span;
	using ::std::cout;
	using ::std::endl;

	auto exitCode{EXIT_FAILURE};
	try
	{
		// Pass the argument list, not including the program name (0th element):
		T program{make_span(argList + 1, argCount - 1)};
		exitCode = program.run();
	}
	catch (const CmdLineError& ex)
	{
		exitCode = T::usage(cout, path{argList[0]}.stem().generic_string(), ex.what());
	}
	catch (const ::std::exception& ex)
	{
		cout << endl
			<< "Exception of type " << typeid(ex).name() << endl
			<< "   " << ex.what() << endl
			<< endl;
	}
	return exitCode;
}

#endif // MAIN_H_INCLUDED


#pragma once

#include <cstdint>
#include <string>
#include <vector>

#include <gsl/span>

namespace terminal_editor {

/// Returns name of passed control character, or nullptr if given byte was not recognized.
/// ISO 30112 defines POSIX control characters as Unicode characters U+0000..U+001F, U+007F..U+009F, U+2028, and U+2029 (Unicode classes Cc, Zl, and Zp) "
/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes
/// @param codePoint    Code point for which name to return.
const char* controlCharacterName(uint32_t codePoint);

/// Describes return value of getFirstCodePoint() function.
struct CodePointInfo {
    bool valid;                          ///< True if valid code point was decoded. False otherwise.
    gsl::span<const char> consumedInput; ///< Bytes consumed from the input data. Length will be from 1 to 6.
    std::string info;                    ///< Arbitrary information about consumed bytes. If 'valid' is false will contain error information.
    uint32_t codePoint;                  ///< Decoded code point. Valid only if 'valid' is true.
};

/// Figures out what grapeheme is at the begining of data and returns it.
/// See: https://pl.wikipedia.org/wiki/UTF-8#Spos%C3%B3b_kodowania
/// @note All invalid byte sequences will be rendered as hex representations, with detailed explanation why it is invalid.
///       All control characters will be rendered as symbolic replacements.
///       Tab characters will be rendered as symbolic replacement.
/// @param data     Input string. It is assumed to be in UTF-8, but can contain invalid characters (which will be rendered as special Graphemes).
/// @param Returns number of bytes from input consumed.
CodePointInfo getFirstCodePoint(gsl::span<const char> data);

/// Parses a line of text into a list of CodePointInfos.
std::vector<CodePointInfo> parseLine(gsl::span<const char> inputData);

/// Analyzes given input data.
/// @param inputData     Input string. It is assumed to be in UTF-8, but can contain invalid characters (which will be annotated specially).
/// @return Valid UTF-8 string that describes what original string contains.
std::string analyzeData(gsl::span<const char> inputData);

/// Appends UTF-8 representation of given code point to a string.
/// @todo Move to some string utilities.
/// @param text         Text to append code point to.
/// @param codePoint    UTF-32 code point to append.
void appendCodePoint(std::string& text, uint32_t codePoint);

/// Appends UTF-8 representation of given code point to an output stream.
/// @todo Move to some string utilities.
/// @param os           Output stream to append code point to.
/// @param codePoint    UTF-32 code point to append.
void appendCodePoint(std::ostream& os, uint32_t codePoint);

} // namespace terminal_editor


#pragma once

#include <memory>
#include <gsl/span>

#include "halley/time/halleytime.h"
#include "../session/network_session.h"
#include "entity_network_remote_peer.h"
#include "halley/bytes/serialization_dictionary.h"
#include "halley/entity/system.h"
#include "halley/entity/world.h"

namespace Halley {
	class EntityFactory;
	class Resources;
	class World;
	class NetworkSession;

	class EntitySessionSharedData : public SharedData {
	public:

	};

	class EntityClientSharedData : public SharedData {
	public:
		std::optional<Rect4i> viewRect;

		void serialize(Serializer& s) const override;
		void deserialize(Deserializer& s) override;
	};

	class EntityNetworkSession : NetworkSession::IListener, NetworkSession::ISharedDataHandler, public IWorldNetworkInterface {
    public:
		class IEntityNetworkSessionListener {
		public:
			virtual ~IEntityNetworkSessionListener() = default;
			virtual void onStartSession(NetworkSession::PeerId myPeerId) = 0;
			virtual void onRemoteEntityCreated(EntityRef entity, NetworkSession::PeerId peerId) {}
			virtual void setupInterpolators(DataInterpolatorSet& interpolatorSet, EntityRef entity, bool remote) = 0;
			virtual bool isEntityInView(EntityRef entity, const EntityClientSharedData& clientData) = 0;
		};

		EntityNetworkSession(std::shared_ptr<NetworkSession> session, Resources& resources, std::set<String> ignoreComponents, IEntityNetworkSessionListener* listener);
		~EntityNetworkSession() override;

		void setWorld(World& world, SystemMessageBridge bridge);

		void sendUpdates(Time t, Rect4i viewRect, gsl::span<const std::pair<EntityId, uint8_t>> entityIds); // Takes pairs of entity id and owner peer id
		void receiveUpdates();

		World& getWorld() const;
		EntityFactory& getFactory() const;
		NetworkSession& getSession() const;
		bool hasWorld() const;

		const EntityFactory::SerializationOptions& getEntitySerializationOptions() const;
		const EntityDataDelta::Options& getEntityDeltaOptions() const;
		const SerializerOptions& getByteSerializationOptions() const;
		SerializationDictionary& getSerializationDictionary();

		Time getMinSendInterval() const;

		void onRemoteEntityCreated(EntityRef entity, NetworkSession::PeerId peerId);
		void requestSetupInterpolators(DataInterpolatorSet& interpolatorSet, EntityRef entity, bool remote);
		void setupOutboundInterpolators(EntityRef entity);

		bool isReadyToStart() const;
		bool isEntityInView(EntityRef entity, const EntityClientSharedData& clientData) const;

		Vector<Rect4i> getRemoteViewPorts() const;

		bool isHost() override;
		bool isRemote(ConstEntityRef entity) const override;
		void sendEntityMessage(EntityRef entity, int messageType, Bytes messageData) override;
		void sendSystemMessage(String targetSystem, int messageType, Bytes messageData, SystemMessageDestination destination, SystemMessageCallback callback) override;

		void sendToAll(EntityNetworkMessage msg);
		void sendToPeer(EntityNetworkMessage msg, NetworkSession::PeerId peerId);

	protected:
		void onStartSession(NetworkSession::PeerId myPeerId) override;
		void onPeerConnected(NetworkSession::PeerId peerId) override;
		void onPeerDisconnected(NetworkSession::PeerId peerId) override;
		std::unique_ptr<SharedData> makeSessionSharedData() override;
		std::unique_ptr<SharedData> makePeerSharedData() override;

	private:
		struct QueuedMessage {
			NetworkSession::PeerId fromPeerId;
			EntityNetworkMessage message;
		};

		struct PendingSysMsgResponse {
			SystemMessageCallback callback;
		};

		Resources& resources;
		std::shared_ptr<EntityFactory> factory;
		IEntityNetworkSessionListener* listener = nullptr;
		SystemMessageBridge messageBridge;
		uint32_t systemMessageId = 0;
		HashMap<uint32_t, PendingSysMsgResponse>  pendingSysMsgResponses;

		EntityFactory::SerializationOptions entitySerializationOptions;
		EntityDataDelta::Options deltaOptions;
		SerializerOptions byteSerializationOptions;
		SerializationDictionary serializationDictionary;

		std::shared_ptr<NetworkSession> session;
		Vector<EntityNetworkRemotePeer> peers;

		Vector<QueuedMessage> queuedPackets;

		HashMap<int, Vector<EntityNetworkMessage>> outbox;

		bool readyToStart = false;

		bool canProcessMessage(const EntityNetworkMessage& msg) const;
		void processMessage(NetworkSession::PeerId fromPeerId, EntityNetworkMessage msg);
		void onReceiveEntityUpdate(NetworkSession::PeerId fromPeerId, EntityNetworkMessage msg);
		void onReceiveReady(NetworkSession::PeerId fromPeerId, const EntityNetworkMessageReadyToStart& msg);
		void onReceiveMessageToEntity(NetworkSession::PeerId fromPeerId, const EntityNetworkMessageEntityMsg& msg);
		void onReceiveSystemMessage(NetworkSession::PeerId fromPeerId, const EntityNetworkMessageSystemMsg& msg);
		void onReceiveSystemMessageResponse(NetworkSession::PeerId fromPeerId, const EntityNetworkMessageSystemMsgResponse& msg);

		void sendMessages();

		void setupDictionary();
	};
}


#pragma once
#include <vector>
#include <gsl/gsl>
#include "halley/utils/utils.h"

namespace Halley
{
	class NetworkPacketBase
	{
	public:
		size_t copyTo(gsl::span<gsl::byte> dst) const;
		size_t getSize() const;
		gsl::span<const gsl::byte> getBytes() const;

		NetworkPacketBase(NetworkPacketBase&& other) = delete;
		NetworkPacketBase& operator=(NetworkPacketBase&& other) = delete;

	protected:
		NetworkPacketBase();
		NetworkPacketBase(gsl::span<const gsl::byte> data, size_t prePadding);

		size_t dataStart;
		std::vector<gsl::byte> data;
	};

	class OutboundNetworkPacket : public NetworkPacketBase
	{
	public:
		OutboundNetworkPacket(const OutboundNetworkPacket& other);
		explicit OutboundNetworkPacket(OutboundNetworkPacket&& other) noexcept;
		explicit OutboundNetworkPacket(gsl::span<const gsl::byte> data);
		explicit OutboundNetworkPacket(const Bytes& data);

		void addHeader(gsl::span<const gsl::byte> src);

		template <typename T>
		void addHeader(const T& h)
		{
			addHeader(gsl::as_bytes(gsl::span<const T>(&h, 1)));
		}

		OutboundNetworkPacket& operator=(OutboundNetworkPacket&& other) noexcept;
	};

	class InboundNetworkPacket : public NetworkPacketBase
	{
	public:
		InboundNetworkPacket();
		explicit InboundNetworkPacket(InboundNetworkPacket&& other);
		explicit InboundNetworkPacket(gsl::span<const gsl::byte> data);
		void extractHeader(gsl::span<gsl::byte> dst);

		template <typename T>
		void extractHeader(T& h)
		{
			extractHeader(gsl::as_writeable_bytes(gsl::span<T>(&h, 1)));
		}

		InboundNetworkPacket& operator=(InboundNetworkPacket&& other);
	};
}


// MIT License
//
// Copyright (c) 2020 SunnyCase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <chino/ddk/kernel.h>
#include <cstdint>
#include <gsl/gsl-lite.hpp>

namespace chino::arch
{
// No asynchronous interrupt is supported by this target
// Only save callee saved registers in context
struct armv7m_thread_context
{
    uintptr_t r4;
    uintptr_t r5;
    uintptr_t r6;
    uintptr_t r7;
    uintptr_t r8;
    uintptr_t r9;
    uintptr_t r10;
    uintptr_t r11;
    uintptr_t lr;
    uintptr_t sp;
    uintptr_t stack_bottom;
};

using thread_context_t = armv7m_thread_context;

struct armv7m_arch
{
    static constexpr size_t ALLOCATE_ALIGNMENT = 8;

    static uint32_t current_processor() noexcept { return 0; }
    static void yield_processor() noexcept;

    static uintptr_t disable_irq() noexcept;
    static void restore_irq(uintptr_t state) noexcept;

    static void init_thread_context(thread_context_t &context, gsl::span<uintptr_t> stack, kernel::thread_thunk_t start, void *arg0, void *arg1) noexcept;
    [[noreturn]] static void start_schedule(thread_context_t &context) noexcept;
    static void yield(thread_context_t &old_context, thread_context_t &new_context) noexcept;

    static void init_stack_check() noexcept;
};

using arch_t = armv7m_arch;
}


/*
   Copyright [2021] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef MCAS_HSTORE_HEAP_MC_SHIM_H
#define MCAS_HSTORE_HEAP_MC_SHIM_H

#include <mm_plugin_itf.h>

#include <ccpm/interfaces.h> /* ownership_callback, (IHeap_expandable, region_vector_t) */
#include <common/byte_span.h>
#include <common/string_view.h>
#include <gsl/span>

#include <cstddef> /* size_t */
#include <functional> /* function */

struct heap_mc_shim
	: public ccpm::IHeap_expandable
{
private:
	MM_plugin_wrapper _mm;
public:
	heap_mc_shim(common::string_view path, ccpm::persister *pe, gsl::span<common::byte_span> range, std::function<bool(const void *)> callee_owns);
	heap_mc_shim(common::string_view path, ccpm::persister *pe);
	heap_mc_shim(MM_plugin_wrapper &&pw);

	bool reconstitute(
		ccpm::region_span regions
		, ccpm::ownership_callback_t resolver
		, bool force_init
	) override;

	status_t allocate(void * & ptr
		, std::size_t bytes
		, std::size_t alignment
	) override;

	status_t free(void * & ptr
		, std::size_t bytes
	) override;

	status_t remaining(std::size_t& out_size) const override;

	ccpm::region_vector_t get_regions() const override;

	void add_regions(ccpm::region_span regions) override;

	bool includes(const void *ptr) const override;

	bool is_crash_consistent() const;
};

#endif


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include <codecvt>
#include <string>
#include <locale>

#include <gsl/gsl>

namespace mira
{
    inline std::string utf16_to_utf8(const wchar_t* input)
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.to_bytes(input);
    }

    inline std::wstring utf8_to_utf16(const char* input)
    {
        std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
        return converter.from_bytes(input);
    }

    inline std::string utf16_to_utf8(const std::wstring& input)
    {
        return utf16_to_utf8(input.data());
    }

    inline std::wstring utf8_to_utf16(const std::string& input)
    {
        return utf8_to_utf16(input.data());
    }

    struct string_compare
    {
        using is_transparent = std::true_type;

        bool operator()(gsl::cstring_span<> a, gsl::cstring_span<> b) const
        {
            return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
        }

        bool operator()(gsl::czstring_span<> a, gsl::czstring_span<> b) const
        {
            return (*this)(a.as_string_span(), b.as_string_span());
        }

        bool operator()(const char* a, const char* b) const
        {
            return strcmp(a, b) < 0;
        }
    };
}


/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#pragma once

#include <memcached/dockey.h>
#include <gsl/gsl>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>

#include "ep_types.h"

class SerialisedDocKey;

/**
 * StoredDocKey is a container for key data
 *
 * Internally an n byte key is stored in a n + sizeof(CollectionID) std::string.
 *  a) We zero terminate so that data() is safe for printing as a c-string.
 *  b) We store the CollectionID in byte 0:3. This is because StoredDocKey
 *    typically ends up being written to disk and the CollectionID forms part
 *    of the on-disk key. Accounting and for for the CollectionID means storage
 *    components don't have to create a new buffer into which they can layout
 *    CollectionID and key data.
 */
class StoredDocKey : public DocKeyInterface<StoredDocKey> {
public:
    /**
     * Construct empty - required for some std containers
     */
    StoredDocKey() : keydata() {
    }

    /**
     * Create a StoredDocKey from a DocKey
     * @param key DocKey that is to be copied-in
     */
    StoredDocKey(const DocKey& key) {
        if (key.getEncoding() == DocKeyEncodesCollectionId::Yes) {
            keydata.resize(key.size());
            std::copy(key.data(), key.data() + key.size(), keydata.begin());
        } else {
            // This key is for the default collection (which has a fixed value)
            keydata.resize(key.size() + 1);
            keydata[0] = DefaultCollectionLeb128Encoded;
            std::copy(key.data(), key.data() + key.size(), keydata.begin() + 1);
        }
    }

    /**
     * Create a StoredDocKey from a std::string (test code uses this)
     * @param key std::string to be copied-in
     * @param cid the CollectionID that the key applies to (and will be encoded
     *        into the stored data)
     */
    StoredDocKey(const std::string& key, CollectionID cid);

    const uint8_t* data() const {
        return reinterpret_cast<const uint8_t*>(keydata.data());
    }

    size_t size() const {
        return keydata.size();
    }

    DocNamespace getDocNamespace() const {
        return getCollectionID();
    }

    CollectionID getCollectionID() const;

    DocKeyEncodesCollectionId getEncoding() const {
        return DocKeyEncodesCollectionId::Yes;
    }

    /**
     * @return a DocKey that views this StoredDocKey but without any
     * collection-ID prefix.
     */
    DocKey makeDocKeyWithoutCollectionID() const;

    /**
     * Intended for debug use only
     * @returns cid:key
     */
    std::string to_string() const;

    /**
     * For tests only
     * @returns the 'key' part of the StoredDocKey
     */
    const char* c_str() const;

    int compare(const StoredDocKey& rhs) const {
        return keydata.compare(rhs.keydata);
    }

    bool operator==(const StoredDocKey& rhs) const {
        return keydata == rhs.keydata;
    }

    bool operator!=(const StoredDocKey& rhs) const {
        return !(*this == rhs);
    }

    bool operator<(const StoredDocKey& rhs) const {
        return keydata < rhs.keydata;
    }

    operator DocKey() const {
        return {keydata, DocKeyEncodesCollectionId::Yes};
    }

protected:
    std::string keydata;
};

std::ostream& operator<<(std::ostream& os, const StoredDocKey& key);

static_assert(sizeof(CollectionID) == sizeof(uint32_t),
              "StoredDocKey: CollectionID has changed size");

/**
 * A hash function for StoredDocKey so they can be used in std::map and friends.
 */
namespace std {
template <>
struct hash<StoredDocKey> {
    std::size_t operator()(const StoredDocKey& key) const {
        return key.hash();
    }
};
}

class MutationLogEntryV2;
class StoredValue;

/**
 * SerialisedDocKey maintains the key data in an allocation that is not owned by
 * the class. The class is essentially immutable, providing a "view" onto the
 * larger block.
 *
 * For example where a StoredDocKey needs to exist as part of a bigger block of
 * data, SerialisedDocKey is the class to use.
 *
 * A limited number of classes are friends and only those classes can construct
 * a SerialisedDocKey.
 */
class SerialisedDocKey : public DocKeyInterface<SerialisedDocKey> {
public:
    /**
     * The copy constructor is deleted due to the bytes living outside of the
     * object.
     */
    SerialisedDocKey(const SerialisedDocKey& obj) = delete;

    const uint8_t* data() const {
        return bytes;
    }

    size_t size() const {
        return length;
    }

    DocNamespace getDocNamespace() const {
        return getCollectionID();
    }

    CollectionID getCollectionID() const;

    DocKeyEncodesCollectionId getEncoding() const {
        return DocKeyEncodesCollectionId::Yes;
    }

    bool operator==(const DocKey& rhs) const;

    /**
     * Return how many bytes are (or need to be) allocated to this object
     */
    size_t getObjectSize() const {
        return getObjectSize(length);
    }

    /**
     * Return how many bytes are needed to store the DocKey
     * @param key a DocKey that needs to be stored in a SerialisedDocKey
     */
    static size_t getObjectSize(const DocKey key) {
        return getObjectSize(key.size());
    }

    /**
     * Create a SerialisedDocKey and return a unique_ptr to the object.
     * Note that the allocation is bigger than sizeof(SerialisedDocKey)
     * @param key a DocKey to be stored as a SerialisedDocKey
     */
    struct SerialisedDocKeyDelete {
        void operator()(SerialisedDocKey* p) {
            p->~SerialisedDocKey();
            delete[] reinterpret_cast<uint8_t*>(p);
        }
    };

    operator DocKey() const {
        return {bytes, length, DocKeyEncodesCollectionId::Yes};
    }

    /**
     * make a SerialisedDocKey and return a unique_ptr to it - this is used
     * in test code only.
     */
    static std::unique_ptr<SerialisedDocKey, SerialisedDocKeyDelete> make(
            const StoredDocKey& key) {
        std::unique_ptr<SerialisedDocKey, SerialisedDocKeyDelete> rval(
                reinterpret_cast<SerialisedDocKey*>(
                        new uint8_t[getObjectSize(key)]));
        new (rval.get()) SerialisedDocKey(key);
        return rval;
    }

protected:
    /**
     * These following classes are "white-listed". They know how to allocate
     * and construct this object so are allowed access to the constructor.
     */
    friend class MutationLogEntryV2;
    friend class MutationLogEntryV3;
    friend class StoredValue;

    SerialisedDocKey() : length(0), bytes() {
    }

    /**
     * Create a SerialisedDocKey from a DocKey. Protected constructor as
     * this must be used by friends who know how to pre-allocate the object
     * storage
     * @param key a DocKey to be copied in
     */
    SerialisedDocKey(const DocKey& key)
        : length(gsl::narrow_cast<uint8_t>(key.size())) {
        if (key.getEncoding() == DocKeyEncodesCollectionId::Yes) {
            std::copy(key.data(), key.data() + key.size(), bytes);
        } else {
            // This key is for the default collection
            bytes[0] = DefaultCollectionLeb128Encoded;
            std::copy(key.data(), key.data() + key.size(), bytes + 1);
            length++;
        }
    }

    /**
     * Create a SerialisedDocKey from a byte_buffer that has no collection data
     * and requires the caller to state the collection-ID
     * This is used by MutationLogEntryV1/V2 to V3 upgrades
     */
    SerialisedDocKey(cb::const_byte_buffer key, CollectionID cid);

    /**
     * Create a SerialisedDocKey from a byte_buffer that has collection data
     */
    SerialisedDocKey(cb::const_byte_buffer key)
        : length(gsl::narrow_cast<uint8_t>(key.size())) {
        std::copy(key.begin(), key.end(), bytes);
    }

    /**
     * Returns the size in bytes of this object - fixed size plus the variable
     * length for the bytes making up the key.
     */
    static size_t getObjectSize(size_t len) {
        return sizeof(SerialisedDocKey) +
               (len - sizeof(SerialisedDocKey().bytes));
    }

    uint8_t length{0};
    uint8_t bytes[1];
};

std::ostream& operator<<(std::ostream& os, const SerialisedDocKey& key);

static_assert(std::is_standard_layout<SerialisedDocKey>::value,
              "SeralisedDocKey: must satisfy is_standard_layout");


#ifdef __cplusplus
#include <string>
#include <algorithm>
#include "halley/data_structures/vector.h"
#include <map>
#include <set>
#include <array>
#include <charconv>
#include <gsl/gsl>
#include <cstddef>
#include <limits>
#include <memory>
#include <chrono>
#include <thread>
#include <mutex>
#include <iomanip>
#include <optional>
#include <atomic>
#include <deque>
#include <iostream>
#include <sstream>
#include <istream>
#include <ostream>
#endif


/* -----------------------------------------------------------------------------
 * Copyright 2021 Jonathan Haigh
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------*/

#ifndef SQ_INCLUDE_GUARD_core_test_Primitive_test_util_h_
#define SQ_INCLUDE_GUARD_core_test_Primitive_test_util_h_

#include "core/Primitive.h"
#include "core/typeutil.h"

#include <gsl/gsl>
#include <string_view>

namespace sq::test {

SQ_ND Primitive to_primitive(PrimitiveString &&v);
SQ_ND Primitive to_primitive(const PrimitiveString &v);
SQ_ND Primitive to_primitive(std::string_view v);
SQ_ND Primitive to_primitive(gsl::czstring<> v);
SQ_ND Primitive to_primitive(PrimitiveInt v);
SQ_ND Primitive to_primitive(int v);
SQ_ND Primitive to_primitive(PrimitiveFloat v);
SQ_ND Primitive to_primitive(PrimitiveBool v);
SQ_ND Primitive to_primitive(PrimitiveNull v);

} // namespace sq::test

#endif // SQ_INCLUDE_GUARD_core_test_Primitive_test_util_h_


#ifndef AMICI_HDF5_H
#define AMICI_HDF5_H

#include <string>
#include <memory>
#include <vector>

#include <H5Cpp.h>

#include <gsl/gsl-lite.hpp>

#undef AMI_HDF5_H_DEBUG

/* Macros for enabling/disabling  HDF5 error auto-printing
 * AMICI_H5_SAVE_ERROR_HANDLER and AMICI_H5_RESTORE_ERROR_HANDLER must be called
 * within the same context, otherwise the stack handler is lost. */
#define AMICI_H5_SAVE_ERROR_HANDLER                                            \
    herr_t (*old_func)(void *);                                                \
    void *old_client_data;                                                     \
    H5Eget_auto1(&old_func, &old_client_data);                                 \
    H5Eset_auto1(NULL, NULL)

#define AMICI_H5_RESTORE_ERROR_HANDLER H5Eset_auto1(old_func, old_client_data)

namespace amici {

class ReturnData;
class ExpData;
class Model;
class Solver;

namespace hdf5 {

/* Functions for reading and writing AMICI data to/from HDF5 files. */

/**
 * @brief Open the given file for writing. Append if exists, create if not.
 * @param hdf5filename
 * @return
 */
H5::H5File createOrOpenForWriting(std::string const& hdf5filename);

/**
 * @brief Read solver options from HDF5 file
 * @param fileId hdf5 file handle to read from
 * @param solver solver to set options on
 * @param datasetPath Path inside the HDF5 file
 */
void readSolverSettingsFromHDF5(const H5::H5File &file, Solver& solver,
                                std::string const& datasetPath);

/**
 * @brief Read solver options from HDF5 file
 * @param hdffile Name of HDF5 file
 * @param solver solver to set options on
 * @param datasetPath Path inside the HDF5 file
 */
void readSolverSettingsFromHDF5(std::string const& hdffile, Solver& solver,
                                std::string const& datasetPath);

/**
 * @brief Read model data from HDF5 file
 * @param hdffile Name of HDF5 file
 * @param model model to set data on
 * @param datasetPath Path inside the HDF5 file
 */
void readModelDataFromHDF5(std::string const& hdffile, Model& model,
                           std::string const& datasetPath);

/**
 * @brief Read model data from HDF5 file
 * @param fileId hdf5 file handle to read from
 * @param model model to set data on
 * @param datasetPath Path inside the HDF5 file
 */
void readModelDataFromHDF5(H5::H5File const&file, Model& model,
                           std::string const& datasetPath);

/**
  * @brief Write ReturnData struct to HDF5 dataset
  * @param rdata Data to write
  * @param hdffile Filename of HDF5 file
  * @param datasetPath Full dataset path inside the HDF5 file (will be created)
  */

void writeReturnData(const ReturnData &rdata,
                     H5::H5File const& file,
                     const std::string& hdf5Location);

void writeReturnData(const ReturnData &rdata,
                     std::string const& hdf5Filename,
                     const std::string& hdf5Location);

void writeReturnDataDiagnosis(const ReturnData &rdata,
                              H5::H5File const& file,
                              const std::string& hdf5Location);

/**
 * @brief Create the given group and possibly parents
 * @param file
 * @param groupPath
 * @param recursively
 */
void createGroup(const H5::H5File &file,
                 std::string const& groupPath,
                 bool recursively = true);

/**
 * @brief readSimulationExpData reads AMICI experimental data from
 * attributes in HDF5 file.
 * @param hdf5Filename Name of HDF5 file
 * @param hdf5Root Path inside the HDF5 file to object having ExpData as
 * attributes
 * @param model The model for which data is to be read
 * @return
 */

std::unique_ptr<ExpData> readSimulationExpData(const std::string &hdf5Filename,
                                               const std::string &hdf5Root,
                                               const Model &model);

/**
 * @brief writeSimulationExpData writes AMICI experimental data to
 * attributes in HDF5 file.
 * @param edata The experimental data which is to be written
 * @param hdf5Filename Name of HDF5 file
 * @param hdf5Root Path inside the HDF5 file to object having ExpData as
 * attributes
 */

void writeSimulationExpData(const ExpData &edata,
                            H5::H5File const& file,
                            const std::string &hdf5Location);

/**
 * @brief attributeExists Check whether an attribute with the given
 * name exists on the given dataset
 * @param fileId The HDF5 file object
 * @param datasetPath Dataset of which attributes should be checked
 * @param attributeName Name of the attribute of interest
 * @return
 */
bool attributeExists(H5::H5File const& file,
                     const std::string &optionsObject,
                     const std::string &attributeName);

bool attributeExists(H5::H5Object const& object,
                     const std::string &attributeName);

void createAndWriteInt1DDataset(H5::H5File const& file,
                                std::string const& datasetName,
                                gsl::span<const int> buffer);

void createAndWriteInt2DDataset(H5::H5File const& file,
                                std::string const& datasetName,
                                gsl::span<const int> buffer, hsize_t m,
                                hsize_t n);

void createAndWriteDouble1DDataset(H5::H5File const& file,
                                   std::string const& datasetName,
                                   gsl::span<const double> buffer);

void createAndWriteDouble2DDataset(H5::H5File const& file,
                                   std::string const& datasetName,
                                   gsl::span<const double> buffer, hsize_t m,
                                   hsize_t n);

void createAndWriteDouble3DDataset(H5::H5File const& file,
                                   std::string const& datasetName,
                                   gsl::span<const double> buffer, hsize_t m,
                                   hsize_t n, hsize_t o);

double getDoubleScalarAttribute(const H5::H5File& file,
                                const std::string &optionsObject,
                                const std::string &attributeName);

int getIntScalarAttribute(const H5::H5File &file,
                          const std::string &optionsObject,
                          const std::string &attributeName);

std::vector<int> getIntDataset1D(const H5::H5File &file,
                                 std::string const& name);

std::vector<double> getDoubleDataset1D(const H5::H5File &file,
                                       std::string const& name);

std::vector<double> getDoubleDataset2D(const H5::H5File &file,
                                       std::string const& name,
                                       hsize_t &m, hsize_t &n);

std::vector<double> getDoubleDataset3D(const H5::H5File &file,
                                       std::string const& name,
                                       hsize_t &m, hsize_t &n, hsize_t &o);

/**
 * @brief Check if the given location (group, link or dataset) exists in the
 * given file
 * @param filename
 * @param location
 * @return
 */
bool locationExists(std::string const& filename, std::string const& location);

bool locationExists(H5::H5File const& file, std::string const& location);

} // namespace hdf5
} // namespace amici

#endif


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef sysop_eb634adc_cca3_4f8f_853c_7852abe06d2d_h
#define sysop_eb634adc_cca3_4f8f_853c_7852abe06d2d_h

#include <gslib/type.h>
#include <gslib/std.h>
#include <ariel/type.h>
#include <ariel/image.h>

__ariel_begin__

enum unimask
{
    declare_mask(um_shift,      0),
    declare_mask(um_sshift,     1),
    declare_mask(um_control,    2),
    declare_mask(um_scontrol,   3),
    declare_mask(um_reserve,    4),
    declare_mask(um_alter,      6),
    declare_mask(um_salter,     7),
    declare_mask(um_lmouse,     8),
    declare_mask(um_rmouse,     9),
};

enum stylemask
{
    declare_mask(sm_hitable,    0),
    declare_mask(sm_visible,    1),
};

/*
 * part1. ascii codes
 * part2. reserved
 * part3. mouse keys
 * part4. joystick keys
 * part5. keyboard controls
 */
enum unikey
{
    uk_null = 0,        /* null */
    uk_soh,             /* start of heading */
    uk_stx,             /* start of text */
    uk_etx,             /* end of text */
    uk_eot,             /* end of transmission */
    uk_enq,             /* enquiry */
    uk_ack,             /* acknowledge */
    uk_bel,             /* bell */
    uk_bs,              /* backspace */
    uk_tab,             /* horizontal tab */
    uk_lf,              /* nl line feed, new line */
    uk_vt,              /* vertical tab */
    uk_ff,              /* np form feed, new page */
    uk_cr,              /* carriage return */
    uk_so,              /* shift out */
    uk_si,              /* shift in */
    uk_dle,             /* data link escape */
    uk_dc1,             /* device control 1 */
    uk_dc2,             /* device control 2 */
    uk_dc3,             /* device control 3 */
    uk_dc4,             /* device control 4 */
    uk_nak,             /* negative acknowledge */
    uk_syn,             /* synchronous idle */
    uk_etb,             /* end of trans. block */
    uk_can,             /* cancel */
    uk_em,              /* end of medium */
    uk_sub,             /* substitute */
    uk_esc,             /* escape */
    uk_fs,              /* file separator */
    uk_gs,              /* group separator */
    uk_rs,              /* record separator */
    uk_us,              /* unit separator */
    uk_sp,              /* space, blank */
                        /* ! - ~ */
    uk_del  = 0x7f,     /* delete */

    mk_left,
    mk_center,
    mk_right,

    vk_insert,          /* insert */
    vk_caps,            /* caps lock */
    vk_pscr,            /* print screen */
    vk_numlock,         /* number lock */
    vk_home,            /* home */
    vk_end,             /* end */
    vk_pageup,          /* page up */
    vk_pagedown,        /* page down */
    vk_left,            /* left arrow */
    vk_up,              /* up arrow */
    vk_right,           /* right arrow */
    vk_down,            /* down arrow */

    vk_shift,
    vk_control,
    vk_alter,

    vk_f1,              /* f1 - f12 */
    vk_f2,
    vk_f3,
    vk_f4,
    vk_f5,
    vk_f6,
    vk_f7,
    vk_f8,
    vk_f9,
    vk_f10,
    vk_f11,
    vk_f12,
};

enum cursor_type
{
    cur_arrow,
    cur_beam,
    cur_cross,
    cur_up_arrow,
    cur_size_nwse,
    cur_size_nesw,
    cur_size_we,
    cur_size_ns,
    cur_size_all,
    cur_hand,
    cur_help,
};

class system_driver;
typedef render_texture2d texture2d;

class __gs_novtable system_notify abstract
{
public:
    virtual void on_show(bool b) = 0;
    virtual void on_create(system_driver* ptr, const rect& rc) = 0;
    virtual void on_close() = 0;
    virtual void on_resize(const rect& rc) = 0;
    virtual void on_paint(const rect& rc) = 0;
    virtual void on_halt() = 0;
    virtual void on_resume() = 0;
    virtual bool on_mouse_down(uint um, unikey uk, const point& pt) = 0;
    virtual bool on_mouse_up(uint um, unikey uk, const point& pt) = 0;
    virtual bool on_mouse_move(uint um, const point& pt) = 0;
    virtual bool on_key_down(uint um, unikey uk) = 0;
    virtual bool on_key_up(uint um, unikey uk) = 0;
    virtual bool on_char(uint um, uint ch) = 0;
    virtual void on_timer(uint tid) = 0;
};

enum clipfmt
{
    cf_text,
    cf_bitmap,
};

class __gs_novtable clipboard_data abstract
{
public:
    virtual ~clipboard_data() {}
    virtual clipfmt get_format() const = 0;
    virtual void* get_ptr() = 0;
    virtual int get_size() const = 0;
    template<class _tdata>
    _tdata* get_data() { return (_tdata*)get_ptr(); }
};

class clipboard_list:
    public list<clipboard_data*>
{
public:
    ~clipboard_list()
    {
        for(auto* p : *this) {
            assert(p);
            delete p;
        }
        clear();
    }
};

struct clipboard_text:
    public clipboard_data, public string
{
    virtual clipfmt get_format() const { return cf_text; }
    virtual void* get_ptr() { return (void*)static_cast<string*>(this); }
    virtual int get_size() const { return string::length(); }
};

struct clipboard_bitmap:
    public clipboard_data, public image
{
    virtual clipfmt get_format() const { return cf_bitmap; }
    virtual void* get_ptr() { return (void*)static_cast<image*>(this); }
    virtual int get_size() const { return -1; }
};

struct system_context
{
    enum
    {
        sct_notify      = 0x01,
        sct_painter     = 0x02,
        sct_rectangle   = 0x04,
        sct_hwnd        = 0x08,
        sct_hinst       = 0x10,
        sct_everything  = 0xffffffff,
    };

    uint                mask;
    system_notify*      notify;
    void*               painter;
    rect                rectangle;
    uint                hwnd;
    uint                hinst;
};

class __gs_novtable system_driver abstract
{
public:
    virtual ~system_driver() {}
    virtual void initialize(const system_context& ctx) = 0;
    virtual void setup() = 0;
    virtual void close() = 0;
    virtual void set_timer(uint tid, int t) = 0;
    virtual void kill_timer(uint tid) = 0;
    virtual void update() = 0;
    virtual void emit(int msgid, void* msg, int size) = 0;
    virtual void set_ime(point pt, const font& ft) = 0;
    virtual void set_cursor(cursor_type curty) = 0;
    //virtual void set_clipboard(const gchar* fmt, const void* ptr, int size) = 0;
    //virtual int get_clipboard(const gchar* fmt, const void*& ptr) = 0;
    //virtual int get_clipboard(clipboard_list& cl, int c) = 0;
};

class __gs_novtable fontsys abstract
{
public:
    virtual ~fontsys() {}
    virtual void initialize() = 0;
    virtual void set_font(const font& f) = 0;
    virtual bool query_size(const gchar* str, int& w, int& h, int len = -1) = 0;
    virtual bool create_text_image(image& img, const gchar* str, int x, int y, const color& cr, int len = -1) = 0;
    virtual bool create_text_texture(texture2d** tex, const gchar* str, int margin, const color& cr, int len = -1) = 0;
    virtual void draw(image& img, const gchar* str, int x, int y, const color& cr, int len = -1) = 0;
};

__ariel_end__

#endif


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include <string>

#include <gsl/gsl>

#include "onnx/onnx_pb.h"

#include "core/graph/basic_types.h"

namespace onnxruntime::utils {

// keep these signatures in sync with DECLARE_MAKE_ATTRIBUTE_FNS below
/** Creates an AttributeProto with the specified name and value. */
ONNX_NAMESPACE::AttributeProto MakeAttribute(std::string attr_name, int64_t value);
/** Creates an AttributeProto with the specified name and values. */
ONNX_NAMESPACE::AttributeProto MakeAttribute(std::string attr_name, gsl::span<const int64_t> values);

#define DECLARE_MAKE_ATTRIBUTE_FNS(type)                                           \
  ONNX_NAMESPACE::AttributeProto MakeAttribute(std::string attr_name, type value); \
  ONNX_NAMESPACE::AttributeProto MakeAttribute(std::string attr_name, gsl::span<const type> values)

DECLARE_MAKE_ATTRIBUTE_FNS(float);
DECLARE_MAKE_ATTRIBUTE_FNS(std::string);
DECLARE_MAKE_ATTRIBUTE_FNS(ONNX_NAMESPACE::TensorProto);
#if !defined(DISABLE_SPARSE_TENSORS)
DECLARE_MAKE_ATTRIBUTE_FNS(ONNX_NAMESPACE::SparseTensorProto);
#endif
DECLARE_MAKE_ATTRIBUTE_FNS(ONNX_NAMESPACE::TypeProto);
DECLARE_MAKE_ATTRIBUTE_FNS(ONNX_NAMESPACE::GraphProto);

#undef DECLARE_MAKE_ATTRIBUTE_FNS

// The below overload is made so the compiler does not attempt to resolve
// string literals with the gsl::span overload
inline ONNX_NAMESPACE::AttributeProto MakeAttribute(std::string attr_name, const char* value) {
  return MakeAttribute(std::move(attr_name), std::string{value});
}

/**
 * Sets an attribute in `node_attributes` with key `attribute.name()` and value `attribute`.
 * If an attribute with the same name exists, it will be overwritten.
 * @return Pair of (iterator to attribute, whether attribute was added (true) or updated (false)).
 */
std::pair<NodeAttributes::iterator, bool> SetNodeAttribute(ONNX_NAMESPACE::AttributeProto attribute,
                                                           NodeAttributes& node_attributes);

}  // namespace onnxruntime::utils


#include "cconfigspace_internal.h"
#include <stdlib.h>
#include <gsl/gsl_rng.h>

const ccs_datum_t ccs_none = CCS_NONE_VAL;
const ccs_datum_t ccs_inactive = CCS_INACTIVE_VAL;
const ccs_datum_t ccs_true = CCS_TRUE_VAL;
const ccs_datum_t ccs_false = CCS_FALSE_VAL;
const ccs_version_t ccs_version = { 0, 1, 0, 0 };

ccs_result_t
ccs_init() {
	gsl_rng_env_setup();
	return CCS_SUCCESS;
}

ccs_result_t
ccs_fini() {
	return CCS_SUCCESS;
}

ccs_version_t
ccs_get_version() {
	return ccs_version;
}

ccs_result_t
ccs_retain_object(ccs_object_t object) {
	_ccs_object_internal_t *obj = (_ccs_object_internal_t *)object;
        if (!obj || obj->refcount <= 0)
		return -CCS_INVALID_OBJECT;
	obj->refcount += 1;
	return CCS_SUCCESS;
}

ccs_result_t
ccs_release_object(ccs_object_t object) {
	_ccs_object_internal_t *obj = (_ccs_object_internal_t *)object;
	if (!obj || obj->refcount <= 0)
		return -CCS_INVALID_OBJECT;
	obj->refcount -= 1;
	if (obj->refcount == 0) {
		if (obj->callbacks) {
			_ccs_object_callback_t *cb = NULL;
			while ( (cb = (_ccs_object_callback_t *)
			              utarray_prev(obj->callbacks, cb)) ) {
				cb->callback(object, cb->user_data);
			}
			utarray_free(obj->callbacks);
		}
		CCS_VALIDATE(obj->ops->del(object));
		free(object);
	}
	return CCS_SUCCESS;
}

ccs_result_t
ccs_object_get_type(ccs_object_t       object,
                     ccs_object_type_t *type_ret) {
	_ccs_object_internal_t *obj = (_ccs_object_internal_t *)object;
	if (!obj)
		return -CCS_INVALID_OBJECT;
	CCS_CHECK_PTR(type_ret);
	*type_ret = obj->type;
	return CCS_SUCCESS;
}

ccs_result_t
ccs_object_get_refcount(ccs_object_t  object,
                         int32_t      *refcount_ret) {
	_ccs_object_internal_t *obj = (_ccs_object_internal_t *)object;
	if (!obj)
		return -CCS_INVALID_OBJECT;
	CCS_CHECK_PTR(refcount_ret);
	*refcount_ret = obj->refcount;
	return CCS_SUCCESS;
}

static const UT_icd _object_callback_icd = {
	sizeof(_ccs_object_callback_t),
	NULL,
	NULL,
	NULL
};

ccs_result_t
ccs_object_set_destroy_callback(ccs_object_t  object,
                                void (*callback)(
                                  ccs_object_t object,
                                  void *user_data),
                                void *user_data) {
	_ccs_object_internal_t *obj = (_ccs_object_internal_t *)object;
	if (!obj)
		return -CCS_INVALID_OBJECT;
	if (!callback)
		return -CCS_INVALID_VALUE;
	if (!obj->callbacks)
		utarray_new(obj->callbacks, &_object_callback_icd);

	_ccs_object_callback_t cb = { callback, user_data };
	utarray_push_back(obj->callbacks, &cb);
	return CCS_SUCCESS;
}


#pragma once
/*
 * (C) Copyright 2020-2021 UCAR
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 */
/*! \addtogroup ioda_cxx_attribute
 *
 * @{
 * \file Has_Attributes.h
 * \brief Interfaces for ioda::Has_Attributes and related classes.
 */

#include <gsl/gsl-lite.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "ioda/Attributes/Attribute.h"
#include "ioda/Exception.h"
#include "ioda/Misc/Dimensions.h"
#include "ioda/Misc/Eigen_Compat.h"
#include "ioda/Types/Type.h"
#include "ioda/defs.h"

namespace ioda {
class Has_Attributes;
namespace detail {
class Has_Attributes_Backend;
class Has_Attributes_Base;
class Variable_Backend;
class Group_Backend;

/// \brief Describes the functions that can add attributes
/// \ingroup ioda_cxx_attribute
/// \details Using the (Curiously Recurring Template
/// Pattern)[https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern] to implement this
/// since we want to use the same functions for placeholder attribute construction.
/// \see Has_Attributes
/// \see Attribute_Creator
template <class DerivedHasAtts>
class CanAddAttributes {
public:
  /// @name Convenience functions for adding attributes
  /// @{

  /// \brief Create and write an Attribute, for arbitrary dimensions.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be created.
  /// \param data is a gsl::span (a pointer-length pair) that contains the data to be written.
  /// \param dimensions is an initializer list representing the size of the metadata. Each element
  /// is a dimension with a certain size.
  /// \returns Another instance of this Has_Attribute. Used for operation chaining.
  /// \throws jedi::xError if data.size() does not match the number of total elements
  ///   described by dimensions.
  /// \see gsl::span for details of how to make a span.
  template <class DataType>
  DerivedHasAtts add(const std::string& attrname, ::gsl::span<const DataType> data,
                     const ::std::vector<Dimensions_t>& dimensions) {
    auto derivedThis = static_cast<DerivedHasAtts*>(this);
    auto att         = derivedThis->template create<DataType>(attrname, dimensions);
    att.template write<DataType>(data);
    return *derivedThis;
  }

  /// \brief Create and write an Attribute, for arbitrary dimensions.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be created.
  /// \param data is an initializer list that contains the data to be written.
  /// \param dimensions is an initializer list representing the size of the metadata. Each
  ///   element is a dimension with a certain size.
  /// \returns Another instance of this Has_Attribute. Used for operation chaining.
  /// \throws jedi::xError if data.size() does not match the number of total
  ///   elements described by dimensions.
  template <class DataType>
  DerivedHasAtts add(const std::string& attrname, ::std::initializer_list<DataType> data,
                     const ::std::vector<Dimensions_t>& dimensions) {
    auto derivedThis = static_cast<DerivedHasAtts*>(this);
    auto att         = derivedThis->template create<DataType>(attrname, dimensions);
    att.template write<DataType>(data);
    return *derivedThis;
  }

  /// \brief Create and write an Attribute, for a single-dimensional span of 1-D data.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be created.
  /// \param data is a gsl::span (a pointer-length pair) that contains the data to be written.
  ///   The new Attribute will be one-dimensional and the length of the overall span.
  /// \returns Another instance of this Has_Attribute. Used for operation chaining.
  /// \see gsl::span for details of how to make a span.
  /// \see gsl::make_span
  template <class DataType>
  DerivedHasAtts add(const std::string& attrname, ::gsl::span<const DataType> data) {
    return add(attrname, data, {gsl::narrow<Dimensions_t>(data.size())});
  }

  /// \brief Create and write an Attribute, for a 1-D initializer list.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be created.
  /// \param data is an initializer list that contains the data to be written.
  ///   The new Attribute will be one-dimensional and the length of the overall span.
  /// \returns Another instance of this Has_Attribute. Used for operation chaining.
  template <class DataType>
  DerivedHasAtts add(const std::string& attrname, ::std::initializer_list<DataType> data) {
    return add(attrname, data, {gsl::narrow<Dimensions_t>(data.size())});
  }

  /// \brief Create and write a single datum of an Attribute.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be created.
  /// \param data is the data to be written. The new Attribute will be zero-dimensional and will
  /// contain only this datum.
  /// \note Even 0-dimensional data have a type, which may be a compound
  /// array (i.e. a single string of variable length).
  /// \returns Another instance of this Has_Attribute. Used for operation chaining.
  template <class DataType>
  DerivedHasAtts add(const std::string& attrname, const DataType& data) {
    return add(attrname, gsl::make_span(&data, 1), {1});
  }

  template <class EigenClass>
  DerivedHasAtts addWithEigenRegular(const std::string& attrname, const EigenClass& data,
                                     bool is2D = true) {
#if 1  //__has_include("Eigen/Dense")
    typedef typename EigenClass::Scalar ScalarType;
    // If d is already in Row Major form, then this is optimized out.
    Eigen::Array<ScalarType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> dout;
    dout.resize(data.rows(), data.cols());
    dout               = data;
    const auto& dconst = dout;  // To make some compilers happy.
    auto sp            = gsl::make_span(dconst.data(), static_cast<int>(data.rows() * data.cols()));

    if (is2D)
      return add(attrname, sp,
                 {gsl::narrow<Dimensions_t>(data.rows()), gsl::narrow<Dimensions_t>(data.cols())});
    else
      return add(attrname, sp);
#else
    static_assert(false, "The Eigen headers cannot be found, so this function cannot be used.");
#endif
  }

  template <class EigenClass>
  DerivedHasAtts addWithEigenTensor(const std::string& attrname, const EigenClass& data) {
#if 1  //__has_include("unsupported/Eigen/CXX11/Tensor")
    typedef typename EigenClass::Scalar ScalarType;
    ioda::Dimensions dims = detail::EigenCompat::getTensorDimensions(data);

    auto derived_this = static_cast<DerivedHasAtts*>(this);
    Attribute att     = derived_this->template create<ScalarType>(attrname, dims.dimsCur);

    att.writeWithEigenTensor(data);
    return *derived_this;
#else
    static_assert(
      false, "The Eigen unsupported/ headers cannot be found, so this function cannot be used.");
#endif
  }

  /// @}
};

/// \brief Describes the functions that can read attributes
/// \ingroup ioda_cxx_attribute
/// \details Uses the (CRTP)[https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern]
///   to implement this as we use the same functions for placeholder attribute construction.
/// \see Has_Attributes
template <class DerivedHasAtts>
class CanReadAttributes {
protected:
  CanReadAttributes() {}

public:
  virtual ~CanReadAttributes() {}

  /// @name Convenience functions for reading attributes
  /// @{

  /// \brief Open and read an Attribute, for expected dimensions.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be read.
  /// \param data is a pointer-size pair to the data buffer that is filled with the metadata's
  ///   contents. It should be pre-sized to accomodate all of the matadata. See
  ///   getDimensions().numElements. Data will be filled in row-major order.
  /// \throws jedi::xError on a size mismatch between Attribute dimensions and data.size().
  template <class DataType>
  const DerivedHasAtts read(const std::string& attrname, gsl::span<DataType> data) const {
    // Attribute att = this->open(attrname);
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.read(data);
    return *(static_cast<const DerivedHasAtts*>(this));
  }

  /// \brief Open and read an Attribute, with unknown dimensions.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be read.
  /// \param data is a vector acting as a data buffer that is filled with the metadata's
  ///   contents. It gets resized as needed. data will be filled in row-major order.
  template <class DataType>
  const DerivedHasAtts read(const std::string& attrname, std::vector<DataType>& data) const {
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.read(data);
    return *(static_cast<const DerivedHasAtts*>(this));
  }

  /// \brief Open and read an Attribute, with unknown dimensions.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be read.
  /// \param data is a valarray acting as a data buffer that is filled with the metadata's
  ///   contents. It gets resized as needed. data will be filled in row-major order.
  template <class DataType>
  const DerivedHasAtts read(const std::string& attrname, std::valarray<DataType>& data) const {
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.read(data);
    return *(static_cast<const DerivedHasAtts*>(this));
  }

  /// \brief Read a datum of an Attribute.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be read.
  /// \param data is a datum of type DataType.
  /// \throws jedi::xError if the underlying data have size > 1.
  template <class DataType>
  const DerivedHasAtts read(const std::string& attrname, DataType& data) const {
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.read<DataType>(data);
    return *(static_cast<const DerivedHasAtts*>(this));
  }

  /// \brief Read a datum of an Attribute.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute to be read.
  /// \returns A datum of type DataType.
  /// \throws jedi::xError if the underlying data have size > 1.
  template <class DataType>
  DataType read(const std::string& attrname) const {
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    return att.read<DataType>();
  }

  template <class EigenClass, bool Resize = detail::EigenCompat::CanResize<EigenClass>::value>
  DerivedHasAtts readWithEigenRegular(const std::string& attrname, EigenClass& data) {
#if 1  // __has_include("Eigen/Dense")
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.readWithEigenRegular<EigenClass, Resize>(data);
    return *(static_cast<const DerivedHasAtts*>(this));
#else
    static_assert(false, "The Eigen headers cannot be found, so this function cannot be used.")
#endif
  }

  template <class EigenClass>
  DerivedHasAtts readWithEigenTensor(const std::string& attrname, EigenClass& data) {
#if 1  //__has_include("unsupported/Eigen/CXX11/Tensor")
    Attribute att = static_cast<const DerivedHasAtts*>(this)->open(attrname);
    att.readWithEigenTensor<EigenClass>(data);
    return *(static_cast<const DerivedHasAtts*>(this));
#else
    static_assert(
      false, "The Eigen unsupported/ headers cannot be found, so this function cannot be used.");
#endif
  }

  /// @}
};

/// \ingroup ioda_cxx_attribute
class IODA_DL Has_Attributes_Base {
private:
  /// Using an opaque object to implement the backend.
  std::shared_ptr<Has_Attributes_Backend> backend_;

  // Backend classes do occasionally need access to the Attribute_Backend object.
  friend class Group_Backend;
  friend class Variable_Backend;

protected:
  Has_Attributes_Base(std::shared_ptr<Has_Attributes_Backend>);

public:
  virtual ~Has_Attributes_Base();

  /// Query the backend and get the type provider.
  virtual detail::Type_Provider* getTypeProvider() const;

  /// @name General Functions
  /// @{
  ///

  /// List all attributes
  /// \returns an unordered vector of attribute names for an object.
  virtual std::vector<std::string> list() const;
  /// List all attributes
  /// \returns an unordered vector of attribute names for an object.
  inline std::vector<std::string> operator()() const { return list(); }

  /// \brief Does an Attribute with the specified name exist?
  /// \param attname is the name of the Attribute that we are looking for.
  /// \returns true if it exists.
  /// \returns false otherwise.
  virtual bool exists(const std::string& attname) const;
  /// \brief Delete an Attribute with the specified name.
  /// \param attname is the name of the Attribute that we are deleting.
  /// \throws jedi::xError if no such attribute exists.
  virtual void remove(const std::string& attname);
  /// \brief Open an Attribute by name
  /// \param name is the name of the Attribute to be opened.
  /// \returns An instance of an Attribute that can be queried (with getDimensions()) and read.
  virtual Attribute open(const std::string& name) const;
  /// \brief Open Attribute by name
  /// \param name is the name of the Attribute to be opened.
  /// \returns An instance of Attribute that can be queried (with getDimensions()) and read.
  inline Attribute operator[](const std::string& name) const { return open(name); }

  /// \brief Open all attributes in an object.
  /// \details This is a collective call, optimized for performance.
  /// \return A sequence of (name, Attribute) pairs.
  virtual std::vector<std::pair<std::string, Attribute>> openAll() const;

  /// \brief Create an Attribute without setting its data.
  /// \param attrname is the name of the Attribute.
  /// \param dimensions is a vector representing the size of the metadata.
  ///   Each element of the vector is a dimension with a certain size.
  /// \param in_memory_datatype is the runtime description of the Attribute's data type.
  /// \returns An instance of Attribute that can be written to.
  virtual Attribute create(const std::string& attrname, const Type& in_memory_dataType,
                           const std::vector<Dimensions_t>& dimensions = {1});

  /// Python compatability function
  inline Attribute _create_py(const std::string& attrname, BasicTypes dataType,
                              const std::vector<Dimensions_t>& dimensions = {1}) {
    return create(attrname, ::ioda::Type(dataType, getTypeProvider()), dimensions);
  }

  /// \brief Create an Attribute without setting its data.
  /// \tparam DataType is the type of the data. I.e. float, int, uint16_t, std::string, etc.
  /// \param attrname is the name of the Attribute.
  /// \param dimensions is a vector representing the size of the metadata. Each element of the
  ///   vector is a dimension with a certain size.
  /// \returns An instance of Attribute that can be written to.
  template <class DataType, class TypeWrapper = Types::GetType_Wrapper<DataType>>
  Attribute create(const std::string& attrname,
                   const std::vector<Dimensions_t>& dimensions = {1}) {
    try {
      Type in_memory_dataType = TypeWrapper::GetType(getTypeProvider());
      auto att                = create(attrname, in_memory_dataType, dimensions);
      return att;
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
  }

  /// \brief Rename an Attribute
  /// \param oldName is the name of the Attribute to be changed.
  /// \param newName is the new name of the Attribute.
  /// \throws jedi::xError if oldName is not found.
  /// \throws jedi::xError if newName already exists.
  virtual void rename(const std::string& oldName, const std::string& newName);

  /// @}
};

class IODA_DL Has_Attributes_Backend : public Has_Attributes_Base {
protected:
  Has_Attributes_Backend();

public:
  virtual ~Has_Attributes_Backend();

  /// \brief Default implementation of Has_Attributes_Base::openAll.
  /// \see Has_Attributes_Base::openAll.
  std::vector<std::pair<std::string, Attribute>> openAll() const override;
};
}  // namespace detail

/** \brief This class exists inside of ioda::Group or ioda::Variable and provides
 *         the interface to manipulating Attributes.
 * \ingroup ioda_cxx_attribute
 *
 * \note It should only be constructed inside of a Group or Variable.
 *       It has no meaning elsewhere.
 * \see Attribute for the class that represents individual attributes.
 * \throws jedi::xError on all exceptions.
 **/
class IODA_DL Has_Attributes : public detail::CanAddAttributes<Has_Attributes>,
                               public detail::CanReadAttributes<Has_Attributes>,
                               public detail::Has_Attributes_Base {
public:
  Has_Attributes();
  Has_Attributes(std::shared_ptr<detail::Has_Attributes_Backend>);
  virtual ~Has_Attributes();
};

}  // namespace ioda

/// @} // End Doxygen block