__kernel void weighted_minkowski(
  __global numeric* output,
  const unsigned int count,
  __global const numeric* input,
  __global const numeric* coordinate_scales,
  const int N,
  const int DIM,
  const numeric P,
  const int MODE) {

  size_t i = get_global_id(0);
  size_t j = get_global_id(1);
  size_t output_index = i + ((size_t) N) * j;

  if (i < (size_t) N && j < (size_t) N &&
      output_index < (size_t) count) {
    if (MODE == 1) {
      numeric sum = (numeric) 0;
      for (int k = 0; k < DIM; ++k) {
        size_t offset = ((size_t) k) * ((size_t) N);
        numeric difference = fabs(input[i + offset] - input[j + offset]);
        sum += coordinate_scales[k] * difference;
      }
      output[output_index] = sum;
    } else if (MODE == 3) {
      numeric maximum = (numeric) 0;
      for (int k = 0; k < DIM; ++k) {
        size_t offset = ((size_t) k) * ((size_t) N);
        numeric difference = fabs(input[i + offset] - input[j + offset]);
        numeric component = coordinate_scales[k] * difference;
        if (component > maximum) maximum = component;
      }
      output[output_index] = maximum;
    } else {
      numeric maximum = (numeric) 0;
      for (int k = 0; k < DIM; ++k) {
        size_t offset = ((size_t) k) * ((size_t) N);
        numeric difference = fabs(input[i + offset] - input[j + offset]);
        numeric component = coordinate_scales[k] * difference;
        if (component > maximum) maximum = component;
      }

      if (maximum == (numeric) 0) {
        output[output_index] = (numeric) 0;
      } else {
        numeric sum = (numeric) 0;
        for (int k = 0; k < DIM; ++k) {
          size_t offset = ((size_t) k) * ((size_t) N);
          numeric difference = fabs(input[i + offset] - input[j + offset]);
          numeric component = coordinate_scales[k] * difference;
          sum += pow(component / maximum, P);
        }
        output[output_index] = maximum * pow(sum, ((numeric) 1) / P);
      }
    }
  }
}
