Accommodate n-Dimensional HR coordinates

refs: #3751

Change-Id: Ib705b671daba56f58e09876a48d8b31649bd7ab1
diff --git a/nlsr.conf b/nlsr.conf
index 37eac9e..329a702 100644
--- a/nlsr.conf
+++ b/nlsr.conf
@@ -101,7 +101,7 @@
 
 
   radius   123.456      ; radius of the router in hyperbolic coordinate system
-  angle    1.45         ; angle of the router in hyperbolic coordinate system
+  angle    1.45,2.36    ; angle of the router in hyperbolic coordinate system
 }
 
 
diff --git a/src/conf-file-processor.cpp b/src/conf-file-processor.cpp
index 211609a..789efc4 100644
--- a/src/conf-file-processor.cpp
+++ b/src/conf-file-processor.cpp
@@ -528,17 +528,32 @@
   }
 
   try {
-    /* Radius and angle is mandatory configuration parameter in hyperbolic section.
+    /* Radius and angle(s) are mandatory configuration parameters in hyperbolic section.
      * Even if router can have hyperbolic routing calculation off but other router
      * in the network may use hyperbolic routing calculation for FIB generation.
      * So each router need to advertise its hyperbolic coordinates in the network
      */
     double radius = section.get<double>("radius");
-    double angle = section.get<double>("angle");
+    std::string angleString = section.get<std::string>("angle");
+
+    std::stringstream ss(angleString);
+    std::vector<double> angles;
+
+    double angle;
+
+    while (ss >> angle)
+    {
+      angles.push_back(angle);
+      if (ss.peek() == ',' || ss.peek() == ' ')
+      {
+        ss.ignore();
+      }
+    }
+
     if (!m_nlsr.getConfParameter().setCorR(radius)) {
       return false;
     }
-    m_nlsr.getConfParameter().setCorTheta(angle);
+    m_nlsr.getConfParameter().setCorTheta(angles);
   }
   catch (const std::exception& ex) {
     std::cerr << ex.what() << std::endl;
diff --git a/src/conf-parameter.cpp b/src/conf-parameter.cpp
index 8498615..e19fe5e 100644
--- a/src/conf-parameter.cpp
+++ b/src/conf-parameter.cpp
@@ -47,7 +47,10 @@
   _LOG_INFO("Max Faces Per Prefix: " << m_maxFacesPerPrefix);
   _LOG_INFO("Hyperbolic Routing: " << m_hyperbolicState);
   _LOG_INFO("Hyp R: " << m_corR);
-  _LOG_INFO("Hyp theta: " << m_corTheta);
+  int i=0;
+  for (auto const& value: m_corTheta) {
+    _LOG_INFO("Hyp Angle " << i++ << ": "<< value);
+  }
   _LOG_INFO("Log Directory: " << m_logDir);
   _LOG_INFO("Seq Directory: " << m_seqFileDir);
 
diff --git a/src/conf-parameter.hpp b/src/conf-parameter.hpp
index c3573bb..9bcd95c 100644
--- a/src/conf-parameter.hpp
+++ b/src/conf-parameter.hpp
@@ -110,7 +110,6 @@
     , m_infoInterestInterval(HELLO_INTERVAL_DEFAULT)
     , m_hyperbolicState(HYPERBOLIC_STATE_OFF)
     , m_corR(0)
-    , m_corTheta(0)
     , m_maxFacesPerPrefix(MAX_FACES_PER_PREFIX_MIN)
     , m_isLog4cxxConfAvailable(false)
   {
@@ -338,12 +337,12 @@
   }
 
   void
-  setCorTheta(double ct)
+  setCorTheta(const std::vector<double>& ct)
   {
     m_corTheta = ct;
   }
 
-  double
+  std::vector<double>
   getCorTheta() const
   {
     return m_corTheta;
@@ -435,7 +434,7 @@
 
   int32_t m_hyperbolicState;
   double m_corR;
-  double m_corTheta;
+  std::vector<double> m_corTheta;
 
   uint32_t m_maxFacesPerPrefix;
 
diff --git a/src/lsa.cpp b/src/lsa.cpp
index 78e5837..ad35a81 100644
--- a/src/lsa.cpp
+++ b/src/lsa.cpp
@@ -34,7 +34,6 @@
 #include "adjacent.hpp"
 #include "logger.hpp"
 
-
 namespace nlsr {
 
 INIT_LOGGER("Lsa");
@@ -138,14 +137,14 @@
 
 CoordinateLsa::CoordinateLsa(const ndn::Name& origR, uint32_t lsn,
                              const ndn::time::system_clock::TimePoint& lt,
-                             double r, double theta)
+                             double r, std::vector<double> theta)
   : Lsa(CoordinateLsa::TYPE_STRING)
 {
   m_origRouter = origR;
   m_lsSeqNo = lsn;
   m_expirationTimePoint = lt;
   m_corRad = r;
-  m_corTheta = theta;
+  m_angles = theta;
 }
 
 const ndn::Name
@@ -159,9 +158,18 @@
 bool
 CoordinateLsa::isEqualContent(const CoordinateLsa& clsa)
 {
+  if (clsa.getCorTheta().size() != m_angles.size()) {
+    return false;
+  }
+
+  std::vector<double> m_angles2 = clsa.getCorTheta();
+  for (unsigned int i = 0; i < clsa.getCorTheta().size(); i++) {
+    if (std::abs(m_angles[i] - m_angles2[i]) > std::numeric_limits<double>::epsilon()) {
+      return false;
+    }
+  }
+
   return (std::abs(m_corRad - clsa.getCorRadius()) <
-          std::numeric_limits<double>::epsilon()) &&
-         (std::abs(m_corTheta - clsa.getCorTheta()) <
           std::numeric_limits<double>::epsilon());
 }
 
@@ -171,7 +179,12 @@
   std::ostringstream os;
   os << m_origRouter << "|" << CoordinateLsa::TYPE_STRING << "|" << m_lsSeqNo << "|"
      << ndn::time::toIsoString(m_expirationTimePoint) << "|" << m_corRad << "|"
-     << m_corTheta << "|";
+     << m_angles.size() << "|";
+
+  for (const auto& angle: m_angles) {
+    os << angle << "|";
+  }
+
   return os.str();
 }
 
@@ -186,6 +199,7 @@
   if (!(m_origRouter.size() > 0)) {
     return false;
   }
+
   try {
     if (*tok_iter++ != CoordinateLsa::TYPE_STRING) {
       return false;
@@ -194,7 +208,11 @@
     m_lsSeqNo  = boost::lexical_cast<uint32_t>(*tok_iter++);
     m_expirationTimePoint = ndn::time::fromIsoString(*tok_iter++);
     m_corRad   = boost::lexical_cast<double>(*tok_iter++);
-    m_corTheta = boost::lexical_cast<double>(*tok_iter++);
+    int numAngles = boost::lexical_cast<uint32_t>(*tok_iter++);
+
+   for (int i = 0; i < numAngles; i++) {
+     m_angles.push_back(boost::lexical_cast<double>(*tok_iter++));
+   }
   }
   catch (const std::exception& e) {
     _LOG_ERROR(e.what());
@@ -212,7 +230,10 @@
   _LOG_DEBUG("  Ls Seq No: " << m_lsSeqNo);
   _LOG_DEBUG("  Ls Lifetime: " << m_expirationTimePoint);
   _LOG_DEBUG("    Hyperbolic Radius: " << m_corRad);
-  _LOG_DEBUG("    Hyperbolic Theta: " << m_corTheta);
+  int i = 0;
+  for(auto const& value: m_angles) {
+    _LOG_DEBUG("    Hyperbolic Theta " << i++ << ": "<< value);
+  }
 }
 
 AdjLsa::AdjLsa(const ndn::Name& origR, uint32_t lsn,
diff --git a/src/lsa.hpp b/src/lsa.hpp
index 9d4f89f..4840c34 100644
--- a/src/lsa.hpp
+++ b/src/lsa.hpp
@@ -282,13 +282,12 @@
   CoordinateLsa()
     : Lsa(CoordinateLsa::TYPE_STRING)
     , m_corRad(0)
-    , m_corTheta(0)
   {
   }
 
   CoordinateLsa(const ndn::Name& origR, uint32_t lsn,
                 const ndn::time::system_clock::TimePoint& lt,
-                double r, double theta);
+                double r, std::vector<double> theta);
 
   const ndn::Name
   getKey() const;
@@ -324,16 +323,16 @@
       m_corRad = cr;
   }
 
-  double
+  const std::vector<double>
   getCorTheta() const
   {
-    return m_corTheta;
+    return m_angles;
   }
 
   void
-  setCorTheta(double ct)
+  setCorTheta(std::vector<double> ct)
   {
-    m_corTheta = ct;
+    m_angles = ct;
   }
 
   bool
@@ -344,7 +343,7 @@
 
 private:
   double m_corRad;
-  double m_corTheta;
+  std::vector<double> m_angles;
 
 public:
   static const std::string TYPE_STRING;
diff --git a/src/route/routing-table-calculator.cpp b/src/route/routing-table-calculator.cpp
index 54d486d..7aa6f48 100644
--- a/src/route/routing-table-calculator.cpp
+++ b/src/route/routing-table-calculator.cpp
@@ -483,37 +483,125 @@
     return UNKNOWN_DISTANCE;
   }
 
