From 29d0c5f754c27cb54c5c1d7970a870c71d8b9d85 Mon Sep 17 00:00:00 2001
From: Carl Philipp Klemm <philipp@uvos.xyz>
Date: Thu, 27 Jun 2024 15:16:04 +0200
Subject: [PATCH] Add header description field to EisSpectra, split EisSpectra
 into a seperate translation unit

---
 CMakeLists.txt         |   1 +
 eisgenerator/eistype.h | 190 +---------------------------
 eisgenerator/spectra.h | 206 +++++++++++++++++++++++++++++++
 eistype.cpp            | 247 -------------------------------------
 spectra.cpp            | 274 +++++++++++++++++++++++++++++++++++++++++
 strops.cpp             |   5 +
 strops.h               |   5 +
 test.cpp               |   2 +-
 8 files changed, 493 insertions(+), 437 deletions(-)
 create mode 100644 eisgenerator/spectra.h
 create mode 100644 spectra.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index fa71b8f..3c043c7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ set(SRC_FILES
 	normalize.cpp
 	basicmath.cpp
 	eistype.cpp
+	spectra.cpp
 	strops.cpp
 	translators.cpp
 	randomgen.cpp
diff --git a/eisgenerator/eistype.h b/eisgenerator/eistype.h
index c71f87d..8670153 100644
--- a/eisgenerator/eistype.h
+++ b/eisgenerator/eistype.h
@@ -256,192 +256,6 @@ public:
 	}
 };
 
