diff --git a/src/algorithms.hpp b/src/algorithms.hpp
index 250cade2905ffe8aa45a9554dab196412d574aff..d627b7558bbf37cc55a9bf974e2246a8fabf2ae2 100644
--- a/src/algorithms.hpp
+++ b/src/algorithms.hpp
@@ -99,6 +99,32 @@ namespace algorithms{
return true;
}
+ template<typename C>
+ bool is_contiguously_numbered(const C &parent_list)
+ {
+ static_assert(
+ std::is_integral<typename C::value_type>::value,
+ "integral type required"
+ );
+
+ std::vector<bool> is_leaf(parent_list.size(), false);
+
+ auto ret = true;
+ for (std::size_t i = 1; i < parent_list.size(); ++i) {
+ if (is_leaf[parent_list[i]]) {
+ ret = false;
+ break;
+ }
+
+ if (parent_list[i] != i-1) {
+ // we have a branch and i-1 is a leaf node
+ is_leaf[i-1] = true;
+ }
+ }
+
+ return ret;
+ }
+
} // namespace algorithms
} // namespace mc
} // namespace nest
diff --git a/src/swcio.cpp b/src/swcio.cpp
index 98db848e2b4768d59399a3340c0d3611104fb4e9..2c3fe4d29ef12745a55af3390e67fcfc6ef1711e 100644
--- a/src/swcio.cpp
+++ b/src/swcio.cpp
@@ -32,7 +32,8 @@ void swc_record::check_consistency() const
{
// Check record type as well; enum's do not offer complete type safety,
// since you can cast anything that fits to its underlying type
- if (type_ < 0 || type_ > custom) {
+ if (static_cast<int>(type_) < 0 ||
+ static_cast<int>(type_) > static_cast<int>(kind::custom)) {
throw std::invalid_argument("unknown record type");
}
@@ -65,7 +66,7 @@ std::ostream &operator<<(std::ostream &os, const swc_record &record)
{
// output in one-based indexing
os << record.id_+1 << " "
- << record.type_ << " "
+ << static_cast<int>(record.type_) << " "
<< std::setprecision(7) << record.x_ << " "
<< std::setprecision(7) << record.y_ << " "
<< std::setprecision(7) << record.z_ << " "
@@ -177,9 +178,9 @@ swc_record_range_clean::swc_record_range_clean(std::istream &is)
{
std::unordered_set<swc_record::id_type> ids;
- std::size_t num_trees = 0;
+ std::size_t num_trees = 0;
swc_record::id_type last_id = -1;
- bool needsort = false;
+ bool needsort = false;
swc_record curr_record;
for (auto c : swc_get_records<swc_io_raw>(is)) {
diff --git a/src/swcio.hpp b/src/swcio.hpp
index 0486a2b511e7afcb13fbc13f7a5977fa4f4db6a4..6a4af5f8192354f44f769dc74ee21507a8a3f096 100644
--- a/src/swcio.hpp
+++ b/src/swcio.hpp
@@ -15,11 +15,8 @@ class swc_record
public:
using id_type = int;
- // FIXME: enum's are not completely type-safe, since they can accept
- // anything that can be casted to their underlying type.
- //
// More on SWC files: http://research.mssm.edu/cnic/swc.html
- enum kind {
+ enum class kind {
undefined = 0,
soma,
axon,
@@ -31,9 +28,9 @@ public:
};
// swc records assume zero-based indexing; root's parent remains -1
- swc_record(kind type, int id,
- float x, float y, float z, float r,
- int parent_id)
+ swc_record(swc_record::kind type, int id,
+ float x, float y, float z, float r,
+ int parent_id)
: type_(type)
, id_(id)
, x_(x)
@@ -46,7 +43,7 @@ public:
}
swc_record()
- : type_(swc_record::undefined)
+ : type_(swc_record::kind::undefined)
, id_(0)
, x_(0)
, y_(0)
@@ -409,4 +406,3 @@ template<typename T = swc_io_clean>
} // namespace io
} // namespace mc
} // namespace nest
-
diff --git a/tests/test_algorithms.cpp b/tests/test_algorithms.cpp
index decf1316702dc5e04133131e838caa09e2539373..3e59db7e5557701f48ebac0d508efe6559b8a9ba 100644
--- a/tests/test_algorithms.cpp
+++ b/tests/test_algorithms.cpp
@@ -169,3 +169,89 @@ TEST(algorithms, is_positive)
)
);
}
+
+TEST(algorithms, is_contiguously_numbered)
+{
+ //
+ // 0
+ // |
+ // 1
+ // |
+ // 2
+ // /|\
+ // 3 7 4
+ // / \
+ // 5 6
+ //
+ EXPECT_FALSE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{0, 0, 1, 2, 2, 3, 4, 2}
+ )
+ );
+
+ //
+ // 0
+ // |
+ // 1
+ // |
+ // 2
+ // /|\
+ // 3 6 5
+ // / \
+ // 4 7
+ //
+ EXPECT_FALSE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{0, 0, 1, 2, 3, 2, 2, 5}
+ )
+ );
+
+ //
+ // 0
+ // |
+ // 1
+ // |
+ // 2
+ // /|\
+ // 3 7 5
+ // / \
+ // 4 6
+ //
+ EXPECT_TRUE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{0, 0, 1, 2, 3, 2, 5, 2}
+ )
+ );
+
+ //
+ // 0
+ // |
+ // 1
+ // / \
+ // 2 7
+ // / \
+ // 3 5
+ // / \
+ // 4 6
+ //
+ EXPECT_TRUE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{0, 0, 1, 2, 3, 2, 5, 1}
+ )
+ );
+
+ // Soma-only list
+ EXPECT_TRUE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{0}
+ )
+ );
+
+ // Empty list
+ EXPECT_TRUE(
+ nest::mc::algorithms::is_contiguously_numbered(
+ std::vector<int>{}
+ )
+ );
+
+}
diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp
index 3de060ed4a69c446167d07c372ee0c872255ee30..635ccb442666b3662745a1f02e064bdbf81b5168 100644
--- a/tests/test_swcio.cpp
+++ b/tests/test_swcio.cpp
@@ -36,43 +36,43 @@ TEST(swc_record, construction)
{
// invalid id
EXPECT_THROW(swc_record record(
- swc_record::custom, -3, 1., 1., 1., 1., 5),
+ swc_record::kind::custom, -3, 1., 1., 1., 1., 5),
std::invalid_argument);
}
{
// invalid parent id
EXPECT_THROW(swc_record record(
- swc_record::custom, 0, 1., 1., 1., 1., -5),
+ swc_record::kind::custom, 0, 1., 1., 1., 1., -5),
std::invalid_argument);
}
{
// invalid radius
EXPECT_THROW(swc_record record(
- swc_record::custom, 0, 1., 1., 1., -1., -1),
+ swc_record::kind::custom, 0, 1., 1., 1., -1., -1),
std::invalid_argument);
}
{
// parent_id > id
EXPECT_THROW(swc_record record(
- swc_record::custom, 0, 1., 1., 1., 1., 2),
+ swc_record::kind::custom, 0, 1., 1., 1., 1., 2),
std::invalid_argument);
}
{
// parent_id == id
EXPECT_THROW(swc_record record(
- swc_record::custom, 0, 1., 1., 1., 1., 0),
+ swc_record::kind::custom, 0, 1., 1., 1., 1., 0),
std::invalid_argument);
}
{
// check standard construction by value
- swc_record record(swc_record::custom, 0, 1., 1., 1., 1., -1);
+ swc_record record(swc_record::kind::custom, 0, 1., 1., 1., 1., -1);
EXPECT_EQ(record.id(), 0);
- EXPECT_EQ(record.type(), swc_record::custom);
+ EXPECT_EQ(record.type(), swc_record::kind::custom);
EXPECT_EQ(record.x(), 1.);
EXPECT_EQ(record.y(), 1.);
EXPECT_EQ(record.z(), 1.);
@@ -83,7 +83,7 @@ TEST(swc_record, construction)
{
// check copy constructor
- swc_record record_orig(swc_record::custom, 0, 1., 1., 1., 1., -1);
+ swc_record record_orig(swc_record::kind::custom, 0, 1., 1., 1., 1., -1);
swc_record record(record_orig);
expect_record_equals(record_orig, record);
}
@@ -95,9 +95,9 @@ TEST(swc_record, comparison)
{
// check comparison operators
- swc_record record0(swc_record::custom, 0, 1., 1., 1., 1., -1);
- swc_record record1(swc_record::custom, 0, 2., 3., 4., 5., -1);
- swc_record record2(swc_record::custom, 1, 2., 3., 4., 5., -1);
+ swc_record record0(swc_record::kind::custom, 0, 1., 1., 1., 1., -1);
+ swc_record record1(swc_record::kind::custom, 0, 2., 3., 4., 5., -1);
+ swc_record record2(swc_record::kind::custom, 1, 2., 3., 4., 5., -1);
EXPECT_EQ(record0, record1);
EXPECT_LT(record0, record2);
EXPECT_GT(record2, record1);
@@ -131,6 +131,31 @@ TEST(swc_parser, invalid_input)
swc_record record;
EXPECT_THROW(is >> record, swc_parse_error);
}
+
+ {
+ // Non-contiguous numbering in branches is considered invalid
+ // 1
+ // / \
+ // 2 3
+ // /
+ // 4
+ std::stringstream is;
+ is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\n";
+ is << "2 1 14.566132 34.873772 7.857000 0.717830 1\n";
+ is << "3 1 14.566132 34.873772 7.857000 0.717830 1\n";
+ is << "4 1 14.566132 34.873772 7.857000 0.717830 2\n";
+
+ std::vector<swc_record> records;
+ try {
+ for (auto c : swc_get_records<swc_io_clean>(is)) {
+ records.push_back(std::move(c));
+ }
+
+ FAIL() << "expected swc_parse_error, none was thrown\n";
+ } catch (const swc_parse_error &e) {
+ SUCCEED();
+ }
+ }
}
@@ -162,7 +187,7 @@ TEST(swc_parser, valid_input)
swc_record record;
EXPECT_NO_THROW(is >> record);
EXPECT_EQ(0, record.id()); // zero-based indexing
- EXPECT_EQ(swc_record::soma, record.type());
+ EXPECT_EQ(swc_record::kind::soma, record.type());
EXPECT_FLOAT_EQ(14.566132, record.x());
EXPECT_FLOAT_EQ(34.873772, record.y());
EXPECT_FLOAT_EQ( 7.857000, record.z());
@@ -173,9 +198,9 @@ TEST(swc_parser, valid_input)
{
// check valid input with a series of records
std::vector<swc_record> records_orig = {
- swc_record(swc_record::soma, 0,
+ swc_record(swc_record::kind::soma, 0,
14.566132, 34.873772, 7.857000, 0.717830, -1),
- swc_record(swc_record::dendrite, 1,
+ swc_record(swc_record::kind::dendrite, 1,
14.566132+1, 34.873772+1, 7.857000+1, 0.717830+1, -1)
};
@@ -229,7 +254,7 @@ TEST(swc_parser, input_cleaning)
is << "2 1 14.566132 34.873772 7.857000 0.717830 1\n";
is << "2 1 14.566132 34.873772 7.857000 0.717830 1\n";
- EXPECT_EQ(2u, swc_get_records(is).size());
+ EXPECT_EQ(2u, swc_get_records<swc_io_clean>(is).size());
}
{
@@ -240,7 +265,7 @@ TEST(swc_parser, input_cleaning)
is << "3 1 14.566132 34.873772 7.857000 0.717830 -1\n";
is << "4 1 14.566132 34.873772 7.857000 0.717830 1\n";
- auto records = swc_get_records(is);
+ auto records = swc_get_records<swc_io_clean>(is);
EXPECT_EQ(2u, records.size());
}
@@ -255,7 +280,7 @@ TEST(swc_parser, input_cleaning)
std::array<swc_record::id_type, 4> expected_id_list = {{ 0, 1, 2, 3 }};
auto expected_id = expected_id_list.cbegin();
- for (auto c : swc_get_records(is)) {
+ for (auto c : swc_get_records<swc_io_clean>(is)) {
EXPECT_EQ(*expected_id, c.id());
++expected_id;
}
@@ -281,7 +306,7 @@ TEST(swc_parser, input_cleaning)
auto expected_id = expected_id_list.cbegin();
auto expected_parent = expected_parent_list.cbegin();
- for (auto c : swc_get_records(is)) {
+ for (auto c : swc_get_records<swc_io_clean>(is)) {
EXPECT_EQ(*expected_id, c.id());
EXPECT_EQ(*expected_parent, c.parent());
++expected_id;
diff --git a/vector b/vector
index 7fc001ef98c01383d384541de08b861328c29405..c2fc6318f6d65ae9d7aad60309f3b53d590a00ec 160000
--- a/vector
+++ b/vector
@@ -1 +1 @@
-Subproject commit 7fc001ef98c01383d384541de08b861328c29405
+Subproject commit c2fc6318f6d65ae9d7aad60309f3b53d590a00ec