-  double srcTheta = srcLsa->getCorTheta();
-  double destTheta = destLsa->getCorTheta();
-
-  double diffTheta = fabs(srcTheta - destTheta);
-
-  if (diffTheta > MATH_PI) {
-    diffTheta = 2 * MATH_PI - diffTheta;
-  }
+  std::vector<double> srcTheta = srcLsa->getCorTheta();
+  std::vector<double> destTheta = destLsa->getCorTheta();
 
   double srcRadius = srcLsa->getCorRadius();
   double destRadius = destLsa->getCorRadius();
 
-  if (srcRadius == UNKNOWN_RADIUS && destRadius == UNKNOWN_RADIUS) {
+  double diffTheta = calculateAngularDistance(srcTheta, destTheta);
+
+  if (srcRadius == UNKNOWN_RADIUS || destRadius == UNKNOWN_RADIUS ||
+      diffTheta == UNKNOWN_DISTANCE) {
     return UNKNOWN_DISTANCE;
   }
 
-  if (diffTheta == 0) {
-    distance = fabs(srcRadius - destRadius);
-  }
-  else {
-    distance = acosh((cosh(srcRadius) * cosh(destRadius)) -
-                     (sinh(srcRadius) * sinh(destRadius) * cos(diffTheta)));
-  }
+  // double r_i, double r_j, double delta_theta, double zeta = 1 (default)
+  distance = calculateHyperbolicDistance(srcRadius, destRadius, diffTheta);
 
   _LOG_TRACE("Distance from " << src << " to " << dest << " is " << distance);
 
   return distance;
 }
 
-void HyperbolicRoutingCalculator::addNextHop(ndn::Name dest, std::string faceUri,
-                                             double cost, RoutingTable& rt)
+double
+HyperbolicRoutingCalculator::calculateAngularDistance(std::vector<double> angleVectorI,
+                                                      std::vector<double> angleVectorJ)
+{
+  // It is not possible for angle vector size to be zero as ensured by conf-file-processor
+
+  // https://en.wikipedia.org/wiki/N-sphere#Spherical_coordinates
+
+  // Check if two vector lengths are the same
+  if (angleVectorI.size() != angleVectorJ.size()) {
+    _LOG_ERROR("Angle vector sizes do not match");
+    return UNKNOWN_DISTANCE;
+  }
+
+  // Check if all angles are within the [0, PI] and [0, 2PI] ranges
+  if (angleVectorI.size() > 1) {
+    for (unsigned int k = 0; k < angleVectorI.size() - 1; k++) {
+      if ((angleVectorI[k] > M_PI && angleVectorI[k] < 0.0) ||
+          (angleVectorJ[k] > M_PI && angleVectorJ[k] < 0.0)) {
+        _LOG_ERROR("Angle outside [0, PI]");
+        return UNKNOWN_DISTANCE;
+      }
+    }
+  }
+  if (angleVectorI[angleVectorI.size()-1] > 2.*M_PI ||
+      angleVectorI[angleVectorI.size()-1] < 0.0) {
+    _LOG_ERROR("Angle not within [0, 2PI]");
+    return UNKNOWN_DISTANCE;
+  }
+
+  if (angleVectorI[angleVectorI.size()-1] > 2.*M_PI ||
+      angleVectorI[angleVectorI.size()-1] < 0.0) {
+    _LOG_ERROR("Angle not within [0, 2PI]");
+    return UNKNOWN_DISTANCE;
+  }
+
+  // deltaTheta = arccos(vectorI . vectorJ) -> do the inner product
+  double innerProduct = 0.0;
+
+  // Calculate x0 of the vectors
+  double x0i = std::cos(angleVectorI[0]);
+  double x0j = std::cos(angleVectorJ[0]);
+
+  // Calculate xn of the vectors
+  double xni = std::sin(angleVectorI[angleVectorI.size() - 1]);
+  double xnj = std::sin(angleVectorJ[angleVectorJ.size() - 1]);
+
+  // Do the aggregation of the (n-1) coordinates (if there is more than one angle)
+  // i.e contraction of all (n-1)-dimensional angular coordinates to one variable
+  for (unsigned int k = 0; k < angleVectorI.size() - 1; k++) {
+    xni *= std::sin(angleVectorI[k]);
+    xnj *= std::sin(angleVectorJ[k]);
+  }
+  innerProduct += (x0i * x0j) + (xni * xnj);
+
+  // If d > 1
+  if (angleVectorI.size() > 1) {
+    for (unsigned int m = 1; m < angleVectorI.size(); m++) {
+      // calculate euclidean coordinates given the angles and assuming R_sphere = 1
+      double xmi = std::cos(angleVectorI[m]);
+      double xmj = std::cos(angleVectorJ[m]);
+      for (unsigned int l = 0; l < m; l++) {
+        xmi *= std::sin(angleVectorI[l]);
+        xmj *= std::sin(angleVectorJ[l]);
+      }
+      innerProduct += xmi * xmj;
+    }
+  }
+
+  // ArcCos of the inner product gives the angular distance
+  // between two points on a d-dimensional sphere
+  return std::acos(innerProduct);
+}
+
+double
+HyperbolicRoutingCalculator::calculateHyperbolicDistance(double rI, double rJ,
+                                                         double deltaTheta)
+{
+  if (deltaTheta == UNKNOWN_DISTANCE) {
+    return UNKNOWN_DISTANCE;
+  }
+
+  // Usually, we set zeta = 1 in all experiments
+  double zeta = 1;
+
+  if (deltaTheta <= 0.0 || rI <= 0.0 || rJ <= 0.0) {
+    _LOG_ERROR("Delta theta or rI or rJ is <= 0");
+    return UNKNOWN_DISTANCE;
+  }
+
+  double xij = (1. / zeta) * std::acosh(std::cosh(zeta*rI) * std::cosh(zeta*rJ) -
+               std::sinh(zeta*rI)*std::sinh(zeta*rJ)*std::cos(deltaTheta));
+  return xij;
+}
+
+void
+HyperbolicRoutingCalculator::addNextHop(ndn::Name dest, std::string faceUri,
+                                        double cost, RoutingTable& rt)
 {
   NextHop hop(faceUri, cost);
   hop.setHyperbolic(true);
diff --git a/src/route/routing-table-calculator.hpp b/src/route/routing-table-calculator.hpp
index 680ef52..6c6ab18 100644
--- a/src/route/routing-table-calculator.hpp
+++ b/src/route/routing-table-calculator.hpp
@@ -223,6 +223,13 @@
   void
   addNextHop(ndn::Name destinationRouter, std::string faceUri, double cost, RoutingTable& rt);
 
+  double
+  calculateHyperbolicDistance(double rI, double rJ, double deltaTheta);
+
+  double
+  calculateAngularDistance(std::vector<double> angleVectorI,
+                           std::vector<double> angleVectorJ);
+
 private:
   const size_t m_nRouters;
   const bool m_isDryRun;
@@ -236,4 +243,4 @@
 
 } // namespace nlsr
 
-#endif //NLSR_ROUTING_TABLE_CALCULATOR_HPP
+#endif // NLSR_ROUTING_TABLE_CALCULATOR_HPP
diff --git a/src/tlv/coordinate-lsa.cpp b/src/tlv/coordinate-lsa.cpp
index cb32d04..92809fe 100644
--- a/src/tlv/coordinate-lsa.cpp
+++ b/src/tlv/coordinate-lsa.cpp
@@ -24,9 +24,12 @@
 
 #include <ndn-cxx/util/concepts.hpp>
 #include <ndn-cxx/encoding/block-helpers.hpp>
+#include "logger.hpp"
 
 namespace nlsr {
-namespace tlv  {
+namespace tlv {
+
+INIT_LOGGER("CoordinateLsa");
 
 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<CoordinateLsa>));
 BOOST_CONCEPT_ASSERT((ndn::WireDecodable<CoordinateLsa>));
@@ -35,7 +38,6 @@
 
 CoordinateLsa::CoordinateLsa()
   : m_hyperbolicRadius(0.0)
-  , m_hyperbolicAngle(0.0)
 {
 }
 
@@ -51,10 +53,14 @@
   size_t totalLength = 0;
   size_t doubleLength = 10;
 
-  const uint8_t* doubleBytes1 = reinterpret_cast<const uint8_t*>(&m_hyperbolicAngle);
-  totalLength += block.prependByteArrayBlock(ndn::tlv::nlsr::Double, doubleBytes1, 8);
-  totalLength += block.prependVarNumber(doubleLength);
-  totalLength += block.prependVarNumber(ndn::tlv::nlsr::HyperbolicAngle);
+  const uint8_t* doubleBytes1;
+  for (auto it = m_hyperbolicAngle.rbegin(); it != m_hyperbolicAngle.rend(); ++it) {
+    doubleBytes1 = reinterpret_cast<const uint8_t*>(&*it);
+
+    totalLength += block.prependByteArrayBlock(ndn::tlv::nlsr::Double, doubleBytes1, 8);
+    totalLength += block.prependVarNumber(doubleLength);
+    totalLength += block.prependVarNumber(ndn::tlv::nlsr::HyperbolicAngle);
+  }
 
   const uint8_t* doubleBytes2 = reinterpret_cast<const uint8_t*>(&m_hyperbolicRadius);
   totalLength += block.prependByteArrayBlock(ndn::tlv::nlsr::Double, doubleBytes2, 8);
@@ -97,7 +103,7 @@
 CoordinateLsa::wireDecode(const ndn::Block& wire)
 {
   m_hyperbolicRadius = 0.0;
-  m_hyperbolicAngle = 0.0;
+  m_hyperbolicAngle.clear();
 
   m_wire = wire;
 
@@ -105,7 +111,7 @@
     std::stringstream error;
     error << "Expected CoordinateLsa Block, but Block is of a different type: #"
           << m_wire.type();
-    throw Error(error.str());
+    BOOST_THROW_EXCEPTION(Error(error.str()));
   }
 
   m_wire.parse();
@@ -117,7 +123,8 @@
     ++val;
   }
   else {
-    throw Error("Missing required LsaInfo field");
+    std::cout << "Missing required LsaInfo field" << std::endl;
+    BOOST_THROW_EXCEPTION(Error("Missing required LsaInfo field"));
   }
 
   if (val != m_wire.elements_end() && val->type() == ndn::tlv::nlsr::HyperbolicRadius) {
@@ -125,31 +132,33 @@
     ndn::Block::element_const_iterator it = val->elements_begin();
     if (it != val->elements_end() && it->type() == ndn::tlv::nlsr::Double) {
       m_hyperbolicRadius = *reinterpret_cast<const double*>(it->value());
+
+      ++val;
     }
     else {
-      throw Error("HyperbolicRadius: Missing required Double field");
+      std::cout << "HyperbolicRadius: Missing required Double field" << std::endl;
+      BOOST_THROW_EXCEPTION(Error("HyperbolicRadius: Missing required Double field"));
     }
-
-    ++val;
   }
   else {
-    throw Error("Missing required HyperbolicRadius field");
+    std::cout << "Missing required HyperbolicRadius field" << std::endl;
+    BOOST_THROW_EXCEPTION(Error("Missing required HyperbolicRadius field"));
   }
 
