<commit_before>#include <gmi_mesh.h>
#include <apf.h>
#include <apfMesh2.h>
#include <apfMDS.h>
#include <PCU.h>
#include <parma.h>

namespace {

const char* modelFile = 0;
const char* meshFile = 0;
const char* outFile = 0;
int partitionFactor = 1;

apf::Mesh2* readMesh()
{
  double t0 = MPI_Wtime();
  apf::Mesh2* m = apf::loadMdsMesh(modelFile,meshFile);
  double t1 = MPI_Wtime();
  if ( ! PCU_Comm_Self())
    printf("time to load %s and %s: %f seconds\n",modelFile,meshFile,t1-t0);
  return m;
}

void freeMesh(apf::Mesh* m)
{
  m->destroyNative();
  apf::destroyMesh(m);
}

apf::Migration* getPlan(apf::Mesh* m)
{
  apf::Splitter* splitter = Parma_MakeRibSplitter(m);
  double t0 = MPI_Wtime();
  apf::MeshTag* weights = Parma_WeighByMemory(m);
  apf::Migration* plan = splitter->split(weights, 1.10, partitionFactor);
  m->destroyTag(weights);
  double t1 = MPI_Wtime();
  delete splitter;
  if ( ! PCU_Comm_Self())
    printf("time to run RIB: %f seconds\n",t1-t0);
  return plan;
}

void runAfter(apf::Mesh2* m)
{
  m->verify();
  double t2 = MPI_Wtime();
  m->writeNative(outFile);
  double t3 = MPI_Wtime();
  if ( ! PCU_Comm_Self())
    printf("time to write %s: %f seconds\n",outFile,t3-t2);
  freeMesh(m);
}

void getConfig(int argc, char** argv)
{
  assert(argc==5);
  modelFile = argv[1];
  meshFile = argv[2];
  outFile = argv[3];
  partitionFactor = atoi(argv[4]);
}

}

int main(int argc, char** argv)
{
  int provided;
  MPI_Init_thread(&argc,&argv,MPI_THREAD_MULTIPLE,&provided);
  assert(provided==MPI_THREAD_MULTIPLE);
  PCU_Comm_Init();
  gmi_register_mesh();
  PCU_Protect();
  getConfig(argc,argv);
  apf::Mesh2* m = readMesh();
  splitMdsMesh(m, getPlan(m), partitionFactor, runAfter);
  PCU_Comm_Free();
  MPI_Finalize();
}

<commit_msg>removed timers and verify from split, timers moved<commit_after>#include <gmi_mesh.h>
#include <apf.h>
#include <apfMesh2.h>
#include <apfMDS.h>
#include <PCU.h>
#include <parma.h>

namespace {

const char* modelFile = 0;
const char* meshFile = 0;
const char* outFile = 0;
int partitionFactor = 1;

void freeMesh(apf::Mesh* m)
{
  m->destroyNative();
  apf::destroyMesh(m);
}

apf::Migration* getPlan(apf::Mesh* m)
{
  apf::Splitter* splitter = Parma_MakeRibSplitter(m);
  apf::MeshTag* weights = Parma_WeighByMemory(m);
  apf::Migration* plan = splitter->split(weights, 1.10, partitionFactor);
  m->destroyTag(weights);
  delete splitter;
  return plan;
}

void runAfter(apf::Mesh2* m)
{
  m->writeNative(outFile);
  freeMesh(m);
}

void getConfig(int argc, char** argv)
{
  assert(argc==5);
  modelFile = argv[1];
  meshFile = argv[2];
  outFile = argv[3];
  partitionFactor = atoi(argv[4]);
}

}

int main(int argc, char** argv)
{
  int provided;
  MPI_Init_thread(&argc,&argv,MPI_THREAD_MULTIPLE,&provided);
  assert(provided==MPI_THREAD_MULTIPLE);
  PCU_Comm_Init();
  gmi_register_mesh();
  PCU_Protect();
  getConfig(argc,argv);
  apf::Mesh2* m = apf::loadMdsMesh(modelFile,meshFile);
  splitMdsMesh(m, getPlan(m), partitionFactor, runAfter);
  PCU_Comm_Free();
  MPI_Finalize();
}

<|endoftext|>


<commit_before>#include <x0/http/HttpRangeDef.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <strings.h>

class range_def_test :
	public CPPUNIT_NS::TestFixture
{
public:
	CPPUNIT_TEST_SUITE(range_def_test);
		CPPUNIT_TEST(range1);
		CPPUNIT_TEST(range2);
		CPPUNIT_TEST(range3);
		CPPUNIT_TEST(range4);
		CPPUNIT_TEST(range5);
		CPPUNIT_TEST(range6);
	CPPUNIT_TEST_SUITE_END();

private:
	void range1()
	{
		x0::HttpRangeDef r;
		x0::ConstBuffer spec("bytes=0-499");

		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 0);
		CPPUNIT_ASSERT(r[0].second == 499);
	}

	void range2()
	{
		x0::HttpRangeDef r;
		x0::ConstBuffer spec("bytes=500-999");

		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 500);
		CPPUNIT_ASSERT(r[0].second == 999);
	}

	void range3()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=-500");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == x0::HttpRangeDef::npos);
		CPPUNIT_ASSERT(r[0].second == 500);
	}

	void range4()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=9500-");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 9500);
		CPPUNIT_ASSERT(r[0].second == x0::HttpRangeDef::npos);
	}

	void range5()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=0-0,-1");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 2);

		CPPUNIT_ASSERT(r[0].first == 0);
		CPPUNIT_ASSERT(r[0].second == 0);

		CPPUNIT_ASSERT(r[1].first == x0::HttpRangeDef::npos);
		CPPUNIT_ASSERT(r[1].second == 1);
	}

	void range6()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=500-700,601-999");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unit_name() == "bytes");
		CPPUNIT_ASSERT(r.size() == 2);

		CPPUNIT_ASSERT(r[0].first == 500);
		CPPUNIT_ASSERT(r[0].second == 700);

		CPPUNIT_ASSERT(r[1].first == 601);
		CPPUNIT_ASSERT(r[1].second == 999);
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(range_def_test);
<commit_msg>adapted to API changes in previous commit.<commit_after>#include <x0/http/HttpRangeDef.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <strings.h>

