Commit 8c16125d authored by Scott Cyphers's avatar Scott Cyphers

Merge branch 'master' into cyphers/view

parents 0064cfd0 8d57ce68
......@@ -44,3 +44,10 @@ SpacesInSquareBrackets: false
SortIncludes: false
ReflowComments: true
IncludeCategories:
- Regex: '^".*'
Priority: 3
- Regex: '^<.*'
Priority: 2
SortIncludes: true
......@@ -27,7 +27,10 @@ const ngraph::ElementType element_type_uint64_t = ngraph::ElementType(64, false,
std::map<std::string, ngraph::ElementType> ngraph::ElementType::m_element_list;
ngraph::ElementType::ElementType(size_t bitwidth, bool is_float, bool is_signed, const std::string& cname)
ngraph::ElementType::ElementType(size_t bitwidth,
bool is_float,
bool is_signed,
const std::string& cname)
: m_bitwidth{bitwidth}
, m_is_float{is_float}
, m_is_signed{is_signed}
......
......@@ -18,8 +18,8 @@
#pragma once
#include <string>
#include <map>
#include <string>
namespace ngraph
{
......@@ -43,10 +43,10 @@ public:
private:
static std::map<std::string, ElementType> m_element_list;
size_t m_bitwidth;
bool m_is_float;
bool m_is_signed;
const std::string m_cname;
size_t m_bitwidth;
bool m_is_float;
bool m_is_signed;
const std::string m_cname;
};
extern const ngraph::ElementType element_type_float;
......
......@@ -14,12 +14,12 @@
*/
#include <chrono>
#include <condition_variable>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <ctime>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <thread>
#include "log.hpp"
......
......@@ -15,9 +15,9 @@
#pragma once
#include <deque>
#include <sstream>
#include <stdexcept>
#include <deque>
namespace nervana
{
......
......@@ -18,7 +18,7 @@
using namespace ngraph;
size_t NameableValue::__counter = 0;
size_t NameableValue::__counter = 0;
std::map<std::string, NameableValue> NameableValue::__all_names;
NameableValue::NameableValue(const std::string& name,
......
......@@ -14,96 +14,94 @@
#pragma once
#include <string>
#include <map>
#include <string>
namespace ngraph
{
//================================================================================================
// NameableValue
// An Axis labels a dimension of a tensor. The op-graph uses
// the identity of Axis objects to pair and specify dimensions in
// symbolic expressions. This system has several advantages over
// using the length and position of the axis as in other frameworks:
//
// 1) Convenience. The dimensions of tensors, which may be nested
// deep in a computation graph, can be specified without having to
// calculate their lengths.
//
// 2) Safety. Axis labels are analogous to types in general-purpose
// programming languages, allowing objects to interact only when
// they are permitted to do so in advance. In symbolic computation,
// this prevents interference between axes that happen to have the
// same lengths but are logically distinct, e.g. if the number of
// training examples and the number of input features are both 50.
//
// TODO: Please add to the list...
//
// Arguments:
// length: The length of the axis.
// batch: Whether the axis is a batch axis.
// recurrent: Whether the axis is a recurrent axis.
//================================================================================================
class NameableValue
{
public:
//!-----------------------------------------------------------------------------------
//! NameableValue
//! An object that can be named.
//!
//! Arguments:
//! graph_label_type: A label that should be used when drawing the graph. Defaults to
//! the class name.
//! name (str): The name of the object.
//! **kwargs: Parameters for related classes.
//!
//! Attributes:
//! graph_label_type: A label that should be used when drawing the graph.
//! id: Unique id for this object.
//!-----------------------------------------------------------------------------------
NameableValue(const std::string& name,
const std::string& graph_label_type = "",
const std::string& doc_string = "");
//================================================================================================
// NameableValue
// An Axis labels a dimension of a tensor. The op-graph uses
// the identity of Axis objects to pair and specify dimensions in
// symbolic expressions. This system has several advantages over
// using the length and position of the axis as in other frameworks:
//
// 1) Convenience. The dimensions of tensors, which may be nested
// deep in a computation graph, can be specified without having to
// calculate their lengths.
//
// 2) Safety. Axis labels are analogous to types in general-purpose
// programming languages, allowing objects to interact only when
// they are permitted to do so in advance. In symbolic computation,
// this prevents interference between axes that happen to have the
// same lengths but are logically distinct, e.g. if the number of
// training examples and the number of input features are both 50.
//
// TODO: Please add to the list...
//
// Arguments:
// length: The length of the axis.
// batch: Whether the axis is a batch axis.
// recurrent: Whether the axis is a recurrent axis.
//================================================================================================
class NameableValue
{
public:
//!-----------------------------------------------------------------------------------
//! NameableValue
//! An object that can be named.
//!
//! Arguments:
//! graph_label_type: A label that should be used when drawing the graph. Defaults to
//! the class name.
//! name (str): The name of the object.
//! **kwargs: Parameters for related classes.
//!
//! Attributes:
//! graph_label_type: A label that should be used when drawing the graph.
//! id: Unique id for this object.
//!-----------------------------------------------------------------------------------
NameableValue(const std::string& name,
const std::string& graph_label_type = "",
const std::string& doc_string = "");
//!-----------------------------------------------------------------------------------
//! graph_label
//! The label used for drawings of the graph.
//!-----------------------------------------------------------------------------------
const std::string& graph_label();
//!-----------------------------------------------------------------------------------
//! graph_label
//! The label used for drawings of the graph.
//!-----------------------------------------------------------------------------------
const std::string& graph_label();
//!-----------------------------------------------------------------------------------
//! name
//! Sets the object name to a unique name based on name.
//!
//! Arguments:
//! name: Prefix for the name
//!-----------------------------------------------------------------------------------
const std::string& name();
//!-----------------------------------------------------------------------------------
//! name
//! Sets the object name to a unique name based on name.
//!
//! Arguments:
//! name: Prefix for the name
//!-----------------------------------------------------------------------------------
const std::string& name();
//!-----------------------------------------------------------------------------------
//! name
//!-----------------------------------------------------------------------------------
void name(const std::string& name);
//!-----------------------------------------------------------------------------------
//! name
//!-----------------------------------------------------------------------------------
void name(const std::string& name);
//!-----------------------------------------------------------------------------------
//! short_name
//!-----------------------------------------------------------------------------------
const std::string& short_name();
//!-----------------------------------------------------------------------------------
//! short_name
//!-----------------------------------------------------------------------------------
const std::string& short_name();
//!-----------------------------------------------------------------------------------
//! named
//!-----------------------------------------------------------------------------------
NameableValue& named(const std::string& name);
//!-----------------------------------------------------------------------------------
//! named
//!-----------------------------------------------------------------------------------
NameableValue& named(const std::string& name);
static size_t __counter;
static std::map<std::string, NameableValue> __all_names;
static size_t __counter;
static std::map<std::string, NameableValue> __all_names;
std::string m_name;
std::string m_graph_label;
std::string m_short_name;
std::string m_doc_string;
};
std::string m_name;
std::string m_graph_label;
std::string m_short_name;
std::string m_doc_string;
};
} // end namespace ngraph
#include <iostream>
#include <algorithm>
#include <iostream>
#include "strides.hpp"
#include "util.hpp"
......
#pragma once
#include <cstdio>
#include <vector>
#include <initializer_list>
#include <vector>
#include "element_type.hpp"
#include "tree.hpp"
......@@ -27,10 +27,9 @@ public:
ElementType et = element_type_float);
const ElementType& get_type() const { return m_element_type; }
tensor_stride full_strides() const;
tensor_stride strides() const;
tensor_size sizes() const;
tensor_stride full_strides() const;
tensor_stride strides() const;
tensor_size sizes() const;
tensor_size operator[](size_t index) const;
......@@ -53,9 +52,8 @@ class ngraph::tensor_stride
public:
tensor_stride();
const ElementType& get_type() const { return m_element_type; }
tensor_stride full_strides() const;
tensor_stride strides() const;
tensor_stride full_strides() const;
tensor_stride strides() const;
tensor_stride reduce_strides() const;
......
......@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <cassert>
#include <cmath>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cassert>
#include "axes.hpp"
#include "util.hpp"
......@@ -268,7 +268,7 @@ Axis ngraph::slice_axis(const Axis& axis, const slice& s)
std::vector<std::string> ngraph::duplicates(const std::vector<Axis>& ax)
{
std::map<std::string, size_t> counts;
std::vector<std::string> rc;
std::vector<std::string> rc;
for (const Axis& axis : ax)
{
auto it = counts.find(axis.name);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <cmath>
#include <exception>
#include <memory>
#include <sstream>
#include <exception>
#include <cmath>
#include "exop.hpp"
#include "op_graph.hpp"
......@@ -404,10 +404,10 @@ void ExOpBlock::add_ops(std::initializer_list<computation_op_ptr> roots, exop_pt
}
}
std::vector<op_ptr> available;
std::vector<op_ptr> available;
std::map<op_ptr, size_t> counts;
std::map<op_ptr, std::vector<op_ptr>> parents;
std::vector<op_ptr> ready;
std::vector<op_ptr> ready;
available.insert(available.end(), roots.begin(), roots.end());
while (available.size() > 0)
......@@ -1012,7 +1012,7 @@ tensor_decl_ptr ExecutionState::ensure_tensor_decl(ExecutionGraph& execut
bool is_constant = false;
bool is_compile_only = false;
tensor_decl = std::make_shared<TensorDecl>(execution_graph,
tensor_decl = std::make_shared<TensorDecl>(execution_graph,
tensor_description_base->element_type(),
tensor_description_base->tensor_size(),
tensor_description_base->is_persistent(),
......@@ -1057,7 +1057,7 @@ tensor_decl_ptr ExecutionGraph::get_tensor_decl(op_ptr op,
bool is_constant = false;
bool is_compile_only = false;
tensor_decl = std::make_shared<TensorDecl>(*this,
tensor_decl = std::make_shared<TensorDecl>(*this,
tensor_description_base->element_type(),
tensor_description_base->tensor_size(),
tensor_description_base->is_persistent(),
......
This diff is collapsed.
This diff is collapsed.
......@@ -14,24 +14,21 @@
#pragma once
#include "mock.hpp"
#include "exop.hpp"
#include "mock.hpp"
namespace ngraph
{
//================================================================================================
// CpuTransformer
//================================================================================================
class CpuTransformer : public Transformer
{
public:
virtual ~CpuTransformer() {}
ExecutionState& execution_state() override { return m_execution_state; }
private:
ExecutionState m_execution_state;
};
//================================================================================================
// CpuTransformer
//================================================================================================
class CpuTransformer : public Transformer
{
public:
virtual ~CpuTransformer() {}
ExecutionState& execution_state() override { return m_execution_state; }
private:
ExecutionState m_execution_state;
};
} // end namespace ngraph
......@@ -14,8 +14,8 @@
#pragma once
#include <vector>
#include <memory>
#include <vector>
#include "element_type.hpp"
#include "strides.hpp"
......
......@@ -14,8 +14,8 @@
#include <sstream>
#include "op_graph.hpp"
#include "axes.hpp"
#include "op_graph.hpp"
#include "util.hpp"
using namespace ngraph;
......@@ -2794,7 +2794,9 @@ ElementWiseOp::ElementWiseOp()
{
}
void ElementWiseOp::ElementWiseOp_init(std::vector<op_ptr>, Axes) {}
void ElementWiseOp::ElementWiseOp_init(std::vector<op_ptr>, Axes)
{
}
//================================================================================================
// UnaryElementWiseOp
......
This diff is collapsed.
#pragma once
#include <algorithm>
#include <functional>
#include <vector>
#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <vector>
#include "util.hpp"
......@@ -51,7 +51,6 @@ public:
bool is_list() const { return m_is_list; }
T get_value() const { return m_value; }
const std::vector<tree>& get_list() const { return m_list; }
static void traverse_tree(tree& s, std::function<void(T*)> func)
{
if (s.is_list())
......
......@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <map>
#include <iomanip>
#include <map>
#include "util.hpp"
......
......@@ -14,184 +14,183 @@
#pragma once
#include <string>
#include <sstream>
#include <vector>
#include <chrono>
#include <algorithm>
#include <map>
#include <chrono>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
namespace ngraph
{
class stopwatch;
extern std::map<std::string, stopwatch*> stopwatch_statistics;
class stopwatch;
extern std::map<std::string, stopwatch*> stopwatch_statistics;
template <typename T>
std::string join(const T& v, const std::string& sep)
{
std::ostringstream ss;
for (const auto& x : v)
template <typename T>
std::string join(const T& v, const std::string& sep)
{
if (&x != &*(v.begin()))
std::ostringstream ss;
for (const auto& x : v)
{
ss << sep;
if (&x != &*(v.begin()))
{
ss << sep;
}
ss << x;
}
ss << x;
return ss.str();
}
return ss.str();
}
template <typename U, typename T>
bool contains(const U& container, const T& obj)
{
bool rc = false;
for (auto o : container)
template <typename U, typename T>
bool contains(const U& container, const T& obj)
{
if (o == obj)
bool rc = false;
for (auto o : container)
{
rc = true;
break;
if (o == obj)
{
rc = true;
break;
}
}
return rc;
}
return rc;
}
template <typename U, typename T>
bool contains_key(const U& container, const T& obj)
{
bool rc = false;
for (auto o : container)
template <typename U, typename T>
bool contains_key(const U& container, const T& obj)
{
if (o.first == obj)
bool rc = false;
for (auto o : container)
{
rc = true;
break;
if (o.first == obj)
{
rc = true;
break;
}
}
return rc;
}
return rc;
}
template <typename U, typename T>
void remove_from(U& container, const T& obj)
{
auto it = container.find(obj);
if (it != container.end())
template <typename U, typename T>
void remove_from(U& container, const T& obj)
{
container.erase(it);
auto it = container.find(obj);
if (it != container.end())
{
container.erase(it);
}
}
}
size_t hash_combine(const std::vector<size_t>& list);
void dump(std::ostream& out, const void*, size_t);
size_t hash_combine(const std::vector<size_t>& list);
void dump(std::ostream& out, const void*, size_t);
std::string to_lower(const std::string& s);
std::string trim(const std::string& s);
std::vector<std::string> split(const std::string& s, char delimiter, bool trim = false);
std::string to_lower(const std::string& s);
std::string trim(const std::string& s);
std::vector<std::string> split(const std::string& s, char delimiter, bool trim = false);
class stopwatch
{
public:
stopwatch() {}
stopwatch(const std::string& name)
: m_name{name}
class stopwatch
{
stopwatch_statistics.insert({m_name, this});
}
public:
stopwatch() {}
stopwatch(const std::string& name)
: m_name{name}
{
stopwatch_statistics.insert({m_name, this});
}
~stopwatch()
{
if (m_name.size() > 0)
~stopwatch()
{
stopwatch_statistics.find(m_name);
if (m_name.size() > 0)
{
stopwatch_statistics.find(m_name);
}
}
}
void start()
{
if (m_active == false)
void start()
{
m_total_count++;
m_active = true;
m_start_time = m_clock.now();
if (m_active == false)
{
m_total_count++;
m_active = true;
m_start_time = m_clock.now();
}
}
}
void stop()
{
if (m_active == true)
void stop()
{
auto end_time = m_clock.now();
m_last_time = end_time - m_start_time;
m_total_time += m_last_time;
m_active = false;
if (m_active == true)
{
auto end_time = m_clock.now();
m_last_time = end_time - m_start_time;
m_total_time += m_last_time;
m_active = false;
}
}
size_t get_call_count() const { return m_total_count; }
size_t get_seconds() const { return get_nanoseconds() / 1e9; }
size_t get_milliseconds() const { return get_nanoseconds() / 1e6; }
size_t get_microseconds() const { return get_nanoseconds() / 1e3; }
size_t get_nanoseconds() const
{
if (m_active)
{
return (m_clock.now() - m_start_time).count();
}
else
{
return m_last_time.count();
}
}
}
size_t get_call_count() const { return m_total_count; }
size_t get_seconds() const { return get_nanoseconds() / 1e9; }
size_t get_milliseconds() const { return get_nanoseconds() / 1e6; }
size_t get_microseconds() const { return get_nanoseconds() / 1e3; }
size_t get_nanoseconds() const
size_t get_total_seconds() const { return get_total_nanoseconds() / 1e9; }
size_t get_total_milliseconds() const { return get_total_nanoseconds() / 1e6; }
size_t get_total_microseconds() const { return get_total_nanoseconds() / 1e3; }
size_t get_total_nanoseconds() const { return m_total_time.count(); }
private:
std::chrono::high_resolution_clock m_clock;
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
bool m_active = false;
std::chrono::nanoseconds m_total_time =
std::chrono::high_resolution_clock::duration::zero();
std::chrono::nanoseconds m_last_time;
size_t m_total_count = 0;
std::string m_name;
};
template <class InputIt, class BinaryOp>
typename std::iterator_traits<InputIt>::value_type
reduce(InputIt first, InputIt last, BinaryOp op)
{
if (m_active)
typename std::iterator_traits<InputIt>::value_type result;
if (first == last)
{
return (m_clock.now() - m_start_time).count();
result = {};
}
else
{
return m_last_time.count();
result = *first++;
while (first != last)
{
result = op(result, *first);
first++;
}
}
return result;
}
size_t get_total_seconds() const { return get_total_nanoseconds() / 1e9; }
size_t get_total_milliseconds() const { return get_total_nanoseconds() / 1e6; }
size_t get_total_microseconds() const { return get_total_nanoseconds() / 1e3; }
size_t get_total_nanoseconds() const { return m_total_time.count(); }
private:
std::chrono::high_resolution_clock m_clock;
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
bool m_active = false;
std::chrono::nanoseconds m_total_time = std::chrono::high_resolution_clock::duration::zero();
std::chrono::nanoseconds m_last_time;
size_t m_total_count = 0;
std::string m_name;
};
template <class InputIt, class BinaryOp>
typename std::iterator_traits<InputIt>::value_type
reduce(InputIt first, InputIt last, BinaryOp op)
{
typename std::iterator_traits<InputIt>::value_type result;
if (first == last)
template <typename T>
T plus(const T& a, const T& b)
{
result = {};
return a + b;
}
else
template <typename T>
T mul(const T& a, const T& b)
{
result = *first++;
while (first != last)
{
result = op(result, *first);
first++;
}
return a * b;
}
return result;
}
template <typename T>
T plus(const T& a, const T& b)
{
return a + b;
}
template <typename T>
T mul(const T& a, const T& b)
{
return a * b;
}
} // end namespace ngraph
......@@ -15,10 +15,10 @@
#pragma once
#include <array>
#include <random>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <cstring>
#include <random>
static std::mt19937_64 random_generator;
......@@ -74,7 +74,6 @@ public:
}
bool operator!=(const uuid_type& other) const { return !(*this == other); }
friend std::ostream& operator<<(std::ostream& out, const uuid_type& id)
{
out << id.to_string();
......
......@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "gtest/gtest.h"
......@@ -310,7 +310,7 @@ TEST(axes, index)
EXPECT_EQ(7, b[1].length());
}
TEST(axes, as_nested_list)
TEST(axes, DISABLED_as_nested_list)
{
Axis C = make_axis(5);
Axis H = make_axis(3);
......@@ -325,7 +325,7 @@ TEST(axes, as_nested_list)
FAIL();
}
TEST(axes, flatten)
TEST(axes, DISABLED_flatten)
{
Axis C = make_axis(5);
Axis H = make_axis(3);
......@@ -336,7 +336,7 @@ TEST(axes, flatten)
EXPECT_TRUE(c.is_flattened());
}
TEST(axes, as_flattened_list)
TEST(axes, DISABLED_as_flattened_list)
{
FAIL();
}
......@@ -364,7 +364,7 @@ TEST(axes, hash_axes)
m2[axes] = 1;
}
TEST(axes, reaxe_0d_to_1d)
TEST(axes, DISABLED_reaxe_0d_to_1d)
{
TensorDescription td{};
ngraph::ndarray x = random(td);
......@@ -382,7 +382,7 @@ TEST(axes, reaxe_0d_to_1d)
FAIL();
}
TEST(axes, reaxe_0d_to_2d)
TEST(axes, DISABLED_reaxe_0d_to_2d)
{
// td = TensorDescription(axes=())
// x = random(td)
......@@ -407,7 +407,7 @@ TEST(axes, reaxe_0d_to_2d)
// I started refactoring into smaller pieces as seen in tests above, but
// stopped ...
//-----------------------------------------------------------------------------------------------
TEST(axes, simple_tensors)
TEST(axes, DISABLED_simple_tensors)
{
// # A simple vector
// td1 = TensorDescription(axes=[ax_A])
......@@ -582,7 +582,7 @@ TEST(axes, axes_map)
// assert axes_after == axes_map.map_axes(axes_before)
}
TEST(axes, axes_map_immutable)
TEST(axes, DISABLED_axes_map_immutable)
{
FAIL();
// axes_map = AxesMap({})
......@@ -591,7 +591,7 @@ TEST(axes, axes_map_immutable)
// axes_map["x"] = "y"
}
TEST(axes, axes_map_init_from_axes)
TEST(axes, DISABLED_axes_map_init_from_axes)
{
FAIL();
// axes_map = AxesMap({ng.make_axis(1, name="aaa"): ng.make_axis(1, name="zzz")})
......
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......
......@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <iostream>
#include <chrono>
#include <iostream>
#include "gtest/gtest.h"
......
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......@@ -22,4 +22,6 @@
using namespace ngraph;
TEST(names, name) {}
TEST(names, name)
{
}
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......
......@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "gtest/gtest.h"
......
......@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......@@ -134,7 +134,9 @@ TEST(util, contains)
EXPECT_FALSE(contains(v1, 8));
}
TEST(util, remove_from) {}
TEST(util, remove_from)
{
}
TEST(util, reduce)
{
......
......@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <vector>
#include <string>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment