<commit_before>/*
 * Copyright 2011-2013 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "render/colorspace.h"

#include "util/util_color.h"
#include "util/util_image.h"
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
#include "util/util_thread.h"
#include "util/util_vector.h"

#ifdef WITH_OCIO
#  include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE;
#endif

CCL_NAMESPACE_BEGIN

/* Builtin colorspaces. */
ustring u_colorspace_auto;
ustring u_colorspace_raw("__builtin_raw");
ustring u_colorspace_srgb("__builtin_srgb");

/* Cached data. */
#ifdef WITH_OCIO
static thread_mutex cache_mutex;
static unordered_map<ustring, ustring, ustringHash> cached_colorspaces;
static unordered_map<ustring, OCIO::ConstProcessorRcPtr, ustringHash> cached_processors;
#endif

ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace)
{
#ifdef WITH_OCIO
  /* Only use this for OpenColorIO color spaces, not the builtin ones. */
  assert(colorspace != u_colorspace_srgb && colorspace != u_colorspace_auto);

  if (colorspace == u_colorspace_raw) {
    return NULL;
  }

  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return NULL;
  }

  /* Cache processor until free_memory(), memory overhead is expected to be
   * small and the processor is likely to be reused. */
  thread_scoped_lock cache_lock(cache_mutex);
  if (cached_processors.find(colorspace) == cached_processors.end()) {
    try {
      cached_processors[colorspace] = config->getProcessor(colorspace.c_str(), "scene_linear");
    }
    catch (OCIO::Exception &exception) {
      cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
      VLOG(1) << "Colorspace " << colorspace.c_str()
              << " can't be converted to scene_linear: " << exception.what();
    }
  }

  const OCIO::Processor *processor = cached_processors[colorspace].get();
  return (ColorSpaceProcessor *)processor;
#else
  /* No OpenColorIO. */
  (void)colorspace;
  return NULL;
#endif
}

ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace,
                                                   const char *file_format,
                                                   bool is_float)
{
  if (colorspace == u_colorspace_auto) {
    /* Auto detect sRGB or raw if none specified. */
    if (is_float) {
      bool srgb = (colorspace == "sRGB" || colorspace == "GammaCorrected" ||
                   (colorspace.empty() &&
                    (strcmp(file_format, "png") == 0 || strcmp(file_format, "tiff") == 0 ||
                     strcmp(file_format, "dpx") == 0 || strcmp(file_format, "jpeg2000") == 0)));
      return srgb ? u_colorspace_srgb : u_colorspace_raw;
    }
    else {
      return u_colorspace_srgb;
    }
  }
  else if (colorspace == u_colorspace_srgb || colorspace == u_colorspace_raw) {
    /* Builtin colorspaces. */
    return colorspace;
  }
  else {
    /* Use OpenColorIO. */
#ifdef WITH_OCIO
    {
      thread_scoped_lock cache_lock(cache_mutex);
      /* Cached lookup. */
      if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
        return cached_colorspaces[colorspace];
      }
    }

    /* Detect if it matches a simple builtin colorspace. */
    bool is_no_op, is_srgb;
    is_builtin_colorspace(colorspace, is_no_op, is_srgb);

    thread_scoped_lock cache_lock(cache_mutex);
    if (is_no_op) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is no-op";
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }
    else if (is_srgb) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is sRGB";
      cached_colorspaces[colorspace] = u_colorspace_srgb;
      return u_colorspace_srgb;
    }

    /* Verify if we can convert from the requested color space. */
    if (!get_processor(colorspace)) {
      OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
      if (!config || !config->getColorSpace(colorspace.c_str())) {
        VLOG(1) << "Colorspace " << colorspace.c_str() << " not found, using raw instead";
      }
      else {
        VLOG(1) << "Colorspace " << colorspace.c_str()
                << " can't be converted to scene_linear, using raw instead";
      }
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }

    /* Convert to/from colorspace with OpenColorIO. */
    VLOG(1) << "Colorspace " << colorspace.string() << " handled through OpenColorIO";
    cached_colorspaces[colorspace] = colorspace;
    return colorspace;
#else
    VLOG(1) << "Colorspace " << colorspace.c_str() << " not available, built without OpenColorIO";
    return u_colorspace_raw;
#endif
  }
}

void ColorSpaceManager::is_builtin_colorspace(ustring colorspace, bool &is_no_op, bool &is_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
  if (!processor) {
    is_no_op = false;
    is_srgb = false;
    return;
  }

  is_no_op = true;
  is_srgb = true;
  for (int i = 0; i < 256; i++) {
    float v = i / 255.0f;

    float cR[3] = {v, 0, 0};
    float cG[3] = {0, v, 0};
    float cB[3] = {0, 0, v};
    float cW[3] = {v, v, v};
    processor->applyRGB(cR);
    processor->applyRGB(cG);
    processor->applyRGB(cB);
    processor->applyRGB(cW);

    /* Make sure that there is no channel crosstalk. */
    if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f ||
        fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) {
      is_no_op = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three primaries combine linearly. */
    if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) ||
        !compare_floats(cB[2], cW[2], 1e-6f, 64)) {
      is_no_op = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three channels behave identically. */
    if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) {
      is_no_op = false;
      is_srgb = false;
      break;
    }

    float out_v = average(make_float3(cW[0], cW[1], cW[2]));
    if (!compare_floats(v, out_v, 1e-6f, 64)) {
      is_no_op = false;
    }
    if (!compare_floats(color_srgb_to_linear(v), out_v, 1e-6f, 64)) {
      is_srgb = false;
    }
  }
#else
  (void)colorspace;
  is_no_op = false;
  is_srgb = false;
#endif
}

#ifdef WITH_OCIO

template<typename T> inline float4 cast_to_float4(T *data)
{
  return make_float4(util_image_cast_to_float(data[0]),
                     util_image_cast_to_float(data[1]),
                     util_image_cast_to_float(data[2]),
                     util_image_cast_to_float(data[3]));
}

template<typename T> inline void cast_from_float4(T *data, float4 value)
{
  data[0] = util_image_cast_from_float<T>(value.x);
  data[1] = util_image_cast_from_float<T>(value.y);
  data[2] = util_image_cast_from_float<T>(value.z);
  data[3] = util_image_cast_from_float<T>(value.w);
}

/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   T *pixels,
                                   size_t width,
                                   size_t height)
{
  /* Process large images in chunks to keep temporary memory requirement down. */
  size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width));
  vector<float4> float_pixels(y_chunk_size * width);

  for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) {
    size_t y1 = std::min(y0 + y_chunk_size, height);
    size_t i = 0;

    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float_pixels[i] = cast_to_float4(pixels + 4 * (y * width + x));
      }
    }

    OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4);
    processor->apply(desc);

    i = 0;
    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = float_pixels[i];
        if (compress_as_srgb) {
          value = color_linear_to_srgb_v4(value);
        }
        cast_from_float4(pixels + 4 * (y * width + x), value);
      }
    }
  }
}

/* Fast version for float images, which OpenColorIO can handle natively. */
template<>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   float *pixels,
                                   size_t width,
                                   size_t height)
{
  OCIO::PackedImageDesc desc(pixels, width, height, 4);
  processor->apply(desc);
}
#endif

template<typename T>
void ColorSpaceManager::to_scene_linear(ustring colorspace,
                                        T *pixels,
                                        size_t width,
                                        size_t height,
                                        size_t depth,
                                        bool compress_as_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);

  if (processor) {
    if (compress_as_srgb) {
      /* Compress output as sRGB. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T, true>(processor, &pixels[z * width * height], width, height);
      }
    }
    else {
      /* Write output as scene linear directly. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T>(processor, &pixels[z * width * height], width, height);
      }
    }
  }
#else
  (void)colorspace;
  (void)pixels;
  (void)width;
  (void)height;
  (void)depth;
  (void)compress_as_srgb;
#endif
}

void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
                                        float *pixel,
                                        int channels)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)processor_;

  if (processor) {
    if (channels == 3) {
      processor->applyRGB(pixel);
    }
    else if (channels == 4) {
      if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
        /* Fast path for RGBA. */
        processor->applyRGB(pixel);
      }
      else {
        /* Unassociate and associate alpha since color management should not
         * be affected by transparency. */
        float alpha = pixel[3];
        float inv_alpha = 1.0f / alpha;

        pixel[0] *= inv_alpha;
        pixel[1] *= inv_alpha;
        pixel[2] *= inv_alpha;

        processor->applyRGB(pixel);

        pixel[0] *= alpha;
        pixel[1] *= alpha;
        pixel[2] *= alpha;
      }
    }
  }
#else
  (void)processor_;
  (void)pixel;
  (void)channels;
#endif
}

void ColorSpaceManager::free_memory()
{
#ifdef WITH_OCIO
  map_free_memory(cached_colorspaces);
  map_free_memory(cached_colorspaces);
#endif
}

/* Template instanstations so we don't have to inline functions. */
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool);

CCL_NAMESPACE_END
<commit_msg>Color management: add functions to detect scene linear and sRGB color spaces<commit_after>/*
 * Copyright 2011-2013 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "render/colorspace.h"

#include "util/util_color.h"
#include "util/util_image.h"
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
#include "util/util_thread.h"
#include "util/util_vector.h"

#ifdef WITH_OCIO
#  include <OpenColorIO/OpenColorIO.h>
namespace OCIO = OCIO_NAMESPACE;
#endif

CCL_NAMESPACE_BEGIN

/* Builtin colorspaces. */
ustring u_colorspace_auto;
ustring u_colorspace_raw("__builtin_raw");
ustring u_colorspace_srgb("__builtin_srgb");