-  if (val != m_wire.elements_end() && val->type() == ndn::tlv::nlsr::HyperbolicAngle) {
-    val->parse();
-    ndn::Block::element_const_iterator it = val->elements_begin();
-    if (it != val->elements_end() && it->type() == ndn::tlv::nlsr::Double) {
-      m_hyperbolicAngle = *reinterpret_cast<const double*>(it->value());
-    }
-    else {
-      throw Error("HyperbolicAngle: Missing required Double field");
-    }
+  for (; val != m_wire.elements_end(); ++val) {
+    if (val->type() == ndn::tlv::nlsr::HyperbolicAngle) {
+      val->parse();
 
-    ++val;
-  }
-  else {
-    throw Error("Missing required HyperbolicAngle field");
+      for (auto it = val->elements_begin(); it != val->elements_end(); ++it) {
+        if (it->type() == ndn::tlv::nlsr::Double) {
+          m_hyperbolicAngle.push_back(*reinterpret_cast<const double*>(it->value()));
+        }
+        else {
+          std::cout << "HyperbolicAngle: Missing required Double field" << std::endl;
+          BOOST_THROW_EXCEPTION(Error("HyperbolicAngle: Missing required Double field"));
+        }
+      }
+    }
   }
 }
 
@@ -158,8 +167,20 @@
 {
   os << "CoordinateLsa("
      << coordinateLsa.getLsaInfo() << ", "
-     << "HyperbolicRadius: " << coordinateLsa.getHyperbolicRadius() << ", "
-     << "HyperbolicAngle: " << coordinateLsa.getHyperbolicAngle() << ")";
+     << "HyperbolicRadius: " << coordinateLsa.getHyperbolicRadius() << ", ";
+
+  os << "HyperbolicAngles: ";
+  int i = 0;
+  for (const auto& value: coordinateLsa.getHyperbolicAngle()) {
+    if (i == 0) {
+      os << value;
+    }
+    else {
+      os << ", " << value;
+    }
+    ++i;
+  }
+  os << ")";
 
   return os;
 }
diff --git a/src/tlv/coordinate-lsa.hpp b/src/tlv/coordinate-lsa.hpp
index 2e5d813..0568cc1 100644
--- a/src/tlv/coordinate-lsa.hpp
+++ b/src/tlv/coordinate-lsa.hpp
@@ -39,9 +39,9 @@
    CoordinateLsa := COORDINATE-LSA-TYPE TLV-LENGTH
                       LsaInfo
                       HyperbolicRadius
-                      HyperbolicAngle
+                      HyperbolicAngle+
 
-   \sa http://redmine.named-data.net/projects/nlsr/wiki/LSDB_DataSet
+   \sa https://redmine.named-data.net/projects/nlsr/wiki/LSDB_DataSet
  */
 class CoordinateLsa
 {
@@ -89,14 +89,14 @@
     return *this;
   }
 
-  double
+  const std::vector<double>
   getHyperbolicAngle() const
   {
     return m_hyperbolicAngle;
   }
 
   CoordinateLsa&
-  setHyperbolicAngle(double hyperbolicAngle)
+  setHyperbolicAngle(const std::vector<double>& hyperbolicAngle)
   {
     m_hyperbolicAngle = hyperbolicAngle;
     m_wire.reset();
@@ -116,7 +116,7 @@
 private:
   LsaInfo m_lsaInfo;
   double m_hyperbolicRadius;
-  double m_hyperbolicAngle;
+  std::vector<double> m_hyperbolicAngle;
 
   mutable ndn::Block m_wire;
 };
diff --git a/tests/publisher/publisher-fixture.hpp b/tests/publisher/publisher-fixture.hpp
index eba000d..30ffa7c 100644
--- a/tests/publisher/publisher-fixture.hpp
+++ b/tests/publisher/publisher-fixture.hpp
@@ -84,7 +84,7 @@
   }
 
   CoordinateLsa
-  createCoordinateLsa(const std::string& origin, double radius, double angle)
+  createCoordinateLsa(const std::string& origin, double radius, std::vector<double> angle)
   {
     CoordinateLsa lsa(origin, 1, ndn::time::system_clock::now(),
                       radius, angle);
@@ -109,7 +109,7 @@
     checkTlvLsaInfo(tlvLsa.getLsaInfo(), lsa);
 
     BOOST_CHECK_EQUAL(tlvLsa.getHyperbolicRadius(), lsa.getCorRadius());
-    BOOST_CHECK_EQUAL(tlvLsa.getHyperbolicAngle(), lsa.getCorTheta());
+    BOOST_CHECK(tlvLsa.getHyperbolicAngle() == lsa.getCorTheta());
   }
 
   void