class range_def_test :
	public CPPUNIT_NS::TestFixture
{
public:
	CPPUNIT_TEST_SUITE(range_def_test);
		CPPUNIT_TEST(range1);
		CPPUNIT_TEST(range2);
		CPPUNIT_TEST(range3);
		CPPUNIT_TEST(range4);
		CPPUNIT_TEST(range5);
		CPPUNIT_TEST(range6);
	CPPUNIT_TEST_SUITE_END();

private:
	void range1()
	{
		x0::HttpRangeDef r;
		x0::ConstBuffer spec("bytes=0-499");

		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 0);
		CPPUNIT_ASSERT(r[0].second == 499);
	}

	void range2()
	{
		x0::HttpRangeDef r;
		x0::ConstBuffer spec("bytes=500-999");

		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 500);
		CPPUNIT_ASSERT(r[0].second == 999);
	}

	void range3()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=-500");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == x0::HttpRangeDef::npos);
		CPPUNIT_ASSERT(r[0].second == 500);
	}

	void range4()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=9500-");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 1);
		CPPUNIT_ASSERT(r[0].first == 9500);
		CPPUNIT_ASSERT(r[0].second == x0::HttpRangeDef::npos);
	}

	void range5()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=0-0,-1");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 2);

		CPPUNIT_ASSERT(r[0].first == 0);
		CPPUNIT_ASSERT(r[0].second == 0);

		CPPUNIT_ASSERT(r[1].first == x0::HttpRangeDef::npos);
		CPPUNIT_ASSERT(r[1].second == 1);
	}

	void range6()
	{
		x0::HttpRangeDef r;

		x0::ConstBuffer spec("bytes=500-700,601-999");
		r.parse(spec);
		CPPUNIT_ASSERT(r.unitName() == "bytes");
		CPPUNIT_ASSERT(r.size() == 2);

		CPPUNIT_ASSERT(r[0].first == 500);
		CPPUNIT_ASSERT(r[0].second == 700);

		CPPUNIT_ASSERT(r[1].first == 601);
		CPPUNIT_ASSERT(r[1].second == 999);
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(range_def_test);
<|endoftext|>


<commit_before>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/thread.h"

#include "base/lazy_instance.h"
#include "base/string_util.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/thread_local.h"
#include "base/waitable_event.h"

namespace base {

// This task is used to trigger the message loop to exit.
class ThreadQuitTask : public Task {
 public:
  virtual void Run() {
    MessageLoop::current()->Quit();
    Thread::SetThreadWasQuitProperly(true);
  }
};

// Used to pass data to ThreadMain.  This structure is allocated on the stack
// from within StartWithOptions.
struct Thread::StartupData {
  // We get away with a const reference here because of how we are allocated.
  const Thread::Options& options;

  // Used to synchronize thread startup.
  WaitableEvent event;

  explicit StartupData(const Options& opt)
      : options(opt),
        event(false, false) {}
};

Thread::Thread(const char* name)
    : stopping_(false),
      startup_data_(NULL),
      thread_(0),
      message_loop_(NULL),
      thread_id_(0),
      name_(name) {
}

Thread::~Thread() {
  Stop();
}

namespace {

// We use this thread-local variable to record whether or not a thread exited
// because its Stop method was called.  This allows us to catch cases where
// MessageLoop::Quit() is called directly, which is unexpected when using a
// Thread to setup and run a MessageLoop.
base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool(
    base::LINKER_INITIALIZED);

}  // namespace

void Thread::SetThreadWasQuitProperly(bool flag) {
  lazy_tls_bool.Pointer()->Set(flag);
}

bool Thread::GetThreadWasQuitProperly() {
  bool quit_properly = true;
#ifndef NDEBUG
  quit_properly = lazy_tls_bool.Pointer()->Get();
#endif
  return quit_properly;
}

bool Thread::Start() {
  return StartWithOptions(Options());
}

bool Thread::StartWithOptions(const Options& options) {
  DCHECK(!message_loop_);

  SetThreadWasQuitProperly(false);

  StartupData startup_data(options);
  startup_data_ = &startup_data;

  if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
    DLOG(ERROR) << "failed to create thread";
    startup_data_ = NULL;  // Record that we failed to start.
    return false;
  }

  // Wait for the thread to start and initialize message_loop_
  startup_data.event.Wait();

  DCHECK(message_loop_);
  return true;
}

void Thread::Stop() {
  if (!thread_was_started())
    return;

  StopSoon();

  // Wait for the thread to exit.
  //
  // TODO(darin): Unfortunately, we need to keep message_loop_ around until
  // the thread exits.  Some consumers are abusing the API.  Make them stop.
  //
  PlatformThread::Join(thread_);

  // The thread should NULL message_loop_ on exit.
  DCHECK(!message_loop_);

  // The thread no longer needs to be joined.
  startup_data_ = NULL;

  stopping_ = false;
}

void Thread::StopSoon() {
  // We should only be called on the same thread that started us.
  DCHECK_NE(thread_id_, PlatformThread::CurrentId());

  if (stopping_ || !message_loop_)
    return;

  stopping_ = true;
  message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
}

void Thread::Run(MessageLoop* message_loop) {
  message_loop->Run();
}

void Thread::ThreadMain() {
  {
    // The message loop for this thread.
    MessageLoop message_loop(startup_data_->options.message_loop_type);

    // Complete the initialization of our Thread object.
    thread_id_ = PlatformThread::CurrentId();
    PlatformThread::SetName(name_.c_str());
    ANNOTATE_THREAD_NAME(name_.c_str());  // Tell the name to race detector.
    message_loop.set_thread_name(name_);
    message_loop_ = &message_loop;
    message_loop_proxy_ = MessageLoopProxy::CreateForCurrentThread();

    // Let the thread do extra initialization.
    // Let's do this before signaling we are started.
    Init();

    startup_data_->event.Signal();
    // startup_data_ can't be touched anymore since the starting thread is now
    // unlocked.

    Run(message_loop_);

    // Let the thread do extra cleanup.
    CleanUp();

    // Assert that MessageLoop::Quit was called by ThreadQuitTask.
    DCHECK(GetThreadWasQuitProperly());

    // We can't receive messages anymore.
    message_loop_ = NULL;
    message_loop_proxy_ = NULL;
  }
  CleanUpAfterMessageLoopDestruction();
  thread_id_ = 0;
}

}  // namespace base
<commit_msg>Annotate a benign data race in Thread::StopSoon BUG=23245 TEST=TSan bots Review URL: http://codereview.chromium.org/2136013<commit_after>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/thread.h"

#include "base/lazy_instance.h"
#include "base/string_util.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/thread_local.h"
#include "base/waitable_event.h"

namespace base {

// This task is used to trigger the message loop to exit.
class ThreadQuitTask : public Task {
 public:
  virtual void Run() {
    MessageLoop::current()->Quit();
    Thread::SetThreadWasQuitProperly(true);
  }
};

// Used to pass data to ThreadMain.  This structure is allocated on the stack
// from within StartWithOptions.
struct Thread::StartupData {
  // We get away with a const reference here because of how we are allocated.
  const Thread::Options& options;

  // Used to synchronize thread startup.
  WaitableEvent event;

  explicit StartupData(const Options& opt)
      : options(opt),
        event(false, false) {}
};

Thread::Thread(const char* name)
    : stopping_(false),
      startup_data_(NULL),
      thread_(0),
      message_loop_(NULL),
      thread_id_(0),
      name_(name) {
}

Thread::~Thread() {
  Stop();
}

namespace {

// We use this thread-local variable to record whether or not a thread exited
// because its Stop method was called.  This allows us to catch cases where
// MessageLoop::Quit() is called directly, which is unexpected when using a
// Thread to setup and run a MessageLoop.
base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool(
    base::LINKER_INITIALIZED);

}  // namespace

void Thread::SetThreadWasQuitProperly(bool flag) {
  lazy_tls_bool.Pointer()->Set(flag);
}

bool Thread::GetThreadWasQuitProperly() {
  bool quit_properly = true;
#ifndef NDEBUG
  quit_properly = lazy_tls_bool.Pointer()->Get();
#endif
  return quit_properly;
}

bool Thread::Start() {
  return StartWithOptions(Options());
}

bool Thread::StartWithOptions(const Options& options) {
  DCHECK(!message_loop_);

  SetThreadWasQuitProperly(false);

  StartupData startup_data(options);
  startup_data_ = &startup_data;

  if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
    DLOG(ERROR) << "failed to create thread";
    startup_data_ = NULL;  // Record that we failed to start.
    return false;
  }

  // Wait for the thread to start and initialize message_loop_
  startup_data.event.Wait();

  DCHECK(message_loop_);
  return true;
}

void Thread::Stop() {
  if (!thread_was_started())
    return;

  StopSoon();

  // Wait for the thread to exit.
  //
  // TODO(darin): Unfortunately, we need to keep message_loop_ around until
  // the thread exits.  Some consumers are abusing the API.  Make them stop.
  //
  PlatformThread::Join(thread_);

  // The thread should NULL message_loop_ on exit.
  DCHECK(!message_loop_);

  // The thread no longer needs to be joined.
  startup_data_ = NULL;

  stopping_ = false;
}

void Thread::StopSoon() {
  // We should only be called on the same thread that started us.

  // Reading thread_id_ without a lock can lead to a benign data race
  // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
  DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());

  if (stopping_ || !message_loop_)
    return;

  stopping_ = true;
  message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
}

void Thread::Run(MessageLoop* message_loop) {
  message_loop->Run();
}

void Thread::ThreadMain() {
  {
    // The message loop for this thread.
    MessageLoop message_loop(startup_data_->options.message_loop_type);

    // Complete the initialization of our Thread object.
    thread_id_ = PlatformThread::CurrentId();
    PlatformThread::SetName(name_.c_str());
    ANNOTATE_THREAD_NAME(name_.c_str());  // Tell the name to race detector.
    message_loop.set_thread_name(name_);
    message_loop_ = &message_loop;
    message_loop_proxy_ = MessageLoopProxy::CreateForCurrentThread();

    // Let the thread do extra initialization.
    // Let's do this before signaling we are started.
    Init();

    startup_data_->event.Signal();
    // startup_data_ can't be touched anymore since the starting thread is now
    // unlocked.

    Run(message_loop_);

    // Let the thread do extra cleanup.
    CleanUp();

    // Assert that MessageLoop::Quit was called by ThreadQuitTask.
    DCHECK(GetThreadWasQuitProperly());

    // We can't receive messages anymore.
    message_loop_ = NULL;
    message_loop_proxy_ = NULL;
  }
  CleanUpAfterMessageLoopDestruction();
  thread_id_ = 0;
}

}  // namespace base
<|endoftext|>


