/* *****************************************************
   THIS IS AN AUTOMATICALLY GENERATED FILE. DO NOT EDIT.
   *****************************************************

   Generated by:
     scitbx.source_generators.array_family.generate_algebras
 */

#ifndef SCITBX_ARRAY_FAMILY_SHARED_ALGEBRA_H
#define SCITBX_ARRAY_FAMILY_SHARED_ALGEBRA_H

#ifndef DOXYGEN_SHOULD_SKIP_THIS

#include <scitbx/array_family/shared.h>

#include <scitbx/array_family/operator_traits_builtin.h>
#include <scitbx/array_family/detail/operator_functors.h>
#include <scitbx/array_family/detail/generic_array_functors.h>
#include <scitbx/array_family/detail/std_imports.h>
#include <scitbx/array_family/misc_functions.h>

namespace scitbx { namespace af {

  template<typename ElementType>
  inline
  shared<ElementType>
  operator-(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_negate<
          return_element_type,
          ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator!(shared<ElementType> const& a) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_logical_not<
          return_element_type,
          ElementType>(), a.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<
    typename binary_operator_traits<
        ElementType1, ElementType2>::arithmetic>
  operator+(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<
      typename binary_operator_traits<
          ElementType1, ElementType2>::arithmetic>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_plus<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator+(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_plus<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator+(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_plus<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>&
  operator+=(
    shared<ElementType1>& a1,
    shared<ElementType2> const& a2) {
    if (a1.size() != a2.size()) throw_range_error();
    array_operation_in_place_a_a(fn::functor_ip_plus<
        ElementType1,
        ElementType2>(),
      a1.begin(), a2.begin(), a1.size());
    return a1;
  }

  template<typename ElementType>
  inline
  shared<ElementType>&
  operator+=(
    shared<ElementType>& a1,
    ElementType const& a2) {
    array_operation_in_place_a_s(fn::functor_ip_plus<
        ElementType,
        ElementType>(),
      a1.begin(), a2, a1.size());
    return a1;
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<
    typename binary_operator_traits<
        ElementType1, ElementType2>::arithmetic>
  operator-(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<
      typename binary_operator_traits<
          ElementType1, ElementType2>::arithmetic>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_minus<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator-(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_minus<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator-(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_minus<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>&
  operator-=(
    shared<ElementType1>& a1,
    shared<ElementType2> const& a2) {
    if (a1.size() != a2.size()) throw_range_error();
    array_operation_in_place_a_a(fn::functor_ip_minus<
        ElementType1,
        ElementType2>(),
      a1.begin(), a2.begin(), a1.size());
    return a1;
  }

  template<typename ElementType>
  inline
  shared<ElementType>&
  operator-=(
    shared<ElementType>& a1,
    ElementType const& a2) {
    array_operation_in_place_a_s(fn::functor_ip_minus<
        ElementType,
        ElementType>(),
      a1.begin(), a2, a1.size());
    return a1;
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<
    typename binary_operator_traits<
        ElementType1, ElementType2>::arithmetic>
  operator*(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<
      typename binary_operator_traits<
          ElementType1, ElementType2>::arithmetic>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_multiplies<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator*(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_multiplies<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator*(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_multiplies<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>&
  operator*=(
    shared<ElementType1>& a1,
    shared<ElementType2> const& a2) {
    if (a1.size() != a2.size()) throw_range_error();
    array_operation_in_place_a_a(fn::functor_ip_multiplies<
        ElementType1,
        ElementType2>(),
      a1.begin(), a2.begin(), a1.size());
    return a1;
  }

  template<typename ElementType>
  inline
  shared<ElementType>&
  operator*=(
    shared<ElementType>& a1,
    ElementType const& a2) {
    array_operation_in_place_a_s(fn::functor_ip_multiplies<
        ElementType,
        ElementType>(),
      a1.begin(), a2, a1.size());
    return a1;
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<
    typename binary_operator_traits<
        ElementType1, ElementType2>::arithmetic>
  operator/(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<
      typename binary_operator_traits<
          ElementType1, ElementType2>::arithmetic>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_divides<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator/(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_divides<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator/(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_divides<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>&
  operator/=(
    shared<ElementType1>& a1,
    shared<ElementType2> const& a2) {
    if (a1.size() != a2.size()) throw_range_error();
    array_operation_in_place_a_a(fn::functor_ip_divides<
        ElementType1,
        ElementType2>(),
      a1.begin(), a2.begin(), a1.size());
    return a1;
  }

  template<typename ElementType>
  inline
  shared<ElementType>&
  operator/=(
    shared<ElementType>& a1,
    ElementType const& a2) {
    array_operation_in_place_a_s(fn::functor_ip_divides<
        ElementType,
        ElementType>(),
      a1.begin(), a2, a1.size());
    return a1;
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<
    typename binary_operator_traits<
        ElementType1, ElementType2>::arithmetic>
  operator%(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<
      typename binary_operator_traits<
          ElementType1, ElementType2>::arithmetic>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_modulus<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator%(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_modulus<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  operator%(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_modulus<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>&
  operator%=(
    shared<ElementType1>& a1,
    shared<ElementType2> const& a2) {
    if (a1.size() != a2.size()) throw_range_error();
    array_operation_in_place_a_a(fn::functor_ip_modulus<
        ElementType1,
        ElementType2>(),
      a1.begin(), a2.begin(), a1.size());
    return a1;
  }

  template<typename ElementType>
  inline
  shared<ElementType>&
  operator%=(
    shared<ElementType>& a1,
    ElementType const& a2) {
    array_operation_in_place_a_s(fn::functor_ip_modulus<
        ElementType,
        ElementType>(),
      a1.begin(), a2, a1.size());
    return a1;
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator&&(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_logical_and<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator&&(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_logical_and<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator&&(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_logical_and<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator||(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_logical_or<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator||(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_logical_or<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator||(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_logical_or<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator==(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_equal_to<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator==(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_equal_to<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator==(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_equal_to<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator!=(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_not_equal_to<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator!=(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_not_equal_to<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator!=(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_not_equal_to<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator>(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_greater<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator>(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_greater<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator>(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_greater<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator<(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_less<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator<(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_less<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator<(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_less<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator>=(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_greater_equal<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator>=(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_greater_equal<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator>=(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_greater_equal<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<bool>
  operator<=(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_less_equal<
          return_element_type,
          ElementType1,
          ElementType2>(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator<=(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_less_equal<
          return_element_type,
          ElementType,
          ElementType>(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  operator<=(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_less_equal<
          return_element_type,
          ElementType,
          ElementType>(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  absolute(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_absolute<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  pow2(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_pow2<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  acos(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_acos<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  cos(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_cos<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  tan(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_tan<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  asin(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_asin<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  cosh(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_cosh<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  tanh(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_tanh<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  atan(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_atan<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  exp(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_exp<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  sin(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_sin<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  fabs(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_fabs<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  log(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_log<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  sinh(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_sinh<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  ceil(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_ceil<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  floor(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_floor<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  log10(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_log10<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  sqrt(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_sqrt<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  abs(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_abs<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  conj(shared<ElementType> const& a) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_conj<return_element_type, ElementType>(), a.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  fmod_positive(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_fmod_positive<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  fmod_positive(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_fmod_positive<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  fmod_positive(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_fmod_positive<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  fmod(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_fmod<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  fmod(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_fmod<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  fmod(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_fmod<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  pow(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_pow<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  pow(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_pow<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  pow(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_pow<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  atan2(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_atan2<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  atan2(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_atan2<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  atan2(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_atan2<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  each_min(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_each_min<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  each_min(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_each_min<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  each_min(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_each_min<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType1, typename ElementType2>
  inline
  shared<ElementType1>
  each_max(
    shared<ElementType1> const& a1,
    shared<ElementType2> const& a2) {
    typedef shared<ElementType1>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_each_max<return_element_type,
          ElementType1, ElementType2>(),
        a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  each_max(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_each_max<return_element_type,
          ElementType, ElementType>(),
        a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  each_max(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<ElementType>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_each_max<return_element_type,
          ElementType, ElementType>(),
        a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  real(shared<std::complex<ElementType> > const& a) {
    typedef shared<ElementType> return_array_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_real<
          ElementType,
          std::complex<ElementType> >(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  imag(shared<std::complex<ElementType> > const& a) {
    typedef shared<ElementType> return_array_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_imag<
          ElementType,
          std::complex<ElementType> >(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  abs(shared<std::complex<ElementType> > const& a) {
    typedef shared<ElementType> return_array_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_abs<
          ElementType,
          std::complex<ElementType> >(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  arg(shared<std::complex<ElementType> > const& a) {
    typedef shared<ElementType> return_array_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_arg<
          ElementType,
          std::complex<ElementType> >(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<ElementType>
  norm(shared<std::complex<ElementType> > const& a) {
    typedef shared<ElementType> return_array_type;
    return return_array_type(a.size(),
      make_init_functor(make_array_functor_a(
        fn::functor_norm<
          ElementType,
          std::complex<ElementType> >(), a.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    shared<int> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          int >(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    int const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          int >(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    std::complex<ElementType> const& a1,
    shared<int> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          int >(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    shared<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          ElementType >(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    ElementType const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          ElementType >(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    std::complex<ElementType> const& a1,
    shared<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          ElementType >(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    shared<std::complex<ElementType> > const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          std::complex<ElementType> >(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<std::complex<ElementType> > const& a1,
    std::complex<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          std::complex<ElementType> >(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    std::complex<ElementType> const& a1,
    shared<std::complex<ElementType> > const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_pow<
          std::complex<ElementType>,
          std::complex<ElementType>,
          std::complex<ElementType> >(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<ElementType> const& a1,
    shared<std::complex<ElementType> > const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_pow<
          std::complex<ElementType>,
          ElementType,
          std::complex<ElementType> >(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    shared<ElementType> const& a1,
    std::complex<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_pow<
          std::complex<ElementType>,
          ElementType,
          std::complex<ElementType> >(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  pow(
    ElementType const& a1,
    shared<std::complex<ElementType> > const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_pow<
          std::complex<ElementType>,
          ElementType,
          std::complex<ElementType> >(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  polar(
    shared<ElementType> const& a1,
    shared<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a(
        fn::functor_polar<
          std::complex<ElementType>,
          ElementType,
          ElementType >(), a1.begin(), a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  polar(
    shared<ElementType> const& a1,
    ElementType const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s(
        fn::functor_polar<
          std::complex<ElementType>,
          ElementType,
          ElementType >(), a1.begin(), a2)));
  }

  template<typename ElementType>
  inline
  shared<std::complex<ElementType> >
  polar(
    ElementType const& a1,
    shared<ElementType> const& a2) {
    typedef shared<std::complex<ElementType> > return_array_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a(
        fn::functor_polar<
          std::complex<ElementType>,
          ElementType,
          ElementType >(), a1, a2.begin())));
  }

  template<typename ElementType>
  inline
  shared<bool>
  approx_equal(
    shared<ElementType> const& a1,
    shared<ElementType> const& a2,
    ElementType const& tolerance) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    if (a1.size() != a2.size()) throw_range_error();
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_a_s(
        fn::functor_approx_equal<return_element_type,
          ElementType, ElementType, ElementType>(),
        a1.begin(), a2.begin(), tolerance)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  approx_equal(
    shared<ElementType> const& a1,
    ElementType const& a2,
    ElementType const& tolerance) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a1.size(),
      make_init_functor(make_array_functor_a_s_s(
        fn::functor_approx_equal<return_element_type,
          ElementType, ElementType, ElementType>(),
        a1.begin(), a2, tolerance)));
  }

  template<typename ElementType>
  inline
  shared<bool>
  approx_equal(
    ElementType const& a1,
    shared<ElementType> const& a2,
    ElementType const& tolerance) {
    typedef shared<bool>
    return_array_type;
    typedef typename return_array_type::value_type return_element_type;
    return return_array_type(a2.size(),
      make_init_functor(make_array_functor_s_a_s(
        fn::functor_approx_equal<return_element_type,
          ElementType, ElementType, ElementType>(),
        a1, a2.begin(), tolerance)));
  }

}} // namespace scitbx::af

#endif // DOXYGEN_SHOULD_SKIP_THIS

#endif // SCITBX_ARRAY_FAMILY_SHARED_ALGEBRA_H