diff --git a/tests/publisher/test-lsa-publisher.cpp b/tests/publisher/test-lsa-publisher.cpp
index 8ee842b..6a3045f 100644
--- a/tests/publisher/test-lsa-publisher.cpp
+++ b/tests/publisher/test-lsa-publisher.cpp
@@ -76,13 +76,20 @@
 {
   ndn::Name thisRouter("/RouterA");
 
-  CoordinateLsa routerALsa = createCoordinateLsa(thisRouter.toUri(), 10.0, 20.0);
+  std::vector<double> anglesA, anglesB, anglesC;
+  anglesA.push_back(20.00);
+  anglesB.push_back(543.21);
+  // Setting two angles for testing routerCLsa
+  anglesC.push_back(0.02);
+  anglesC.push_back(1.23);
+
+  CoordinateLsa routerALsa = createCoordinateLsa(thisRouter.toUri(), 10.0, anglesA);
   lsdb.installCoordinateLsa(routerALsa);
 
-  CoordinateLsa routerBLsa = createCoordinateLsa("/RouterB", 123.45, 543.21);
+  CoordinateLsa routerBLsa = createCoordinateLsa("/RouterB", 123.45, anglesB);
   lsdb.installCoordinateLsa(routerBLsa);
 
-  CoordinateLsa routerCLsa = createCoordinateLsa("/RouterC", 0.01, 0.02);
+  CoordinateLsa routerCLsa = createCoordinateLsa("/RouterC", 0.01, anglesC);
   lsdb.installCoordinateLsa(routerCLsa);
 
   CoordinateLsaPublisher publisher(lsdb, *face, keyChain);
diff --git a/tests/publisher/test-lsdb-dataset-interest-handler.cpp b/tests/publisher/test-lsdb-dataset-interest-handler.cpp
index 8e9855a..e08d1f2 100644
--- a/tests/publisher/test-lsdb-dataset-interest-handler.cpp
+++ b/tests/publisher/test-lsdb-dataset-interest-handler.cpp
@@ -70,8 +70,10 @@
   addAdjacency(adjLsa, "/RouterA/adjacency1", "udp://face-1", 10);
   lsdb.installAdjLsa(adjLsa);
 
+  std::vector<double> angles = {20.00, 30.00};
+
   // Install coordinate LSA
-  CoordinateLsa coordinateLsa = createCoordinateLsa("/RouterA", 10.0, 20.0);
+  CoordinateLsa coordinateLsa = createCoordinateLsa("/RouterA", 10.0, angles);
   lsdb.installCoordinateLsa(coordinateLsa);
 
   // Install Name LSA
diff --git a/tests/publisher/test-lsdb-status-publisher.cpp b/tests/publisher/test-lsdb-status-publisher.cpp
index be21670..c9412ad 100644
--- a/tests/publisher/test-lsdb-status-publisher.cpp
+++ b/tests/publisher/test-lsdb-status-publisher.cpp
@@ -49,14 +49,18 @@
   addAdjacency(routerBAdjLsa, "/RouterB/adjacency3", "udp://face-3", 30);
   lsdb.installAdjLsa(routerBAdjLsa);
 
+  std::vector<double> anglesA = {20.00},
+                      anglesB = {543.21},
+                      anglesC = {0.02, 2.25};
+
   // Install coordinate LSAs
-  CoordinateLsa routerACorLsa = createCoordinateLsa("/RouterA", 10.0, 20.0);
+  CoordinateLsa routerACorLsa = createCoordinateLsa("/RouterA", 10.0, anglesA);
   lsdb.installCoordinateLsa(routerACorLsa);
 
-  CoordinateLsa routerBCorLsa = createCoordinateLsa("/RouterB", 123.45, 543.21);
+  CoordinateLsa routerBCorLsa = createCoordinateLsa("/RouterB", 123.45, anglesB);
   lsdb.installCoordinateLsa(routerBCorLsa);
 
-  CoordinateLsa routerCCorLsa = createCoordinateLsa("/RouterC", 0.01, 0.02);
+  CoordinateLsa routerCCorLsa = createCoordinateLsa("/RouterC", 0.01, anglesC);
   lsdb.installCoordinateLsa(routerCCorLsa);
 
   // Install Name LSAs
diff --git a/tests/test-conf-file-processor.cpp b/tests/test-conf-file-processor.cpp
index e818ac3..df347f1 100644
--- a/tests/test-conf-file-processor.cpp
+++ b/tests/test-conf-file-processor.cpp
@@ -98,6 +98,14 @@
   "  angle    1.45\n"
   "}\n\n";
 
+const std::string SECTION_HYPERBOLIC_ANGLES_ON =
+    "hyperbolic\n"
+    "{\n"
+    "  state on\n"
+    "  radius   123.456\n"
+    "  angle    1.45,2.25\n"
+    "}\n\n";
+
 const std::string SECTION_HYPERBOLIC_OFF =
   "hyperbolic\n"
   "{\n"
@@ -128,6 +136,10 @@
 const std::string CONFIG_HYPERBOLIC = SECTION_GENERAL + SECTION_NEIGHBORS +
                                       SECTION_HYPERBOLIC_ON + SECTION_FIB + SECTION_ADVERTISING;
 
+const std::string CONFIG_HYPERBOLIC_ANGLES = SECTION_GENERAL + SECTION_NEIGHBORS +
+                                             SECTION_HYPERBOLIC_ANGLES_ON + SECTION_FIB +
+                                             SECTION_ADVERTISING;
+
 class ConfFileProcessorFixture : public BaseFixture
 {
 public:
@@ -365,7 +377,22 @@
   ConfParameter& conf = nlsr.getConfParameter();
   BOOST_CHECK_EQUAL(conf.getHyperbolicState(), 1);
   BOOST_CHECK_EQUAL(conf.getCorR(), 123.456);
-  BOOST_CHECK_EQUAL(conf.getCorTheta(), 1.45);
+  std::vector<double> angles;
+  angles.push_back(1.45);
+  BOOST_CHECK(conf.getCorTheta() == angles);
+}
+
+BOOST_AUTO_TEST_CASE(Hyperbolic2)
+{
+  processConfigurationString(CONFIG_HYPERBOLIC_ANGLES);
+
+  ConfParameter& conf = nlsr.getConfParameter();
+  BOOST_CHECK_EQUAL(conf.getHyperbolicState(), 1);
+  BOOST_CHECK_EQUAL(conf.getCorR(), 123.456);
+  std::vector<double> angles;
+  angles.push_back(1.45);
+  angles.push_back(2.25);
+  BOOST_CHECK(conf.getCorTheta() == angles);
 }
 
 BOOST_AUTO_TEST_CASE(DefaultValuesGeneral)
diff --git a/tests/test-conf-parameter.cpp b/tests/test-conf-parameter.cpp
index 2d69a27..55a65b1 100644
--- a/tests/test-conf-parameter.cpp
+++ b/tests/test-conf-parameter.cpp
@@ -61,7 +61,9 @@
 
   cp1.setCorR(2.5);
 
-  cp1.setCorTheta(102.5);
+  std::vector<double> angles = {102.5};
+
+  cp1.setCorTheta(angles);
 
   cp1.setInfoInterestInterval(3);
 
@@ -89,7 +91,7 @@
 
   BOOST_CHECK_EQUAL(cp1.getHyperbolicState(), 1);
 
-  BOOST_CHECK_CLOSE(cp1.getCorTheta(), 102.5, 0.0001);
+  BOOST_CHECK(cp1.getCorTheta() == angles);
 
   BOOST_CHECK_EQUAL(cp1.getInfoInterestInterval(), 3);
 }
diff --git a/tests/test-hyperbolic-calculator.cpp b/tests/test-hyperbolic-calculator.cpp
index 0001ae4..030fe25 100644
--- a/tests/test-hyperbolic-calculator.cpp
+++ b/tests/test-hyperbolic-calculator.cpp
@@ -51,11 +51,11 @@
     , adjacencies(nlsr.getAdjacencyList())
     , lsdb(nlsr.getLsdb())
   {
-    setUpTopology();
   }
 
   // Triangle topology with routers A, B, C connected