-class EisSpectra
-{
-public:
-	static constexpr int F_VERSION_MAJOR = 1;
-	static constexpr int F_VERSION_MINOR = 0;
-	static constexpr int F_VERSION_PATCH = 0;
-	static constexpr char F_MAGIC[] = "EISF";
-
-public:
-	std::vector<DataPoint> data;
-	std::string model;
-	std::string header;
-	std::vector<double> labels;
-	std::vector<std::string> labelNames;
-
-public:
-	/**
-	* @brief Constructs an EisSpectra.
-	*
-	* @param data Vector of the data points of the spectra.
-	* @param model Model description string for this spectra.
-	* @param header A free-form text that will be included in any save of this spectra.
-	* @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
-	* @param labelNames An optional vector of names describing every input of the model.
-	*/
-	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
-			   std::vector<double> labels = std::vector<double>(),
-			   std::vector<std::string> labelNames = std::vector<std::string>());
-
-	/**
-	* @brief Constructs an EisSpectra.
-	*
-	* This function differs from the above only in the datatype of the label.
-	*
-	* @param data Vector of the data points of the spectra.
-	* @param model Model description string for this spectra.
-	* @param header A free-form text that will be included in any save of this spectra.
-	* @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
-	* @param labelNames An optional vector of names describing every input of the model.
-	*/
-	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
-			   std::vector<float> labels, std::vector<std::string> labelNames = std::vector<std::string>());
-
-	/**
-	* @brief Constructs an EisSpectra.
-	*
-	* This function differs from the above only in the datatype of the label.
-	*
-	* @param data Vector of the data points of the spectra.
-	* @param model Model description string for this spectra.
-	* @param header A free-from text that will be included in any save of this spectra.
-	* @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
-	* @param labelNames An optional vector of names describing every input of the model.
-	*/
-	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
-			   std::vector<size_t> labels, std::vector<std::string> labelNames = std::vector<std::string>());
-
-	/**
-	* @brief Constructs an EisSpectra.
-	*
-	* This function differs from the above only in the datatype of the label.
-	*
-	* @param data Vector of the data points of the spectra.
-	* @param model Model description string for this spectra.
-	* @param header A free-form text that will be included in any save of this spectra.
-	* @param label A value corresponding to all inputs of the model.
-	* @param maxLabel The number of inputs of the model.
-	*/
-	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
-			   size_t label, size_t maxLabel, std::vector<std::string> labelNames = std::vector<std::string>());
-
-	/**
-	* @brief Constructs a EisSpectra by loading an EIS file from disk.
-	*
-	* @throw eis::file_error if there is an error loading the file
-	* @param path The path to the file.
-	*/
-	EisSpectra(const std::filesystem::path& path){*this = loadFromDisk(path);}
-
-	EisSpectra(){}
-
-	/**
-	* @brief Constructs a EisSpectra by loading a EIS file from disk.
-	*
-	* This function has the attribute [[nodiscard]]
-	*
-	* @throw eis::file_error if there is an error loading the file
-	* @param path The path to the file.
-	* @return The EisSpectra parsed from the file.
-	*/
-	[[nodiscard]] static EisSpectra loadFromDisk(const std::filesystem::path& path);
-
-	/**
-	* @brief Constructs a EisSpectra by loading a EIS file from a stream.
-	*
-	* This function has the attribute [[nodiscard]]
-	*
-	* @throw eis::file_error if there is an error loading the file
-	* @param stream The stream that contains the EIS file.
-	* @return The EisSpectra parsed from the stream.
-	*/
-	[[nodiscard]] static EisSpectra loadFromStream(std::istream& stream);
-
-	/**
-	* @brief Sets all input values up to a maximum given by maxLabel to the value given by label.
-	*
-	* @param label The value to apply to all inputs.
-	* @param maxLabel The maximum number of values to set.
-	*/
-	void setLabel(size_t label, size_t maxLabel);
-
-	/**
-	* @brief Gets the input value of this model, where it is a single value.
-	*
-	* @return The input value,
-	*/
-	size_t getLabel();
-
-	/**
-	* @brief Sets the input values of this model.
-	*
-	* @param label The input values.
-	*/
-	void setSzLabels(std::vector<size_t> label);
-
-	/**
-	* @brief Sets the input values of this model.
-	*
-	* @param label The input values.
-	*/
-	void setLabels(const std::vector<double>& labelsIn);
-
-	/**
-	* @brief Sets the input values of this model.
-	*
-	* @param label The input values.
-	*/
-	void setLabels(const std::vector<float>& labelsIn);
-
-	/**
-	* @brief Sets the input values of this model.
-	*
-	* @param label The input values.
-	*/
-	std::vector<size_t> getSzLabels() const;
-
-	/**
-	* @brief Returns true if there are multiple inputs, false otherwise.
-	*
-	* @return true if there are multiple inputs, false otherwise.
-	*/
-	bool isMulticlass();
-
-	/**
-	* @brief Returns the inputs as a vector.
-	*
-	* @return The inputs as a vector.
-	*/
-	std::vector<fvalue> getFvalueLabels();
-
-	/**
-	* @brief Saves the spectra to disk.
-	*
-	* @param path A path to the file on disk where the spectra shall be saved.
-	* @return true on success, false on failure.
-	*/
-	bool saveToDisk(const std::filesystem::path& path) const;
-
-	/**
-	 * @brief Saves the spectra in the given stream.
-	 *
-	 * @param stream A std::ostream into which the spectra will be saved.
-	 */
-	void saveToStream(std::ostream& stream) const;
-};
-
-/**
- * @brief Deprecated function use eis::EisSpectra::saveToDisk instead.
- */
-[[deprecated]] bool saveToDisk(const EisSpectra& data, const std::filesystem::path& path);
-
-/**
- * @brief Deprecated function use eis::EisSpectra::loadFromDisk instead.
- */
-[[deprecated]] [[nodiscard]] EisSpectra loadFromDisk(const std::filesystem::path& path);
-
 /**
 * @brief Returns the a vector of DataPoints as a pair of valarrays.
 *
@@ -468,6 +282,4 @@ std::ostream &operator<<(std::ostream &s, eis::DataPoint const& dp);
 
 std::ostream &operator<<(std::ostream &s, eis::Range const& range);
 
-std::ostream &operator<<(std::ostream &s, eis::EisSpectra const& spectra);
-
-
+#include "spectra.h"
diff --git a/eisgenerator/spectra.h b/eisgenerator/spectra.h
new file mode 100644
index 0000000..2d7a430
--- /dev/null
+++ b/eisgenerator/spectra.h
@@ -0,0 +1,206 @@
+#pragma once
+/**
+ * @addtogroup TYPES
+ * @{
+ */
+
+#include <string>
+#include <vector>
+
+#include "eistype.h"
+
+namespace eis
+{
+
+class EisSpectra
+{
+public:
+	static constexpr int F_VERSION_MAJOR = 1;
+	static constexpr int F_VERSION_MINOR = 1;
+	static constexpr int F_VERSION_PATCH = 0;
+	static constexpr char F_MAGIC[] = "EISF";
+
+public:
+	std::vector<DataPoint> data;
+	std::string model;
+	std::string headerDescription;
+	std::string header;
+	std::vector<double> labels;
+	std::vector<std::string> labelNames;
+
+public:
+	/**
+	 * @brief Constructs an EisSpectra.
+	 *
+	 * @param data Vector of the data points of the spectra.
+	 * @param model Model description string for this spectra.
+	 * @param header A free-form text that will be included in any save of this spectra.
+	 * @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
+	 * @param labelNames An optional vector of names describing every input of the model.
+	 */
+	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
+			   std::vector<double> labels = std::vector<double>(),
+			   std::vector<std::string> labelNames = std::vector<std::string>());
+
+	/**
+	 * @brief Constructs an EisSpectra.
+	 *
+	 * This function differs from the above only in the datatype of the label.
+	 *
+	 * @param data Vector of the data points of the spectra.
+	 * @param model Model description string for this spectra.
+	 * @param header A free-form text that will be included in any save of this spectra.
+	 * @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
+	 * @param labelNames An optional vector of names describing every input of the model.
+	 */
+	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
+			   std::vector<float> labels, std::vector<std::string> labelNames = std::vector<std::string>());
+
+	/**
+	 * @brief Constructs an EisSpectra.
+	 *
+	 * This function differs from the above only in the datatype of the label.
+	 *
+	 * @param data Vector of the data points of the spectra.
+	 * @param model Model description string for this spectra.
+	 * @param header A free-from text that will be included in any save of this spectra.
+	 * @param labels An optional vector of giving the values of the inputs of the model to get this spectra.
+	 * @param labelNames An optional vector of names describing every input of the model.
+	 */
+	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
+			   std::vector<size_t> labels, std::vector<std::string> labelNames = std::vector<std::string>());
+
+	/**
+	 * @brief Constructs an EisSpectra.
+	 *
+	 * This function differs from the above only in the datatype of the label.
+	 *
+	 * @param data Vector of the data points of the spectra.
+	 * @param model Model description string for this spectra.
+	 * @param header A free-form text that will be included in any save of this spectra.
+	 * @param label A value corresponding to all inputs of the model.
+	 * @param maxLabel The number of inputs of the model.
+	 */
+	EisSpectra(const std::vector<DataPoint>& data, const std::string& model, const std::string& header,
+			   size_t label, size_t maxLabel, std::vector<std::string> labelNames = std::vector<std::string>());
+
+	/**
+	 * @brief Constructs a EisSpectra by loading an EIS file from disk.
+	 *
+	 * @throw eis::file_error if there is an error loading the file
+	 * @param path The path to the file.
+	 */
+	EisSpectra(const std::filesystem::path& path){*this = loadFromDisk(path);}
+
+	EisSpectra(){}
+
+	/**
+	 * @brief Constructs a EisSpectra by loading a EIS file from disk.
+	 *
+	 * This function has the attribute [[nodiscard]]
+	 *
+	 * @throw eis::file_error if there is an error loading the file
+	 * @param path The path to the file.
+	 * @return The EisSpectra parsed from the file.
+	 */
+	[[nodiscard]] static EisSpectra loadFromDisk(const std::filesystem::path& path);
+
+	/**
+	 * @brief Constructs a EisSpectra by loading a EIS file from a stream.
+	 *
+	 * This function has the attribute [[nodiscard]]
+	 *
+	 * @throw eis::file_error if there is an error loading the file
+	 * @param stream The stream that contains the EIS file.
+	 * @return The EisSpectra parsed from the stream.
+	 */
+	[[nodiscard]] static EisSpectra loadFromStream(std::istream& stream);
+
+	/**
+	 * @brief Sets all input values up to a maximum given by maxLabel to the value given by label.
+	 *
+	 * @param label The value to apply to all inputs.
+	 * @param maxLabel The maximum number of values to set.
+	 */
+	void setLabel(size_t label, size_t maxLabel);
+
+	/**
+	 * @brief Gets the input value of this model, where it is a single value.
+	 *
+	 * @return The input value,
+	 */
+	size_t getLabel();
+
+	/**
+	 * @brief Sets the input values of this model.
+	 *
+	 * @param label The input values.
+	 */
+	void setSzLabels(std::vector<size_t> label);
+
+	/**
+	 * @brief Sets the input values of this model.
+	 *
+	 * @param label The input values.
+	 */
+	void setLabels(const std::vector<double>& labelsIn);
+
+	/**
+	 * @brief Sets the input values of this model.
+	 *
+	 * @param label The input values.
+	 */
+	void setLabels(const std::vector<float>& labelsIn);
+
+	/**
+	 * @brief Sets the input values of this model.
+	 *
+	 * @param label The input values.
+	 */
+	std::vector<size_t> getSzLabels() const;
+
+	/**
+	 * @brief Returns true if there are multiple inputs, false otherwise.
+	 *
+	 * @return true if there are multiple inputs, false otherwise.
+	 */
+	bool isMulticlass();
+
+	/**
+	 * @brief Returns the inputs as a vector.
+	 *
+	 * @return The inputs as a vector.
+	 */
+	std::vector<fvalue> getFvalueLabels();
+
+	/**
+	 * @brief Saves the spectra to disk.
+	 *
+	 * @param path A path to the file on disk where the spectra shall be saved.
+	 * @return true on success, false on failure.
+	 */
+	bool saveToDisk(const std::filesystem::path& path) const;
+
+	/**
+	 * @brief Saves the spectra in the given stream.
+	 *
+	 * @param stream A std::ostream into which the spectra will be saved.
+	 */
+	void saveToStream(std::ostream& stream) const;
+};
+
+/**
+ * @brief Deprecated function use eis::EisSpectra::saveToDisk instead.
+ */
+[[deprecated]] bool saveToDisk(const EisSpectra& data, const std::filesystem::path& path);
+
+/**
+ * @brief Deprecated function use eis::EisSpectra::loadFromDisk instead.
+ */
+[[deprecated]] [[nodiscard]] EisSpectra loadFromDisk(const std::filesystem::path& path);
+
+}
+
+std::ostream &operator<<(std::ostream &s, eis::EisSpectra const& spectra);
+
+/** @} */
diff --git a/eistype.cpp b/eistype.cpp
index 6061d79..638821c 100644
--- a/eistype.cpp
+++ b/eistype.cpp
@@ -20,14 +20,12 @@
 //
 
 #include "eistype.h"
-#include <fstream>
 #include <sstream>
 #include <string>
 #include <vector>
 
 #include "strops.h"
 #include "log.h"
-#include "basicmath.h"
 
 using namespace eis;
 
@@ -155,245 +153,6 @@ std::ostream &operator<<(std::ostream &s, Range const& range)
 	return s;
 }
 
-EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn,
-	const std::string& headerIn, std::vector<double> labelsIn, std::vector<std::string> labelNamesIn):
-data(dataIn), model(modelIn), header(headerIn), labels(labelsIn), labelNames(labelNamesIn)
-{
-
-}
-
-EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn,
-	const std::string& headerIn, std::vector<float> labelsIn, std::vector<std::string> labelNamesIn):
-data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
-{
-	setLabels(labelsIn);
-}
-
-EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn, const std::string& headerIn,
-	std::vector<size_t> labelsIn, std::vector<std::string> labelNamesIn):
-data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
-{
-	setSzLabels(labelsIn);
-}
-
-EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn, const std::string& headerIn,
-			   size_t label, size_t maxLabel, std::vector<std::string> labelNamesIn):
-data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
-{
-	setLabel(label, maxLabel);
-}
-
-void EisSpectra::setLabel(size_t label, size_t maxLabel)
-{
-	labels.assign(maxLabel, 0);
-	labels[label] = 1;
-}
-
-void EisSpectra::setSzLabels(std::vector<size_t> labelsIn)
-{
-	labels.assign(labelsIn.size(), 0);
-	for(size_t i = 0; i < labelsIn.size(); ++i)
-		labels[i] = static_cast<double>(labelsIn[i]);
-}
-
-std::vector<size_t> EisSpectra::getSzLabels() const
-{
-	std::vector<size_t> out(labels.size());
-	for(size_t i = 0; i < labels.size(); ++i)
-		out[i] = static_cast<size_t>(labels[i]);
-	return out;
-}
-
-size_t EisSpectra::getLabel()
-{
-	for(size_t i = 0; i < labels.size(); ++i)
-		if(labels[i] != 0)
-			return i;
-	return 0;
-}
-
-bool EisSpectra::isMulticlass()
-{
-	bool foundFirst = false;
-	for(size_t i = 0; i < labels.size(); ++i)
-	{
-		if(labels[i] != 0)
-		{
-			if(foundFirst)
-				return true;
-			else
-				foundFirst = true;
-		}
-	}
-	return false;
-}
-
-void EisSpectra::setLabels(const std::vector<float>& labelsIn)
-{
-	labels.assign(labelsIn.size(), 0);
-	for(size_t i = 0; i < labels.size(); ++i)
-		labels[i] = labelsIn[i];
-}
-
-void EisSpectra::setLabels(const std::vector<double>& labelsIn)
-{
-	labels = labelsIn;
-}
-
-std::vector<fvalue> EisSpectra::getFvalueLabels()
-{
-	if constexpr(std::is_same<fvalue, double>::value)
-	{
-		#pragma GCC diagnostic push
-		#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-		return *reinterpret_cast<std::vector<fvalue>*>(&labels);
-		#pragma GCC diagnostic pop
-	}
-	else
-	{
-		std::vector<fvalue> out(labels.size());
-		for(size_t i = 0; i < labels.size(); ++i)
-			out[i] = static_cast<fvalue>(labels[i]);
-		return out;
-	}
-}
-
-void EisSpectra::saveToStream(std::ostream& stream) const
-{
-	stream<<std::scientific;
-	stream<<F_MAGIC<<", "<<std::to_string(F_VERSION_MAJOR)<<'.'
-		<<std::to_string(F_VERSION_MINOR)<<'.'<<std::to_string(F_VERSION_PATCH)<<'\n';
-
-	stream<<'"'<<model<<'"'<<(!header.empty() ? ", " : "");
-	stream<<header;
-
-	if(!labels.empty())
-	{
-		if(!labelNames.empty())
-		{
-			stream<<"\nlabelsNames\n";
-			std::string labelLine;
-			for(const std::string& name : labelNames)
-				labelLine += "\"" + name + "\", ";
-			labelLine.pop_back();
-			labelLine.pop_back();
-			stream<<labelLine;
-		}
-		stream<<"\nlabels\n";
-
-		std::stringstream labelSs;
-		for(double label : labels)
-			labelSs<<label<<", ";
-		std::string labelLine = labelSs.str();
-		labelLine.pop_back();
-		labelLine.pop_back();
-		stream<<labelLine;
-	}
-
-	stream<<"\nomega, real, im\n";
-
-	for(const eis::DataPoint& point : data)
-		stream<<point.omega<<", "<<point.im.real()<<", "<<point.im.imag()<<'\n';
-}
-
-bool EisSpectra::saveToDisk(const std::filesystem::path& path) const
-{
-	std::fstream file;
-	file.open(path, std::ios_base::out | std::ios_base::trunc);
-	if(!file.is_open())
-	{
-		Log(Log::ERROR)<<"can not open "<<path<<" for writing\n";
-		return false;
-	}
-
-	saveToStream(file);
-
-	file.close();
-	return true;
-}
-
-EisSpectra EisSpectra::loadFromStream(std::istream& stream)
-{
-	EisSpectra out;
-	std::string line;
-	std::getline(stream, line);
-	std::vector<std::string> tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
-
-	if(tokens.size() < 2 || tokens[0] != F_MAGIC)
-	{
-		throw file_error("not a valid EISGenerator file or stream");
-	}
-	else
-	{
-		std::vector<std::string> versionTokens = tokenize(tokens[1], '.');
-		if(versionTokens.size() != 3 || std::stoi(versionTokens[0]) > F_VERSION_MAJOR || std::stoi(versionTokens[1]) > F_VERSION_MINOR)
-			throw file_error("saved by a newer version of EISGenerator, can not open");
-	}
-
-	std::getline(stream, line);
-	tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
-	stripQuotes(tokens[0]);
-	out.model = tokens[0];
-	line.erase(line.begin(), line.begin()+tokens.size());
-	out.header = line;
-
-	while(stream.good())
-	{
-		std::getline(stream, line);
-		if(line.starts_with("labelsNames"))
-		{
-			std::getline(stream, line);
-			out.labelNames = tokenizeBinaryIgnore(line, ',', '"', '\\');
-			continue;
-		}
-		else if(line.starts_with("labels"))
-		{
-			std::getline(stream, line);
-			std::vector<std::string> tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
-			for(const std::string& token : tokens)
-				out.labels.push_back(std::stod(token));
-			continue;
-		}
-		else if(line.empty() || line[0] == '#' || line.starts_with("omega"))
-		{
-			continue;
-		}
-		tokens = tokenize(line, ',');
-		if(tokens.size() != 3)
-			throw file_error("invalid line: " + line);
-
-		#pragma GCC diagnostic push
-		#pragma GCC diagnostic ignored "-Wnarrowing"
-		if constexpr (std::is_same<fvalue, double>::value)
-			out.data.push_back(DataPoint({std::stod(tokens[1]), std::stod(tokens[2])}, std::stod(tokens[0])));
-		else
-			out.data.push_back(DataPoint({std::stof(tokens[1]), std::stof(tokens[2])}, std::stof(tokens[0])));
-		#pragma GCC diagnostic pop
-
-		eis::removeDuplicates(out.data);
-	}
-
-	return out;
-}
-
-EisSpectra EisSpectra::loadFromDisk(const std::filesystem::path& path)
-{
-	std::fstream file;
-	file.open(path, std::ios_base::in);
-	if(!file.is_open())
-		throw file_error("can not open " + path.string() + " for reading\n");
-
-	try
-	{
-		EisSpectra out = loadFromStream(file);
-		return out;
-	}
-	catch(const file_error& err)
-	{
-		throw file_error(path.string() + std::string(": ") + err.what());
-	}
-}
-
 static VersionFixed version = {VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH};
 
 const VersionFixed& getVersion()