/* Cached data. */
#ifdef WITH_OCIO
static thread_mutex cache_mutex;
static unordered_map<ustring, ustring, ustringHash> cached_colorspaces;
static unordered_map<ustring, OCIO::ConstProcessorRcPtr, ustringHash> cached_processors;
#endif

ColorSpaceProcessor *ColorSpaceManager::get_processor(ustring colorspace)
{
#ifdef WITH_OCIO
  /* Only use this for OpenColorIO color spaces, not the builtin ones. */
  assert(colorspace != u_colorspace_srgb && colorspace != u_colorspace_auto);

  if (colorspace == u_colorspace_raw) {
    return NULL;
  }

  OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  if (!config) {
    return NULL;
  }

  /* Cache processor until free_memory(), memory overhead is expected to be
   * small and the processor is likely to be reused. */
  thread_scoped_lock cache_lock(cache_mutex);
  if (cached_processors.find(colorspace) == cached_processors.end()) {
    try {
      cached_processors[colorspace] = config->getProcessor(colorspace.c_str(), "scene_linear");
    }
    catch (OCIO::Exception &exception) {
      cached_processors[colorspace] = OCIO::ConstProcessorRcPtr();
      VLOG(1) << "Colorspace " << colorspace.c_str()
              << " can't be converted to scene_linear: " << exception.what();
    }
  }

  const OCIO::Processor *processor = cached_processors[colorspace].get();
  return (ColorSpaceProcessor *)processor;
#else
  /* No OpenColorIO. */
  (void)colorspace;
  return NULL;
#endif
}

ustring ColorSpaceManager::detect_known_colorspace(ustring colorspace,
                                                   const char *file_format,
                                                   bool is_float)
{
  if (colorspace == u_colorspace_auto) {
    /* Auto detect sRGB or raw if none specified. */
    if (is_float) {
      bool srgb = (colorspace == "sRGB" || colorspace == "GammaCorrected" ||
                   (colorspace.empty() &&
                    (strcmp(file_format, "png") == 0 || strcmp(file_format, "tiff") == 0 ||
                     strcmp(file_format, "dpx") == 0 || strcmp(file_format, "jpeg2000") == 0)));
      return srgb ? u_colorspace_srgb : u_colorspace_raw;
    }
    else {
      return u_colorspace_srgb;
    }
  }
  else if (colorspace == u_colorspace_srgb || colorspace == u_colorspace_raw) {
    /* Builtin colorspaces. */
    return colorspace;
  }
  else {
    /* Use OpenColorIO. */
#ifdef WITH_OCIO
    {
      thread_scoped_lock cache_lock(cache_mutex);
      /* Cached lookup. */
      if (cached_colorspaces.find(colorspace) != cached_colorspaces.end()) {
        return cached_colorspaces[colorspace];
      }
    }

    /* Detect if it matches a simple builtin colorspace. */
    bool is_scene_linear, is_srgb;
    is_builtin_colorspace(colorspace, is_scene_linear, is_srgb);

    thread_scoped_lock cache_lock(cache_mutex);
    if (is_scene_linear) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is no-op";
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }
    else if (is_srgb) {
      VLOG(1) << "Colorspace " << colorspace.string() << " is sRGB";
      cached_colorspaces[colorspace] = u_colorspace_srgb;
      return u_colorspace_srgb;
    }

    /* Verify if we can convert from the requested color space. */
    if (!get_processor(colorspace)) {
      OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
      if (!config || !config->getColorSpace(colorspace.c_str())) {
        VLOG(1) << "Colorspace " << colorspace.c_str() << " not found, using raw instead";
      }
      else {
        VLOG(1) << "Colorspace " << colorspace.c_str()
                << " can't be converted to scene_linear, using raw instead";
      }
      cached_colorspaces[colorspace] = u_colorspace_raw;
      return u_colorspace_raw;
    }

    /* Convert to/from colorspace with OpenColorIO. */
    VLOG(1) << "Colorspace " << colorspace.string() << " handled through OpenColorIO";
    cached_colorspaces[colorspace] = colorspace;
    return colorspace;
#else
    VLOG(1) << "Colorspace " << colorspace.c_str() << " not available, built without OpenColorIO";
    return u_colorspace_raw;
#endif
  }
}

void ColorSpaceManager::is_builtin_colorspace(ustring colorspace,
                                              bool &is_scene_linear,
                                              bool &is_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);
  if (!processor) {
    is_scene_linear = false;
    is_srgb = false;
    return;
  }

  is_scene_linear = true;
  is_srgb = true;
  for (int i = 0; i < 256; i++) {
    float v = i / 255.0f;

    float cR[3] = {v, 0, 0};
    float cG[3] = {0, v, 0};
    float cB[3] = {0, 0, v};
    float cW[3] = {v, v, v};
    processor->applyRGB(cR);
    processor->applyRGB(cG);
    processor->applyRGB(cB);
    processor->applyRGB(cW);

    /* Make sure that there is no channel crosstalk. */
    if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f ||
        fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three primaries combine linearly. */
    if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) ||
        !compare_floats(cB[2], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }
    /* Make sure that the three channels behave identically. */
    if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) {
      is_scene_linear = false;
      is_srgb = false;
      break;
    }

    float out_v = average(make_float3(cW[0], cW[1], cW[2]));
    if (!compare_floats(v, out_v, 1e-6f, 64)) {
      is_scene_linear = false;
    }
    if (!compare_floats(color_srgb_to_linear(v), out_v, 1e-6f, 64)) {
      is_srgb = false;
    }
  }
#else
  (void)colorspace;
  is_scene_linear = false;
  is_srgb = false;
#endif
}

#ifdef WITH_OCIO

template<typename T> inline float4 cast_to_float4(T *data)
{
  return make_float4(util_image_cast_to_float(data[0]),
                     util_image_cast_to_float(data[1]),
                     util_image_cast_to_float(data[2]),
                     util_image_cast_to_float(data[3]));
}

template<typename T> inline void cast_from_float4(T *data, float4 value)
{
  data[0] = util_image_cast_from_float<T>(value.x);
  data[1] = util_image_cast_from_float<T>(value.y);
  data[2] = util_image_cast_from_float<T>(value.z);
  data[3] = util_image_cast_from_float<T>(value.w);
}

/* Slower versions for other all data types, which needs to convert to float and back. */
template<typename T, bool compress_as_srgb = false>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   T *pixels,
                                   size_t width,
                                   size_t height)
{
  /* Process large images in chunks to keep temporary memory requirement down. */
  size_t y_chunk_size = max(1, 16 * 1024 * 1024 / (sizeof(float4) * width));
  vector<float4> float_pixels(y_chunk_size * width);

  for (size_t y0 = 0; y0 < height; y0 += y_chunk_size) {
    size_t y1 = std::min(y0 + y_chunk_size, height);
    size_t i = 0;

    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float_pixels[i] = cast_to_float4(pixels + 4 * (y * width + x));
      }
    }

    OCIO::PackedImageDesc desc((float *)float_pixels.data(), width, y_chunk_size, 4);
    processor->apply(desc);

    i = 0;
    for (size_t y = y0; y < y1; y++) {
      for (size_t x = 0; x < width; x++, i++) {
        float4 value = float_pixels[i];
        if (compress_as_srgb) {
          value = color_linear_to_srgb_v4(value);
        }
        cast_from_float4(pixels + 4 * (y * width + x), value);
      }
    }
  }
}

/* Fast version for float images, which OpenColorIO can handle natively. */
template<>
inline void processor_apply_pixels(const OCIO::Processor *processor,
                                   float *pixels,
                                   size_t width,
                                   size_t height)
{
  OCIO::PackedImageDesc desc(pixels, width, height, 4);
  processor->apply(desc);
}
#endif

template<typename T>
void ColorSpaceManager::to_scene_linear(ustring colorspace,
                                        T *pixels,
                                        size_t width,
                                        size_t height,
                                        size_t depth,
                                        bool compress_as_srgb)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)get_processor(colorspace);

  if (processor) {
    if (compress_as_srgb) {
      /* Compress output as sRGB. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T, true>(processor, &pixels[z * width * height], width, height);
      }
    }
    else {
      /* Write output as scene linear directly. */
      for (size_t z = 0; z < depth; z++) {
        processor_apply_pixels<T>(processor, &pixels[z * width * height], width, height);
      }
    }
  }
#else
  (void)colorspace;
  (void)pixels;
  (void)width;
  (void)height;
  (void)depth;
  (void)compress_as_srgb;
#endif
}

void ColorSpaceManager::to_scene_linear(ColorSpaceProcessor *processor_,
                                        float *pixel,
                                        int channels)
{
#ifdef WITH_OCIO
  const OCIO::Processor *processor = (const OCIO::Processor *)processor_;

  if (processor) {
    if (channels == 3) {
      processor->applyRGB(pixel);
    }
    else if (channels == 4) {
      if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
        /* Fast path for RGBA. */
        processor->applyRGB(pixel);
      }
      else {
        /* Unassociate and associate alpha since color management should not
         * be affected by transparency. */
        float alpha = pixel[3];
        float inv_alpha = 1.0f / alpha;

        pixel[0] *= inv_alpha;
        pixel[1] *= inv_alpha;
        pixel[2] *= inv_alpha;

        processor->applyRGB(pixel);

        pixel[0] *= alpha;
        pixel[1] *= alpha;
        pixel[2] *= alpha;
      }
    }
  }
#else
  (void)processor_;
  (void)pixel;
  (void)channels;