<commit_before>/*!
 * \file glyph_render_data_restricted_rays.hpp
 * \brief file glyph_render_data_restricted_rays.hpp
 *
 * Copyright 2018 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/rect.hpp>
#include <fastuidraw/text/glyph_render_data.hpp>
#include <fastuidraw/painter/painter_enums.hpp>

namespace fastuidraw
{
/*!\addtogroup Text
 * @{
 */

  /*!
   * A GlyphRenderDataRestrictedRays represents the data needed to
   * build a glyph to render it with a modification to the technique
   * of "GPU-Centered Font Rendering Directly from Glyph Outlines"
   * by Eric Lengyel. The modifications we have to the technique are
   * as follows.
   *  - We break the glyph's box into a hierarchy of boxes where
   *    each leaf node has a list of what curves are in the box
   *    together with a single sample point inside the box giving
   *    the winding number at the sample point.
   *  - To compute the winding number, one runs the technique
   *    on the ray connecting the fragment position to the winding
   *    sample position and increment the value by the winding value
   *    of the sample. Here the main caveat is that one needs to
   *    ignore any intersection that are not between the fragment
   *    position and the sample position.
   *  - The shader (which can be fetched with the function
   *    fastuidraw::glsl::restricted_rays_compute_coverage())
   *    tracks the closest curve (in a local L1-metric scaled to
   *    window coordinates) to the fragment position that increments
   *    the winding value and also tracks the closest curve that
   *    decrements the winding value. Using those two values together
   *    with the winding value allows the shader to compute a coverage
   *    value to perform anti-aliasing.
   */
  class GlyphRenderDataRestrictedRays:public GlyphRenderData
  {
  public:
    /*!
     * Enumeration to describe the hierarchy of bounding
     * boxes as packed into the data. A node in the
     * hierarchy is a single 32-bit value. A leaf in the
     * hierarchy is a single 32-bit value followed by a
     * single sample point which has a winding value
     * and offset position packed as according to \ref
     * winding_sample_packing_t.
     */
    enum hierarchy_packing_t
      {

        /*!
         * This is the number of bits used to store the
         * offsets to a child node.
         */
        hierarchy_child_offset_numbits = 15u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the number bit used to encode the offset
         * to where the list of curves for the box is
         * located. The list of curves is packed as
         * according to \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_numbits = 16u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the number of bits used to encode the size
         * of the list of curves for the box is located.
         * The list of curves is packed as according to
         * \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_size_numbits = 15u,

        /*!
         * If this bit is up, indicates that the 32-bit value
         * is holding node data. If the bit is down indicates
         * that the element is a leaf and the value holds the
         * properties of the curve list in the box and the next
         * value holds the winding sample information for the
         * box and are packed as according to \ref
         * winding_sample_packing_t.
         */
        hierarchy_is_node_bit = 0u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This bit
         * indicates if the split of the node is horizontal
         * of verical. A value of 0 indicates that the split
         * happens in the x-coordinate (i.e. the child nodes
         * have the same values for min-y and max-y) and a
         * value of 1 indicates the split happens in the
         * y-coordinate.
         */
        hierarchy_splitting_coordinate_bit = hierarchy_is_node_bit + 1u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This is
         * the first bit holding the offset from the start
         * of the geomertic data of the glyph for the child
         * node which comes before the split, i.e. the child
         * on the left or bottom side.
         */
        hierarchy_child0_offset_bit0 = hierarchy_splitting_coordinate_bit + 1u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This is
         * the first bit holding the offset from the start
         * of the geomertic data of the glyph for the child
         * node which comes after the split, i.e. the child
         * on the right or top side.
         */
        hierarchy_child1_offset_bit0 = hierarchy_child0_offset_bit0 + hierarchy_child_offset_numbits,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the first bit used to encode the offset
         * to where the list of curves for the box is
         * located. The list of curves is packed as
         * according to \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_bit0 = hierarchy_is_node_bit + 1u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the first bit used to encode the size
         * of the list of curves for the box is located.
         * The list of curves is packed as according to
         * \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_size_bit0 = hierarchy_leaf_curve_list_bit0 + hierarchy_leaf_curve_list_numbits,
      };

    /*!
     * Enumeration to describe how the winding samples
     * of a leaf-box of the hierarchy are packed. The
     * position of the sample is the bottom left corner
     * of the node offset by a delta:
     * $ Delta = RelativeDelta * BoxDimensions / DeltaFactor $
     * where PackedDelta is extracted from the 32-bit
     * value as a pair of 8-bit values located at bits
     * \ref delta_x_bit0 and \ref delta_y_bit0; DeltaFactor
     * is given by \ref delta_div_factor and BoxDimensions
     * is the width and height of the box of the leaf.
     */
    enum winding_sample_packing_t
      {
        /*!
         * Winding values are stored biased (in order
         * to be able to store negative winding values)
         * This is the value to add to the unpacked
         * winding number found at bit \ref
         * winding_value_bit0
         */
        winding_bias = 32768u,

        /*!
         * The first bit used to encode the winding value
         * (which is stored biased by \ref winding_bias).
         */
        winding_value_bit0 = 0u,

        /*!
         * The number of bits used to encode the winding value
         * (which is stored biased by \ref winding_bias).
         */
        winding_value_numbits = 16u,

        /*!
         * The amount by which to divide the delta
         */
        delta_div_factor = 256,

        /*!
         * The first bit used to store the delta x-coordinate
         */
        delta_x_bit0 = 16u,

        /*!
         * The first bit used to store the delta y-coordinate
         */
        delta_y_bit0 = 24u,

        /*!
         * The number of bits used to store the delta x-coordinate
         * and delta y-coordinate values.
         */
        delta_numbits = 8u,
      };

    /*!
     * Enumeration to describe how a list of curves is packed.
     * Each 32-bit value holds the data for two curves.
     * A curve entry is 16-bit value whose highest bit gives the
     * degree of the curve and the remaining 15-bits give the
     * offset to the location of the curve's control points.
     */
    enum curve_list_packing_t
      {
        /*!
         * The number of bits to store a single curve entry.
         */
        curve_numbits = 16u,

        /*!
         * The first bit used for the first curve of the entry.
         */
        curve_entry0_bit0 = 0u,

        /*!
         * The first bit used for the second curve of the entry.
         */
        curve_entry1_bit0 = 16u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * if this bit of the value is up, then the curve referenced
         * is a quadratic Bezier curve having control points. Otherwise,
         * it is a line segment connecting its two points.
         */
        curve_is_quadratic_bit = 15u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * this is the first bit used to store the offset to the
         * location of the points of the curve (packed as according
         * to \ref point_packing_t).
         */
        curve_location_bit0 = 0u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * this is the number of bits used to store the offset to the
         * location of the points of the curve (packed as according
         * to \ref point_packing_t).
         */
        curve_location_numbits = 15u,
      };

    /*!
     * Points are packed as (fp16, fp16) pairs.
     */
    enum point_packing_t
      {
      };

    enum
      {
        /* The glyph coordinate value in each coordiante varies
         * from -\ref glyph_coord_value to +\ref glyph_coord_value
         */
        glyph_coord_value = 32,
      };

    /*!
     * This enumeration describes the meaning of the
     * attributes. The glyph shader is to assume that
     * the glyph-coordinates at the min-corner is
     * (-\ref glyph_coord_value, -\ref glyph_coord_value)
     * and the glyph coordiantes at the max-corner is
     * (+\ref glyph_coord_value, +\ref glyph_coord_value)
     */
    enum attribute_values_t
      {
        /*!
         * Value is 0 if on min-x side of glyph, value is
         * 1 if on max-x side of glyph; packed as uint.
         */
        glyph_normalized_x,

        /*!
         * Value is 0 if on min-y side of glyph, value is
         * 1 if on max-y side of glyph; packed as uint.
         */
        glyph_normalized_y,

        /*!
         * the index into GlyphAttribute::m_data storing
         * the fill rule and the offset into the store for
         * the glyph data. The offset is encoded in the
         * lower 31 bits (i.e. just mask off bit31) and
         * the fill rule is non-zero fill rule if bit31
         * is down and the odd-even fill rule if bit31 is
         * up.
         */
        glyph_offset,

        /*!
         * Number attribute values needed.
         */
        glyph_num_attributes
      };

    /*!
     * Ctor.
     */
    GlyphRenderDataRestrictedRays(void);

    ~GlyphRenderDataRestrictedRays();

    /*!
     * Start a contour. Before starting a new contour
     * the previous contour must be closed by calling
     * line_to() or quadratic_to() connecting to the
     * start point of the previous contour.
     * \param pt start point of the new contour
     */
    void
    move_to(ivec2 pt);

    /*!
     * Add a line segment connecting the end point of the
     * last curve or line segment of the current contour to
     * a given point.
     * \param pt end point of the new line segment
     */
    void
    line_to(ivec2 pt);

    /*!
     * Add a quadratic curveconnecting the end point of the
     * last curve or line segment of the current contour
     * \param ct control point of the quadratic curve
     * \param pt end point of the quadratic curve
     */
    void
    quadratic_to(ivec2 ct, ivec2 pt);

    /*!
     * Finalize the input data after which no more contours or curves may be added;
     * all added contours must be closed before calling finalize(). Once finalize()
     * is called, no further data can be added. How the data is broken into bounding
     * boxes is specified by
     * - units_per_EM argument (see below)
     * - GlyphGenerateParams::restricted_rays_minimum_render_size()
     * - GlyphGenerateParams::restricted_rays_split_thresh()
     * - GlyphGenerateParams::restricted_rays_max_recursion()
     * All contours added must be closed as well.
     * \param f fill rule to use for rendering, must be one of
     *          PainterEnums::nonzero_fill_rule or \ref
     *          PainterEnums::odd_even_fill_rule.
     * \param glyph_rect the rect of the glyph
     * \param units_per_EM the units per EM for the glyph; this value together with
     *                     GlyphGenerateParams::restricted_rays_minimum_render_size()
     *                     is used to decide how close a curve may be to a bounding
     *                     box to decide if it is included.
     */
    void
    finalize(enum PainterEnums::fill_rule_t f, const RectT<int> &glyph_rect,
             float units_per_EM);

    /*!
     * Finalize the input data after which no more contours or curves may be added;
     * all added contours must be closed before calling finale(). Once finalize()
     * is called, no further data can be added. Instead of using methods from
     * \ref GlyphGenerateParams, directly specify how the data is broken into
     * boxes.
     * \param f fill rule to use for rendering, must be one of
     *          PainterEnums::nonzero_fill_rule or \ref
     *          PainterEnums::odd_even_fill_rule.
     * \param glyph_rect the rect of the glyph
     * \param split_thresh if the number of curves within a box is greater than
     *                     this value, the box is split
     * \param max_recursion the maximum level of recursion allowed in splitting
     *                      the data into boxes
     * \param near_thresh horizontal and vertical threshhold to decide if a curve
     *                    outside of a box should be added to a box
     */
    void
    finalize(enum PainterEnums::fill_rule_t f, const RectT<int> &glyph_rect,
             int split_thresh, int max_recursion, vec2 near_thresh);

    /*!
     * Query the data; may only be called after finalize(). Returns
     * \ref routine_fail if finalize() has not yet been called.
     * \param gpu_data location to which to write a c_array to the
     *                 GPU data.
     */
    enum return_code
    query(c_array<const fastuidraw::generic_data> *gpu_data) const;

    virtual
    enum fastuidraw::return_code
    upload_to_atlas(GlyphAtlasProxy &atlas_proxy,
                    GlyphAttribute::Array &attributes) const;

  private:
    void *m_d;
  };