@@ -401,12 +160,6 @@ const VersionFixed& getVersion()
 	return version;
 }
 
-std::ostream &operator<<(std::ostream &s, EisSpectra const& spectra)
-{
-	spectra.saveToStream(s);
-	return s;
-}
-
 std::ostream &operator<<(std::ostream &s, DataPoint const& dp)
 {
 	s<<dp.im;
diff --git a/spectra.cpp b/spectra.cpp
new file mode 100644
index 0000000..3c139fc
--- /dev/null
+++ b/spectra.cpp
@@ -0,0 +1,274 @@
+#include <fstream>
+
+#include "spectra.h"
+#include "eistype.h"
+#include "strops.h"
+#include "log.h"
+#include "basicmath.h"
+
+namespace eis
+{
+
+EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn,
+	const std::string& headerIn, std::vector<double> labelsIn, std::vector<std::string> labelNamesIn):
+data(dataIn), model(modelIn), header(headerIn), labels(labelsIn), labelNames(labelNamesIn)
+{
+
+}
+
+EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn,
+	const std::string& headerIn, std::vector<float> labelsIn, std::vector<std::string> labelNamesIn):
+data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
+{
+	setLabels(labelsIn);
+}
+
+EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn, const std::string& headerIn,
+	std::vector<size_t> labelsIn, std::vector<std::string> labelNamesIn):
+data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
+{
+	setSzLabels(labelsIn);
+}
+
+EisSpectra::EisSpectra(const std::vector<DataPoint>& dataIn, const std::string& modelIn, const std::string& headerIn,
+			   size_t label, size_t maxLabel, std::vector<std::string> labelNamesIn):
+data(dataIn), model(modelIn), header(headerIn), labelNames(labelNamesIn)
+{
+	setLabel(label, maxLabel);
+}
+
+void EisSpectra::setLabel(size_t label, size_t maxLabel)
+{
+	labels.assign(maxLabel, 0);
+	labels[label] = 1;
+}
+
+void EisSpectra::setSzLabels(std::vector<size_t> labelsIn)
+{
+	labels.assign(labelsIn.size(), 0);
+	for(size_t i = 0; i < labelsIn.size(); ++i)
+		labels[i] = static_cast<double>(labelsIn[i]);
+}
+
+std::vector<size_t> EisSpectra::getSzLabels() const
+{
+	std::vector<size_t> out(labels.size());
+	for(size_t i = 0; i < labels.size(); ++i)
+		out[i] = static_cast<size_t>(labels[i]);
+	return out;
+}
+
+size_t EisSpectra::getLabel()
+{
+	for(size_t i = 0; i < labels.size(); ++i)
+		if(labels[i] != 0)
+			return i;
+	return 0;
+}
+
+bool EisSpectra::isMulticlass()
+{
+	bool foundFirst = false;
+	for(size_t i = 0; i < labels.size(); ++i)
+	{
+		if(labels[i] != 0)
+		{
+			if(foundFirst)
+				return true;
+			else
+				foundFirst = true;
+		}
+	}
+	return false;
+}
+
+void EisSpectra::setLabels(const std::vector<float>& labelsIn)
+{
+	labels.assign(labelsIn.size(), 0);
+	for(size_t i = 0; i < labels.size(); ++i)
+		labels[i] = labelsIn[i];
+}
+
+void EisSpectra::setLabels(const std::vector<double>& labelsIn)
+{
+	labels = labelsIn;
+}
+
+std::vector<fvalue> EisSpectra::getFvalueLabels()
+{
+	if constexpr(std::is_same<fvalue, double>::value)
+	{
+		#pragma GCC diagnostic push
+		#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+		return *reinterpret_cast<std::vector<fvalue>*>(&labels);
+		#pragma GCC diagnostic pop
+	}
+	else
+	{
+		std::vector<fvalue> out(labels.size());
+		for(size_t i = 0; i < labels.size(); ++i)
+			out[i] = static_cast<fvalue>(labels[i]);
+		return out;
+	}
+}
+
+void EisSpectra::saveToStream(std::ostream& stream) const
+{
+	stream<<std::scientific;
+	stream<<F_MAGIC<<", "<<std::to_string(F_VERSION_MAJOR)<<'.'
+		<<std::to_string(F_VERSION_MINOR)<<'.'<<std::to_string(F_VERSION_PATCH)<<'\n';
+
+	stream<<'"'<<model<<'"'<<'\n'<<(headerDescription.empty() ? "None" : headerDescription)<<'\n'<<(header.empty() ? "None" : header);
+
+	if(!labels.empty())
+	{
+		if(!labelNames.empty())
+		{
+			stream<<"\nlabelsNames\n";
+			std::string labelLine;
+			for(const std::string& name : labelNames)
+				labelLine += "\"" + name + "\", ";
+			labelLine.pop_back();
+			labelLine.pop_back();
+			stream<<labelLine;
+		}
+		stream<<"\nlabels\n";
+
+		std::stringstream labelSs;
+		for(double label : labels)
+			labelSs<<label<<", ";
+		std::string labelLine = labelSs.str();
+		labelLine.pop_back();
+		labelLine.pop_back();
+		stream<<labelLine;
+	}
+
+	stream<<"\nomega, real, im\n";
+
+	for(const eis::DataPoint& point : data)
+		stream<<point.omega<<", "<<point.im.real()<<", "<<point.im.imag()<<'\n';
+}
+
+bool EisSpectra::saveToDisk(const std::filesystem::path& path) const
+{
+	std::fstream file;
+	file.open(path, std::ios_base::out | std::ios_base::trunc);
+	if(!file.is_open())
+	{
+		Log(Log::ERROR)<<"can not open "<<path<<" for writing\n";
+		return false;
+	}
+
+	saveToStream(file);
+
+	file.close();
+	return true;
+}
+
+EisSpectra EisSpectra::loadFromStream(std::istream& stream)
+{
+	EisSpectra out;
+	std::string line;
+	std::getline(stream, line);
+	std::vector<std::string> tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
+	VersionFixed fileVersion;
+
+	if(tokens.size() < 2 || tokens[0] != F_MAGIC)
+	{
+		throw file_error("not a valid EISGenerator file or stream");
+	}
+	else
+	{
+		std::vector<std::string> versionTokens = tokenize(tokens[1], '.');
+		if(versionTokens.size() != 3)
+			throw file_error("could not load file version from file");
+		fileVersion.major = std::stoi(versionTokens[0]);
+		fileVersion.minor = std::stoi(versionTokens[1]);
+		fileVersion.patch = std::stoi(versionTokens[3]);
+		if(fileVersion.major > F_VERSION_MAJOR || fileVersion.minor > F_VERSION_MINOR)
+			throw file_error("saved by a newer version of EISGenerator, can not open");
+	}
+
+	if(fileVersion.minor == F_VERSION_MINOR)
+	{
+		std::getline(stream, line);
+		out.model = line;
+		std::getline(stream, line);
+		out.headerDescription = line == "None" ? "" : line;
+		std::getline(stream, line);
+		out.header = line == "None" ? "" : line;
+	}
+	else
+	{
+		std::getline(stream, line);
+		tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
+		stripQuotes(tokens[0]);
+		out.model = tokens[0];
+		line.erase(line.begin(), line.begin()+tokens.size());
+		out.header = line;
+	}
+
+	while(stream.good())
+	{
+		std::getline(stream, line);
+		if(line.starts_with("labelsNames"))
+		{
+			std::getline(stream, line);
+			out.labelNames = tokenizeBinaryIgnore(line, ',', '"', '\\');
+			continue;
+		}
+		else if(line.starts_with("labels"))
+		{
+			std::getline(stream, line);
+			std::vector<std::string> tokens = tokenizeBinaryIgnore(line, ',', '"', '\\');
+			for(const std::string& token : tokens)
+				out.labels.push_back(std::stod(token));
+			continue;
+		}
+		else if(line.empty() || line[0] == '#' || line.starts_with("omega"))
+		{
+			continue;
+		}
+		tokens = tokenize(line, ',');
+		if(tokens.size() != 3)
+			throw file_error("invalid line: " + line);
+
+		#pragma GCC diagnostic push
+		#pragma GCC diagnostic ignored "-Wnarrowing"
+		if constexpr (std::is_same<fvalue, double>::value)
+			out.data.push_back(DataPoint({std::stod(tokens[1]), std::stod(tokens[2])}, std::stod(tokens[0])));
+		else
+			out.data.push_back(DataPoint({std::stof(tokens[1]), std::stof(tokens[2])}, std::stof(tokens[0])));
+		#pragma GCC diagnostic pop
+
+		eis::removeDuplicates(out.data);
+	}
+
+	return out;
+}
+
+EisSpectra EisSpectra::loadFromDisk(const std::filesystem::path& path)
+{
+	std::fstream file;
+	file.open(path, std::ios_base::in);
+	if(!file.is_open())
+		throw file_error("can not open " + path.string() + " for reading\n");
+
+	try
+	{
+		EisSpectra out = loadFromStream(file);
+		return out;
+	}
+	catch(const file_error& err)
+	{
+		throw file_error(path.string() + std::string(": ") + err.what());
+	}
+}
+
+}
+
+std::ostream &operator<<(std::ostream &s, eis::EisSpectra const& spectra)
+{
+	spectra.saveToStream(s);
+	return s;
+}
diff --git a/strops.cpp b/strops.cpp
index 5fc6644..767f8e0 100644
--- a/strops.cpp
+++ b/strops.cpp
@@ -25,6 +25,9 @@
 
 #include "log.h"
 