#endif
}

void ColorSpaceManager::free_memory()
{
#ifdef WITH_OCIO
  map_free_memory(cached_colorspaces);
  map_free_memory(cached_colorspaces);
#endif
}

/* Template instanstations so we don't have to inline functions. */
template void ColorSpaceManager::to_scene_linear(ustring, uchar *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, ushort *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, half *, size_t, size_t, size_t, bool);
template void ColorSpaceManager::to_scene_linear(ustring, float *, size_t, size_t, size_t, bool);

CCL_NAMESPACE_END
<|endoftext|>


<commit_before>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_border_color(MGLSampler * self, PyObject * value) {
    PyObject * border_color = PySequence_Fast(value, "not iterable");
    if (!border_color) {
        return -1;
    }
    float color[4] = {};
    switch (PySequence_Fast_GET_SIZE(border_color)) {
        case 4:
            color[3] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 3));
        case 3:
            color[2] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 2));
        case 2:
            color[1] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 1));
        case 1:
            color[0] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 0));
        default:
            // TODO: error
            return -1;
    }

	self->context->gl.SamplerParameterfv(self->sampler_obj, GL_TEXTURE_BORDER_COLOR, color);
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
	float anisotropy = PyFloat_AsDouble(value);
    if (anisotropy < 1.0) {
        anisotropy = 1.0;
    }
    // if (anisotropy > max_anisotropy) {
    //     anisotropy = max_anisotropy;
    // }
    SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
    return 0;
}

int MGLSampler_set_lod_range(MGLSampler * self, PyObject * value) {
    PyObject * lod_range = PySequence_Fast(value, "not iterable");
    if (!lod_range) {
        return -1;
    }

    int min_lod;
    int max_lod;
    float lod_bias = 0.0f;
    switch (PySequence_Fast_GET_SIZE(lod_range)) {
        case 3:
            lod_bias = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(lod_range, 2));
        case 2:
            min_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 0));
            max_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 1));
        default:
            // TODO: error
            return -1;
    }

    // SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, max_lod);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_LOD_BIAS, lod_bias);
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"border_color", 0, (setter)MGLSampler_set_border_color, 0, 0},
    {"lod_range", 0, (setter)MGLSampler_set_lod_range, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<commit_msg>fix anisotropy setter<commit_after>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_border_color(MGLSampler * self, PyObject * value) {
    PyObject * border_color = PySequence_Fast(value, "not iterable");
    if (!border_color) {
        return -1;
    }
    float color[4] = {};
    switch (PySequence_Fast_GET_SIZE(border_color)) {
        case 4:
            color[3] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 3));
        case 3:
            color[2] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 2));
        case 2:
            color[1] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 1));
        case 1:
            color[0] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 0));
        default:
            // TODO: error
            return -1;
    }

	self->context->gl.SamplerParameterfv(self->sampler_obj, GL_TEXTURE_BORDER_COLOR, color);
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
	float anisotropy = (float)PyFloat_AsDouble(value);
    if (anisotropy < 1.0) {
        anisotropy = 1.0;
    }
    // if (anisotropy > max_anisotropy) {
    //     anisotropy = max_anisotropy;
    // }
    // SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
    return 0;
}