-  void setUpTopology()
+  void setUpTopology(std::vector<double> anglesA, std::vector<double> anglesB,
+                     std::vector<double> anglesC)
   {
     INIT_LOGGERS("/tmp", "TRACE");
 
@@ -70,7 +70,8 @@
     AdjLsa adjA(a.getName(), 1, MAX_TIME, 2, adjacencies);
     lsdb.installAdjLsa(adjA);
 
-    CoordinateLsa coordA(adjA.getOrigRouter(), 1, MAX_TIME, 16.23, 2.97);
+
+    CoordinateLsa coordA(adjA.getOrigRouter(), 1, MAX_TIME, 16.23, anglesA);
     lsdb.installCoordinateLsa(coordA);
 
     // Router B
@@ -84,7 +85,7 @@
     AdjLsa adjB(b.getName(), 1, MAX_TIME, 2, adjacencyListB);
     lsdb.installAdjLsa(adjB);
 
-    CoordinateLsa coordB(adjB.getOrigRouter(), 1, MAX_TIME, 16.59, 3.0);
+    CoordinateLsa coordB(adjB.getOrigRouter(), 1, MAX_TIME, 16.59, anglesB);
     lsdb.installCoordinateLsa(coordB);
 
     // Router C
@@ -98,12 +99,55 @@
     AdjLsa adjC(c.getName(), 1, MAX_TIME, 2, adjacencyListC);
     lsdb.installAdjLsa(adjC);
 
-    CoordinateLsa coordC(adjC.getOrigRouter(), 1, MAX_TIME, 14.11, 2.99);
+    CoordinateLsa coordC(adjC.getOrigRouter(), 1, MAX_TIME, 14.11, anglesC);
     lsdb.installCoordinateLsa(coordC);
 
     map.createFromAdjLsdb(nlsr);
   }
 
+  void runTest(const double& expectedCost)
+  {
+    HyperbolicRoutingCalculator calculator(map.getMapSize(), false, ROUTER_A_NAME);
+    calculator.calculatePaths(map, routingTable, lsdb, adjacencies);
+
+    RoutingTableEntry* entryB = routingTable.findRoutingTableEntry(ROUTER_B_NAME);
+
+    // Router A should be able to get to B through B with cost 0 and to B through C
+    NexthopList& bHopList = entryB->getNexthopList();
+    BOOST_REQUIRE_EQUAL(bHopList.getNextHops().size(), 2);
+
+    for (std::set<NextHop, NextHopComparator>::iterator it = bHopList.begin(); it != bHopList.end(); ++it) {
+      std::string faceUri = it->getConnectingFaceUri();
+      uint64_t cost = it->getRouteCostAsAdjustedInteger();
+
+      BOOST_CHECK((faceUri == ROUTER_B_FACE && cost == 0) ||
+                  (faceUri == ROUTER_C_FACE && cost == applyHyperbolicFactorAndRound(expectedCost)));
+    }
+
+    RoutingTableEntry* entryC = routingTable.findRoutingTableEntry(ROUTER_C_NAME);
+
+    // Router A should be able to get to C through C with cost 0 and to C through B
+    NexthopList& cHopList = entryC->getNexthopList();
+    BOOST_REQUIRE_EQUAL(cHopList.getNextHops().size(), 2);
+
+    for (std::set<NextHop, NextHopComparator>::iterator it = cHopList.begin(); it != cHopList.end(); ++it) {
+      std::string faceUri = it->getConnectingFaceUri();
+      uint64_t cost = it->getRouteCostAsAdjustedInteger();
+
+      BOOST_CHECK((faceUri == ROUTER_B_FACE && cost == applyHyperbolicFactorAndRound(expectedCost)) ||
+                  (faceUri == ROUTER_C_FACE && cost == 0));
+    }
+  }
+
+  uint64_t
+  applyHyperbolicFactorAndRound(double d)
+  {
+    // Hyperbolic costs in the tests were calculated with 1*10^-9 precision.
+    // A factor larger than 1*10^9 will cause the tests to fail.
+    BOOST_REQUIRE(NextHop::HYPERBOLIC_COST_ADJUSTMENT_FACTOR <= 1000000000);
+    return round(NextHop::HYPERBOLIC_COST_ADJUSTMENT_FACTOR*d);
+  }
+
 public:
   std::shared_ptr<ndn::util::DummyClientFace> face;
   Nlsr nlsr;
@@ -130,49 +174,26 @@
 const std::string HyperbolicCalculatorFixture::ROUTER_B_FACE = "udp4://10.0.0.2";
 const std::string HyperbolicCalculatorFixture::ROUTER_C_FACE = "udp4://10.0.0.3";
 
-uint64_t
-applyHyperbolicFactorAndRound(double d)
-{
-  // Hyperbolic costs in the tests were calculated with 1*10^-9 precision.
-  // A factor larger than 1*10^9 will cause the tests to fail.
-  BOOST_REQUIRE(NextHop::HYPERBOLIC_COST_ADJUSTMENT_FACTOR <= 1000000000);
-  return round(NextHop::HYPERBOLIC_COST_ADJUSTMENT_FACTOR*d);
-}
-
 BOOST_FIXTURE_TEST_SUITE(TestHyperbolicRoutingCalculator, HyperbolicCalculatorFixture)
 
 BOOST_AUTO_TEST_CASE(Basic)
 {
-  HyperbolicRoutingCalculator calculator(map.getMapSize(), false, ROUTER_A_NAME);
-  calculator.calculatePaths(map, routingTable, lsdb, adjacencies);
+  std::vector<double> anglesA = {2.97},
+                      anglesB = {3.0},
+                      anglesC = {2.99};
+  setUpTopology(anglesA, anglesB, anglesC);
 
-  RoutingTableEntry* entryB = routingTable.findRoutingTableEntry(ROUTER_B_NAME);
+  runTest(20.103356956);
+}
 