/*! @} */
}
<commit_msg>fastuidraw/text/glyph_render_data_restricted_rays: make the fixed value much larger, with the previous smaller fixed-coordinate value extreme magnification can result in bizarre bleeding<commit_after>/*!
 * \file glyph_render_data_restricted_rays.hpp
 * \brief file glyph_render_data_restricted_rays.hpp
 *
 * Copyright 2018 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/rect.hpp>
#include <fastuidraw/text/glyph_render_data.hpp>
#include <fastuidraw/painter/painter_enums.hpp>

namespace fastuidraw
{
/*!\addtogroup Text
 * @{
 */

  /*!
   * A GlyphRenderDataRestrictedRays represents the data needed to
   * build a glyph to render it with a modification to the technique
   * of "GPU-Centered Font Rendering Directly from Glyph Outlines"
   * by Eric Lengyel. The modifications we have to the technique are
   * as follows.
   *  - We break the glyph's box into a hierarchy of boxes where
   *    each leaf node has a list of what curves are in the box
   *    together with a single sample point inside the box giving
   *    the winding number at the sample point.
   *  - To compute the winding number, one runs the technique
   *    on the ray connecting the fragment position to the winding
   *    sample position and increment the value by the winding value
   *    of the sample. Here the main caveat is that one needs to
   *    ignore any intersection that are not between the fragment
   *    position and the sample position.
   *  - The shader (which can be fetched with the function
   *    fastuidraw::glsl::restricted_rays_compute_coverage())
   *    tracks the closest curve (in a local L1-metric scaled to
   *    window coordinates) to the fragment position that increments
   *    the winding value and also tracks the closest curve that
   *    decrements the winding value. Using those two values together
   *    with the winding value allows the shader to compute a coverage
   *    value to perform anti-aliasing.
   */
  class GlyphRenderDataRestrictedRays:public GlyphRenderData
  {
  public:
    /*!
     * Enumeration to describe the hierarchy of bounding
     * boxes as packed into the data. A node in the
     * hierarchy is a single 32-bit value. A leaf in the
     * hierarchy is a single 32-bit value followed by a
     * single sample point which has a winding value
     * and offset position packed as according to \ref
     * winding_sample_packing_t.
     */
    enum hierarchy_packing_t
      {

        /*!
         * This is the number of bits used to store the
         * offsets to a child node.
         */
        hierarchy_child_offset_numbits = 15u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the number bit used to encode the offset
         * to where the list of curves for the box is
         * located. The list of curves is packed as
         * according to \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_numbits = 16u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the number of bits used to encode the size
         * of the list of curves for the box is located.
         * The list of curves is packed as according to
         * \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_size_numbits = 15u,

        /*!
         * If this bit is up, indicates that the 32-bit value
         * is holding node data. If the bit is down indicates
         * that the element is a leaf and the value holds the
         * properties of the curve list in the box and the next
         * value holds the winding sample information for the
         * box and are packed as according to \ref
         * winding_sample_packing_t.
         */
        hierarchy_is_node_bit = 0u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This bit
         * indicates if the split of the node is horizontal
         * of verical. A value of 0 indicates that the split
         * happens in the x-coordinate (i.e. the child nodes
         * have the same values for min-y and max-y) and a
         * value of 1 indicates the split happens in the
         * y-coordinate.
         */
        hierarchy_splitting_coordinate_bit = hierarchy_is_node_bit + 1u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This is
         * the first bit holding the offset from the start
         * of the geomertic data of the glyph for the child
         * node which comes before the split, i.e. the child
         * on the left or bottom side.
         */
        hierarchy_child0_offset_bit0 = hierarchy_splitting_coordinate_bit + 1u,

        /*!
         * For case where the element is a node, i.e. the
         * bit \ref hierarchy_is_node_bit is up. This is
         * the first bit holding the offset from the start
         * of the geomertic data of the glyph for the child
         * node which comes after the split, i.e. the child
         * on the right or top side.
         */
        hierarchy_child1_offset_bit0 = hierarchy_child0_offset_bit0 + hierarchy_child_offset_numbits,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the first bit used to encode the offset
         * to where the list of curves for the box is
         * located. The list of curves is packed as
         * according to \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_bit0 = hierarchy_is_node_bit + 1u,

        /*!
         * For case where the element is leaf, i.e. the
         * bit \ref hierarchy_is_node_bit is down. This
         * is the first bit used to encode the size
         * of the list of curves for the box is located.
         * The list of curves is packed as according to
         * \ref curve_list_packing_t.
         */
        hierarchy_leaf_curve_list_size_bit0 = hierarchy_leaf_curve_list_bit0 + hierarchy_leaf_curve_list_numbits,
      };

    /*!
     * Enumeration to describe how the winding samples
     * of a leaf-box of the hierarchy are packed. The
     * position of the sample is the bottom left corner
     * of the node offset by a delta:
     * $ Delta = RelativeDelta * BoxDimensions / DeltaFactor $
     * where PackedDelta is extracted from the 32-bit
     * value as a pair of 8-bit values located at bits
     * \ref delta_x_bit0 and \ref delta_y_bit0; DeltaFactor
     * is given by \ref delta_div_factor and BoxDimensions
     * is the width and height of the box of the leaf.
     */
    enum winding_sample_packing_t
      {
        /*!
         * Winding values are stored biased (in order
         * to be able to store negative winding values)
         * This is the value to add to the unpacked
         * winding number found at bit \ref
         * winding_value_bit0
         */
        winding_bias = 32768u,

        /*!
         * The first bit used to encode the winding value
         * (which is stored biased by \ref winding_bias).
         */
        winding_value_bit0 = 0u,

        /*!
         * The number of bits used to encode the winding value
         * (which is stored biased by \ref winding_bias).
         */
        winding_value_numbits = 16u,

        /*!
         * The amount by which to divide the delta
         */
        delta_div_factor = 256,

        /*!
         * The first bit used to store the delta x-coordinate
         */
        delta_x_bit0 = 16u,

        /*!
         * The first bit used to store the delta y-coordinate
         */
        delta_y_bit0 = 24u,

        /*!
         * The number of bits used to store the delta x-coordinate
         * and delta y-coordinate values.
         */
        delta_numbits = 8u,
      };

    /*!
     * Enumeration to describe how a list of curves is packed.
     * Each 32-bit value holds the data for two curves.
     * A curve entry is 16-bit value whose highest bit gives the
     * degree of the curve and the remaining 15-bits give the
     * offset to the location of the curve's control points.
     */
    enum curve_list_packing_t
      {
        /*!
         * The number of bits to store a single curve entry.
         */
        curve_numbits = 16u,

        /*!
         * The first bit used for the first curve of the entry.
         */
        curve_entry0_bit0 = 0u,

        /*!
         * The first bit used for the second curve of the entry.
         */
        curve_entry1_bit0 = 16u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * if this bit of the value is up, then the curve referenced
         * is a quadratic Bezier curve having control points. Otherwise,
         * it is a line segment connecting its two points.
         */
        curve_is_quadratic_bit = 15u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * this is the first bit used to store the offset to the
         * location of the points of the curve (packed as according
         * to \ref point_packing_t).
         */
        curve_location_bit0 = 0u,

        /*!
         * Given an unpacked curve entry (which is 16-bits wide),
         * this is the number of bits used to store the offset to the
         * location of the points of the curve (packed as according
         * to \ref point_packing_t).
         */
        curve_location_numbits = 15u,
      };

    /*!
     * Points are packed as (fp16, fp16) pairs.
     */
    enum point_packing_t
      {
      };

    enum
      {
        /* The glyph coordinate value in each coordiante varies
         * from -\ref glyph_coord_value to +\ref glyph_coord_value
         */
        glyph_coord_value = 2048,
      };

    /*!
     * This enumeration describes the meaning of the
     * attributes. The glyph shader is to assume that
     * the glyph-coordinates at the min-corner is
     * (-\ref glyph_coord_value, -\ref glyph_coord_value)
     * and the glyph coordiantes at the max-corner is
     * (+\ref glyph_coord_value, +\ref glyph_coord_value)
     */
    enum attribute_values_t
      {
        /*!
         * Value is 0 if on min-x side of glyph, value is
         * 1 if on max-x side of glyph; packed as uint.
         */
        glyph_normalized_x,

        /*!
         * Value is 0 if on min-y side of glyph, value is
         * 1 if on max-y side of glyph; packed as uint.
         */
        glyph_normalized_y,

        /*!
         * the index into GlyphAttribute::m_data storing
         * the fill rule and the offset into the store for
         * the glyph data. The offset is encoded in the
         * lower 31 bits (i.e. just mask off bit31) and
         * the fill rule is non-zero fill rule if bit31
         * is down and the odd-even fill rule if bit31 is
         * up.
         */
        glyph_offset,

        /*!
         * Number attribute values needed.
         */
        glyph_num_attributes
      };

    /*!
     * Ctor.
     */
    GlyphRenderDataRestrictedRays(void);

    ~GlyphRenderDataRestrictedRays();

    /*!
     * Start a contour. Before starting a new contour
     * the previous contour must be closed by calling
     * line_to() or quadratic_to() connecting to the
     * start point of the previous contour.
     * \param pt start point of the new contour
     */
    void
    move_to(ivec2 pt);

    /*!
     * Add a line segment connecting the end point of the
     * last curve or line segment of the current contour to
     * a given point.
     * \param pt end point of the new line segment
     */
    void
    line_to(ivec2 pt);

    /*!
     * Add a quadratic curveconnecting the end point of the
     * last curve or line segment of the current contour
     * \param ct control point of the quadratic curve
     * \param pt end point of the quadratic curve
     */
    void
    quadratic_to(ivec2 ct, ivec2 pt);

    /*!
     * Finalize the input data after which no more contours or curves may be added;
     * all added contours must be closed before calling finalize(). Once finalize()
     * is called, no further data can be added. How the data is broken into bounding
     * boxes is specified by
     * - units_per_EM argument (see below)
     * - GlyphGenerateParams::restricted_rays_minimum_render_size()
     * - GlyphGenerateParams::restricted_rays_split_thresh()
     * - GlyphGenerateParams::restricted_rays_max_recursion()
     * All contours added must be closed as well.
     * \param f fill rule to use for rendering, must be one of
     *          PainterEnums::nonzero_fill_rule or \ref
     *          PainterEnums::odd_even_fill_rule.
     * \param glyph_rect the rect of the glyph
     * \param units_per_EM the units per EM for the glyph; this value together with
     *                     GlyphGenerateParams::restricted_rays_minimum_render_size()
     *                     is used to decide how close a curve may be to a bounding
     *                     box to decide if it is included.
     */
    void
    finalize(enum PainterEnums::fill_rule_t f, const RectT<int> &glyph_rect,
             float units_per_EM);

    /*!
     * Finalize the input data after which no more contours or curves may be added;
     * all added contours must be closed before calling finale(). Once finalize()
     * is called, no further data can be added. Instead of using methods from
     * \ref GlyphGenerateParams, directly specify how the data is broken into
     * boxes.
     * \param f fill rule to use for rendering, must be one of
     *          PainterEnums::nonzero_fill_rule or \ref
     *          PainterEnums::odd_even_fill_rule.
     * \param glyph_rect the rect of the glyph
     * \param split_thresh if the number of curves within a box is greater than
     *                     this value, the box is split
     * \param max_recursion the maximum level of recursion allowed in splitting
     *                      the data into boxes
     * \param near_thresh horizontal and vertical threshhold to decide if a curve
     *                    outside of a box should be added to a box
     */
    void
    finalize(enum PainterEnums::fill_rule_t f, const RectT<int> &glyph_rect,
             int split_thresh, int max_recursion, vec2 near_thresh);

    /*!
     * Query the data; may only be called after finalize(). Returns
     * \ref routine_fail if finalize() has not yet been called.
     * \param gpu_data location to which to write a c_array to the
     *                 GPU data.
     */
    enum return_code
    query(c_array<const fastuidraw::generic_data> *gpu_data) const;

    virtual
    enum fastuidraw::return_code
    upload_to_atlas(GlyphAtlasProxy &atlas_proxy,
                    GlyphAttribute::Array &attributes) const;

  private:
    void *m_d;
  };