+namespace eis
+{
+
 char getOpposingBracketChar(const char ch)
 {
 	switch(ch)
@@ -214,3 +217,5 @@ std::string stripWhitespace(const std::string& in)
 	}
 	return out;
 }
+
+}
diff --git a/strops.h b/strops.h
index e844c74..6db94a2 100644
--- a/strops.h
+++ b/strops.h
@@ -24,6 +24,9 @@
 #include <sstream>
 #include <algorithm>
 
+namespace eis
+{
+
 std::vector<std::string> tokenize(const std::string& str, const char delim = ' ', const char ignBracketStart = '\0',
 								  const char ignBracketEnd = '\0', const char escapeChar = '\0');
 std::vector<std::string> tokenizeBinaryIgnore(const std::string& str, const char delim, const char ignoreBraket = '\0',
@@ -40,3 +43,5 @@ std::string stripWhitespace(const std::string& in);
 void stripQuotes(std::string& in);
 
 size_t eisRemoveUnneededBrackets(std::string& in, long int bracketStart = -1);
+
+}
diff --git a/test.cpp b/test.cpp
index 5ed71c0..3727152 100644
--- a/test.cpp
+++ b/test.cpp
@@ -342,7 +342,7 @@ static bool modelConsistancy()
 static bool uneededBrackets()
 {
 	std::string tst("(c-(rc)-(r-c(r)))");
-	eisRemoveUnneededBrackets(tst);
+	eis::eisRemoveUnneededBrackets(tst);
 	std::string expected("c-rc-(r-cr)");
 	if(tst == expected)
 	{
-- 
GitLab