-  // Router A should be able to get to B through B with cost 0 and to B through C
-  NexthopList& bHopList = entryB->getNexthopList();
-  BOOST_REQUIRE_EQUAL(bHopList.getNextHops().size(), 2);
+BOOST_AUTO_TEST_CASE(BasicMultipleAngles)
+{
+  std::vector<double> anglesA = {2.97,1.22},
+                      anglesB = {3.0, 0.09},
+                      anglesC = {321, 2.99};
+  setUpTopology(anglesA, anglesB, anglesC);
 
-  for (std::set<NextHop, NextHopComparator>::iterator it = bHopList.begin(); it != bHopList.end(); ++it) {
-    std::string faceUri = it->getConnectingFaceUri();
-    uint64_t cost = it->getRouteCostAsAdjustedInteger();
-
-    BOOST_CHECK((faceUri == ROUTER_B_FACE && cost == 0) ||
-                (faceUri == ROUTER_C_FACE && cost == applyHyperbolicFactorAndRound(20.103356956)));
-  }
-
-  RoutingTableEntry* entryC = routingTable.findRoutingTableEntry(ROUTER_C_NAME);
-
-  // Router A should be able to get to C through C with cost 0 and to C through B
-  NexthopList& cHopList = entryC->getNexthopList();
-  BOOST_REQUIRE_EQUAL(cHopList.getNextHops().size(), 2);
-
-  for (std::set<NextHop, NextHopComparator>::iterator it = cHopList.begin(); it != cHopList.end(); ++it) {
-    std::string faceUri = it->getConnectingFaceUri();
-    uint64_t cost = it->getRouteCostAsAdjustedInteger();
-
-    BOOST_CHECK((faceUri == ROUTER_B_FACE && cost == applyHyperbolicFactorAndRound(20.103356956)) ||
-                (faceUri == ROUTER_C_FACE && cost == 0));
-  }
+  runTest(30.655296361);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/test-lsa.cpp b/tests/test-lsa.cpp
index d7c97f9..e501c59 100644
--- a/tests/test-lsa.cpp
+++ b/tests/test-lsa.cpp
@@ -100,11 +100,14 @@
 BOOST_AUTO_TEST_CASE(CoordinateLsaConstructorAndGetters)
 {
   ndn::time::system_clock::TimePoint testTimePoint =  ndn::time::system_clock::now();
-  CoordinateLsa clsa1("router1", 12, testTimePoint, 2.5, 30.0);
-  CoordinateLsa clsa2("router1", 12, testTimePoint, 2.5, 30.0);
+  std::vector<double> angles1, angles2;
+  angles1.push_back(30.0);
+  angles2.push_back(30.0);
+  CoordinateLsa clsa1("router1", 12, testTimePoint, 2.5, angles1);
+  CoordinateLsa clsa2("router1", 12, testTimePoint, 2.5, angles2);
 
   BOOST_CHECK_CLOSE(clsa1.getCorRadius(), 2.5, 0.0001);
-  BOOST_CHECK_CLOSE(clsa1.getCorTheta(), 30.0, 0.0001);
+  BOOST_CHECK(clsa1.getCorTheta() == angles1);
 
   BOOST_CHECK(clsa1.isEqualContent(clsa2));
 
@@ -124,7 +127,6 @@
   adjList.insert(adj2);
 
   ndn::time::system_clock::TimePoint testTimePoint = ndn::time::system_clock::now();
-
   std::ostringstream ss;
   ss << testTimePoint;
 
@@ -133,21 +135,21 @@
   AdjLsa lsa("router1", 12, testTimePoint, adjList.getSize(), adjList);
 
   std::string EXPECTED_OUTPUT =
-    "Adj Lsa:\n"
-    "  Origination Router: /router1\n"
-    "  Ls Type: adjacency\n"
-    "  Ls Seq No: 12\n"
-    "  Ls Lifetime: " + TEST_TIME_POINT_STRING + "\n"
-    "  Adjacents: \n"
-    "    Adjacent 1:\n"
-    "      Adjacent Name: /adjacent1\n"
-    "      Connecting FaceUri: ://\n"
-    "      Link Cost: 10\n"
-    "    Adjacent 2:\n"
-    "      Adjacent Name: /adjacent2\n"
-    "      Connecting FaceUri: ://\n"
-    "      Link Cost: 10\n"
-    "adj_lsa_end";
+   "Adj Lsa:\n"
+   "  Origination Router: /router1\n"
+   "  Ls Type: adjacency\n"
+   "  Ls Seq No: 12\n"
+   "  Ls Lifetime: " + TEST_TIME_POINT_STRING + "\n"
+   "  Adjacents: \n"
+   "    Adjacent 1:\n"
+   "      Adjacent Name: /adjacent1\n"
+   "      Connecting FaceUri: ://\n"
+   "      Link Cost: 10\n"
+   "    Adjacent 2:\n"
+   "      Adjacent Name: /adjacent2\n"
+   "      Connecting FaceUri: ://\n"
+   "      Link Cost: 10\n"
+   "adj_lsa_end";
 
   std::ostringstream os;
   os << lsa;
@@ -200,7 +202,8 @@
   BOOST_CHECK_EQUAL(nlsa1.getData(), nlsa2.getData());
 
   //Coordinate LSA
-  CoordinateLsa clsa1("router1", 12, testTimePoint, 2.5, 30.0);
+  std::vector<double> angles = {30, 40.0};
+  CoordinateLsa clsa1("router1", 12, testTimePoint, 2.5, angles);
   CoordinateLsa clsa2;
 
   BOOST_CHECK(clsa2.initializeFromContent(clsa1.getData()));
diff --git a/tests/test-sync-logic-handler.cpp b/tests/test-sync-logic-handler.cpp
index 76ca830..6e47e1c 100644
--- a/tests/test-sync-logic-handler.cpp
+++ b/tests/test-sync-logic-handler.cpp
@@ -209,9 +209,11 @@
                 3 , adjList);
   lsdb.installAdjLsa(adjLsa);
 
+  std::vector<double> angles = {0.0};
+
   // Install Cor LSA
   CoordinateLsa corLsa(originRouter, 1000, ndn::time::system_clock::TimePoint::max(),
-                       0,0);
+                       0, angles);
   lsdb.installCoordinateLsa(corLsa);
 
   std::string updateName = nlsr.getConfParameter().getLsaPrefix().toUri() +
diff --git a/tests/tlv/test-coordinate-lsa.cpp b/tests/tlv/test-coordinate-lsa.cpp
index f7789c4..fa05d95 100644
--- a/tests/tlv/test-coordinate-lsa.cpp
+++ b/tests/tlv/test-coordinate-lsa.cpp
@@ -24,7 +24,7 @@
 #include "../boost-test.hpp"
 
 namespace nlsr {
-namespace tlv  {
+namespace tlv {
 namespace test {
 
 BOOST_AUTO_TEST_SUITE(TlvTestCoordinateLsa)
@@ -53,7 +53,9 @@
   coordinateLsa.setLsaInfo(lsaInfo);
 
   coordinateLsa.setHyperbolicRadius(1.65);
-  coordinateLsa.setHyperbolicAngle(1.78);
+  std::vector<double> angles;
+  angles.push_back(1.78);
+  coordinateLsa.setHyperbolicAngle(angles);
 
   const ndn::Block& wire = coordinateLsa.wireEncode();
 
@@ -73,7 +75,8 @@
   BOOST_REQUIRE_EQUAL(coordinateLsa.getLsaInfo().getExpirationPeriod(),
                       ndn::time::milliseconds(10000));
   BOOST_REQUIRE_EQUAL(coordinateLsa.getHyperbolicRadius(), 1.65);
-  BOOST_REQUIRE_EQUAL(coordinateLsa.getHyperbolicAngle(), 1.78);
+  std::vector<double> angles = {1.78};
+  BOOST_REQUIRE(coordinateLsa.getHyperbolicAngle() == angles);
 }
 
 BOOST_AUTO_TEST_CASE(CoordinateLsaOutputStream)
@@ -87,7 +90,8 @@
   coordinateLsa.setLsaInfo(lsaInfo);
 
   coordinateLsa.setHyperbolicRadius(1.65);
-  coordinateLsa.setHyperbolicAngle(1.78);
+  std::vector<double> angles = {1.78};
+  coordinateLsa.setHyperbolicAngle(angles);
 
   std::ostringstream os;
   os << coordinateLsa;
@@ -97,9 +101,23 @@
                                         "SequenceNumber: 128, "
                                         "ExpirationPeriod: 10000 milliseconds), "
                                 "HyperbolicRadius: 1.65, "
-                                "HyperbolicAngle: 1.78)");
+                                "HyperbolicAngles: 1.78)");
+
+  angles.push_back(3.21);
+  coordinateLsa.setHyperbolicAngle(angles);
+
+  std::ostringstream os2;
+  os2 << coordinateLsa;
+
+  BOOST_CHECK_EQUAL(os2.str(), "CoordinateLsa("
+                                "LsaInfo(OriginRouter: /test, "
+                                        "SequenceNumber: 128, "
+                                        "ExpirationPeriod: 10000 milliseconds), "
+                                "HyperbolicRadius: 1.65, "
+                                "HyperbolicAngles: 1.78, 3.21)");
 }
 
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace test
diff --git a/tests/tlv/test-lsdb-status.cpp b/tests/tlv/test-lsdb-status.cpp
index 5b15fa0..12284e8 100644
--- a/tests/tlv/test-lsdb-status.cpp
+++ b/tests/tlv/test-lsdb-status.cpp
@@ -22,9 +22,11 @@
 #include "tlv/lsdb-status.hpp"
 
 #include "../boost-test.hpp"