int MGLSampler_set_lod_range(MGLSampler * self, PyObject * value) {
    PyObject * lod_range = PySequence_Fast(value, "not iterable");
    if (!lod_range) {
        return -1;
    }

    int min_lod;
    int max_lod;
    float lod_bias = 0.0f;
    switch (PySequence_Fast_GET_SIZE(lod_range)) {
        case 3:
            lod_bias = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(lod_range, 2));
        case 2:
            min_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 0));
            max_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 1));
        default:
            // TODO: error
            return -1;
    }

    // SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, max_lod);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_LOD_BIAS, lod_bias);
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"border_color", 0, (setter)MGLSampler_set_border_color, 0, 0},
    {"lod_range", 0, (setter)MGLSampler_set_lod_range, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<|endoftext|>


<commit_before>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2017 Axel Waggershauser
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "GenericGFPoly.h"

#include "GenericGF.h"
#include "ZXConfig.h"
#include "ZXContainerAlgorithms.h"

#include <algorithm>
#include <cassert>
#include <stdexcept>

namespace ZXing {

int
GenericGFPoly::evaluateAt(int a) const
{
	if (a == 0)
		// Just return the x^0 coefficient
		return constant();

	if (a == 1)
		// Just the sum of the coefficients
		return Reduce(_coefficients, 0, [](auto a, auto b) { return a ^ b; });

	int result = _coefficients[0];
	for (size_t i = 1; i < _coefficients.size(); ++i)
		result = _field->multiply(a, result) ^ _coefficients[i];
	return result;
}

GenericGFPoly& GenericGFPoly::addOrSubtract(GenericGFPoly& other)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (isZero()) {
		swap(*this, other);
		return *this;
	}

	if (other.isZero())
		return *this;

	auto& smallerCoefs = other._coefficients;
	auto& largerCoefs = _coefficients;
	if (smallerCoefs.size() > largerCoefs.size())
		std::swap(smallerCoefs, largerCoefs);

	size_t lengthDiff = largerCoefs.size() - smallerCoefs.size();

	// high-order terms only found in higher-degree polynomial's coefficients stay untouched
	for (size_t i = lengthDiff; i < largerCoefs.size(); ++i)
		largerCoefs[i] ^= smallerCoefs[i - lengthDiff];

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::multiply(const GenericGFPoly& other)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (isZero() || other.isZero())
		return setMonomial(0);

	auto& a = _coefficients;
	auto& b = other._coefficients;

	// To disable the use of the malloc cache, simply uncomment:
	// Coefficients _cache;
	_cache.resize(a.size() + b.size() - 1);
	std::fill(_cache.begin(), _cache.end(), 0);
	for (size_t i = 0; i < a.size(); ++i)
		for (size_t j = 0; j < b.size(); ++j)
			_cache[i + j] ^= _field->multiply(a[i], b[j]);

	_coefficients.swap(_cache);

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::multiplyByMonomial(int coefficient, int degree)
{
	assert(degree >= 0);

	if (coefficient == 0)
		return setMonomial(0);

	for (int& c : _coefficients)
		c = _field->multiply(c, coefficient);

	_coefficients.resize(_coefficients.size() + degree, 0);

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::divide(const GenericGFPoly& other, GenericGFPoly& quotient)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (other.isZero())
		throw std::invalid_argument("Divide by 0");

	quotient.setField(*_field);
	quotient.setMonomial(0);
	auto& remainder = *this;

	const int inverseDenominatorLeadingTerm = _field->inverse(other.leadingCoefficient());

	ZX_THREAD_LOCAL GenericGFPoly temp;
	temp.setField(*_field);

	while (remainder.degree() >= other.degree() && !remainder.isZero()) {
		int degreeDifference = remainder.degree() - other.degree();
		int scale = _field->multiply(remainder.leadingCoefficient(), inverseDenominatorLeadingTerm);
		temp.setMonomial(scale, degreeDifference);
		quotient.addOrSubtract(temp);
		temp = other;
		temp.multiplyByMonomial(scale, degreeDifference);
		remainder.addOrSubtract(temp);
	}

	return *this;
}

void GenericGFPoly::normalize()
{
	auto firstNonZero = FindIf(_coefficients, [](int c){ return c != 0; });
	// Leading term must be non-zero for anything except the constant polynomial "0"
	if (firstNonZero != _coefficients.begin()) {
		if (firstNonZero == _coefficients.end()) {
			_coefficients.resize(1, 0);
		} else {
			std::copy(firstNonZero, _coefficients.end(), _coefficients.begin());
			_coefficients.resize(_coefficients.end() - firstNonZero);
		}
	}
}

} // namespace ZXing
<commit_msg>ReedSolomon: use expanded synthetic division instead of long division<commit_after>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2017 Axel Waggershauser
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "GenericGFPoly.h"

#include "GenericGF.h"
#include "ZXConfig.h"
#include "ZXContainerAlgorithms.h"

#include <algorithm>
#include <cassert>
#include <stdexcept>

namespace ZXing {

int
GenericGFPoly::evaluateAt(int a) const
{
	if (a == 0)
		// Just return the x^0 coefficient
		return constant();

	if (a == 1)
		// Just the sum of the coefficients
		return Reduce(_coefficients, 0, [](auto a, auto b) { return a ^ b; });

	int result = _coefficients[0];
	for (size_t i = 1; i < _coefficients.size(); ++i)
		result = _field->multiply(a, result) ^ _coefficients[i];
	return result;
}

GenericGFPoly& GenericGFPoly::addOrSubtract(GenericGFPoly& other)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (isZero()) {
		swap(*this, other);
		return *this;
	}

	if (other.isZero())
		return *this;

	auto& smallerCoefs = other._coefficients;
	auto& largerCoefs = _coefficients;
	if (smallerCoefs.size() > largerCoefs.size())
		std::swap(smallerCoefs, largerCoefs);

	size_t lengthDiff = largerCoefs.size() - smallerCoefs.size();

	// high-order terms only found in higher-degree polynomial's coefficients stay untouched
	for (size_t i = lengthDiff; i < largerCoefs.size(); ++i)
		largerCoefs[i] ^= smallerCoefs[i - lengthDiff];

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::multiply(const GenericGFPoly& other)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (isZero() || other.isZero())
		return setMonomial(0);

	auto& a = _coefficients;
	auto& b = other._coefficients;

	// To disable the use of the malloc cache, simply uncomment:
	// Coefficients _cache;
	_cache.resize(a.size() + b.size() - 1);
	std::fill(_cache.begin(), _cache.end(), 0);
	for (size_t i = 0; i < a.size(); ++i)
		for (size_t j = 0; j < b.size(); ++j)
			_cache[i + j] ^= _field->multiply(a[i], b[j]);

	_coefficients.swap(_cache);

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::multiplyByMonomial(int coefficient, int degree)
{
	assert(degree >= 0);

	if (coefficient == 0)
		return setMonomial(0);

	for (int& c : _coefficients)
		c = _field->multiply(c, coefficient);

	_coefficients.resize(_coefficients.size() + degree, 0);

	normalize();
	return *this;
}

GenericGFPoly&
GenericGFPoly::divide(const GenericGFPoly& other, GenericGFPoly& quotient)
{
	assert(_field == other._field); // "GenericGFPolys do not have same GenericGF field"

	if (other.isZero())
		throw std::invalid_argument("Divide by 0");

	quotient.setField(*_field);
	if (degree() < other.degree()) {
		// the remainder is this and the quotient is 0
		quotient.setMonomial(0);
		return *this;
	}

	// use Expanded Synthetic Division (see https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders):
	// we use the memory from this (the divident) and swap it with quotient, wich will then accumulate the result as
	// [quotient : remainder]. we later copy back the remainder into this and shorten the quotient.
	std::swap(*this, quotient);
	auto& divisor = other._coefficients;
	auto& result = quotient._coefficients;
	auto normalizer = _field->inverse(divisor[0]);
	for (int i = 0; i < Size(result) - (Size(divisor) - 1); ++i) {
		auto& ci = result[i];
		if (ci == 0)
			continue;

		ci = _field->multiply(ci, normalizer);

		// we always skip the first coefficient of the divisior, because it's only used to normalize the dividend coefficient
		for (int j = 1; j < Size(divisor); ++j)
			result[i + j] ^= _field->multiply(divisor[j], ci); // equivalent to: result[i + j] += -divisor[j] * ci
	}

	// extract the normalized remainder from result
	auto firstNonZero = std::find_if(result.end() - other.degree(), result.end(), [](int c){ return c != 0; });
	if (firstNonZero == result.end()) {
		setMonomial(0);
	} else {
		_coefficients.resize(result.end() - firstNonZero);
		std::copy(firstNonZero, result.end(), _coefficients.begin());
	}
	// cut off the tail with the remainder to leave the quotient
	result.resize(result.size() - other.degree());

	return *this;
}

void GenericGFPoly::normalize()
{
	auto firstNonZero = FindIf(_coefficients, [](int c){ return c != 0; });
	// Leading term must be non-zero for anything except the constant polynomial "0"
	if (firstNonZero != _coefficients.begin()) {
		if (firstNonZero == _coefficients.end()) {
			_coefficients.resize(1, 0);
		} else {
			std::copy(firstNonZero, _coefficients.end(), _coefficients.begin());
			_coefficients.resize(_coefficients.end() - firstNonZero);
		}
	}
}

} // namespace ZXing
<|endoftext|>


<commit_before>// Copyright © 2011, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#ifndef __VM_H
#define __VM_H

#include <stdlib.h>

#include "memmanager.hh"
#include "store.hh"
#include "threadpool.hh"
#include "atomtable.hh"

typedef bool (*PreemptionTest)(void* data);

class VirtualMachine {
public:
  VirtualMachine(PreemptionTest preemptionTest,
                 void* preemptionTestData = nullptr) :
    _preemptionTest(preemptionTest), _preemptionTestData(preemptionTestData) {

    memoryManager.init();
  }

  StableNode* newVariable();

  void* malloc(size_t size) {
    return memoryManager.malloc(size);
  }

  void free(void* ptr, size_t size) {
    memoryManager.free(ptr, size);
  }

  void run();

  inline
  bool testPreemption();

  ThreadPool& getThreadPool() { return threadPool; }
private:
  friend class Thread;
  friend class Implementation<Atom>;

  VirtualMachine(const VirtualMachine& src) {}

  friend void* operator new (size_t size, VM vm);
  friend void* operator new[] (size_t size, VM vm);

  void* getMemory(size_t size) {
    return memoryManager.getMemory(size);
  }

  // Called from the constructor of Thread
  void scheduleThread(Thread* thread);

  ThreadPool threadPool;
  AtomTable atomTable;

  PreemptionTest _preemptionTest;
  void* _preemptionTestData;

  MemoryManager memoryManager;
};

void* operator new (size_t size, VM vm) {
  return vm->getMemory(size);
}

void* operator new[] (size_t size, VM vm) {
  return vm->getMemory(size);
}

///////////////////////////
// Inline VirtualMachine //
///////////////////////////

bool VirtualMachine::testPreemption() {
  return _preemptionTest(_preemptionTestData);
}

#endif // __VM_H
<commit_msg>Add methods newStaticArray and deleteStaticArray in VM.<commit_after>// Copyright © 2011, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#ifndef __VM_H
#define __VM_H

#include <stdlib.h>

#include "memmanager.hh"
#include "store.hh"
#include "threadpool.hh"
#include "atomtable.hh"

typedef bool (*PreemptionTest)(void* data);

class VirtualMachine {
public:
  VirtualMachine(PreemptionTest preemptionTest,
                 void* preemptionTestData = nullptr) :
    _preemptionTest(preemptionTest), _preemptionTestData(preemptionTestData) {

    memoryManager.init();
  }

  StableNode* newVariable();

  void* malloc(size_t size) {
    return memoryManager.malloc(size);
  }

  void free(void* ptr, size_t size) {
    memoryManager.free(ptr, size);
  }

  template <class T>
  StaticArray<T> newStaticArray(size_t size) {
    void* memory = malloc(size * sizeof(T));
    return StaticArray<T>(static_cast<T*>(memory), size);
  }

  template <class T>
  void deleteStaticArray(StaticArray<T> array, size_t size) {
    void* memory = static_cast<void*>((T*) array);
    free(memory, size * sizeof(T));
  }

  void run();

  inline
  bool testPreemption();

  ThreadPool& getThreadPool() { return threadPool; }
private:
  friend class Thread;
  friend class Implementation<Atom>;

  VirtualMachine(const VirtualMachine& src) {}

  friend void* operator new (size_t size, VM vm);
  friend void* operator new[] (size_t size, VM vm);

  void* getMemory(size_t size) {
    return memoryManager.getMemory(size);
  }

  // Called from the constructor of Thread
  void scheduleThread(Thread* thread);

  ThreadPool threadPool;
  AtomTable atomTable;

  PreemptionTest _preemptionTest;
  void* _preemptionTestData;

  MemoryManager memoryManager;
};

void* operator new (size_t size, VM vm) {
  return vm->getMemory(size);
}

void* operator new[] (size_t size, VM vm) {
  return vm->getMemory(size);
}

///////////////////////////
// Inline VirtualMachine //
///////////////////////////

bool VirtualMachine::testPreemption() {
  return _preemptionTest(_preemptionTestData);
}

#endif // __VM_H
<|endoftext|>


<commit_before>/* Copyright (C) 2018, Project Pluto.  See LICENSE.  */

#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "norad.h"

#define PI 3.141592653589793238462643383279502884197
#define TWOPI (2. * PI)
#define MINUTES_PER_DAY 1440.
#define MINUTES_PER_DAY_SQUARED (MINUTES_PER_DAY * MINUTES_PER_DAY)
#define MINUTES_PER_DAY_CUBED (MINUTES_PER_DAY * MINUTES_PER_DAY_SQUARED)
#define AE 1.0
                             /* distance units, earth radii */

/* TLEs have four angles on line 2,  given in the form DDD.DDDD.  This
can be parsed more quickly as an integer,  then cast to double and
converted to radians,  all in one step.    */

static int get_angle( const char *buff)
{
   int rval = 0;

   while( *buff == ' ')
      buff++;
   while( *buff != ' ')
      {
      if( *buff != '.')
         rval = rval * 10 + (int)( *buff - '0');
      buff++;
      }
   return( rval);
}

/* Converts the quasi scientific notation of the "Motion Dot Dot/6" or
"BSTAR" field to double.  The input will always be of the form

sdddddSe

   ....where s is blank or + or -;  ddddd is a five-digit mantissa;
S is + or - or blank;  and e is a single-digit exponent.  A decimal
point is assumed before the five-digit mantissa.  */

static double sci( const char *string)
{
   double rval = 0.;

   if( string[1] != ' ')
      {
      const int ival = atoi( string);

      if( ival)
         {
         rval = (double)ival * 1.e-5;
         if( string[7] != '0')
            {
            int exponent = string[7] - '0';

            if( string[6] == '-')
               while( exponent--)
                  rval *= .1;
            else
               while( exponent--)
                  rval *= 10.;
            }
         }
      }
   return( rval);
}

/* Does a checksum modulo 10 on the given line.  Digits = their
value, '-' = 1, all other chars = 0.  Returns 0 if ok, a negative
value if it's definitely not a TLE line,  positive if it's all OK
except the checksum.  This last was added because people sometimes
want to use TLEs without worrying about the checksum. */

int DLL_FUNC tle_checksum( const char *buff)
{
   int rval = 0;
   int count = 69;

   if( (*buff != '1' && *buff != '2') || buff[1] != ' ')
      return( -1);
   while( --count)
      {
      if( *buff > '0' && *buff <= '9')
         rval += *buff - '0';
      else if( *buff == '-')
         rval++;
      if( *buff < ' ' || *buff > 'z')           /* invalid character */
         return( -2);
      buff++;
      }
   rval -= *buff++ - '0';
   if( *buff > ' ')                 /* line unterminated */
      rval = -3;
   else
      {
      rval %= 10;
      if( rval < 0)
         rval += 10;
      }
   return( rval % 10);
}

static inline int mutant_dehex( const char ichar)
{
   int rval;

   if( ichar <= '9' && ichar >= '0')
      rval = ichar - '0';
   else if( ichar >= 'A' && ichar <= 'Z')
      rval = ichar + 10 - 'A';
   else
      rval = -1;
   return( rval);
}

/* The "standard" SDP4 model fails badly for very high-flying satellites.
As a non-standard extension,  I'm simply storing state vectors for them,
using the following somewhat odd scheme :

1 40391U 15007B   15091.99922241 sxxxxxxxx syyyyyyyy szzzzzzzzH  9997
2 49391 [valid range, accuracy]  saaaaaaaa sbbbbbbbb scccccccc    0 8

   Epoch,  int'l & NORAD IDs are stored in the standard manner.  The
'ephemeris type' is H (rather than the otherwise universal 0).  The
xyz position and vx, vy, vz velocity are stored as 8-digit signed
base-36 integers,  hence a range of +/- 36^8 = about +/- 2.82x10^12.

  x, y, z are in meters,  and hence cover a range +/- 18.9 AU.
vx, vy, vz are in 10^-4 m/s,  range +/- 94% c.  */

static double get_high_value( const char *iptr)
{
   int64_t rval = 0;

   assert( *iptr == '+' || *iptr == '-');
   if( *iptr == '+' || *iptr == '-')
      {
      int i, digit;

      for( i = 1; i < 9; i++)
         {
         digit = mutant_dehex( iptr[i]);
         assert( digit >= 0);
         rval = rval * (int64_t)36 + (int64_t)digit;
         }
      if( *iptr == '-')
         rval = -rval;
      }
   return( (double)rval);
}

/* Traditionally,  NORAD numbers were stored as five digits.  In 2020, new
detectors threatened to go past 100K objects;  the 'Alpha-5' scheme allows
the first byte to be replaced by an uppercase letter,  with I and O
skipped.  That gets us to 339999.  Note that NORAD is encouraging us to
switch to other formats (XML, JSON,  etc.) that allow nine-digit numbers.

   Should 340K numbers prove insufficient,  the following scheme would
use all 34^5 = 45435424 possible combinations.  d = digit, L = letter,
x = either.  We also could use lowercase letters and some others to
get 10^9 combinations within five bytes with backward compatibility.

(1) ddddd = 'traditional' scheme provides 100000 combinations;
(2) Ldddd = Alpha5 scheme adds 240000;
(3) xxxxL = 34^4*24      = 32072064 more;
(4) xxxLd = 34^3*24*10   =  9432960 more;
(5) xxLdd = 34^2*24*100  =  2774400 more;
(6) xLddd = 34*24*1000   =   816000 more. */

static int base34_to_int( const char c)
{
   int offset;

   if( c >= '0' && c <= '9')
      offset = '0';
   else if( c >= 'A' && c <= 'H')
      offset = 'A' - 10;
   else if( c >= 'J' && c <= 'N')
      offset = 'J' - 18;
   else if( c >= 'P' && c <= 'Z')
      offset = 'P' - 23;
   else
      return( -1);
   return( c - offset);
}

static int get_norad_number( const char *buff)
{
   const int ten_thousands = base34_to_int( *buff);

   if( ten_thousands >= 0)
      return( ten_thousands * 10000 + atoi( buff + 1));
   else
      return( 0);
}

static inline double get_eight_places( const char *ptr)
{
   return( (double)atoi( ptr) + (double)atoi(ptr + 4) * 1e-8);
}

/* Meteor 2-08                                                           */
/* 1 13113U          88245.60005115 0.00000076           63463-4 0  5998 */
/* 2 13113  82.5386 288.0994 0015973 147.1294 213.0868 13.83869004325321 */

#define J2000 2451545.5
#define J1900 (J2000 - 36525. - 1.)

/* parse_elements returns:
         0 if the elements are parsed without error;
         1 if they're OK except the first line has a checksum error;
         2 if they're OK except the second line has a checksum error;
         3 if they're OK except both lines have checksum errors;
         a negative value if the lines aren't at all parseable */

int DLL_FUNC parse_elements( const char *line1, const char *line2, tle_t *sat)
{
   int rval, checksum_problem = 0;

   if( *line1 != '1' || *line2 != '2')
      rval = -4;
   else
      {
      rval = tle_checksum( line1);
      if( rval > 0)
         {
         checksum_problem = 1;  /* there's a checksum problem,  but it's */
         rval = 0;              /* not fatal; continue processing the TLE */
         }
      }

   if( rval)
      rval -= 100;
   else
      {
      rval = tle_checksum( line2);
      if( rval > 0)
         {
         checksum_problem |= 2;  /* there's a checksum problem,  but it's */
         rval = 0;               /* not fatal; continue processing the TLE */
         }
      }

   if( !rval)
      {
      char tbuff[13];
      int year = line1[19] - '0';

      if( line1[18] >= '0')
         year += (line1[18] - '0') * 10;
      if( year < 57)          /* cycle around Y2K */
         year += 100;
      sat->epoch = get_eight_places( line1 + 20) + J1900
             + (double)( year * 365 + (year - 1) / 4);
      sat->norad_number = get_norad_number( line1 + 2);
      memcpy( tbuff, line1 + 64, 4);
      tbuff[4] = '\0';
      sat->bulletin_number = atoi( tbuff);
      sat->classification = line1[7];       /* almost always 'U' */
      memcpy( sat->intl_desig, line1 + 9, 8);
      sat->intl_desig[8] = '\0';
      memcpy( tbuff, line2 + 63, 5);
      tbuff[5] = '\0';
      sat->revolution_number = atoi( tbuff);
      sat->ephemeris_type = line1[62];
      if( sat->ephemeris_type == 'H')
         {
         size_t i;
         double *state_vect = &sat->xincl;

         for( i = 0; i < 3; i++)
            {
            state_vect[i]     = get_high_value( line1 + 33 + i * 10);
            state_vect[i + 3] = get_high_value( line2 + 33 + i * 10) * 1e-4;
            }
         return( 0);
         }

      sat->xmo = (double)get_angle( line2 + 43) * (PI / 180e+4);
      sat->xnodeo = (double)get_angle( line2 + 17) * (PI / 180e+4);
      sat->omegao = (double)get_angle( line2 + 34) * (PI / 180e+4);
      sat->xincl = (double)get_angle( line2 + 8) * (PI / 180e+4);
      sat->eo = atoi( line2 + 26) * 1.e-7;

      /* Make sure mean motion is null-terminated, since rev. no.
          may immediately follow. */
      memcpy( tbuff, line2 + 51, 12);
      tbuff[12] = '\0';
            /* Input mean motion,  derivative of mean motion and second  */
            /* deriv of mean motion,  are all in revolutions and days.   */
            /* Convert them here to radians and minutes:                 */
      sat->xno = get_eight_places( tbuff) * TWOPI / MINUTES_PER_DAY;
      sat->xndt2o = (double)atoi( line1 + 35)
                        * 1.e-8 * TWOPI / MINUTES_PER_DAY_SQUARED;
      if( line1[33] == '-')
         sat->xndt2o *= -1.;
      sat->xndd6o = sci( line1 + 44) * TWOPI / MINUTES_PER_DAY_CUBED;

      sat->bstar = sci( line1 + 53) * AE;
      }
   return( rval ? rval : checksum_problem);
}
<commit_msg>Many TLEs store the NORAD number with spaces instead of leading zeroes.<commit_after>/* Copyright (C) 2018, Project Pluto.  See LICENSE.  */

#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "norad.h"

#define PI 3.141592653589793238462643383279502884197
#define TWOPI (2. * PI)
#define MINUTES_PER_DAY 1440.
#define MINUTES_PER_DAY_SQUARED (MINUTES_PER_DAY * MINUTES_PER_DAY)
#define MINUTES_PER_DAY_CUBED (MINUTES_PER_DAY * MINUTES_PER_DAY_SQUARED)
#define AE 1.0
                             /* distance units, earth radii */

/* TLEs have four angles on line 2,  given in the form DDD.DDDD.  This
can be parsed more quickly as an integer,  then cast to double and
converted to radians,  all in one step.    */

static int get_angle( const char *buff)
{
   int rval = 0;

   while( *buff == ' ')
      buff++;
   while( *buff != ' ')
      {
      if( *buff != '.')
         rval = rval * 10 + (int)( *buff - '0');
      buff++;
      }
   return( rval);
}

/* Converts the quasi scientific notation of the "Motion Dot Dot/6" or
"BSTAR" field to double.  The input will always be of the form

sdddddSe

   ....where s is blank or + or -;  ddddd is a five-digit mantissa;
S is + or - or blank;  and e is a single-digit exponent.  A decimal
point is assumed before the five-digit mantissa.  */

static double sci( const char *string)
{
   double rval = 0.;

   if( string[1] != ' ')
      {
      const int ival = atoi( string);

      if( ival)
         {
         rval = (double)ival * 1.e-5;
         if( string[7] != '0')
            {
            int exponent = string[7] - '0';

            if( string[6] == '-')
               while( exponent--)
                  rval *= .1;
            else
               while( exponent--)
                  rval *= 10.;
            }
         }
      }
   return( rval);
}

/* Does a checksum modulo 10 on the given line.  Digits = their
value, '-' = 1, all other chars = 0.  Returns 0 if ok, a negative
value if it's definitely not a TLE line,  positive if it's all OK
except the checksum.  This last was added because people sometimes
want to use TLEs without worrying about the checksum. */

int DLL_FUNC tle_checksum( const char *buff)
{
   int rval = 0;
   int count = 69;

   if( (*buff != '1' && *buff != '2') || buff[1] != ' ')
      return( -1);
   while( --count)
      {
      if( *buff > '0' && *buff <= '9')
         rval += *buff - '0';
      else if( *buff == '-')
         rval++;
      if( *buff < ' ' || *buff > 'z')           /* invalid character */
         return( -2);
      buff++;
      }
   rval -= *buff++ - '0';
   if( *buff > ' ')                 /* line unterminated */
      rval = -3;
   else
      {
      rval %= 10;
      if( rval < 0)
         rval += 10;
      }
   return( rval % 10);
}

static inline int mutant_dehex( const char ichar)
{
   int rval;

   if( ichar <= '9' && ichar >= '0')
      rval = ichar - '0';
   else if( ichar >= 'A' && ichar <= 'Z')
      rval = ichar + 10 - 'A';
   else
      rval = -1;
   return( rval);
}

/* The "standard" SDP4 model fails badly for very high-flying satellites.
As a non-standard extension,  I'm simply storing state vectors for them,
using the following somewhat odd scheme :

1 40391U 15007B   15091.99922241 sxxxxxxxx syyyyyyyy szzzzzzzzH  9997
2 49391 [valid range, accuracy]  saaaaaaaa sbbbbbbbb scccccccc    0 8

   Epoch,  int'l & NORAD IDs are stored in the standard manner.  The
'ephemeris type' is H (rather than the otherwise universal 0).  The
xyz position and vx, vy, vz velocity are stored as 8-digit signed
base-36 integers,  hence a range of +/- 36^8 = about +/- 2.82x10^12.

  x, y, z are in meters,  and hence cover a range +/- 18.9 AU.
vx, vy, vz are in 10^-4 m/s,  range +/- 94% c.  */

static double get_high_value( const char *iptr)
{
   int64_t rval = 0;

   assert( *iptr == '+' || *iptr == '-');
   if( *iptr == '+' || *iptr == '-')
      {
      int i, digit;

      for( i = 1; i < 9; i++)
         {
         digit = mutant_dehex( iptr[i]);
         assert( digit >= 0);
         rval = rval * (int64_t)36 + (int64_t)digit;
         }
      if( *iptr == '-')
         rval = -rval;
      }
   return( (double)rval);
}

/* Traditionally,  NORAD numbers were stored as five digits.  In 2020, new
detectors threatened to go past 100K objects;  the 'Alpha-5' scheme allows
the first byte to be replaced by an uppercase letter,  with I and O
skipped.  That gets us to 339999.  Note that NORAD is encouraging us to
switch to other formats (XML, JSON,  etc.) that allow nine-digit numbers.

   Should 340K numbers prove insufficient,  the following scheme would
use all 34^5 = 45435424 possible combinations.  d = digit, L = letter,
x = either.  We also could use lowercase letters and some others to
get 10^9 combinations within five bytes with backward compatibility.

(1) ddddd = 'traditional' scheme provides 100000 combinations;
(2) Ldddd = Alpha5 scheme adds 240000;
(3) xxxxL = 34^4*24      = 32072064 more;
(4) xxxLd = 34^3*24*10   =  9432960 more;
(5) xxLdd = 34^2*24*100  =  2774400 more;
(6) xLddd = 34*24*1000   =   816000 more. */

static int base34_to_int( const char c)
{
   int offset;

   if( c == ' ')
      return( 0);
   if( c >= '0' && c <= '9')
      offset = '0';
   else if( c >= 'A' && c <= 'H')
      offset = 'A' - 10;
   else if( c >= 'J' && c <= 'N')
      offset = 'J' - 18;
   else if( c >= 'P' && c <= 'Z')
      offset = 'P' - 23;
   else
      return( -1);
   return( c - offset);
}

static int get_norad_number( const char *buff)
{
   const int ten_thousands = base34_to_int( *buff);

   if( ten_thousands >= 0)
      return( ten_thousands * 10000 + atoi( buff + 1));
   else
      return( 0);
}

static inline double get_eight_places( const char *ptr)
{
   return( (double)atoi( ptr) + (double)atoi(ptr + 4) * 1e-8);
}

/* Meteor 2-08                                                           */
/* 1 13113U          88245.60005115 0.00000076           63463-4 0  5998 */
/* 2 13113  82.5386 288.0994 0015973 147.1294 213.0868 13.83869004325321 */

#define J2000 2451545.5
#define J1900 (J2000 - 36525. - 1.)

/* parse_elements returns:
         0 if the elements are parsed without error;
         1 if they're OK except the first line has a checksum error;
         2 if they're OK except the second line has a checksum error;
         3 if they're OK except both lines have checksum errors;
         a negative value if the lines aren't at all parseable */

int DLL_FUNC parse_elements( const char *line1, const char *line2, tle_t *sat)
{
   int rval, checksum_problem = 0;

   if( *line1 != '1' || *line2 != '2')
      rval = -4;
   else
      {
      rval = tle_checksum( line1);
      if( rval > 0)
         {
         checksum_problem = 1;  /* there's a checksum problem,  but it's */
         rval = 0;              /* not fatal; continue processing the TLE */
         }
      }

   if( rval)
      rval -= 100;
   else
      {
      rval = tle_checksum( line2);
      if( rval > 0)
         {
         checksum_problem |= 2;  /* there's a checksum problem,  but it's */
         rval = 0;               /* not fatal; continue processing the TLE */
         }
      }

   if( !rval)
      {
      char tbuff[13];
      int year = line1[19] - '0';

      if( line1[18] >= '0')
         year += (line1[18] - '0') * 10;
      if( year < 57)          /* cycle around Y2K */
         year += 100;
      sat->epoch = get_eight_places( line1 + 20) + J1900
             + (double)( year * 365 + (year - 1) / 4);
      sat->norad_number = get_norad_number( line1 + 2);
      memcpy( tbuff, line1 + 64, 4);
      tbuff[4] = '\0';
      sat->bulletin_number = atoi( tbuff);
      sat->classification = line1[7];       /* almost always 'U' */
      memcpy( sat->intl_desig, line1 + 9, 8);
      sat->intl_desig[8] = '\0';
      memcpy( tbuff, line2 + 63, 5);
      tbuff[5] = '\0';
      sat->revolution_number = atoi( tbuff);
      sat->ephemeris_type = line1[62];
      if( sat->ephemeris_type == 'H')
         {
         size_t i;
         double *state_vect = &sat->xincl;

         for( i = 0; i < 3; i++)
            {
            state_vect[i]     = get_high_value( line1 + 33 + i * 10);
            state_vect[i + 3] = get_high_value( line2 + 33 + i * 10) * 1e-4;
            }
         return( 0);
         }

      sat->xmo = (double)get_angle( line2 + 43) * (PI / 180e+4);
      sat->xnodeo = (double)get_angle( line2 + 17) * (PI / 180e+4);
      sat->omegao = (double)get_angle( line2 + 34) * (PI / 180e+4);
      sat->xincl = (double)get_angle( line2 + 8) * (PI / 180e+4);
      sat->eo = atoi( line2 + 26) * 1.e-7;

      /* Make sure mean motion is null-terminated, since rev. no.
          may immediately follow. */
      memcpy( tbuff, line2 + 51, 12);
      tbuff[12] = '\0';
            /* Input mean motion,  derivative of mean motion and second  */
            /* deriv of mean motion,  are all in revolutions and days.   */
            /* Convert them here to radians and minutes:                 */
      sat->xno = get_eight_places( tbuff) * TWOPI / MINUTES_PER_DAY;
      sat->xndt2o = (double)atoi( line1 + 35)
                        * 1.e-8 * TWOPI / MINUTES_PER_DAY_SQUARED;
      if( line1[33] == '-')
         sat->xndt2o *= -1.;
      sat->xndd6o = sci( line1 + 44) * TWOPI / MINUTES_PER_DAY_CUBED;

      sat->bstar = sci( line1 + 53) * AE;
      }
   return( rval ? rval : checksum_problem);
}
<|endoftext|>


<commit_before>#include <string>
#include <iostream>
#include <stdio.h>
#include "language.h"
#include "runner.h"
using namespace std;

Runner::Runner(string source_file, string testcase_file){
    _source_file = source_file;
    _testcase_file = testcase_file;
}
string Runner::source_file() const{
    return _source_file;
}
string Runner::testcase_file() const{
    return _testcase_file;
}
Language Runner::language() const{
    return Unknown;
}
int Runner::compile() const{
    return 0;
}
int Runner::execute() const{
    FILE* fp;
    char buf[1024];
    if((fp = popen(execute_command().c_str(), "r")) != NULL){
        fputs("test0\n", fp);
        fputs("test1\n", fp);
        cout << language() << ": executing...\n";
        while(fgets(buf, sizeof(buf), fp) != NULL){
            cout << buf << endl;
        }
        cout << language() << ": executing...\n";
        return pclose(fp);
    }
    return -1;
}
int Runner::cleanup() const{
    return 0;
}
void Runner::run() const{
    cout << "compiling..." << endl;
    compile();
    cout << "executing..." << endl;
    execute();
}
<commit_msg>Feature child process<commit_after>#include <string>
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include "language.h"
#include "runner.h"
using namespace std;

Runner::Runner(string source_file, string testcase_file){
    _source_file = source_file;
    _testcase_file = testcase_file;
}
string Runner::source_file() const{
    return _source_file;
}
string Runner::testcase_file() const{
    return _testcase_file;
}
Language Runner::language() const{
    return Unknown;
}
int Runner::compile() const{
    return 0;
}
int Runner::execute() const{
    int pipe_to_child[2];
    int pipe_from_child[2];
    if(pipe(pipe_to_child) < 0){
        perror("popen");
        exit(EXIT_FAILURE);
    }
    if(pipe(pipe_from_child) < 0){
        perror("popen");
        close(pipe_to_child[0]);
        close(pipe_to_child[1]);
        exit(EXIT_FAILURE);
    }
    int pid = fork();
    if(pid < 0){
        perror("fork");
        close(pipe_to_child[0]);
        close(pipe_to_child[1]);
        close(pipe_from_child[0]);
        close(pipe_from_child[1]);
        exit(EXIT_FAILURE);
    }
    if(pid == 0){
        close(pipe_to_child[1]);
        close(pipe_from_child[0]);
        dup2(pipe_to_child[0], fileno(stdin));
        dup2(pipe_from_child[1], fileno(stdout));
        close(pipe_to_child[0]);
        close(pipe_from_child[1]);
        if(execlp("sh", "sh", "-c", execute_command().c_str(), NULL) < 0){
            perror("execute");
            exit(EXIT_FAILURE);
        }
    }
    write(pipe_to_child[1], "send message\n", 13);
    write(pipe_to_child[1], "send mesg\n", 10);
    close(pipe_to_child[1]);
    char buff[255];
    read(pipe_from_child[0], buff, 255);
    printf("parent process:\n\n%s", buff);
    return 0;
}
int Runner::cleanup() const{
    return 0;
}
void Runner::run() const{
    cout << "compiling..." << endl;
    if(compile() != 0){
        cerr << "Failed compile." << endl;
        exit(EXIT_FAILURE);
    }
    cout << "executing..." << endl;
    execute();
}
<|endoftext|>


<commit_before>//include <tclap/CmdLine.h>
#include <algorithm>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include "bin_file.hpp"
#include "prenexusrenderer.hpp"
#include "renderer.hpp"
#include "version.hpp"

using std::cerr;
using std::cout;
using std::endl;
using std::invalid_argument;
using std::string;
using std::stringstream;
using std::vector;
namespace po = boost::program_options;

typedef uint32_t event_t;

struct Config{
  bool byte_swap;
  bool show_data;
  bool show_line_num;
  bool multi_file;
  bool sum_data;
  bool sum_block;
  bool is_event;
  bool get_pix_range;
  vector<size_t> integrate;
};

static const size_t BLOCK_SIZE=1024;
static const event_t ERROR=0x80000000;
static const string DEFAULT_TYPE="char";

static string::size_type count_occur(const string &str, const string &ch){
  string::size_type count = 0;
  string::size_type index = 0;

  // infinite loop to make sure that the entire string is parsed.
  while(true)
    {
      index = str.find(ch,index+1);
      if(index==string::npos)
        {
          break;
        }
      count++;
    }
  return count;
}

static bool my_isnotdigit(char c){
  return !isdigit(c);
}

static int str_to_int(const string &str){
  if(str.substr(0,1)=="-")
    return -1*str_to_int(str.substr(1,str.size()));

  string::const_iterator it = str.begin();
  it = std::find_if(it,str.end(),my_isnotdigit);

  if(it!=str.end())
    throw invalid_argument("str_to_int(string) argument is not an integer");

  return atol(str.c_str());
}

extern vector<string> split(const string &source,const string &split)
{
  string::size_type number=count_occur(source,split);
  if(number==0)
    {
      vector<string> result;
      result.push_back(source);
      return result;
    }

  vector<string> result;
  string::size_type start=0;
  string::size_type stop=0;
  string inner;
  while(true)
    {
      stop=source.find(split,start);
      if(stop==string::npos)
        {
          result.push_back(source.substr(start));
          break;
        }
      else
        {
          result.push_back(source.substr(start,stop-start));
          start=stop+split.size();
        }
    }
  return result;
}

static vector<int> str_to_intArray(const string & array){
  vector<string> str_array = split(array, ",");
  vector<int> result(0);
  size_t length = str_array.size();
  for (size_t i = 0; i < length; ++i) {
    result.push_back(str_to_int(str_array[i]));
  }
  return result;
}

template <typename NumT>
string thing_to_str(const NumT thing) {
  std::stringstream s;
  s << thing;
  return s.str();
}

template <typename NumT>
string thing_to_str(const vector<NumT> &thing) {
  std::stringstream s;
  size_t length = thing.size();
  for ( size_t i = 0 ; i < length ; ++i ){
    s << thing[i] << " ";
  }
  return s.str();
}

string pixid_str(const size_t pixid, const vector<size_t> &bounds) {
  size_t rank = bounds.size();
  if (rank<=0) {
    return "";
  }
  size_t max_index=1;
  for (size_t i = 0 ; i<rank ; ++i ){
    max_index*=bounds[i];
  }
  if(pixid>max_index) {
    std::stringstream s;
    s << "Pixel id outside of known bounds: " << pixid << ">" << max_index;
    throw std::runtime_error(s.str());
  }
  size_t my_id = pixid;
  vector<size_t> indices(bounds.size());
  for (size_t i = 0 ; i < rank-1 ; ++i ) {
    indices[i] = my_id / bounds[i+1];
    my_id = my_id - indices[i] * bounds[i+1];
  }
  indices[rank-1] = my_id;

  return thing_to_str(indices);
}

/**
 * The main entry point for the program.
 */
int main(int argc, char** argv)
{
  // ---------- Declare the supported options.
  po::options_description generic_options("Generic options");
  generic_options.add_options()
    ("help,h", "Produce this help message.")
    ("version,v", "Print version string.")
    ;

  stringstream typesHelp;
  typesHelp << "Set the type of the data. Allowed values are: " << render::getKnownDataDescr() << ", " << prenexus::getKnownDataDescr();
  po::options_description config_options("Configuration");
  config_options.add_options()
    ("type,t", po::value<string>()->default_value(DEFAULT_TYPE), typesHelp.str().c_str())
    ("offset", po::value<size_t>()->default_value(0), "Skip to this position (in bytes) in the file.")
    ("length", po::value<size_t>()->default_value(0), "Number of items to read (NOT in bytes). Zero means read to end of file.")
    ("records", po::value<size_t>()->default_value(1), "Number of items to print per line")
    ("byteswap", "Perform byte swapping on the data")
    ("quiet,q", "Do not print out values")
    ("lines", "show line numbers")
    ;

  po::options_description hidden_options;
  hidden_options.add_options()
    ("filename", po::value< vector<string> >(), "input files")
    ;

  // all options available on the command line
  po::options_description cmdline_options;
  cmdline_options.add(generic_options).add(config_options).add(hidden_options);

  // all options visible to the user
  po::options_description visible_options("Allowed options");
  visible_options.add(generic_options).add(config_options);

  // ---------- parse the command line
  po::positional_options_description p;
  p.add("filename", -1);
  po::variables_map vm;
  po::store(po::command_line_parser(argc, argv).options(cmdline_options)
	    .positional(p).run(), vm);
  po::notify(vm);

  // ---------- get everything out of the options
  // work with generic options
  if (vm.count("help")) {
    cout << "Usage: " << argv[0] << " [options] <filenames>" << endl;
    cout << endl;
    cout << visible_options << endl;
    return 1;
  }
  if (vm.count("version")) {
    cout << "morebin version " << VERSION << " built " << DATE << endl;
    cout << endl;
    cout << "http://github.com/peterfpeterson/morebin/";
    if (VERSION.find("SNAPSHOT") == string::npos)
      cout << "tree/v" << VERSION;
    cout << endl;
    return 0;
  }

  // config options
  size_t offset = 0;
  if (vm.count("offset"))
    offset = vm["offset"].as<size_t>();
  size_t length = 0;
  if (vm.count("length"))
    length = vm["length"].as<size_t>();
  size_t numItemsPerLine = 1;
  if (vm.count("records"))
    numItemsPerLine = vm["records"].as<size_t>();
  bool byteswap = (vm.count("byteswap") > 0);
  bool quiet = (vm.count("quiet") > 0);
  string dataType = DEFAULT_TYPE;
  if (vm.count("type"))
    dataType = vm["type"].as<string>();
  bool showLines = (vm.count("lines") > 0);

  // hidden options
  vector<string> files;
  if (vm.count("filename"))
  {
    files = vm["filename"].as< vector<string> >();
  }
  else
  {
    cerr << "ERROR: failed to supply <filename>" << endl;
    //cmd.getOutput()->usage(cmd);
    return -1;
  }

  // ---------- push through the list of files
  try {
    bool multiFile = (files.size() > 1);
    render::Renderer * renderer = NULL;
    try {
      renderer = new render::Renderer();
      renderer->setDataDescr(dataType);
    } catch (std::runtime_error &e) {
      // TODO should print a warning
      renderer = new prenexus::PrenexusRenderer();
      renderer->setDataDescr(dataType);
    }
    renderer->quiet(quiet);
    renderer->showLines(showLines);
    renderer->numItemsPerLine(numItemsPerLine);

    for (std::size_t i = 0; i < files.size(); i++) {
      BinFile file(files[i]);
      file.setByteSwap(byteswap);
      if (multiFile) {
	cout << "******************************" << endl;
	cout << "* " << files[i] << " size:" << file.size_in_bytes() << endl;
	cout << "******************************" << endl;
      }
      renderer->showData(file, offset, length);
    }

    delete renderer;
  } catch(std::runtime_error &e) {
    cerr << "RUNTIME ERROR:" << e.what() << endl;
    return -1;
  }

  return 0;
}
<commit_msg>Adding better description of the types.<commit_after>//include <tclap/CmdLine.h>
#include <algorithm>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include "bin_file.hpp"
#include "prenexusrenderer.hpp"
#include "renderer.hpp"
#include "version.hpp"

using std::cerr;
using std::cout;
using std::endl;
using std::invalid_argument;
using std::string;
using std::stringstream;
using std::vector;
namespace po = boost::program_options;

typedef uint32_t event_t;

struct Config{
  bool byte_swap;
  bool show_data;
  bool show_line_num;
  bool multi_file;
  bool sum_data;
  bool sum_block;
  bool is_event;
  bool get_pix_range;
  vector<size_t> integrate;
};

static const size_t BLOCK_SIZE=1024;
static const event_t ERROR=0x80000000;
static const string DEFAULT_TYPE="char";

static string::size_type count_occur(const string &str, const string &ch){
  string::size_type count = 0;
  string::size_type index = 0;

  // infinite loop to make sure that the entire string is parsed.
  while(true)
    {
      index = str.find(ch,index+1);
      if(index==string::npos)
        {
          break;
        }
      count++;
    }
  return count;
}

static bool my_isnotdigit(char c){
  return !isdigit(c);
}

static int str_to_int(const string &str){
  if(str.substr(0,1)=="-")
    return -1*str_to_int(str.substr(1,str.size()));

  string::const_iterator it = str.begin();
  it = std::find_if(it,str.end(),my_isnotdigit);

  if(it!=str.end())
    throw invalid_argument("str_to_int(string) argument is not an integer");

  return atol(str.c_str());
}

extern vector<string> split(const string &source,const string &split)
{
  string::size_type number=count_occur(source,split);
  if(number==0)
    {
      vector<string> result;
      result.push_back(source);
      return result;
    }

  vector<string> result;
  string::size_type start=0;
  string::size_type stop=0;
  string inner;
  while(true)
    {
      stop=source.find(split,start);
      if(stop==string::npos)
        {
          result.push_back(source.substr(start));
          break;
        }
      else
        {
          result.push_back(source.substr(start,stop-start));
          start=stop+split.size();
        }
    }
  return result;
}

static vector<int> str_to_intArray(const string & array){
  vector<string> str_array = split(array, ",");
  vector<int> result(0);
  size_t length = str_array.size();
  for (size_t i = 0; i < length; ++i) {
    result.push_back(str_to_int(str_array[i]));
  }
  return result;
}

template <typename NumT>
string thing_to_str(const NumT thing) {
  std::stringstream s;
  s << thing;
  return s.str();
}

template <typename NumT>
string thing_to_str(const vector<NumT> &thing) {
  std::stringstream s;
  size_t length = thing.size();
  for ( size_t i = 0 ; i < length ; ++i ){
    s << thing[i] << " ";
  }
  return s.str();
}

string pixid_str(const size_t pixid, const vector<size_t> &bounds) {
  size_t rank = bounds.size();
  if (rank<=0) {
    return "";
  }
  size_t max_index=1;
  for (size_t i = 0 ; i<rank ; ++i ){
    max_index*=bounds[i];
  }
  if(pixid>max_index) {
    std::stringstream s;
    s << "Pixel id outside of known bounds: " << pixid << ">" << max_index;
    throw std::runtime_error(s.str());
  }
  size_t my_id = pixid;
  vector<size_t> indices(bounds.size());
  for (size_t i = 0 ; i < rank-1 ; ++i ) {
    indices[i] = my_id / bounds[i+1];
    my_id = my_id - indices[i] * bounds[i+1];
  }
  indices[rank-1] = my_id;

  return thing_to_str(indices);
}

/**
 * The main entry point for the program.
 */
int main(int argc, char** argv)
{
  // ---------- Declare the supported options.
  po::options_description generic_options("Generic options");
  generic_options.add_options()
    ("help,h", "Produce this help message.")
    ("version,v", "Print version string.")
    ;

  stringstream typesHelp;
  typesHelp << "Set the type of the data. Allowed values are: " << render::getKnownDataDescr() << ", " << prenexus::getKnownDataDescr();
  po::options_description config_options("Configuration");
  config_options.add_options()
    ("type,t", po::value<string>()->default_value(DEFAULT_TYPE), typesHelp.str().c_str())
    ("offset", po::value<size_t>()->default_value(0), "Skip to this position (in bytes) in the file.")
    ("length", po::value<size_t>()->default_value(0), "Number of items to read (NOT in bytes). Zero means read to end of file.")
    ("records", po::value<size_t>()->default_value(1), "Number of items to print per line")
    ("byteswap", "Perform byte swapping on the data")
    ("quiet,q", "Do not print out values")
    ("lines", "show line numbers")
    ;

  po::options_description hidden_options;
  hidden_options.add_options()
    ("filename", po::value< vector<string> >(), "input files")
    ;

  // all options available on the command line
  po::options_description cmdline_options;
  cmdline_options.add(generic_options).add(config_options).add(hidden_options);

  // all options visible to the user
  po::options_description visible_options("Allowed options");
  visible_options.add(generic_options).add(config_options);

  // ---------- parse the command line
  po::positional_options_description p;
  p.add("filename", -1);
  po::variables_map vm;
  po::store(po::command_line_parser(argc, argv).options(cmdline_options)
	    .positional(p).run(), vm);
  po::notify(vm);

  // ---------- get everything out of the options
  // work with generic options
  if (vm.count("help")) {
    cout << "Usage: " << argv[0] << " [options] <filenames>" << endl;
    cout << endl;

    cout << "Source code located at http://github.com/peterfpeterson/morebin" << endl;
    cout << endl;

    cout << visible_options << endl;
    cout << endl;

    cout << "Notes on complex types:" << endl;
    cout << "  The following types are printed in colums (in order)" << endl;
    cout <<  endl;
    cout << "  event" << endl;
    cout << "\ttof(uint32_t) Time of flight." << endl;
    cout << "\tpid(uint32_t) Pixel identifier as published by the DAS/DAE/DAQ." << endl;
    cout << "  oldpulseid" << endl;
    cout << "\ttime(uint32_t,uint32_t) Time of the pulse" << endl;
    cout << "\tevent_index(uint64_t)   The index of the first event for this pulse." << endl;
    cout << "  pulseid" << endl;
    cout << "\ttime(uint32_t,uint32_t) Time of the pulse" << endl;
    cout << "\tevent_index(uint64_t)   The index of the first event for this pulse." << endl;
    cout << "\tpCurrent(double)        The proton charge for the pulse." << endl;
    cout << "  rtdl" << endl;
    cout << "\ttime(uint32_t,uint32_t) Time of the pulse" << endl;
    cout << "\tpulseType(uint32_t)" << endl;
    cout << "\tvetoStatus(uint32_t)" << endl;
    cout << "\tpCurrent(uint32_t)      The proton charge for the pulse." << endl;
    cout << "\tspare(uint32_t)" << endl;
    return 1;
  }
  if (vm.count("version")) {
    cout << "morebin version " << VERSION << " built " << DATE << endl;
    cout << endl;
    cout << "http://github.com/peterfpeterson/morebin/";
    if (VERSION.find("SNAPSHOT") == string::npos)
      cout << "tree/v" << VERSION;
    cout << endl;
    return 0;
  }

  // config options
  size_t offset = 0;
  if (vm.count("offset"))
    offset = vm["offset"].as<size_t>();
  size_t length = 0;
  if (vm.count("length"))
    length = vm["length"].as<size_t>();
  size_t numItemsPerLine = 1;
  if (vm.count("records"))
    numItemsPerLine = vm["records"].as<size_t>();
  bool byteswap = (vm.count("byteswap") > 0);
  bool quiet = (vm.count("quiet") > 0);
  string dataType = DEFAULT_TYPE;
  if (vm.count("type"))
    dataType = vm["type"].as<string>();
  bool showLines = (vm.count("lines") > 0);

  // hidden options
  vector<string> files;
  if (vm.count("filename"))
  {
    files = vm["filename"].as< vector<string> >();
  }
  else
  {
    cerr << "ERROR: failed to supply <filename>" << endl;
    //cmd.getOutput()->usage(cmd);
    return -1;
  }

  // ---------- push through the list of files
  try {
    bool multiFile = (files.size() > 1);
    render::Renderer * renderer = NULL;
    try {
      renderer = new render::Renderer();
      renderer->setDataDescr(dataType);
    } catch (std::runtime_error &e) {
      // TODO should print a warning
      renderer = new prenexus::PrenexusRenderer();
      renderer->setDataDescr(dataType);
    }
    renderer->quiet(quiet);
    renderer->showLines(showLines);
    renderer->numItemsPerLine(numItemsPerLine);

    for (std::size_t i = 0; i < files.size(); i++) {
      BinFile file(files[i]);
      file.setByteSwap(byteswap);
      if (multiFile) {
	cout << "******************************" << endl;
	cout << "* " << files[i] << " size:" << file.size_in_bytes() << endl;
	cout << "******************************" << endl;
      }
      renderer->showData(file, offset, length);
    }

    delete renderer;
  } catch(std::runtime_error &e) {
    cerr << "RUNTIME ERROR:" << e.what() << endl;
    return -1;
  }

  return 0;
}
<|endoftext|>