/*! @} */
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <test.h>

///////////////////////////////////////////////////////////////////////////////
UnitTest::UnitTest ()
: _planned (0)
, _counter (0)
, _passed (0)
, _failed (0)
, _skipped (0)
{
}

///////////////////////////////////////////////////////////////////////////////
UnitTest::UnitTest (int planned)
: _planned (planned)
, _counter (0)
, _passed (0)
, _failed (0)
, _skipped (0)
{
  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
UnitTest::~UnitTest ()
{
  float percentPassed = 0.0;
  if (_planned > 0)
    percentPassed = (100.0 * _passed) / std::max (_planned, _passed + _failed + _skipped);

  if (_counter < _planned)
  {
    std::cout << "# Only "
              << _counter
              << " tests, out of a planned "
              << _planned
              << " were run.\n";
    _skipped += _planned - _counter;
  }

  else if (_counter > _planned)
    std::cout << "# "
              << _counter
              << " tests were run, but only "
              << _planned
              << " were planned.\n";

  std::cout << "# "
            << _passed
            << " passed, "
            << _failed
            << " failed, "
            << _skipped
            << " skipped. "
            << std::setprecision (3) << percentPassed
            << "% passed.\n";
  exit (_failed > 0);
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::plan (int planned)
{
  _planned = planned;
  _counter = 0;
  _passed = 0;
  _failed = 0;
  _skipped = 0;

  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::planMore (int extra)
{
  _planned += extra;
  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::ok (bool expression, const std::string& name)
{
  ++_counter;

  if (expression)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::notok (bool expression, const std::string& name)
{
  ++_counter;

  if (!expression)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (bool actual, bool expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (size_t actual, size_t expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (int actual, int expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (double actual, double expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (double actual, double expected, double tolerance, const std::string& name)
{
  ++_counter;
  if (fabs (actual - expected) <= tolerance)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (unsigned char actual, unsigned char expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (
  const std::string& actual,
  const std::string& expected,
  const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: '"
              << expected
              << "'"
              << "\n#      got: '"
              << actual
              << "'\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (
  const char* actual,
  const char* expected,
  const std::string& name)
{
  ++_counter;
  if (! strcmp (actual, expected))
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: '"
              << expected
              << "'"
              << "\n#      got: '"
              << actual
              << "'\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::diag (const std::string& text)
{
  auto start = text.find_first_not_of (" \t\n\r\f");
  auto end   = text.find_last_not_of  (" \t\n\r\f");
  std::cout << "# " << text.substr (start, end - start + 1) << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::pass (const std::string& text)
{
  ++_counter;
  ++_passed;
  std::cout << green ("ok")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::fail (const std::string& text)
{
  ++_counter;
  ++_failed;
  std::cout << red ("not ok")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::skip (const std::string& text)
{
  ++_counter;
  ++_skipped;
  std::cout << yellow ("skip")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::red (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[31m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::green (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[32m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::yellow (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[33m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
<commit_msg>Test: Added missing include for Cygwin<commit_after>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2015, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <test.h>

///////////////////////////////////////////////////////////////////////////////
UnitTest::UnitTest ()
: _planned (0)
, _counter (0)
, _passed (0)
, _failed (0)
, _skipped (0)
{
}

///////////////////////////////////////////////////////////////////////////////
UnitTest::UnitTest (int planned)
: _planned (planned)
, _counter (0)
, _passed (0)
, _failed (0)
, _skipped (0)
{
  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
UnitTest::~UnitTest ()
{
  float percentPassed = 0.0;
  if (_planned > 0)
    percentPassed = (100.0 * _passed) / std::max (_planned, _passed + _failed + _skipped);

  if (_counter < _planned)
  {
    std::cout << "# Only "
              << _counter
              << " tests, out of a planned "
              << _planned
              << " were run.\n";
    _skipped += _planned - _counter;
  }

  else if (_counter > _planned)
    std::cout << "# "
              << _counter
              << " tests were run, but only "
              << _planned
              << " were planned.\n";

  std::cout << "# "
            << _passed
            << " passed, "
            << _failed
            << " failed, "
            << _skipped
            << " skipped. "
            << std::setprecision (3) << percentPassed
            << "% passed.\n";
  exit (_failed > 0);
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::plan (int planned)
{
  _planned = planned;
  _counter = 0;
  _passed = 0;
  _failed = 0;
  _skipped = 0;

  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::planMore (int extra)
{
  _planned += extra;
  std::cout << "1.." << _planned << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::ok (bool expression, const std::string& name)
{
  ++_counter;

  if (expression)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::notok (bool expression, const std::string& name)
{
  ++_counter;

  if (!expression)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (bool actual, bool expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (size_t actual, size_t expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (int actual, int expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (double actual, double expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (double actual, double expected, double tolerance, const std::string& name)
{
  ++_counter;
  if (fabs (actual - expected) <= tolerance)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (unsigned char actual, unsigned char expected, const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: "
              << expected
              << "\n#      got: "
              << actual
              << "\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (
  const std::string& actual,
  const std::string& expected,
  const std::string& name)
{
  ++_counter;
  if (actual == expected)
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: '"
              << expected
              << "'"
              << "\n#      got: '"
              << actual
              << "'\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::is (
  const char* actual,
  const char* expected,
  const std::string& name)
{
  ++_counter;
  if (! strcmp (actual, expected))
  {
    ++_passed;
    std::cout << green ("ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n";
  }
  else
  {
    ++_failed;
    std::cout << red ("not ok")
              << " "
              << _counter
              << " - "
              << name
              << "\n# expected: '"
              << expected
              << "'"
              << "\n#      got: '"
              << actual
              << "'\n";
  }
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::diag (const std::string& text)
{
  auto start = text.find_first_not_of (" \t\n\r\f");
  auto end   = text.find_last_not_of  (" \t\n\r\f");
  std::cout << "# " << text.substr (start, end - start + 1) << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::pass (const std::string& text)
{
  ++_counter;
  ++_passed;
  std::cout << green ("ok")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::fail (const std::string& text)
{
  ++_counter;
  ++_failed;
  std::cout << red ("not ok")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
void UnitTest::skip (const std::string& text)
{
  ++_counter;
  ++_skipped;
  std::cout << yellow ("skip")
            << " "
            << _counter
            << " - "
            << text
            << "\n";
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::red (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[31m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::green (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[32m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
std::string UnitTest::yellow (const std::string& input)
{
  if (isatty (fileno (stdout)))
    return std::string ("\033[33m" + input + "\033[0m");

  return input;
}

///////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>#include <cassert>
#include <iostream>
#include <time.h>
#include <cstdlib>
#include <vector>
#include <deque>
#include "gemmbitserial.hpp"
#include "mnistdata.h"

using namespace std;
using namespace gemmbitserial;

#define VERBOSE_TEST(x) ;
//#define VERBOSE_TEST(x) x

// Generate a random vector of -1 and +1 values of given dimension
template <typename T>
void generateRandomVector_Bipolar(size_t dim, T * ret) {
  for(size_t i = 0; i < dim; i++) {
    ret[i] = (rand() % 2 == 0) ? 1 : -1;
  }
}

/**
* Generate a random vector with given dimension and number of bits
*/
template <typename T>
void generateRandomVector(size_t bits, size_t dim, T * ret, bool allowNeg = false) {
  assert(bits <= (sizeof(T) * 8));
  if(bits == 1 && allowNeg) {
    // generate bipolar values
    generateRandomVector_Bipolar(dim, ret);
    return;
  }
  int32_t minVal = 0;
  int32_t maxVal = (1 << bits);
  for(size_t i = 0; i < dim; i++) {
    ret[i] = (rand() % maxVal) - (allowNeg ? maxVal/2 : 0);
  }
}

template <typename LHSType, typename RHSType>
void naive_int_gemm(LHSType * lhs, RHSType * rhs, int32_t * res, int rows, int depth, int cols) {
  for(int k = 0; k < cols; k++) {
    for(int i = 0; i < rows; i++) {
      int32_t acc = 0;
      for(int j = 0; j < depth; j++) {
        acc += lhs[i * depth + j] * rhs[k * depth + j];
      }
      res[k * rows + i] = acc;
    }
  }
}

template <typename T>
void naive_sum_rows(T * m, int32_t * res, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    int32_t acc = 0;
    for(int k = 0; k < cols; k++) {
      acc += m[i * cols + k];
    }
    res[i] = acc;
  }
}

template <typename T>
void printmatrix(T * mat, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      cout << (int) mat[i * cols + j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

template <typename T>
void printmatrixdiff(const T * mat1, const T * mat2, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      if(mat1[i * cols + j] != mat2[i * cols + j]) {
        cout << "Difference at (i,j) = " << i << " " << j << " Mat1: " << (int)mat1[i * cols + j] << " Mat2: " << mat2[i * cols + j] << endl;
      }
    }
  }
  cout << endl;
}

void printBitSerialMatrix(BitSerialMatrix * bsm) {
  cout << "BitSerialMatrix with bits " << bsm->nbits << " rows " << bsm->nrows << " cols " << bsm->ncols << endl;
  for(int b = 0; b < bsm->nbits; b++) {
    cout << "bit " << b << ":" << endl;
    for(int r = 0; r < bsm->nrows; r++) {
      for(int c = 0; c < bsm->ncols; c++) {
        cout << (bsm->get(b,r,c) ? 1 : 0) << " ";
      }
      cout << endl << endl;
    }
  }
}

bool test_rowwise_sum() {
  vector<size_t> param_bits {1, 2, 3, 4};
  vector<size_t> param_dims {4, 16, 17, 32, 77, 100, 1023};
  vector<int> param_signed {1, 0};
  unsigned int numConfigs = 0, ok = 0, nok = 0;
  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      for(auto & sgnd: param_signed) {
        bool isSigned = (bool) sgnd;
        int8_t * rnd_mat = new int8_t[d*d];
        int32_t * res_ret = new int32_t[d];
        int32_t * res_golden = new int32_t[d];
        generateRandomVector(b, d*d, rnd_mat, isSigned);
        BitSerialMatrix bsm = BitSerialMatrix::alloc(b, d, d, isSigned);
        bsm.importRegular(rnd_mat);
        sumRows(bsm, res_ret);
        naive_sum_rows(rnd_mat, res_golden, d, d);
        int res = memcmp(res_ret, res_golden, d);
        if(res == 0) {
          ok++;
        } else {
          nok++;
        }
        //printmatrix(rnd_mat, d, d);
        //printmatrix(res_golden, d, 1);
        //printmatrix(res_ret, d, 1);
        BitSerialMatrix::dealloc(bsm);
        delete [] rnd_mat;
        delete [] res_golden;
        delete [] res_ret;
        numConfigs++;
        VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);
      }
    }
  }
  cout << "Row-wise sum tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_conversions() {
  vector<size_t> param_bits {1, 2, 3, 7};
  vector<size_t> param_dims {4, 16, 17, 32, 77, 100, 1023};
  vector<int> param_signed {1, 0};
  unsigned int numConfigs = 0, ok = 0, nok = 0;

  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      for(auto & sgnd: param_signed) {
        int8_t * res_chk = new int8_t[d*d];
        int8_t * rnd_vec = new int8_t[d*d];
        assert(res_chk != 0 && rnd_vec != 0);
        generateRandomVector(b, d*d, rnd_vec, (bool) sgnd);

        BitSerialMatrix bsm = BitSerialMatrix::alloc(b, d, d, (bool) sgnd);
        bsm.importRegular(rnd_vec);
        bsm.exportRegular(res_chk);
        //printmatrix(rnd_vec, d, d);
        //printmatrix(res_chk, d, d);
        int res = memcmp(rnd_vec, res_chk, d);
        if(res == 0) {
          ok++;
        } else {
          nok++;
        }
        delete [] rnd_vec;
        delete [] res_chk;
        BitSerialMatrix::dealloc(bsm);
        numConfigs++;
        VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);
      }
    }
  }
  cout << "Conversion tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_matrix_matrix() {
  vector<size_t> param_bits {2, 3, 4};
  vector<size_t> param_dims {3, 5, 7, 16, 17, 18, 30, 31, 32, 100, 177, 256};

  deque<bool> param_allow_neg {false, true};
  unsigned int numConfigs = 0, ok = 0, nok = 0;
  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      uint8_t * rnd_mat_a = new uint8_t[d*d*2];
      uint8_t * rnd_mat_b = new uint8_t[2*d*d*3];
      int32_t * res_mat_golden = new int32_t[d*d*3];
      generateRandomVector(b, d*d*2, rnd_mat_a);
      generateRandomVector(b, 2*d*d*3, rnd_mat_b);
      naive_int_gemm(rnd_mat_a, rnd_mat_b, res_mat_golden, d, 2*d, d*3);
      GEMMContext ctx = allocGEMMContext(d, 2*d, 3*d, b, b, false, false);
      ctx.lhs.importRegular(rnd_mat_a);
      ctx.rhs.importRegular(rnd_mat_b);

      gemmBitSerial(ctx);
      //ctx.printSummary();
      //printmatrix(rnd_mat_a, d, d*2);
      //printmatrix(rnd_mat_b, d*3, d*2);
      //printmatrix(res_mat_golden, d*3, d);
      //printmatrix(ctx.res, d*3, d);

      int rbytes = d*d*3*sizeof(int32_t);
      int res = memcmp(ctx.res, res_mat_golden, rbytes);
      if(res == 0) {
        ok++;
      } else {
        nok++;
        //printmatrixdiff(res_mat, res_mat_golden, 3*d, d);
      }
      delete [] rnd_mat_a;
      delete [] rnd_mat_b;
      delete [] res_mat_golden;
      deallocGEMMContext(ctx);
      numConfigs++;
      VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);

    }
  }
  cout << "Matrix matrix multiplication tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_mnist() {
  // test bit serial gemm using real-life matrix data from a MNIST neural net
  GEMMContext ctx = allocGEMMContext(
    MNIST_OUT, MNIST_IN, 1, MNIST_WBITS, MNIST_ABITS, MNIST_WSIGN, MNIST_ASIGN
  );
  ctx.lhs.importRegular(mnist_weights);
  ctx.rhs.importRegular(mnist_in);
  gemmBitSerial(ctx);
  int res = memcmp(ctx.res, mnist_res_golden, MNIST_OUT*sizeof(int32_t));
  cout << "MNIST matrix-vector: " << (res == 0 ? "OK" : "NOK") << endl;
  if(res != 0) {
    printmatrixdiff(ctx.res, mnist_res_golden, 1, MNIST_OUT);
  }
  deallocGEMMContext(ctx);
  return res == 0;
}

bool test_bipolar() {
  unsigned int numConfigs = 0, ok = 0, nok = 0;
  size_t d = 4, dc = 4;
  size_t lhs_bits = 1, rhs_bits = 2;
  bool lhs_sign = true, rhs_sign = false;
  int8_t * bipolar_mat = new int8_t[d*d];
  uint8_t * regular_mat = new uint8_t[d*dc];
  int32_t * res_golden = new int32_t[d*dc];
  int32_t * res_chk = new int32_t[d*dc];
  GEMMContext ctx = allocGEMMContext(
    d, d, dc, lhs_bits, rhs_bits, lhs_sign, rhs_sign
  );
  generateRandomVector_Bipolar(d*d, bipolar_mat);
  generateRandomVector(rhs_bits, d*dc, regular_mat);
  ctx.lhs.importRegular(bipolar_mat);
  ctx.rhs.importRegular(regular_mat);
  gemmBitSerial(ctx);
  naive_int_gemm(bipolar_mat, regular_mat, res_golden, d, d, dc);
  //printmatrix(bipolar_mat, d, d);
  //printmatrix(regular_mat, dc, d);
  //printmatrix(res_golden, dc, d);
  //printmatrix(ctx.res, dc, d);
  int res = memcmp(res_golden, ctx.res, sizeof(int32_t)*d*dc);
  if(res == 0) {
    ok++;
  } else {
    nok++;
  }
  numConfigs++;
  delete [] bipolar_mat;
  delete [] regular_mat;
  delete [] res_golden;
  delete [] res_chk;
  deallocGEMMContext(ctx);
  cout << "Bipolar matrix matrix multiplication tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

int main(int argc, char const *argv[]) {
  srand(time(NULL));
  bool all_ok = true;
  all_ok &= test_conversions();
  all_ok &= test_rowwise_sum();
  all_ok &= test_mnist();
  all_ok &= test_matrix_matrix();
  all_ok &= test_bipolar();

  if(all_ok) {
    cout << "All tests completed successfully" << endl;
  } else {
    cout << "Some tests failed" << endl;
  }
  return 0;
}
<commit_msg>add more bipolar times regular tests<commit_after>#include <cassert>
#include <iostream>
#include <time.h>
#include <cstdlib>
#include <vector>
#include <deque>
#include "gemmbitserial.hpp"
#include "mnistdata.h"

using namespace std;
using namespace gemmbitserial;

#define VERBOSE_TEST(x) ;
//#define VERBOSE_TEST(x) x

// Generate a random vector of -1 and +1 values of given dimension
template <typename T>
void generateRandomVector_Bipolar(size_t dim, T * ret) {
  for(size_t i = 0; i < dim; i++) {
    ret[i] = (rand() % 2 == 0) ? 1 : -1;
  }
}

/**
* Generate a random vector with given dimension and number of bits
*/
template <typename T>
void generateRandomVector(size_t bits, size_t dim, T * ret, bool allowNeg = false) {
  assert(bits <= (sizeof(T) * 8));
  if(bits == 1 && allowNeg) {
    // generate bipolar values
    generateRandomVector_Bipolar(dim, ret);
    return;
  }
  int32_t minVal = 0;
  int32_t maxVal = (1 << bits);
  for(size_t i = 0; i < dim; i++) {
    ret[i] = (rand() % maxVal) - (allowNeg ? maxVal/2 : 0);
  }
}

template <typename LHSType, typename RHSType>
void naive_int_gemm(LHSType * lhs, RHSType * rhs, int32_t * res, int rows, int depth, int cols) {
  for(int k = 0; k < cols; k++) {
    for(int i = 0; i < rows; i++) {
      int32_t acc = 0;
      for(int j = 0; j < depth; j++) {
        acc += lhs[i * depth + j] * rhs[k * depth + j];
      }
      res[k * rows + i] = acc;
    }
  }
}

template <typename T>
void naive_sum_rows(T * m, int32_t * res, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    int32_t acc = 0;
    for(int k = 0; k < cols; k++) {
      acc += m[i * cols + k];
    }
    res[i] = acc;
  }
}

template <typename T>
void printmatrix(T * mat, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      cout << (int) mat[i * cols + j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

template <typename T>
void printmatrixdiff(const T * mat1, const T * mat2, int rows, int cols) {
  for(int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      if(mat1[i * cols + j] != mat2[i * cols + j]) {
        cout << "Difference at (i,j) = " << i << " " << j << " Mat1: " << (int)mat1[i * cols + j] << " Mat2: " << mat2[i * cols + j] << endl;
      }
    }
  }
  cout << endl;
}

void printBitSerialMatrix(BitSerialMatrix * bsm) {
  cout << "BitSerialMatrix with bits " << bsm->nbits << " rows " << bsm->nrows << " cols " << bsm->ncols << endl;
  for(int b = 0; b < bsm->nbits; b++) {
    cout << "bit " << b << ":" << endl;
    for(int r = 0; r < bsm->nrows; r++) {
      for(int c = 0; c < bsm->ncols; c++) {
        cout << (bsm->get(b,r,c) ? 1 : 0) << " ";
      }
      cout << endl << endl;
    }
  }
}

bool test_rowwise_sum() {
  vector<size_t> param_bits {1, 2, 3, 4};
  vector<size_t> param_dims {4, 16, 17, 32, 77, 100, 1023};
  vector<int> param_signed {1, 0};
  unsigned int numConfigs = 0, ok = 0, nok = 0;
  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      for(auto & sgnd: param_signed) {
        bool isSigned = (bool) sgnd;
        int8_t * rnd_mat = new int8_t[d*d];
        int32_t * res_ret = new int32_t[d];
        int32_t * res_golden = new int32_t[d];
        generateRandomVector(b, d*d, rnd_mat, isSigned);
        BitSerialMatrix bsm = BitSerialMatrix::alloc(b, d, d, isSigned);
        bsm.importRegular(rnd_mat);
        sumRows(bsm, res_ret);
        naive_sum_rows(rnd_mat, res_golden, d, d);
        int res = memcmp(res_ret, res_golden, d);
        if(res == 0) {
          ok++;
        } else {
          nok++;
        }
        //printmatrix(rnd_mat, d, d);
        //printmatrix(res_golden, d, 1);
        //printmatrix(res_ret, d, 1);
        BitSerialMatrix::dealloc(bsm);
        delete [] rnd_mat;
        delete [] res_golden;
        delete [] res_ret;
        numConfigs++;
        VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);
      }
    }
  }
  cout << "Row-wise sum tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_conversions() {
  vector<size_t> param_bits {1, 2, 3, 7};
  vector<size_t> param_dims {4, 16, 17, 32, 77, 100, 1023};
  vector<int> param_signed {1, 0};
  unsigned int numConfigs = 0, ok = 0, nok = 0;

  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      for(auto & sgnd: param_signed) {
        int8_t * res_chk = new int8_t[d*d];
        int8_t * rnd_vec = new int8_t[d*d];
        assert(res_chk != 0 && rnd_vec != 0);
        generateRandomVector(b, d*d, rnd_vec, (bool) sgnd);

        BitSerialMatrix bsm = BitSerialMatrix::alloc(b, d, d, (bool) sgnd);
        bsm.importRegular(rnd_vec);
        bsm.exportRegular(res_chk);
        //printmatrix(rnd_vec, d, d);
        //printmatrix(res_chk, d, d);
        int res = memcmp(rnd_vec, res_chk, d);
        if(res == 0) {
          ok++;
        } else {
          nok++;
        }
        delete [] rnd_vec;
        delete [] res_chk;
        BitSerialMatrix::dealloc(bsm);
        numConfigs++;
        VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);
      }
    }
  }
  cout << "Conversion tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_matrix_matrix() {
  vector<size_t> param_bits {2, 3, 4};
  vector<size_t> param_dims {3, 5, 7, 16, 17, 18, 30, 31, 32, 100, 177, 256};

  deque<bool> param_allow_neg {false, true};
  unsigned int numConfigs = 0, ok = 0, nok = 0;
  for(auto & b: param_bits) {
    for(auto & d: param_dims) {
      uint8_t * rnd_mat_a = new uint8_t[d*d*2];
      uint8_t * rnd_mat_b = new uint8_t[2*d*d*3];
      int32_t * res_mat_golden = new int32_t[d*d*3];
      generateRandomVector(b, d*d*2, rnd_mat_a);
      generateRandomVector(b, 2*d*d*3, rnd_mat_b);
      naive_int_gemm(rnd_mat_a, rnd_mat_b, res_mat_golden, d, 2*d, d*3);
      GEMMContext ctx = allocGEMMContext(d, 2*d, 3*d, b, b, false, false);
      ctx.lhs.importRegular(rnd_mat_a);
      ctx.rhs.importRegular(rnd_mat_b);

      gemmBitSerial(ctx);
      //ctx.printSummary();
      //printmatrix(rnd_mat_a, d, d*2);
      //printmatrix(rnd_mat_b, d*3, d*2);
      //printmatrix(res_mat_golden, d*3, d);
      //printmatrix(ctx.res, d*3, d);

      int rbytes = d*d*3*sizeof(int32_t);
      int res = memcmp(ctx.res, res_mat_golden, rbytes);
      if(res == 0) {
        ok++;
      } else {
        nok++;
        //printmatrixdiff(res_mat, res_mat_golden, 3*d, d);
      }
      delete [] rnd_mat_a;
      delete [] rnd_mat_b;
      delete [] res_mat_golden;
      deallocGEMMContext(ctx);
      numConfigs++;
      VERBOSE_TEST(cout << "Bits = " << b << " dim = " << d << " result = " << res << endl);

    }
  }
  cout << "Matrix matrix multiplication tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

bool test_mnist() {
  // test bit serial gemm using real-life matrix data from a MNIST neural net
  GEMMContext ctx = allocGEMMContext(
    MNIST_OUT, MNIST_IN, 1, MNIST_WBITS, MNIST_ABITS, MNIST_WSIGN, MNIST_ASIGN
  );
  ctx.lhs.importRegular(mnist_weights);
  ctx.rhs.importRegular(mnist_in);
  gemmBitSerial(ctx);
  int res = memcmp(ctx.res, mnist_res_golden, MNIST_OUT*sizeof(int32_t));
  cout << "MNIST matrix-vector: " << (res == 0 ? "OK" : "NOK") << endl;
  if(res != 0) {
    printmatrixdiff(ctx.res, mnist_res_golden, 1, MNIST_OUT);
  }
  deallocGEMMContext(ctx);
  return res == 0;
}

bool test_bipolar_times_regular() {
  vector<size_t> param_regularmatrix_bits {2, 3, 4};
  vector<size_t> param_dims {3, 5, 7, 16, 17, 18, 30, 31, 32, 100, 177, 256};
  vector<int> param_signed {1, 0};

  unsigned int numConfigs = 0, ok = 0, nok = 0;
  // TODO when bipolar times bipolar is covered, merge into matrix matrix
  for(auto & rhs_bits: param_regularmatrix_bits) {
    for(auto & d: param_dims) {
      for(auto & sgnd: param_signed) {
        const size_t lhs_bits = 1;
        const bool lhs_sign = true;
        const bool rhs_sign = (bool) sgnd;
        int8_t * bipolar_mat = new int8_t[d*d];
        int8_t * regular_mat = new int8_t[d*d];
        int32_t * res_golden = new int32_t[d*d];
        int32_t * res_chk = new int32_t[d*d];
        GEMMContext ctx = allocGEMMContext(
          d, d, d, lhs_bits, rhs_bits, lhs_sign, rhs_sign
        );
        generateRandomVector_Bipolar(d*d, bipolar_mat);
        generateRandomVector(rhs_bits, d*d, regular_mat, rhs_sign);
        ctx.lhs.importRegular(bipolar_mat);
        ctx.rhs.importRegular(regular_mat);
        gemmBitSerial(ctx);
        naive_int_gemm(bipolar_mat, regular_mat, res_golden, d, d, d);
        //printmatrix(bipolar_mat, d, d);
        //printmatrix(regular_mat, d, d);
        //printmatrix(res_golden, d, d);
        //printmatrix(ctx.res, d, d);
        int res = memcmp(res_golden, ctx.res, sizeof(int32_t)*d*d);
        if(res == 0) {
          ok++;
        } else {
          nok++;
        }
        numConfigs++;
        delete [] bipolar_mat;
        delete [] regular_mat;
        delete [] res_golden;
        delete [] res_chk;
        deallocGEMMContext(ctx);
      }
    }
  }
  cout << "Bipolar matrix matrix multiplication tests: " << ok << " OK, " << nok << " NOK" << endl;
  return ok == numConfigs;
}

int main(int argc, char const *argv[]) {
  srand(time(NULL));
  bool all_ok = true;
  all_ok &= test_conversions();
  all_ok &= test_rowwise_sum();
  all_ok &= test_mnist();
  all_ok &= test_matrix_matrix();
  all_ok &= test_bipolar_times_regular();

  if(all_ok) {
    cout << "All tests completed successfully" << endl;
  } else {
    cout << "Some tests failed" << endl;
  }
  return 0;
}
<|endoftext|>