+#include <boost/mpl/vector.hpp>
+#include <boost/lexical_cast.hpp>
 
 namespace nlsr {
-namespace tlv  {
+namespace tlv {
 namespace test {
 
 BOOST_AUTO_TEST_SUITE(TlvTestLsdbStatus)
@@ -123,7 +125,9 @@
   coordinateLsa.setLsaInfo(lsaInfo);
 
   coordinateLsa.setHyperbolicRadius(1.65);
-  coordinateLsa.setHyperbolicAngle(1.78);
+  std::vector<double> angles;
+  angles.push_back(1.78);
+  coordinateLsa.setHyperbolicAngle(angles);
 
   lsdbStatus.addCoordinateLsa(coordinateLsa);
 
@@ -183,7 +187,9 @@
   BOOST_CHECK_EQUAL(lsaInfo.getExpirationPeriod(), ndn::time::milliseconds(10000));
 
   BOOST_REQUIRE_EQUAL(it3->getHyperbolicRadius(), 1.65);
-  BOOST_REQUIRE_EQUAL(it3->getHyperbolicAngle(), 1.78);
+  std::vector<double> angles;
+  angles.push_back(1.78);
+  BOOST_REQUIRE(it3->getHyperbolicAngle() == angles);
 
   BOOST_CHECK_EQUAL(lsdbStatus.hasCoordinateLsas(), true);
 
@@ -250,7 +256,9 @@
   coordinateLsa.setLsaInfo(lsaInfo);
 
   coordinateLsa.setHyperbolicRadius(1.65);
-  coordinateLsa.setHyperbolicAngle(1.78);
+  std::vector<double> angles;
+  angles.push_back(1.78);
+  coordinateLsa.setHyperbolicAngle(angles);
 
   lsdbStatus.addCoordinateLsa(coordinateLsa);
   BOOST_CHECK_EQUAL(lsdbStatus.hasCoordinateLsas(), true);
@@ -268,8 +276,23 @@
   BOOST_CHECK_EQUAL(lsdbStatus.hasNameLsas(), false);
 }
 
-BOOST_AUTO_TEST_CASE(LsdbStatusOutputStream)
+class Theta
 {
+public:
+  std::vector<double> angles = {1.78};
+};
+
+class ThetaAndPhi
+{
+public:
+  std::vector<double> angles = {1.78, 3.21};
+};
+
+typedef boost::mpl::vector<Theta, ThetaAndPhi> HyperbolicAngleVectorFixture;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(LsdbStatusOutputStream, HRAngleVector, HyperbolicAngleVectorFixture)
+{
+  HRAngleVector angleVector;
   LsdbStatus lsdbStatus;
 
   LsaInfo lsaInfo;
@@ -289,22 +312,34 @@
 
   lsdbStatus.addAdjacencyLsa(adjacencyLsa);
 
-  // CoordinateLsa
-  CoordinateLsa coordinateLsa;
-  coordinateLsa.setLsaInfo(lsaInfo);
-
-  coordinateLsa.setHyperbolicRadius(1.65);
-  coordinateLsa.setHyperbolicAngle(1.78);
-
-  lsdbStatus.addCoordinateLsa(coordinateLsa);
-
-  // NameLsa
+    // NameLsa
   NameLsa nameLsa;
   nameLsa.setLsaInfo(lsaInfo);
   nameLsa.addName("name1");
 
   lsdbStatus.addNameLsa(nameLsa);
 
+  // CoordinateLsa
+  CoordinateLsa coordinateLsa;
+  coordinateLsa.setLsaInfo(lsaInfo);
+
+  coordinateLsa.setHyperbolicRadius(1.65);
+  coordinateLsa.setHyperbolicAngle(angleVector.angles);
+
+  std::string outputAngles = "HyperbolicAngles: ";
+  for (uint i = 0; i < angleVector.angles.size(); i++) {
+    std::string angle = boost::lexical_cast<std::string>(angleVector.angles[i]);
+    if (i == angleVector.angles.size()-1) {
+      outputAngles += angle;
+    }
+    else {
+      outputAngles += angle + ", ";
+    }
+  }
+  outputAngles += "), ";
+
+  lsdbStatus.addCoordinateLsa(coordinateLsa);
+
   std::ostringstream os;
   os << lsdbStatus;
 
@@ -321,7 +356,7 @@
                                     "SequenceNumber: 128, "
                                     "ExpirationPeriod: 10000 milliseconds), "
                                   "HyperbolicRadius: 1.65, "
-                                  "HyperbolicAngle: 1.78), "
+                                  + outputAngles +
                                 "NameLsa("
                                   "LsaInfo("
                                     "OriginRouter: /test, "
diff --git a/tools/nlsrc.cpp b/tools/nlsrc.cpp
index 1edb5f7..e60efc2 100644
--- a/tools/nlsrc.cpp
+++ b/tools/nlsrc.cpp
@@ -297,8 +297,11 @@
 
   os << getLsaInfoString(lsa.getLsaInfo()) << std::endl;
 
-  os << "      angle=" << lsa.getHyperbolicAngle() << std::endl;
-  os << "      radius=" << lsa.getHyperbolicRadius() << std::endl;
+  int i = 0;
+  for (auto const& value: lsa.getHyperbolicAngle()) {
+    os << "    Hyp Angle " << i++ << ": "<< value << " ";
+  }
+  os << "\n   radius=" << lsa.getHyperbolicRadius() << std::endl;
 
   router.coordinateLsaString = os.str();
 }