Spatial C++ Library
Generic Multi-Dimensional Containers and Spatial Operations
spatial_math.hpp
1 // -*- C++ -*-
2 //
3 // Copyright Sylvain Bougerel 2009 - 2013.
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file COPYING or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
22 #ifndef SPATIAL_MATH_HPP
23 #define SPATIAL_MATH_HPP
24 
25 #include <limits>
26 #include <cmath>
27 #include <sstream>
28 #include "spatial_import_type_traits.hpp"
29 #include "../exception.hpp"
30 #include "spatial_check_concept.hpp"
31 
32 namespace spatial
33 {
34  namespace except
35  {
40  template<typename Tp>
41  inline typename enable_if<import::is_arithmetic<Tp> >::type
43  {
44  if (x < Tp()) // Tp() == 0 by convention
45  {
46  std::stringstream out;
47  out << x << " is negative";
48  throw invalid_distance(out.str());
49  }
50  }
51 
69  template <typename Tp>
71  inline typename enable_if_c
72  <std::numeric_limits<Tp>::is_integer
73  && std::numeric_limits<Tp>::is_signed, Tp>::type
74  check_abs(Tp x)
75  {
76  if (x == (std::numeric_limits<Tp>::min)())
77  {
78  std::stringstream out;
79  out << "absolute of " << x << " caused overflow";
80  throw arithmetic_error(out.str());
81  }
82  return std::abs(x);
83  }
84 
85  template <typename Tp>
86  inline typename enable_if_c
87  <!std::numeric_limits<Tp>::is_integer
88  || !std::numeric_limits<Tp>::is_signed, Tp>::type
89  check_abs(Tp x)
90  { return std::abs(x); }
92 
107  template <typename Tp>
108  inline typename enable_if<import::is_arithmetic<Tp>, Tp>::type
109  check_positive_add(Tp x, Tp y)
110  {
111  // The additional bracket is to avoid conflict with MSVC min/max macros
112  if (((std::numeric_limits<Tp>::max)() - x) < y)
113  {
114  std::stringstream out;
115  out << x << " + " << y << " caused overflow";
116  throw arithmetic_error(out.str());
117  }
118  return x + y;
119  }
120 
131  template <typename Tp>
132  inline typename enable_if<import::is_arithmetic<Tp>, Tp>::type
134  {
135  Tp zero = Tp(); // get 0
136  if (x != zero)
137  {
138  Tp abs = check_abs(x);
139  if (((std::numeric_limits<Tp>::max)() / abs) < abs)
140  {
141  std::stringstream out;
142  out << "square(" << x << ") caused overflow";
143  throw arithmetic_error(out.str());
144  }
145  return x * x;
146  }
147  return zero;
148  }
149 
164  template <typename Tp>
165  inline typename enable_if<import::is_arithmetic<Tp>, Tp>::type
166  check_positive_mul(Tp x, Tp y)
167  {
168  Tp zero = Tp(); // get 0
169  if (x != zero)
170  {
171  if (((std::numeric_limits<Tp>::max)() / x) < y)
172  {
173  std::stringstream out;
174  out << x << " * " << y << " caused overflow";
175  throw arithmetic_error(out.str());
176  }
177  return x * y;
178  }
179  return zero;
180  }
181  } // spatial except
182 
183  namespace math
184  {
190  template <typename Key, typename Difference, typename Unit>
191  inline typename enable_if<import::is_arithmetic<Unit>, Unit>::type
193  (dimension_type dim, const Key& origin, const Key& key, Difference diff)
194  {
195  Unit d = diff(dim, origin, key);
196 #ifdef SPATIAL_SAFER_ARITHMETICS
197  return except::check_square(d);
198 #else
199  return d * d;
200 #endif
201  }
202 
207  template <typename Key, typename Difference, typename Unit>
208  inline typename enable_if<import::is_arithmetic<Unit>, Unit>::type
210  (dimension_type rank, const Key& origin, const Key& key, Difference diff)
211  {
212  Unit sum = square_euclid_distance_to_plane<Key, Difference, Unit>
213  (0, origin, key, diff);
214  for (dimension_type i = 1; i < rank; ++i)
215  {
216 #ifdef SPATIAL_SAFER_ARITHMETICS
218  (square_euclid_distance_to_plane<Key, Difference, Unit>
219  (i, origin, key, diff), sum);
220 #else
222  <Key, Difference, Unit>(i, origin, key, diff);
223 #endif
224  }
225  return sum;
226  }
227 
232  template <typename Key, typename Difference, typename Unit>
233  inline typename enable_if<import::is_floating_point<Unit>, Unit>::type
235  (dimension_type dim, const Key& origin, const Key& key, Difference diff)
236  {
237  return std::abs(diff(dim, origin, key)); // floating types abs is always okay!
238  }
239 
260  template <typename Key, typename Difference, typename Unit>
261  inline typename enable_if<import::is_floating_point<Unit>, Unit>::type
263  (dimension_type rank, const Key& origin, const Key& key, Difference diff)
264  {
265 #ifdef SPATIAL_SAFER_ARITHMETICS
266  // Find a non zero maximum or return 0
267  Unit max = euclid_distance_to_plane<Key, Difference, Unit>
268  (0, origin, key, diff);
269  dimension_type max_dim = 0;
270  for (dimension_type i = 1; i < rank; ++i)
271  {
272  Unit d = euclid_distance_to_plane<Key, Difference, Unit>
273  (i, origin, key, diff);
274  if (d > max) { max = d; max_dim = i; }
275  }
276  const Unit zero = Unit();
277  if (max == zero) return zero; // they're all zero!
278  // Compute the distance
279  Unit sum = zero;
280  for (dimension_type i = 0; i < max_dim; ++i)
281  {
282  Unit div = diff(i, origin, key) / max;
283  sum += div * div;
284  }
285  for (dimension_type i = max_dim + 1; i < rank; ++i)
286  {
287  Unit div = diff(i, origin, key) / max;
288  sum += div * div;
289  }
290  return except::check_positive_mul(max, std::sqrt(((Unit) 1.0) + sum));
291 #else
292  return std::sqrt(square_euclid_distance_to_key<Key, Difference, Unit>
293  (rank, origin, key, diff));
294 #endif
295  }
296 
302  template <typename Key, typename Difference, typename Unit>
303  inline typename enable_if<import::is_arithmetic<Unit>, Unit>::type
305  (dimension_type dim, const Key& origin, const Key& key, Difference diff)
306  {
307 #ifdef SPATIAL_SAFER_ARITHMETICS
308  return except::check_abs(diff(dim, origin, key));
309 #else
310  return std::abs(diff(dim, origin, key));
311 #endif
312  }
313 
317  template <typename Key, typename Difference, typename Unit>
318  inline typename enable_if<import::is_arithmetic<Unit>, Unit>::type
320  (dimension_type rank, const Key& origin, const Key& key, Difference diff)
321  {
322  Unit sum = manhattan_distance_to_plane<Key, Difference, Unit>
323  (0, origin, key, diff);
324  for (dimension_type i = 1; i < rank; ++i)
325  {
326 #ifdef SPATIAL_SAFER_ARITHMETICS
328  (manhattan_distance_to_plane<Key, Difference, Unit>
329  (i, origin, key, diff), sum);
330 #else
332  <Key, Difference, Unit>(i, origin, key, diff);
333 #endif
334  }
335  return sum;
336  }
337 
338  /*
339  // For a future implementation where we take earth-like spheroid as an
340  // example for non-euclidian spaces, or manifolds.
341  math::great_circle_distance_to_key
342  math::great_circle_distance_to_plane
343  math::vincenty_distance_to_key
344  math::vincenty_distance_to_plane
345  */
346  } // namespace math
347 }
348 
349 #endif // SPATIAL_MATH_HPP
enable_if< import::is_arithmetic< Tp >, Tp >::type check_square(Tp x)
This arithmetic check is only used when the macro SPATIAL_SAFER_ARITHMETICS is defined.
enable_if< import::is_arithmetic< Unit >, Unit >::type square_euclid_distance_to_key(dimension_type rank, const Key &origin, const Key &key, Difference diff)
Compute the square value of the distance between origin and key.
enable_if_c< std::numeric_limits< Tp >::is_integer &&std::numeric_limits< Tp >::is_signed, Tp >::type check_abs(Tp x)
This arithmetic check is only used when the macro SPATIAL_SAFER_ARITHMETICS is defined.
enable_if< import::is_floating_point< Unit >, Unit >::type euclid_distance_to_key(dimension_type rank, const Key &origin, const Key &key, Difference diff)
Computes the euclidian distance between 2 points.
enable_if< import::is_arithmetic< Tp > >::type check_positive_distance(Tp x)
Check that the distance given x has a positive value.
Thrown to report that an arithmetic error has occured during a calculation.
Definition: exception.hpp:123
enable_if< import::is_arithmetic< Unit >, Unit >::type manhattan_distance_to_key(dimension_type rank, const Key &origin, const Key &key, Difference diff)
Compute the manhattan distance between origin and key.
Thrown to report that an negative distance has been passed as a parameter while distances are expecte...
Definition: exception.hpp:110
enable_if< import::is_arithmetic< Tp >, Tp >::type check_positive_add(Tp x, Tp y)
This arithmetic check is only used when the macro SPATIAL_SAFER_ARITHMETICS is defined.
std::size_t dimension_type
Defines the type for the dimension as being a size.
Definition: spatial.hpp:71
If B is true, spatial::enable_if has a public member typedef type, equal to Tp; otherwise, there is no member typedef.
The main namespace used in the library.
Definition: algorithm.hpp:23
enable_if< import::is_floating_point< Unit >, Unit >::type euclid_distance_to_plane(dimension_type dim, const Key &origin, const Key &key, Difference diff)
Compute the distance between the origin and the closest point to the plane orthogonal to the axis of ...
enable_if< import::is_arithmetic< Unit >, Unit >::type square_euclid_distance_to_plane(dimension_type dim, const Key &origin, const Key &key, Difference diff)
Compute the distance between the origin and the closest point to the plane orthogonal to the axis of ...
enable_if< import::is_arithmetic< Tp >, Tp >::type check_positive_mul(Tp x, Tp y)
This arithmetic check is only used when the macro SPATIAL_SAFER_ARITHMETICS is defined.
enable_if< import::is_arithmetic< Unit >, Unit >::type manhattan_distance_to_plane(dimension_type dim, const Key &origin, const Key &key, Difference diff)
Compute the distance between the origin and the closest point to the plane orthogonal to the axis of ...