<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <chrono>
#include <thread>
#include <xmmintrin.h>
#include <pmmintrin.h>

#include "engine.hpp"

namespace rack {

bool gPaused = false;
std::vector<Module*> gModules;
std::vector<Wire*> gWires;
bool gCpuMeters = false;

static bool running = false;
static float sampleRate;
static float sampleTime;

static std::mutex mutex;
static std::thread thread;
static VIPMutex vipMutex;

// Parameter interpolation
static Module *smoothModule = NULL;
static int smoothParamId;
static float smoothValue;

float Light::getBrightness() {
	// LEDs are diodes, so don't allow reverse current.
	// For some reason, instead of the RMS, the sqrt of RMS looks better
	return powf(fmaxf(0.f, value), 0.25f);
}

void Light::setBrightnessSmooth(float brightness, float frames) {
	float v = (brightness > 0.f) ? brightness * brightness : 0.f;
	if (v < value) {
		// Fade out light with lambda = framerate
		value += (v - value) * sampleTime * frames * 60.f;
	}
	else {
		// Immediately illuminate light
		value = v;
	}
}

void Wire::step() {
	float value = outputModule->outputs[outputId].value;
	inputModule->inputs[inputId].value = value;
}

void engineInit() {
	engineSetSampleRate(44100.0);
}

void engineDestroy() {
	// Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself before the WINDOW was destroyed.
	assert(gWires.empty());
	assert(gModules.empty());
}

static void engineStep() {
	// Param interpolation
	if (smoothModule) {
		float value = smoothModule->params[smoothParamId].value;
		const float lambda = 60.0; // decay rate is 1 graphics frame
		float delta = smoothValue - value;
		float newValue = value + delta * lambda * sampleTime;
		if (value == newValue) {
			// Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats)
			smoothModule->params[smoothParamId].value = smoothValue;
			smoothModule = NULL;
		}
		else {
			smoothModule->params[smoothParamId].value = newValue;
		}
	}

	// Step modules
	for (Module *module : gModules) {
		std::chrono::high_resolution_clock::time_point startTime;
		if (gCpuMeters) {
			startTime = std::chrono::high_resolution_clock::now();
		}

		module->step();

		if (gCpuMeters) {
			auto stopTime = std::chrono::high_resolution_clock::now();
			float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * sampleRate;
			module->cpuTime += (cpuTime - module->cpuTime) * sampleTime * 10.f;
		}

		// TODO skip this step when plug lights are disabled
		// Step ports
		for (Input &input : module->inputs) {
			if (input.active) {
				float value = input.value / 5.f;
				input.plugLights[0].setBrightnessSmooth(value);
				input.plugLights[1].setBrightnessSmooth(-value);
			}
		}
		for (Output &output : module->outputs) {
			if (output.active) {
				float value = output.value / 5.f;
				output.plugLights[0].setBrightnessSmooth(value);
				output.plugLights[1].setBrightnessSmooth(-value);
			}
		}
	}

	// Step cables by moving their output values to inputs
	for (Wire *wire : gWires) {
		wire->step();
	}
}

static void engineRun() {
	// Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
	// https://software.intel.com/en-us/node/682949
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
	_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);

	// Every time the engine waits and locks a mutex, it steps this many frames
	const int mutexSteps = 64;
	// Time in seconds that the engine is rushing ahead of the estimated clock time
	double ahead = 0.0;
	auto lastTime = std::chrono::high_resolution_clock::now();

	while (running) {
		vipMutex.wait();

		if (!gPaused) {
			std::lock_guard<std::mutex> lock(mutex);
			for (int i = 0; i < mutexSteps; i++) {
				engineStep();
			}
		}

		double stepTime = mutexSteps * sampleTime;
		ahead += stepTime;
		auto currTime = std::chrono::high_resolution_clock::now();
		const double aheadFactor = 2.0;
		ahead -= aheadFactor * std::chrono::duration<double>(currTime - lastTime).count();
		lastTime = currTime;
		ahead = fmaxf(ahead, 0.0);

		// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
		// The number of steps to wait before possibly sleeping
		const double aheadMax = 1.0; // seconds
		if (ahead > aheadMax) {
			std::this_thread::sleep_for(std::chrono::duration<double>(stepTime));
		}
	}
}

void engineStart() {
	running = true;
	thread = std::thread(engineRun);
}

void engineStop() {
	running = false;
	thread.join();
}

void engineAddModule(Module *module) {
	assert(module);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check that the module is not already added
	auto it = std::find(gModules.begin(), gModules.end(), module);
	assert(it == gModules.end());
	gModules.push_back(module);
}

void engineRemoveModule(Module *module) {
	assert(module);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// If a param is being smoothed on this module, stop smoothing it immediately
	if (module == smoothModule) {
		smoothModule = NULL;
	}
	// Check that all wires are disconnected
	for (Wire *wire : gWires) {
		assert(wire->outputModule != module);
		assert(wire->inputModule != module);
	}
	// Check that the module actually exists
	auto it = std::find(gModules.begin(), gModules.end(), module);
	assert(it != gModules.end());
	// Remove it
	gModules.erase(it);
}

static void updateActive() {
	// Set everything to inactive
	for (Module *module : gModules) {
		for (Input &input : module->inputs) {
			input.active = false;
		}
		for (Output &output : module->outputs) {
			output.active = false;
		}
	}
	// Set inputs/outputs to active
	for (Wire *wire : gWires) {
		wire->outputModule->outputs[wire->outputId].active = true;
		wire->inputModule->inputs[wire->inputId].active = true;
	}
}

void engineAddWire(Wire *wire) {
	assert(wire);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check wire properties
	assert(wire->outputModule);
	assert(wire->inputModule);
	// Check that the wire is not already added, and that the input is not already used by another cable
	for (Wire *wire2 : gWires) {
		assert(wire2 != wire);
		assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
	}
	// Add the wire
	gWires.push_back(wire);
	updateActive();
}

void engineRemoveWire(Wire *wire) {
	assert(wire);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check that the wire is already added
	auto it = std::find(gWires.begin(), gWires.end(), wire);
	assert(it != gWires.end());
	// Set input to 0V
	wire->inputModule->inputs[wire->inputId].value = 0.0;
	// Remove the wire
	gWires.erase(it);
	updateActive();
}

void engineSetParam(Module *module, int paramId, float value) {
	module->params[paramId].value = value;
}

void engineSetParamSmooth(Module *module, int paramId, float value) {
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state
	if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
		smoothModule->params[smoothParamId].value = smoothValue;
	}
	smoothModule = module;
	smoothParamId = paramId;
	smoothValue = value;
}

void engineSetSampleRate(float newSampleRate) {
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	sampleRate = newSampleRate;
	sampleTime = 1.0 / sampleRate;
	// onSampleRateChange
	for (Module *module : gModules) {
		module->onSampleRateChange();
	}
}

float engineGetSampleRate() {
	return sampleRate;
}

float engineGetSampleTime() {
	return sampleTime;
}

} // namespace rack
<commit_msg>Clamp cables to +/-12V<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <chrono>
#include <thread>
#include <xmmintrin.h>
#include <pmmintrin.h>

#include "engine.hpp"

namespace rack {

bool gPaused = false;
std::vector<Module*> gModules;
std::vector<Wire*> gWires;
bool gCpuMeters = false;

static bool running = false;
static float sampleRate;
static float sampleTime;

static std::mutex mutex;
static std::thread thread;
static VIPMutex vipMutex;

// Parameter interpolation
static Module *smoothModule = NULL;
static int smoothParamId;
static float smoothValue;

float Light::getBrightness() {
	// LEDs are diodes, so don't allow reverse current.
	// For some reason, instead of the RMS, the sqrt of RMS looks better
	return powf(fmaxf(0.f, value), 0.25f);
}

void Light::setBrightnessSmooth(float brightness, float frames) {
	float v = (brightness > 0.f) ? brightness * brightness : 0.f;
	if (v < value) {
		// Fade out light with lambda = framerate
		value += (v - value) * sampleTime * frames * 60.f;
	}
	else {
		// Immediately illuminate light
		value = v;
	}
}

void Wire::step() {
	float value = outputModule->outputs[outputId].value;
	// Assume a +/-12V power supply (like Eurorack), and prevent voltages outside the power range.
	value = clamp(value, -12.f, 12.f);
	inputModule->inputs[inputId].value = value;
}

void engineInit() {
	engineSetSampleRate(44100.0);
}

void engineDestroy() {
	// Make sure there are no wires or modules in the rack on destruction. This suggests that a module failed to remove itself before the WINDOW was destroyed.
	assert(gWires.empty());
	assert(gModules.empty());
}

static void engineStep() {
	// Param interpolation
	if (smoothModule) {
		float value = smoothModule->params[smoothParamId].value;
		const float lambda = 60.0; // decay rate is 1 graphics frame
		float delta = smoothValue - value;
		float newValue = value + delta * lambda * sampleTime;
		if (value == newValue) {
			// Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats)
			smoothModule->params[smoothParamId].value = smoothValue;
			smoothModule = NULL;
		}
		else {
			smoothModule->params[smoothParamId].value = newValue;
		}
	}

	// Step modules
	for (Module *module : gModules) {
		std::chrono::high_resolution_clock::time_point startTime;
		if (gCpuMeters) {
			startTime = std::chrono::high_resolution_clock::now();
		}

		module->step();

		if (gCpuMeters) {
			auto stopTime = std::chrono::high_resolution_clock::now();
			float cpuTime = std::chrono::duration<float>(stopTime - startTime).count() * sampleRate;
			module->cpuTime += (cpuTime - module->cpuTime) * sampleTime * 10.f;
		}

		// TODO skip this step when plug lights are disabled
		// Step ports
		for (Input &input : module->inputs) {
			if (input.active) {
				float value = input.value / 5.f;
				input.plugLights[0].setBrightnessSmooth(value);
				input.plugLights[1].setBrightnessSmooth(-value);
			}
		}
		for (Output &output : module->outputs) {
			if (output.active) {
				float value = output.value / 5.f;
				output.plugLights[0].setBrightnessSmooth(value);
				output.plugLights[1].setBrightnessSmooth(-value);
			}
		}
	}

	// Step cables by moving their output values to inputs
	for (Wire *wire : gWires) {
		wire->step();
	}
}

static void engineRun() {
	// Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
	// https://software.intel.com/en-us/node/682949
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
	_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);

	// Every time the engine waits and locks a mutex, it steps this many frames
	const int mutexSteps = 64;
	// Time in seconds that the engine is rushing ahead of the estimated clock time
	double ahead = 0.0;
	auto lastTime = std::chrono::high_resolution_clock::now();

	while (running) {
		vipMutex.wait();

		if (!gPaused) {
			std::lock_guard<std::mutex> lock(mutex);
			for (int i = 0; i < mutexSteps; i++) {
				engineStep();
			}
		}

		double stepTime = mutexSteps * sampleTime;
		ahead += stepTime;
		auto currTime = std::chrono::high_resolution_clock::now();
		const double aheadFactor = 2.0;
		ahead -= aheadFactor * std::chrono::duration<double>(currTime - lastTime).count();
		lastTime = currTime;
		ahead = fmaxf(ahead, 0.0);

		// Avoid pegging the CPU at 100% when there are no "blocking" modules like AudioInterface, but still step audio at a reasonable rate
		// The number of steps to wait before possibly sleeping
		const double aheadMax = 1.0; // seconds
		if (ahead > aheadMax) {
			std::this_thread::sleep_for(std::chrono::duration<double>(stepTime));
		}
	}
}

void engineStart() {
	running = true;
	thread = std::thread(engineRun);
}

void engineStop() {
	running = false;
	thread.join();
}

void engineAddModule(Module *module) {
	assert(module);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check that the module is not already added
	auto it = std::find(gModules.begin(), gModules.end(), module);
	assert(it == gModules.end());
	gModules.push_back(module);
}

void engineRemoveModule(Module *module) {
	assert(module);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// If a param is being smoothed on this module, stop smoothing it immediately
	if (module == smoothModule) {
		smoothModule = NULL;
	}
	// Check that all wires are disconnected
	for (Wire *wire : gWires) {
		assert(wire->outputModule != module);
		assert(wire->inputModule != module);
	}
	// Check that the module actually exists
	auto it = std::find(gModules.begin(), gModules.end(), module);
	assert(it != gModules.end());
	// Remove it
	gModules.erase(it);
}

static void updateActive() {
	// Set everything to inactive
	for (Module *module : gModules) {
		for (Input &input : module->inputs) {
			input.active = false;
		}
		for (Output &output : module->outputs) {
			output.active = false;
		}
	}
	// Set inputs/outputs to active
	for (Wire *wire : gWires) {
		wire->outputModule->outputs[wire->outputId].active = true;
		wire->inputModule->inputs[wire->inputId].active = true;
	}
}

void engineAddWire(Wire *wire) {
	assert(wire);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check wire properties
	assert(wire->outputModule);
	assert(wire->inputModule);
	// Check that the wire is not already added, and that the input is not already used by another cable
	for (Wire *wire2 : gWires) {
		assert(wire2 != wire);
		assert(!(wire2->inputModule == wire->inputModule && wire2->inputId == wire->inputId));
	}
	// Add the wire
	gWires.push_back(wire);
	updateActive();
}

void engineRemoveWire(Wire *wire) {
	assert(wire);
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Check that the wire is already added
	auto it = std::find(gWires.begin(), gWires.end(), wire);
	assert(it != gWires.end());
	// Set input to 0V
	wire->inputModule->inputs[wire->inputId].value = 0.0;
	// Remove the wire
	gWires.erase(it);
	updateActive();
}

void engineSetParam(Module *module, int paramId, float value) {
	module->params[paramId].value = value;
}

void engineSetParamSmooth(Module *module, int paramId, float value) {
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	// Since only one param can be smoothed at a time, if another param is currently being smoothed, skip to its final state
	if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
		smoothModule->params[smoothParamId].value = smoothValue;
	}
	smoothModule = module;
	smoothParamId = paramId;
	smoothValue = value;
}

void engineSetSampleRate(float newSampleRate) {
	VIPLock vipLock(vipMutex);
	std::lock_guard<std::mutex> lock(mutex);
	sampleRate = newSampleRate;
	sampleTime = 1.0 / sampleRate;
	// onSampleRateChange
	for (Module *module : gModules) {
		module->onSampleRateChange();
	}
}

float engineGetSampleRate() {
	return sampleRate;
}

float engineGetSampleTime() {
	return sampleTime;
}

} // namespace rack
<|endoftext|>


<commit_before>#include "libtiff.hh"

using namespace node;
using namespace v8;

Persistent<FunctionTemplate> TIFFFile::constructor_template;

namespace node {

  void TIFFFile::Initialize(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(TIFFFile::New);
    constructor_template = Persistent<FunctionTemplate>::New(t);
    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    constructor_template->SetClassName(String::NewSymbol("TIFFFile"));

    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("IMAGEWIDTH"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("IMAGELENGTH"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("BITSPERSAMPLE"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("SAMPLESPERPIXEL"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("PLANARCONFIG"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("PHOTOMETRIC"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("ORIENTATION"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("scanlineSize"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("filename"), TIFFFile::fieldsGetter);

    NODE_SET_PROTOTYPE_METHOD(constructor_template, "pushAsPage", TIFFFile::pushAsPage);

    target->Set(String::NewSymbol("TIFFFile"), constructor_template->GetFunction());
  }

  Handle<Value> TIFFFile::New(const Arguments &args) {
    HandleScope scope;
    TIFFFile *tifffile;

    /* Validate input parameters */
    if (args.Length() == 2 && args[0]->IsString() && args[1]->IsString()) {
      String::Utf8Value filename(args[0]);
      String::Utf8Value mode(args[1]);

      /* Open tiff file */
      TIFF* tif = TIFFOpen(*filename, *mode);
      /* Check for success */
      if (tif != NULL) {
        /* Create TIFFFile object */
        tifffile = new TIFFFile(tif, *filename, (strcmp(*mode, "r") != 0));
      } else {
        /* Throw an error */
        return ThrowException(Exception::TypeError(String::New("Can't open tiff file.")));
      }
    } else {
      /* Throw an error */
      return ThrowException(Exception::TypeError(String::New("Invalid params (String filename, String mode)")));
    }
    /* Wrap object */
    tifffile->Wrap(args.This());
    /* Return new TIFFFile */
    return args.This();
  }

  Handle<Value> TIFFFile::fieldsGetter(Local< String > property, const AccessorInfo &info) {
        HandleScope scope;
        TIFFFile *self = ObjectWrap::Unwrap<TIFFFile>(info.This());

        String::Utf8Value propName(property);

        if (strcmp(*propName, "IMAGEWIDTH") == 0) {
          uint32 imageWidth;
          TIFFGetField(self->tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
          return scope.Close(Uint32::New(imageWidth));
        }

        if (strcmp(*propName, "IMAGELENGTH") == 0) {
          uint32 imageLength;
          TIFFGetField(self->tif, TIFFTAG_IMAGELENGTH, &imageLength);
          return scope.Close(Uint32::New(imageLength));
        }

        if (strcmp(*propName, "BITSPERSAMPLE") == 0) {
          uint16 bps;
          TIFFGetField(self->tif, TIFFTAG_BITSPERSAMPLE, &bps);
          return scope.Close(Uint32::New(bps));
        }

        if (strcmp(*propName, "SAMPLESPERPIXEL") == 0) {
          uint16 spp;
          TIFFGetField(self->tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
          return scope.Close(Uint32::New(spp));
        }

        if (strcmp(*propName, "PLANARCONFIG") == 0) {
          uint16 pc;
          TIFFGetField(self->tif, TIFFTAG_PLANARCONFIG, &pc);
          return scope.Close(Uint32::New(pc));
        }

        if (strcmp(*propName, "PHOTOMETRIC") == 0) {
          uint16 pm;
          TIFFGetField(self->tif, TIFFTAG_PHOTOMETRIC, &pm);
          return scope.Close(Uint32::New(pm));
        }

        if (strcmp(*propName, "ORIENTATION") == 0) {
          uint16 o9n;
          TIFFGetFieldDefaulted(self->tif, TIFFTAG_ORIENTATION, &o9n);
          return scope.Close(Uint32::New(o9n));
        }

        if (strcmp(*propName, "scanlineSize") == 0) {
          int32 ss;
          ss = TIFFScanlineSize(self->tif);
          return scope.Close(Int32::New(ss));
        }

        if (strcmp(*propName, "filename") == 0) {
          return scope.Close(String::New(self->filename));
        }
  }

  Handle<Value> TIFFFile::pushAsPage(const Arguments &args) {
    HandleScope scope;
    TIFFFile *self = ObjectWrap::Unwrap<TIFFFile>(args.This());

    if (!self->forWrite) {
      return ThrowException(Exception::TypeError(String::New("Destination tiff must be opened for writing.")));
    }

    if (args.Length() != 1 || !TIFFFile::HasInstance(args[0])) {
      return ThrowException(Exception::TypeError(String::New("Invalid params (TIFFFile source)")));
    }
    TIFFFile *src = ObjectWrap::Unwrap<TIFFFile>(args[0]->ToObject());
    if (src->forWrite) {
      return ThrowException(Exception::TypeError(String::New("Source tiff must be opened for reading.")));
    }

    uint32 imageWidth, imageLength;
    uint16 bitsPerSample, samplesPerPixel, planarConfig, photometric, orientation;
    int32 scanlineSize;
    tdata_t buf;

    TIFFGetField(src->tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
    TIFFGetField(src->tif, TIFFTAG_IMAGELENGTH, &imageLength);
    TIFFGetField(src->tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
    TIFFGetField(src->tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
    TIFFGetField(src->tif, TIFFTAG_PLANARCONFIG, &planarConfig);
    TIFFGetField(src->tif, TIFFTAG_PHOTOMETRIC, &photometric);
    TIFFGetFieldDefaulted(src->tif, TIFFTAG_ORIENTATION, &orientation);
    scanlineSize = TIFFScanlineSize(src->tif);

    TIFFSetField(self->tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    TIFFSetField(self->tif, TIFFTAG_IMAGEWIDTH, imageWidth);
    TIFFSetField(self->tif, TIFFTAG_IMAGELENGTH, imageLength);
    TIFFSetField(self->tif, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
    TIFFSetField(self->tif, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
    TIFFSetField(self->tif, TIFFTAG_PLANARCONFIG, planarConfig);
    TIFFSetField(self->tif, TIFFTAG_PHOTOMETRIC, photometric);
    TIFFSetField(self->tif, TIFFTAG_ORIENTATION, orientation);
    TIFFSetField(self->tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
    TIFFSetField(self->tif, TIFFTAG_PAGENUMBER, self->pageNumber, self->pageNumber + 1);

    buf = _TIFFmalloc(scanlineSize);
    if (planarConfig == PLANARCONFIG_CONTIG) {
      for (uint32 i = 0; i < imageLength; i++) {
        TIFFReadScanline(src->tif, buf, i);
        TIFFWriteScanline(self->tif, buf, i);
      }
    } else if (planarConfig == PLANARCONFIG_SEPARATE) {
      for (uint16 s = 0; s < samplesPerPixel; s++) {
        for (uint32 i = 0; i < imageLength; i++) {
          TIFFReadScanline(self->tif, buf, i, s);
          TIFFWriteScanline(self->tif, buf, i, s);
        }
      }
      for (uint32 i = 0; i < imageLength; i++) {
        TIFFReadScanline(src->tif, buf, i, 0);
        TIFFWriteScanline(self->tif, buf, i, 0);
      }
    }
    _TIFFfree(buf);

    TIFFWriteDirectory(self->tif);
    self->pageNumber++;
    return args.This();
  }

}

// Exporting function
extern "C" void
init (v8::Handle<v8::Object> target)
{
    HandleScope scope;

    TIFFFile::Initialize(target);
}

NODE_MODULE(tiff_multipage, init);<commit_msg>Make compiler happy<commit_after>#include "libtiff.hh"

using namespace node;
using namespace v8;

Persistent<FunctionTemplate> TIFFFile::constructor_template;

namespace node {

  void TIFFFile::Initialize(Handle<Object> target) {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(TIFFFile::New);
    constructor_template = Persistent<FunctionTemplate>::New(t);
    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    constructor_template->SetClassName(String::NewSymbol("TIFFFile"));

    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("IMAGEWIDTH"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("IMAGELENGTH"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("BITSPERSAMPLE"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("SAMPLESPERPIXEL"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("PLANARCONFIG"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("PHOTOMETRIC"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("ORIENTATION"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("scanlineSize"), TIFFFile::fieldsGetter);
    constructor_template->InstanceTemplate()->
      SetAccessor(String::NewSymbol("filename"), TIFFFile::fieldsGetter);

    NODE_SET_PROTOTYPE_METHOD(constructor_template, "pushAsPage", TIFFFile::pushAsPage);

    target->Set(String::NewSymbol("TIFFFile"), constructor_template->GetFunction());
  }

  Handle<Value> TIFFFile::New(const Arguments &args) {
    HandleScope scope;
    TIFFFile *tifffile;

    /* Validate input parameters */
    if (args.Length() == 2 && args[0]->IsString() && args[1]->IsString()) {
      String::Utf8Value filename(args[0]);
      String::Utf8Value mode(args[1]);

      /* Open tiff file */
      TIFF* tif = TIFFOpen(*filename, *mode);
      /* Check for success */
      if (tif != NULL) {
        /* Create TIFFFile object */
        tifffile = new TIFFFile(tif, *filename, (strcmp(*mode, "r") != 0));
      } else {
        /* Throw an error */
        return ThrowException(Exception::TypeError(String::New("Can't open tiff file.")));
      }
    } else {
      /* Throw an error */
      return ThrowException(Exception::TypeError(String::New("Invalid params (String filename, String mode)")));
    }
    /* Wrap object */
    tifffile->Wrap(args.This());
    /* Return new TIFFFile */
    return args.This();
  }

  Handle<Value> TIFFFile::fieldsGetter(Local< String > property, const AccessorInfo &info) {
        HandleScope scope;
        TIFFFile *self = ObjectWrap::Unwrap<TIFFFile>(info.This());

        String::Utf8Value propName(property);

        if (strcmp(*propName, "IMAGEWIDTH") == 0) {
          uint32 imageWidth;
          TIFFGetField(self->tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
          return scope.Close(Uint32::New(imageWidth));
        } else if (strcmp(*propName, "IMAGELENGTH") == 0) {
          uint32 imageLength;
          TIFFGetField(self->tif, TIFFTAG_IMAGELENGTH, &imageLength);
          return scope.Close(Uint32::New(imageLength));
        } else if (strcmp(*propName, "BITSPERSAMPLE") == 0) {
          uint16 bps;
          TIFFGetField(self->tif, TIFFTAG_BITSPERSAMPLE, &bps);
          return scope.Close(Uint32::New(bps));
        } else if (strcmp(*propName, "SAMPLESPERPIXEL") == 0) {
          uint16 spp;
          TIFFGetField(self->tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
          return scope.Close(Uint32::New(spp));
        } else if (strcmp(*propName, "PLANARCONFIG") == 0) {
          uint16 pc;
          TIFFGetField(self->tif, TIFFTAG_PLANARCONFIG, &pc);
          return scope.Close(Uint32::New(pc));
        } else if (strcmp(*propName, "PHOTOMETRIC") == 0) {
          uint16 pm;
          TIFFGetField(self->tif, TIFFTAG_PHOTOMETRIC, &pm);
          return scope.Close(Uint32::New(pm));
        } else if (strcmp(*propName, "ORIENTATION") == 0) {
          uint16 o9n;
          TIFFGetFieldDefaulted(self->tif, TIFFTAG_ORIENTATION, &o9n);
          return scope.Close(Uint32::New(o9n));
        } else if (strcmp(*propName, "scanlineSize") == 0) {
          int32 ss;
          ss = TIFFScanlineSize(self->tif);
          return scope.Close(Int32::New(ss));
        } else if (strcmp(*propName, "filename") == 0) {
          return scope.Close(String::New(self->filename));
        } else {
          return scope.Close(Undefined());
        }
  }

  Handle<Value> TIFFFile::pushAsPage(const Arguments &args) {
    HandleScope scope;
    TIFFFile *self = ObjectWrap::Unwrap<TIFFFile>(args.This());

    if (!self->forWrite) {
      return ThrowException(Exception::TypeError(String::New("Destination tiff must be opened for writing.")));
    }

    if (args.Length() != 1 || !TIFFFile::HasInstance(args[0])) {
      return ThrowException(Exception::TypeError(String::New("Invalid params (TIFFFile source)")));
    }
    TIFFFile *src = ObjectWrap::Unwrap<TIFFFile>(args[0]->ToObject());
    if (src->forWrite) {
      return ThrowException(Exception::TypeError(String::New("Source tiff must be opened for reading.")));
    }

    uint32 imageWidth, imageLength;
    uint16 bitsPerSample, samplesPerPixel, planarConfig, photometric, orientation;
    int32 scanlineSize;
    tdata_t buf;

    TIFFGetField(src->tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
    TIFFGetField(src->tif, TIFFTAG_IMAGELENGTH, &imageLength);
    TIFFGetField(src->tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
    TIFFGetField(src->tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
    TIFFGetField(src->tif, TIFFTAG_PLANARCONFIG, &planarConfig);
    TIFFGetField(src->tif, TIFFTAG_PHOTOMETRIC, &photometric);
    TIFFGetFieldDefaulted(src->tif, TIFFTAG_ORIENTATION, &orientation);
    scanlineSize = TIFFScanlineSize(src->tif);

    TIFFSetField(self->tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    TIFFSetField(self->tif, TIFFTAG_IMAGEWIDTH, imageWidth);
    TIFFSetField(self->tif, TIFFTAG_IMAGELENGTH, imageLength);
    TIFFSetField(self->tif, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
    TIFFSetField(self->tif, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
    TIFFSetField(self->tif, TIFFTAG_PLANARCONFIG, planarConfig);
    TIFFSetField(self->tif, TIFFTAG_PHOTOMETRIC, photometric);
    TIFFSetField(self->tif, TIFFTAG_ORIENTATION, orientation);
    TIFFSetField(self->tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
    TIFFSetField(self->tif, TIFFTAG_PAGENUMBER, self->pageNumber, self->pageNumber + 1);

    buf = _TIFFmalloc(scanlineSize);
    if (planarConfig == PLANARCONFIG_CONTIG) {
      for (uint32 i = 0; i < imageLength; i++) {
        TIFFReadScanline(src->tif, buf, i);
        TIFFWriteScanline(self->tif, buf, i);
      }
    } else if (planarConfig == PLANARCONFIG_SEPARATE) {
      for (uint16 s = 0; s < samplesPerPixel; s++) {
        for (uint32 i = 0; i < imageLength; i++) {
          TIFFReadScanline(self->tif, buf, i, s);
          TIFFWriteScanline(self->tif, buf, i, s);
        }
      }
      for (uint32 i = 0; i < imageLength; i++) {
        TIFFReadScanline(src->tif, buf, i, 0);
        TIFFWriteScanline(self->tif, buf, i, 0);
      }
    }
    _TIFFfree(buf);

    TIFFWriteDirectory(self->tif);
    self->pageNumber++;
    return args.This();
  }

}

// Exporting function
extern "C" void
init (v8::Handle<v8::Object> target)
{
    HandleScope scope;

    TIFFFile::Initialize(target);
}

NODE_MODULE(tiff_multipage, init);<|endoftext|>


<commit_before>#include "json_ui.hh"

#include "display_buffer.hh"
#include "keys.hh"
#include "file.hh"
#include "event_manager.hh"
#include "value.hh"
#include "unit_tests.hh"

#include <utility>

namespace Kakoune
{

template<typename T>
String to_json(ArrayView<const T> array)
{
    String res;
    for (auto& elem : array)
    {
        if (not res.empty())
            res += ", ";
        res += to_json(elem);
    }
    return "[" + res + "]";
}

template<typename T, MemoryDomain D>
String to_json(const Vector<T, D>& vec) { return to_json(ArrayView<const T>{vec}); }

String to_json(int i) { return to_string(i); }
String to_json(bool b) { return b ? "true" : "false"; }
String to_json(StringView str)
{
    String res;
    res.reserve(str.length() + 4);
    res += '"';
    for (auto it = str.begin(), end = str.end(); it != end; )
    {
        auto next = std::find_if(it, end, [](char c) {
            return c == '\\' or c == '"' or (c >= 0 and c <= 0x1F);
        });

        res += StringView{it, next};
        if (next == end)
            break;

        char buf[7] = {'\\', *next, 0};
        if (*next >= 0 and *next <= 0x1F)
            sprintf(buf, "\\u%04x", *next);

        res += buf;
        it = next+1;
    }
    res += '"';
    return res;
}

String to_json(Color color)
{
    if (color.color == Kakoune::Color::RGB)
    {
        char buffer[10];
        sprintf(buffer, R"("#%02x%02x%02x")", color.r, color.g, color.b);
        return buffer;
    }
    return to_json(color_to_str(color));
}

String to_json(Attribute attributes)
{
    struct { Attribute attr; StringView name; }
    attrs[] {
        { Attribute::Exclusive, "exclusive" },
        { Attribute::Underline, "underline" },
        { Attribute::Reverse, "reverse" },
        { Attribute::Blink, "blink" },
        { Attribute::Bold, "bold" },
        { Attribute::Dim, "dim" },
        { Attribute::Italic, "italic" },
    };

    String res;
    for (auto& attr : attrs)
    {
        if (not (attributes & attr.attr))
            continue;

        if (not res.empty())
            res += ", ";
        res += to_json(attr.name);
    }
    return "[" + res + "]";
}

String to_json(Face face)
{
    return format(R"(\{ "fg": {}, "bg": {}, "attributes": {} })",
                  to_json(face.fg), to_json(face.bg), to_json(face.attributes));
}

String to_json(const DisplayAtom& atom)
{
    return format(R"(\{ "face": {}, "contents": {} })", to_json(atom.face), to_json(atom.content()));
}

String to_json(const DisplayLine& line)
{
    return to_json(line.atoms());
}

String to_json(CharCoord coord)
{
    return format(R"(\{ "line": {}, "column": {} })", coord.line, coord.column);
}

String to_json(MenuStyle style)
{
    switch (style)
    {
        case MenuStyle::Prompt: return R"("prompt")";
        case MenuStyle::Inline: return R"("inline")";
    }
    return "";
}

String to_json(InfoStyle style)
{
    switch (style)
    {
        case InfoStyle::Prompt: return R"("prompt")";
        case InfoStyle::Inline: return R"("inline")";
        case InfoStyle::InlineAbove: return R"("inlineAbove")";
        case InfoStyle::InlineBelow: return R"("inlineBelow")";
        case InfoStyle::MenuDoc: return R"("menuDoc")";
    }
    return "";
}

String concat()
{
    return "";
}

template<typename First, typename... Args>
String concat(First&& first, Args&&... args)
{
    if (sizeof...(Args) != 0)
        return to_json(first) + ", " + concat(args...);
    return to_json(first);
}

template<typename... Args>
void rpc_call(StringView method, Args&&... args)
{
    auto q = format(R"(\{ "jsonrpc": "2.0", "method": "{}", "params": [{}] }{})",
                    method, concat(std::forward<Args>(args)...), "\n");

    write_stdout(q);
}

JsonUI::JsonUI()
    : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
        parse_requests(mode);
      }}, m_dimensions{24, 80}
{
    set_signal_handler(SIGINT, SIG_DFL);
}

void JsonUI::draw(const DisplayBuffer& display_buffer,
                  const Face& default_face, const Face& padding_face)
{
    rpc_call("draw", display_buffer.lines(), default_face, padding_face);
}

void JsonUI::draw_status(const DisplayLine& status_line,
                         const DisplayLine& mode_line,
                         const Face& default_face)
{
    rpc_call("draw_status", status_line, mode_line, default_face);
}

bool JsonUI::is_key_available()
{
    return not m_pending_keys.empty();
}

Key JsonUI::get_key()
{
    kak_assert(not m_pending_keys.empty());
    Key key = m_pending_keys.front();
    m_pending_keys.erase(m_pending_keys.begin());
    return key;
}

void JsonUI::menu_show(ConstArrayView<DisplayLine> items,
                       CharCoord anchor, Face fg, Face bg,
                       MenuStyle style)
{
    rpc_call("menu_show", items, anchor, fg, bg, style);
}

void JsonUI::menu_select(int selected)
{
    rpc_call("menu_show", selected);
}

void JsonUI::menu_hide()
{
    rpc_call("menu_hide");
}

void JsonUI::info_show(StringView title, StringView content,
                       CharCoord anchor, Face face,
                       InfoStyle style)
{
    rpc_call("info_show", title, content, anchor, face, style);
}

void JsonUI::info_hide()
{
    rpc_call("info_hide");
}

void JsonUI::refresh(bool force)
{
    rpc_call("refresh", force);
}

void JsonUI::set_input_callback(InputCallback callback)
{
    m_input_callback = std::move(callback);
}

void JsonUI::set_ui_options(const Options& options)
{
    // rpc_call("set_ui_options", options);
}

CharCoord JsonUI::dimensions()
{
    return m_dimensions;
}

using JsonArray = Vector<Value>;
using JsonObject = IdMap<Value>;

static bool is_digit(char c) { return c >= '0' and c <= '9'; }

std::tuple<Value, const char*>
parse_json(const char* pos, const char* end)
{
    using Result = std::tuple<Value, const char*>;

    if (not skip_while(pos, end, is_blank))
        return {};

    if (is_digit(*pos))
    {
        auto digit_end = pos;
        skip_while(digit_end, end, is_digit);
        return Result{ Value{str_to_int({pos, end})}, digit_end };
    }
    if (end - pos > 4 and StringView{pos, pos+4} == "true")
        return Result{ Value{true}, pos+4 };
    if (end - pos > 5 and StringView{pos, pos+5} == "false")
        return Result{ Value{false}, pos+5 };
    if (*pos == '"')
    {
        String value;
        bool escaped = false;
        ++pos;
        for (auto string_end = pos; string_end != end; ++string_end)
        {
            if (escaped)
            {
                escaped = false;
                value += StringView{pos, string_end};
                value.back() = *string_end;
                pos = string_end+1;
                continue;
            }
            if (*string_end == '\\')
                escaped = true;
            if (*string_end == '"')
            {
                value += StringView{pos, string_end};
                return Result{std::move(value), string_end+1};
            }
        }
        return {};
    }
    if (*pos == '[')
    {
        JsonArray array;
        ++pos;
        while (true)
        {
            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            array.push_back(std::move(element));
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == ']')
                return Result{std::move(array), pos+1};
            else
                throw runtime_error("unable to parse array, expected ',' or ']'");
        }
    }
    if (*pos == '{')
    {
        JsonObject object;
        ++pos;
        while (true)
        {
            Value name_value;
            std::tie(name_value, pos) = parse_json(pos, end);
            if (not name_value)
                return {};

            String& name = name_value.as<String>();
            if (not skip_while(pos, end, is_blank))
                return {};
            if (*pos++ != ':')
                throw runtime_error("expected :");

            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            object.append({ std::move(name), std::move(element) });
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == '}')
                return Result{std::move(object), pos+1};
            else
                throw runtime_error("unable to parse object, expected ',' or '}'");
        }
    }
    throw runtime_error("Could not parse json");
}

void JsonUI::eval_json(const Value& json)
{
    const JsonObject& object = json.as<JsonObject>();
    auto json_it = object.find("jsonrpc");
    if (json_it == object.end() or json_it->value.as<String>() != "2.0")
        throw runtime_error("invalid json rpc request");

    auto method_it = object.find("method");
    if (method_it == object.end())
        throw runtime_error("invalid json rpc request (method missing)");
    StringView method = method_it->value.as<String>();

    auto params_it = object.find("params");
    if (params_it == object.end())
        throw runtime_error("invalid json rpc request (params missing)");
    const JsonArray& params = params_it->value.as<JsonArray>();

    if (method == "keys")
    {
        for (auto& key_val : params)
        {
            for (auto& key : parse_keys(key_val.as<String>()))
                m_pending_keys.push_back(key);
        }
    }
    else if (method == "resize")
    {
        if (params.size() != 2)
            throw runtime_error("resize expects 2 parameters");

        CharCoord dim{params[0].as<int>(), params[1].as<int>()};
        m_dimensions = dim;
        m_pending_keys.push_back(resize(dim));
    }
    else
        throw runtime_error("unknown method");
}

static bool stdin_ready()
{
    fd_set  rfds;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    return select(1, &rfds, nullptr, nullptr, &tv) == 1;
}

void JsonUI::parse_requests(EventMode mode)
{
    constexpr size_t bufsize = 1024;
    char buf[bufsize];
    while (stdin_ready())
    {
        ssize_t size = read(0, buf, bufsize);
        if (size == -1 or size == 0)
            break;

        m_requests += StringView{buf, buf + size};
    }

    if (not m_requests.empty())
    {
        Value json;
        const char* pos;
        std::tie(json, pos) = parse_json(m_requests.begin(), m_requests.end());
        if (json)
        {
            try
            {
                eval_json(json);
            }
            catch (runtime_error& error)
            {
                write_stderr(format("error while executing request '{}': '{}'",
                                    StringView{m_requests.begin(), pos}, error.what()));
            }
            m_requests = String{pos, m_requests.end()};
        }
    }

    while (not m_pending_keys.empty())
        m_input_callback(mode);
}

UnitTest test_json_parser{[]()
{
    StringView json = R"({ "jsonrpc": "2.0", "method": "keys", "params": [ "b", "l", "a", "h" ] })";
    auto value = std::get<0>(parse_json(json.begin(), json.end()));
    kak_assert(value);
}};

}
<commit_msg>Fix missing include in json_ui.cc<commit_after>#include "json_ui.hh"

#include "display_buffer.hh"
#include "keys.hh"
#include "file.hh"
#include "event_manager.hh"
#include "value.hh"
#include "unit_tests.hh"

#include <utility>

#include <unistd.h>

namespace Kakoune
{

template<typename T>
String to_json(ArrayView<const T> array)
{
    String res;
    for (auto& elem : array)
    {
        if (not res.empty())
            res += ", ";
        res += to_json(elem);
    }
    return "[" + res + "]";
}

template<typename T, MemoryDomain D>
String to_json(const Vector<T, D>& vec) { return to_json(ArrayView<const T>{vec}); }

String to_json(int i) { return to_string(i); }
String to_json(bool b) { return b ? "true" : "false"; }
String to_json(StringView str)
{
    String res;
    res.reserve(str.length() + 4);
    res += '"';
    for (auto it = str.begin(), end = str.end(); it != end; )
    {
        auto next = std::find_if(it, end, [](char c) {
            return c == '\\' or c == '"' or (c >= 0 and c <= 0x1F);
        });

        res += StringView{it, next};
        if (next == end)
            break;

        char buf[7] = {'\\', *next, 0};
        if (*next >= 0 and *next <= 0x1F)
            sprintf(buf, "\\u%04x", *next);

        res += buf;
        it = next+1;
    }
    res += '"';
    return res;
}

String to_json(Color color)
{
    if (color.color == Kakoune::Color::RGB)
    {
        char buffer[10];
        sprintf(buffer, R"("#%02x%02x%02x")", color.r, color.g, color.b);
        return buffer;
    }
    return to_json(color_to_str(color));
}

String to_json(Attribute attributes)
{
    struct { Attribute attr; StringView name; }
    attrs[] {
        { Attribute::Exclusive, "exclusive" },
        { Attribute::Underline, "underline" },
        { Attribute::Reverse, "reverse" },
        { Attribute::Blink, "blink" },
        { Attribute::Bold, "bold" },
        { Attribute::Dim, "dim" },
        { Attribute::Italic, "italic" },
    };

    String res;
    for (auto& attr : attrs)
    {
        if (not (attributes & attr.attr))
            continue;

        if (not res.empty())
            res += ", ";
        res += to_json(attr.name);
    }
    return "[" + res + "]";
}

String to_json(Face face)
{
    return format(R"(\{ "fg": {}, "bg": {}, "attributes": {} })",
                  to_json(face.fg), to_json(face.bg), to_json(face.attributes));
}

String to_json(const DisplayAtom& atom)
{
    return format(R"(\{ "face": {}, "contents": {} })", to_json(atom.face), to_json(atom.content()));
}

String to_json(const DisplayLine& line)
{
    return to_json(line.atoms());
}

String to_json(CharCoord coord)
{
    return format(R"(\{ "line": {}, "column": {} })", coord.line, coord.column);
}

String to_json(MenuStyle style)
{
    switch (style)
    {
        case MenuStyle::Prompt: return R"("prompt")";
        case MenuStyle::Inline: return R"("inline")";
    }
    return "";
}

String to_json(InfoStyle style)
{
    switch (style)
    {
        case InfoStyle::Prompt: return R"("prompt")";
        case InfoStyle::Inline: return R"("inline")";
        case InfoStyle::InlineAbove: return R"("inlineAbove")";
        case InfoStyle::InlineBelow: return R"("inlineBelow")";
        case InfoStyle::MenuDoc: return R"("menuDoc")";
    }
    return "";
}

String concat()
{
    return "";
}

template<typename First, typename... Args>
String concat(First&& first, Args&&... args)
{
    if (sizeof...(Args) != 0)
        return to_json(first) + ", " + concat(args...);
    return to_json(first);
}

template<typename... Args>
void rpc_call(StringView method, Args&&... args)
{
    auto q = format(R"(\{ "jsonrpc": "2.0", "method": "{}", "params": [{}] }{})",
                    method, concat(std::forward<Args>(args)...), "\n");

    write_stdout(q);
}

JsonUI::JsonUI()
    : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
        parse_requests(mode);
      }}, m_dimensions{24, 80}
{
    set_signal_handler(SIGINT, SIG_DFL);
}

void JsonUI::draw(const DisplayBuffer& display_buffer,
                  const Face& default_face, const Face& padding_face)
{
    rpc_call("draw", display_buffer.lines(), default_face, padding_face);
}

void JsonUI::draw_status(const DisplayLine& status_line,
                         const DisplayLine& mode_line,
                         const Face& default_face)
{
    rpc_call("draw_status", status_line, mode_line, default_face);
}

bool JsonUI::is_key_available()
{
    return not m_pending_keys.empty();
}

Key JsonUI::get_key()
{
    kak_assert(not m_pending_keys.empty());
    Key key = m_pending_keys.front();
    m_pending_keys.erase(m_pending_keys.begin());
    return key;
}

void JsonUI::menu_show(ConstArrayView<DisplayLine> items,
                       CharCoord anchor, Face fg, Face bg,
                       MenuStyle style)
{
    rpc_call("menu_show", items, anchor, fg, bg, style);
}

void JsonUI::menu_select(int selected)
{
    rpc_call("menu_show", selected);
}

void JsonUI::menu_hide()
{
    rpc_call("menu_hide");
}

void JsonUI::info_show(StringView title, StringView content,
                       CharCoord anchor, Face face,
                       InfoStyle style)
{
    rpc_call("info_show", title, content, anchor, face, style);
}

void JsonUI::info_hide()
{
    rpc_call("info_hide");
}

void JsonUI::refresh(bool force)
{
    rpc_call("refresh", force);
}

void JsonUI::set_input_callback(InputCallback callback)
{
    m_input_callback = std::move(callback);
}

void JsonUI::set_ui_options(const Options& options)
{
    // rpc_call("set_ui_options", options);
}

CharCoord JsonUI::dimensions()
{
    return m_dimensions;
}

using JsonArray = Vector<Value>;
using JsonObject = IdMap<Value>;

static bool is_digit(char c) { return c >= '0' and c <= '9'; }

std::tuple<Value, const char*>
parse_json(const char* pos, const char* end)
{
    using Result = std::tuple<Value, const char*>;

    if (not skip_while(pos, end, is_blank))
        return {};

    if (is_digit(*pos))
    {
        auto digit_end = pos;
        skip_while(digit_end, end, is_digit);
        return Result{ Value{str_to_int({pos, end})}, digit_end };
    }
    if (end - pos > 4 and StringView{pos, pos+4} == "true")
        return Result{ Value{true}, pos+4 };
    if (end - pos > 5 and StringView{pos, pos+5} == "false")
        return Result{ Value{false}, pos+5 };
    if (*pos == '"')
    {
        String value;
        bool escaped = false;
        ++pos;
        for (auto string_end = pos; string_end != end; ++string_end)
        {
            if (escaped)
            {
                escaped = false;
                value += StringView{pos, string_end};
                value.back() = *string_end;
                pos = string_end+1;
                continue;
            }
            if (*string_end == '\\')
                escaped = true;
            if (*string_end == '"')
            {
                value += StringView{pos, string_end};
                return Result{std::move(value), string_end+1};
            }
        }
        return {};
    }
    if (*pos == '[')
    {
        JsonArray array;
        ++pos;
        while (true)
        {
            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            array.push_back(std::move(element));
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == ']')
                return Result{std::move(array), pos+1};
            else
                throw runtime_error("unable to parse array, expected ',' or ']'");
        }
    }
    if (*pos == '{')
    {
        JsonObject object;
        ++pos;
        while (true)
        {
            Value name_value;
            std::tie(name_value, pos) = parse_json(pos, end);
            if (not name_value)
                return {};

            String& name = name_value.as<String>();
            if (not skip_while(pos, end, is_blank))
                return {};
            if (*pos++ != ':')
                throw runtime_error("expected :");

            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            object.append({ std::move(name), std::move(element) });
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == '}')
                return Result{std::move(object), pos+1};
            else
                throw runtime_error("unable to parse object, expected ',' or '}'");
        }
    }
    throw runtime_error("Could not parse json");
}

void JsonUI::eval_json(const Value& json)
{
    const JsonObject& object = json.as<JsonObject>();
    auto json_it = object.find("jsonrpc");
    if (json_it == object.end() or json_it->value.as<String>() != "2.0")
        throw runtime_error("invalid json rpc request");

    auto method_it = object.find("method");
    if (method_it == object.end())
        throw runtime_error("invalid json rpc request (method missing)");
    StringView method = method_it->value.as<String>();

    auto params_it = object.find("params");
    if (params_it == object.end())
        throw runtime_error("invalid json rpc request (params missing)");
    const JsonArray& params = params_it->value.as<JsonArray>();

    if (method == "keys")
    {
        for (auto& key_val : params)
        {
            for (auto& key : parse_keys(key_val.as<String>()))
                m_pending_keys.push_back(key);
        }
    }
    else if (method == "resize")
    {
        if (params.size() != 2)
            throw runtime_error("resize expects 2 parameters");

        CharCoord dim{params[0].as<int>(), params[1].as<int>()};
        m_dimensions = dim;
        m_pending_keys.push_back(resize(dim));
    }
    else
        throw runtime_error("unknown method");
}

static bool stdin_ready()
{
    fd_set  rfds;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    return select(1, &rfds, nullptr, nullptr, &tv) == 1;
}

void JsonUI::parse_requests(EventMode mode)
{
    constexpr size_t bufsize = 1024;
    char buf[bufsize];
    while (stdin_ready())
    {
        ssize_t size = ::read(0, buf, bufsize);
        if (size == -1 or size == 0)
            break;

        m_requests += StringView{buf, buf + size};
    }

    if (not m_requests.empty())
    {
        Value json;
        const char* pos;
        std::tie(json, pos) = parse_json(m_requests.begin(), m_requests.end());
        if (json)
        {
            try
            {
                eval_json(json);
            }
            catch (runtime_error& error)
            {
                write_stderr(format("error while executing request '{}': '{}'",
                                    StringView{m_requests.begin(), pos}, error.what()));
            }
            m_requests = String{pos, m_requests.end()};
        }
    }

    while (not m_pending_keys.empty())
        m_input_callback(mode);
}

UnitTest test_json_parser{[]()
{
    StringView json = R"({ "jsonrpc": "2.0", "method": "keys", "params": [ "b", "l", "a", "h" ] })";
    auto value = std::get<0>(parse_json(json.begin(), json.end()));
    kak_assert(value);
}};

}
<|endoftext|>


<commit_before>// llgsim.cpp - an example of using the simulation object to run a
//            - simulation of the llg dynamics of a single
//            - non interacting particle.
//
// Oliver W. Laslett (2016)
// O.Laslett@soton.ac.uk
//
#include <maggie.hpp>
#include <vector>
using plist=std::vector<Particle>;

#include <boost/multi_array.hpp>
using array_d=boost::multi_array<double, 1>;
using matrix_d=boost::multi_array<double, 2>;
using boost::extents;
using bidx=boost::multi_array_types::index;

#include <inih/INIReader.h>
#include <iostream>
using std::cout; using std::endl;
int main()
{
    // Read the ini file to get the particle properties
    // by default, read llgsim.ini
    INIReader reader( "llgsim.ini");
    if(reader.ParseError() < 0 )
    {
        cout << "Cannot load llgsim.ini" << endl;
        return 1;
    }

    // The first thing to do is to set up the particle cluster
    // in this case we just have one particle, so lets define that:
    double gyromagnetic_constant{ Constants::GYROMAG };
    double damping{ reader.GetReal( "particle", "alpha", -1 ) }; // 0.01
    double Ms{ reader.GetReal( "particle", "ms", -1 ) }; // 1400e3
    double diameter{ reader.GetReal( "particle", "diameter", -1 ) }; // 5e-9
    double anisotropy_const{ reader.GetReal( "particle", "anisotropy", -1 ) };
    array_d anisotropy_axis( extents[3] );
    anisotropy_axis[0] = reader.GetReal( "particle", "anis_x", -1 );
    anisotropy_axis[1] = reader.GetReal( "particle", "anis_y", -1 );
    anisotropy_axis[2] = reader.GetReal( "particle", "anis_z", -1 );
    auto p = Particle( gyromagnetic_constant, damping, Ms, diameter,
                       anisotropy_const, anisotropy_axis );

    // The particle cluster then needs a list of particles and their
    // respective locations. In this case its easy.
    plist ps;
    ps.push_back( p );

    matrix_d locs( extents[1][3] );
    for( auto &i : locs[0] )
        i = 0.0;

    auto cluster = ParticleCluster( ps, locs );

    // Now we have defined the particle parameters and geometry we can
    // set up the environment

    // first the initial state of the magnetisation
    array_d init( extents[3] );
    init[0] = 1.0;
    init[1] = 0.0;
    init[2] = 0.0;
    std::vector<array_d> states = { init };

    // now the time step and simulation length
    double dt{ 1e-14 };
    int time_steps{ 100000 };

    // Finally we choose the temperature and external field
    double temp{ 30 };
    array_d field( extents[3] );
    field[0] = 0.0; field[1] = 0.0; field[2] = 100e3;

    auto mysim = Simulation( cluster, states, dt, time_steps, temp, field );

    // The run command runs an LLG simulation of the particle for the
    // specified number of time steps and saves the magnetisation data
    // to the hard disk
    mysim.run();

    // and that is that! open up pandas and take a look
    return 1;
}
<commit_msg>Get external field from ini file<commit_after>// llgsim.cpp - an example of using the simulation object to run a
//            - simulation of the llg dynamics of a single
//            - non interacting particle.
//
// Oliver W. Laslett (2016)
// O.Laslett@soton.ac.uk
//
#include <maggie.hpp>
#include <vector>
using plist=std::vector<Particle>;

#include <boost/multi_array.hpp>
using array_d=boost::multi_array<double, 1>;
using matrix_d=boost::multi_array<double, 2>;
using boost::extents;
using bidx=boost::multi_array_types::index;

#include <inih/INIReader.h>
#include <iostream>
using std::cout; using std::endl;
int main()
{
    // Read the ini file to get the particle properties
    // by default, read llgsim.ini
    INIReader reader( "llgsim.ini");
    if(reader.ParseError() < 0 )
    {
        cout << "Cannot load llgsim.ini" << endl;
        return 1;
    }

    // The first thing to do is to set up the particle cluster
    // in this case we just have one particle, so lets define that:
    double gyromagnetic_constant{ Constants::GYROMAG };
    double damping{ reader.GetReal( "particle", "alpha", -1 ) }; // 0.01
    double Ms{ reader.GetReal( "particle", "ms", -1 ) }; // 1400e3
    double diameter{ reader.GetReal( "particle", "diameter", -1 ) }; // 5e-9
    double anisotropy_const{ reader.GetReal( "particle", "anisotropy", -1 ) };
    array_d anisotropy_axis( extents[3] );
    anisotropy_axis[0] = reader.GetReal( "particle", "anis_x", -1 );
    anisotropy_axis[1] = reader.GetReal( "particle", "anis_y", -1 );
    anisotropy_axis[2] = reader.GetReal( "particle", "anis_z", -1 );
    auto p = Particle( gyromagnetic_constant, damping, Ms, diameter,
                       anisotropy_const, anisotropy_axis );

    // The particle cluster then needs a list of particles and their
    // respective locations. In this case its easy.
    plist ps;
    ps.push_back( p );

    matrix_d locs( extents[1][3] );
    for( auto &i : locs[0] )
        i = 0.0;

    auto cluster = ParticleCluster( ps, locs );

    // Now we have defined the particle parameters and geometry we can
    // set up the environment

    // first the initial state of the magnetisation
    array_d init( extents[3] );
    init[0] = 1.0;
    init[1] = 0.0;
    init[2] = 0.0;
    std::vector<array_d> states = { init };

    // now the time step and simulation length
    double dt{ 1e-14 };
    int time_steps{ 100000 };

    // Finally we choose the temperature and external field
    double temp{ 30 };
    array_d field( extents[3] );
    field[0] = reader.GetReal("particle", "Happ_x", -1 );
    field[1] = reader.GetReal("particle", "Happ_y", -1 );
    field[2] = reader.GetReal("particle", "Happ_z", -1 );

    auto mysim = Simulation( cluster, states, dt, time_steps, temp, field );

    // The run command runs an LLG simulation of the particle for the
    // specified number of time steps and saves the magnetisation data
    // to the hard disk
    mysim.run();

    // and that is that! open up pandas and take a look
    return 1;
}
<|endoftext|>


<commit_before>/*
Copyright 2016 University of Auckland

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 "libcellml/component.h"
#include "libcellml/logger.h"
#include "libcellml/types.h"

#include <algorithm>
#include <vector>

namespace libcellml {

/**
 * @brief The Logger::LoggerImpl struct.
 *
 * This struct is the private implementation struct for the LoggerImpl class.  Separating
 * the implementation from the definition allows for greater flexibility when
 * distributing the code.
 */
struct Logger::LoggerImpl
{
    std::vector<size_t> mErrors;
    std::vector<size_t> mWarnings;
    std::vector<size_t> mHints;
    std::vector<IssuePtr> mIssues;
};

Logger::Logger()
    : mPimpl(new LoggerImpl())
{
}

Logger::~Logger()
{
    delete mPimpl;
}

size_t Logger::errorCount() const
{
    return mPimpl->mErrors.size();
}

IssuePtr Logger::error(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mErrors.size()) && (mPimpl->mErrors.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mErrors.at(index));
    }
    return err;
}

size_t Logger::warningCount() const
{
    return mPimpl->mWarnings.size();
}

IssuePtr Logger::warning(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mWarnings.size()) && (mPimpl->mWarnings.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mWarnings.at(index));
    }
    return err;
}

size_t Logger::hintCount() const
{
    return mPimpl->mHints.size();
}

IssuePtr Logger::hint(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mHints.size()) && (mPimpl->mHints.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mHints.at(index));
    }
    return err;
}

void Logger::removeAllIssues()
{
    mPimpl->mIssues.clear();
    mPimpl->mErrors.clear();
    mPimpl->mWarnings.clear();
    mPimpl->mHints.clear();
}

void Logger::addIssue(const IssuePtr &issue)
{
    // When an issue is added
    mPimpl->mIssues.push_back(issue);
    size_t index = mPimpl->mIssues.size() - 1;
    // Update the appropriate array based on its level
    switch (issue->level()) {
    case libcellml::Issue::Level::ERROR:
        mPimpl->mErrors.push_back(index);
        break;
    case libcellml::Issue::Level::WARNING:
        mPimpl->mWarnings.push_back(index);
        break;
    case libcellml::Issue::Level::HINT:
        mPimpl->mHints.push_back(index);
        break;
    }
}

size_t Logger::issueCount() const
{
    return mPimpl->mIssues.size();
}

IssuePtr Logger::issue(size_t index) const
{
    IssuePtr err = nullptr;
    if (index < mPimpl->mIssues.size()) {
        err = mPimpl->mIssues.at(index);
    }
    return err;
}

IssuePtr Logger::issue(size_t index, libcellml::Issue::Level level) const
{
    switch (level) {
    case libcellml::Issue::Level::ERROR:
        return error(index);
    case libcellml::Issue::Level::WARNING:
        return warning(index);
    case libcellml::Issue::Level::HINT:
        return hint(index);
    }
}

IssuePtr Logger::issue(size_t index, std::vector<libcellml::Issue::Level> &levels) const
{
    size_t i = 0;
    index++;
    while (i < mPimpl->mIssues.size()) {
        for (auto issue : mPimpl->mIssues) {
            if (std::find(levels.begin(), levels.end(), issue->level()) != levels.end()) {
                i++;
            }
            if (i == index) {
                return issue;
            }
        }
    }
    return nullptr;
}

} // namespace libcellml
<commit_msg>Coverage take 2<commit_after>/*
Copyright 2016 University of Auckland

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 "libcellml/component.h"
#include "libcellml/logger.h"
#include "libcellml/types.h"

#include <algorithm>
#include <vector>

namespace libcellml {

/**
 * @brief The Logger::LoggerImpl struct.
 *
 * This struct is the private implementation struct for the LoggerImpl class.  Separating
 * the implementation from the definition allows for greater flexibility when
 * distributing the code.
 */
struct Logger::LoggerImpl
{
    std::vector<size_t> mErrors;
    std::vector<size_t> mWarnings;
    std::vector<size_t> mHints;
    std::vector<IssuePtr> mIssues;
};

Logger::Logger()
    : mPimpl(new LoggerImpl())
{
}

Logger::~Logger()
{
    delete mPimpl;
}

size_t Logger::errorCount() const
{
    return mPimpl->mErrors.size();
}

IssuePtr Logger::error(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mErrors.size()) && (mPimpl->mErrors.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mErrors.at(index));
    }
    return err;
}

size_t Logger::warningCount() const
{
    return mPimpl->mWarnings.size();
}

IssuePtr Logger::warning(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mWarnings.size()) && (mPimpl->mWarnings.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mWarnings.at(index));
    }
    return err;
}

size_t Logger::hintCount() const
{
    return mPimpl->mHints.size();
}

IssuePtr Logger::hint(size_t index) const
{
    IssuePtr err = nullptr;
    if ((index < mPimpl->mHints.size()) && (mPimpl->mHints.at(index) < mPimpl->mIssues.size())) {
        err = mPimpl->mIssues.at(mPimpl->mHints.at(index));
    }
    return err;
}

void Logger::removeAllIssues()
{
    mPimpl->mIssues.clear();
    mPimpl->mErrors.clear();
    mPimpl->mWarnings.clear();
    mPimpl->mHints.clear();
}

void Logger::addIssue(const IssuePtr &issue)
{
    // When an issue is added
    mPimpl->mIssues.push_back(issue);
    size_t index = mPimpl->mIssues.size() - 1;
    // Update the appropriate array based on its level
    switch (issue->level()) {
    case libcellml::Issue::Level::ERROR:
        mPimpl->mErrors.push_back(index);
        break;
    case libcellml::Issue::Level::WARNING:
        mPimpl->mWarnings.push_back(index);
        break;
    case libcellml::Issue::Level::HINT:
        mPimpl->mHints.push_back(index);
        break;
    }
}

size_t Logger::issueCount() const
{
    return mPimpl->mIssues.size();
}

IssuePtr Logger::issue(size_t index) const
{
    IssuePtr err = nullptr;
    if (index < mPimpl->mIssues.size()) {
        err = mPimpl->mIssues.at(index);
    }
    return err;
}

IssuePtr Logger::issue(size_t index, libcellml::Issue::Level level) const
{
    IssuePtr e = nullptr;
    switch (level) {
    case libcellml::Issue::Level::ERROR:
        e = error(index);
        break;
    case libcellml::Issue::Level::WARNING:
        e = warning(index);
        break;
    case libcellml::Issue::Level::HINT:
        e = hint(index);
        break;
    }
    return e;
}

IssuePtr Logger::issue(size_t index, std::vector<libcellml::Issue::Level> &levels) const
{
    size_t i = 0;
    index++;
    while (i < mPimpl->mIssues.size()) {
        for (auto issue : mPimpl->mIssues) {
            if (std::find(levels.begin(), levels.end(), issue->level()) != levels.end()) {
                i++;
            }
            if (i == index) {
                return issue;
            }
        }
    }
    return nullptr;
}

} // namespace libcellml
<|endoftext|>


<commit_before>// Copyright 2010-2013 RethinkDB, all rights reserved.
#ifndef ERRORS_HPP_
#define ERRORS_HPP_

#include <errno.h>
#include <signal.h>
#include <stdlib.h>

// Boost 1.53.0 would not build on OS X (using Apple's Clang 4.0, which is based
// off of clang 3.1svn).  See https://github.com/rethinkdb/rethinkdb/issues/377
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
#error "BOOST_NO_CXX11_RVALUE_REFERENCES should have been defined by the Makefile."
#endif

#ifndef DISABLE_BREAKPOINTS
#ifdef __linux__
#if defined __i386 || defined __x86_64
#define BREAKPOINT __asm__ volatile ("int3")
#else   /* not x86/amd64 */
#define BREAKPOINT raise(SIGTRAP)
#endif  /* x86/amd64 */
#endif /* __linux__ */

#ifdef __MACH__
#define BREAKPOINT raise(SIGTRAP)
#endif
#else /* Breakpoints Disabled */
#define BREAKPOINT
#endif /* DISABLE_BREAKPOINTS */

#define CT_ASSERT(e) {enum { compile_time_assert_error = 1/(!!(e)) };}

#ifndef NDEBUG
#define DEBUG_ONLY(...) __VA_ARGS__
#define DEBUG_ONLY_CODE(expr) do { expr; } while (0)
#else
#define DEBUG_ONLY(...)
#define DEBUG_ONLY_CODE(expr) ((void)(0))
#endif

/* This macro needs to exist because gcc and icc disagree on how to number the
 * attributes to methods. ICC does different number for methods and functions
 * and it's too unwieldy to make programs use these macros so we're just going
 * to disable them in icc.
 * With this macro attribute number starts at 1. If it is a nonstatic method
 * then "1" refers to the class pointer ("this").
 * */
#ifdef __ICC
#define NON_NULL_ATTR(arg)
#else
#define NON_NULL_ATTR(arg) __attribute__((nonnull(arg)))
#endif

#define NORETURN __attribute__((noreturn))

/* Error handling
 *
 * There are several ways to report errors in RethinkDB:
 *  fail_due_to_user_error(msg, ...)    fail and report line number/stack trace. Should only be used when the user
 *                                      is at fault (e.g. provided wrong database file name) and it is reasonable to
 *                                      fail instead of handling the problem more gracefully.
 *
 *  The following macros are used only for the cases of programmer error checking. For the time being they are also used
 *  for system error checking (especially the *_err variants).
 *
 *  crash(msg, ...)                 always fails and reports line number and such. Never returns.
 *  crash_or_trap(msg, ...)         same as above, but traps into debugger if it is present instead of terminating.
 *                                  That means that it possibly can return, and one can continue stepping through the code in the debugger.
 *                                  All off the rassert/guarantee functions use crash_or_trap.
 *  rassert(cond)                   makes sure cond is true and is a no-op in release mode
 *  rassert(cond, msg, ...)         ditto, with additional message and possibly some arguments used for formatting
 *  rassert_err(cond)               same as rassert(cond), but also print errno error description
 *  rassert_err(cond, msg, ...)     same as rassert(cond, msg, ...), but also print errno error description
 *  guarantee(cond)                 same as rassert(cond), but the check is still done in release mode. Do not use for expensive checks!
 *  guarantee(cond, msg, ...)       same as rassert(cond, msg, ...), but the check is still done in release mode. Do not use for expensive checks!
 *  guarantee_err(cond)             same as guarantee(cond), but also print errno error description
 *  guarantee_err(cond, msg, ...)   same as guarantee(cond, msg, ...), but also print errno error description
 *  guarantee_xerr(cond, err, msg, ...) same as guarantee_err(cond, msg, ...), but also allows to specify errno as err argument
 *                                  (useful for async io functions, which return negated errno)
 *
 * The names rassert* are used instead of assert* because /usr/include/assert.h undefines assert macro and redefines it with its own version
 * every single time it gets included.
 */

#ifndef NDEBUG
#define DEBUG_VAR
#else
#define DEBUG_VAR __attribute__((unused))
#endif

#define UNUSED __attribute__((unused))

#define MUST_USE __attribute__((warn_unused_result))

#define fail_due_to_user_error(msg, ...) do {  \
        report_user_error(msg, ##__VA_ARGS__); \
        BREAKPOINT;                            \
        exit(EXIT_FAILURE);                    \
    } while (0)

#define crash(msg, ...) do {                                        \
        report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \
        BREAKPOINT; /* this used to be abort(), but it didn't cause VALGRIND to print a backtrace */ \
        abort();                                                    \
    } while (0)

#define nice_crash(...) do {          \
        fprintf(stderr, __VA_ARGS__); \
        exit(EXIT_FAILURE);           \
    } while (0)

#define crash_or_trap(msg, ...) do {                                \
        report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \
        BREAKPOINT;                                                 \
    } while (0)

void report_fatal_error(const char*, int, const char*, ...) __attribute__((format (printf, 3, 4)));
void report_user_error(const char*, ...) __attribute__((format (printf, 1, 2)));

// Possibly using buf to store characters, returns a pointer to a strerror-style error string.  This
// has the same contract as the GNU (char *)-returning strerror_r.  The return value is a pointer to
// a nul-terminated string, either equal to buf or pointing at a statically allocated string.
MUST_USE const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen);

#define stringify(x) #x

#define format_assert_message(assert_type, cond) assert_type " failed: [" stringify(cond) "] "
#define guarantee(cond, msg...) do {    \
        if (!(cond)) {                  \
            crash_or_trap(format_assert_message("Guarantee", cond) msg); \
        }                               \
    } while (0)

#define nice_guarantee(cond, msg, ...) do { \
        if (!(cond)) {                      \
            nice_crash(msg, ##__VA_ARGS__); \
        }                                   \
    } while (0)

#define guarantee_xerr(cond, err, msg, args...) do {                            \
        int guarantee_xerr_errsv = (err);                                       \
        if (!(cond)) {                                                          \
            if (guarantee_xerr_errsv == 0) {                                    \
                crash_or_trap(format_assert_message("Guarantee", cond) msg, ##args); \
            } else {                                                            \
                char guarantee_xerr_buf[250];                                      \
                const char *errstr = errno_string_maybe_using_buffer(guarantee_xerr_errsv, guarantee_xerr_buf, sizeof(guarantee_xerr_buf)); \
                crash_or_trap(format_assert_message("Guarantee", cond) " (errno %d - %s) " msg, guarantee_xerr_errsv, errstr, ##args); \
            }                                                                   \
        }                                                                       \
    } while (0)
#define guarantee_err(cond, msg, args...) guarantee_xerr(cond, errno, msg, ##args)

#define unreachable(msg, ...) crash("Unreachable code: " msg, ##__VA_ARGS__)    // can't use crash_or_trap since code needs to depend on its noreturn property
#define not_implemented(msg, ...) crash_or_trap("Not implemented: " msg, ##__VA_ARGS__)

#ifdef NDEBUG
#define rassert(cond, msg...) ((void)(0))
#define rassert_err(cond, msg...) ((void)(0))
#else
#define rassert(cond, msg...) do {                                        \
        if (!(cond)) {                                                    \
            crash_or_trap(format_assert_message("Assertion", cond) msg);  \
        }                                                                 \
    } while (0)
#define rassert_err(cond, msg, args...) do {                                \
        int rassert_err_errsv = errno;                                      \
        if (!(cond)) {                                                      \
            if (rassert_err_errsv == 0) {                                   \
                crash_or_trap(format_assert_message("Assert", cond) msg);   \
            } else {                                                        \
                char rassert_err_buf[250];                                  \
                const char *errstr = errno_string_maybe_using_buffer(rassert_err_errsv, rassert_err_buf, sizeof(rassert_err_buf)); \
                crash_or_trap(format_assert_message("Assert", cond) " (errno %d - %s) " msg, rassert_err_errsv, errstr, ##args);  \
            }                                                               \
        }                                                                   \
    } while (0)
#endif

void install_generic_crash_handler();

// If you include errors.hpp before including a Boost library, then Boost assertion
// failures will be forwarded to the RethinkDB error mechanism.
#define BOOST_ENABLE_ASSERT_HANDLER
namespace boost {
void assertion_failed(char const * expr, char const * function, char const * file, long line);  // NOLINT(runtime/int)
}

#ifdef MCHECK
void mcheck_init(void);
void mcheck_all(void);
#endif

#define DISABLE_COPYING(T)                      \
    T(const T&) = delete;                       \
    T& operator=(const T&) = delete

/* Put these after functions to indicate what they throw. In release mode, they
turn into noops so that the compiler doesn't have to generate exception-checking
code everywhere. If you need to add an exception specification for compatibility
with e.g. a virtual method, don't use these, or your code won't compile in
release mode. */
#ifdef NDEBUG
#define THROWS_NOTHING
#define THROWS_ONLY(...)
#else
#define THROWS_NOTHING throw ()
#define THROWS_ONLY(...) throw (__VA_ARGS__)
#endif

// This is a workaround for old versions of boost causing a compilation error
#include <boost/config.hpp> // NOLINT(build/include_order)
#undef BOOST_HAS_RVALUE_REFS

#endif /* ERRORS_HPP_ */
<commit_msg>changing boost rvalue refs workaround to only be enacted for the specific versions of boost that exhibit the problem<commit_after>// Copyright 2010-2013 RethinkDB, all rights reserved.
#ifndef ERRORS_HPP_
#define ERRORS_HPP_

#include <errno.h>
#include <signal.h>
#include <stdlib.h>

// Boost 1.53.0 would not build on OS X (using Apple's Clang 4.0, which is based
// off of clang 3.1svn).  See https://github.com/rethinkdb/rethinkdb/issues/377
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
#error "BOOST_NO_CXX11_RVALUE_REFERENCES should have been defined by the Makefile."
#endif

#ifndef DISABLE_BREAKPOINTS
#ifdef __linux__
#if defined __i386 || defined __x86_64
#define BREAKPOINT __asm__ volatile ("int3")
#else   /* not x86/amd64 */
#define BREAKPOINT raise(SIGTRAP)
#endif  /* x86/amd64 */
#endif /* __linux__ */

#ifdef __MACH__
#define BREAKPOINT raise(SIGTRAP)
#endif
#else /* Breakpoints Disabled */
#define BREAKPOINT
#endif /* DISABLE_BREAKPOINTS */

#define CT_ASSERT(e) {enum { compile_time_assert_error = 1/(!!(e)) };}

#ifndef NDEBUG
#define DEBUG_ONLY(...) __VA_ARGS__
#define DEBUG_ONLY_CODE(expr) do { expr; } while (0)
#else
#define DEBUG_ONLY(...)
#define DEBUG_ONLY_CODE(expr) ((void)(0))
#endif

/* This macro needs to exist because gcc and icc disagree on how to number the
 * attributes to methods. ICC does different number for methods and functions
 * and it's too unwieldy to make programs use these macros so we're just going
 * to disable them in icc.
 * With this macro attribute number starts at 1. If it is a nonstatic method
 * then "1" refers to the class pointer ("this").
 * */
#ifdef __ICC
#define NON_NULL_ATTR(arg)
#else
#define NON_NULL_ATTR(arg) __attribute__((nonnull(arg)))
#endif

#define NORETURN __attribute__((noreturn))

/* Error handling
 *
 * There are several ways to report errors in RethinkDB:
 *  fail_due_to_user_error(msg, ...)    fail and report line number/stack trace. Should only be used when the user
 *                                      is at fault (e.g. provided wrong database file name) and it is reasonable to
 *                                      fail instead of handling the problem more gracefully.
 *
 *  The following macros are used only for the cases of programmer error checking. For the time being they are also used
 *  for system error checking (especially the *_err variants).
 *
 *  crash(msg, ...)                 always fails and reports line number and such. Never returns.
 *  crash_or_trap(msg, ...)         same as above, but traps into debugger if it is present instead of terminating.
 *                                  That means that it possibly can return, and one can continue stepping through the code in the debugger.
 *                                  All off the rassert/guarantee functions use crash_or_trap.
 *  rassert(cond)                   makes sure cond is true and is a no-op in release mode
 *  rassert(cond, msg, ...)         ditto, with additional message and possibly some arguments used for formatting
 *  rassert_err(cond)               same as rassert(cond), but also print errno error description
 *  rassert_err(cond, msg, ...)     same as rassert(cond, msg, ...), but also print errno error description
 *  guarantee(cond)                 same as rassert(cond), but the check is still done in release mode. Do not use for expensive checks!
 *  guarantee(cond, msg, ...)       same as rassert(cond, msg, ...), but the check is still done in release mode. Do not use for expensive checks!
 *  guarantee_err(cond)             same as guarantee(cond), but also print errno error description
 *  guarantee_err(cond, msg, ...)   same as guarantee(cond, msg, ...), but also print errno error description
 *  guarantee_xerr(cond, err, msg, ...) same as guarantee_err(cond, msg, ...), but also allows to specify errno as err argument
 *                                  (useful for async io functions, which return negated errno)
 *
 * The names rassert* are used instead of assert* because /usr/include/assert.h undefines assert macro and redefines it with its own version
 * every single time it gets included.
 */

#ifndef NDEBUG
#define DEBUG_VAR
#else
#define DEBUG_VAR __attribute__((unused))
#endif

#define UNUSED __attribute__((unused))

#define MUST_USE __attribute__((warn_unused_result))

#define fail_due_to_user_error(msg, ...) do {  \
        report_user_error(msg, ##__VA_ARGS__); \
        BREAKPOINT;                            \
        exit(EXIT_FAILURE);                    \
    } while (0)

#define crash(msg, ...) do {                                        \
        report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \
        BREAKPOINT; /* this used to be abort(), but it didn't cause VALGRIND to print a backtrace */ \
        abort();                                                    \
    } while (0)

#define nice_crash(...) do {          \
        fprintf(stderr, __VA_ARGS__); \
        exit(EXIT_FAILURE);           \
    } while (0)

#define crash_or_trap(msg, ...) do {                                \
        report_fatal_error(__FILE__, __LINE__, msg, ##__VA_ARGS__); \
        BREAKPOINT;                                                 \
    } while (0)

void report_fatal_error(const char*, int, const char*, ...) __attribute__((format (printf, 3, 4)));
void report_user_error(const char*, ...) __attribute__((format (printf, 1, 2)));

// Possibly using buf to store characters, returns a pointer to a strerror-style error string.  This
// has the same contract as the GNU (char *)-returning strerror_r.  The return value is a pointer to
// a nul-terminated string, either equal to buf or pointing at a statically allocated string.
MUST_USE const char *errno_string_maybe_using_buffer(int errsv, char *buf, size_t buflen);

#define stringify(x) #x

#define format_assert_message(assert_type, cond) assert_type " failed: [" stringify(cond) "] "
#define guarantee(cond, msg...) do {    \
        if (!(cond)) {                  \
            crash_or_trap(format_assert_message("Guarantee", cond) msg); \
        }                               \
    } while (0)

#define nice_guarantee(cond, msg, ...) do { \
        if (!(cond)) {                      \
            nice_crash(msg, ##__VA_ARGS__); \
        }                                   \
    } while (0)

#define guarantee_xerr(cond, err, msg, args...) do {                            \
        int guarantee_xerr_errsv = (err);                                       \
        if (!(cond)) {                                                          \
            if (guarantee_xerr_errsv == 0) {                                    \
                crash_or_trap(format_assert_message("Guarantee", cond) msg, ##args); \
            } else {                                                            \
                char guarantee_xerr_buf[250];                                      \
                const char *errstr = errno_string_maybe_using_buffer(guarantee_xerr_errsv, guarantee_xerr_buf, sizeof(guarantee_xerr_buf)); \
                crash_or_trap(format_assert_message("Guarantee", cond) " (errno %d - %s) " msg, guarantee_xerr_errsv, errstr, ##args); \
            }                                                                   \
        }                                                                       \
    } while (0)
#define guarantee_err(cond, msg, args...) guarantee_xerr(cond, errno, msg, ##args)

#define unreachable(msg, ...) crash("Unreachable code: " msg, ##__VA_ARGS__)    // can't use crash_or_trap since code needs to depend on its noreturn property
#define not_implemented(msg, ...) crash_or_trap("Not implemented: " msg, ##__VA_ARGS__)

#ifdef NDEBUG
#define rassert(cond, msg...) ((void)(0))
#define rassert_err(cond, msg...) ((void)(0))
#else
#define rassert(cond, msg...) do {                                        \
        if (!(cond)) {                                                    \
            crash_or_trap(format_assert_message("Assertion", cond) msg);  \
        }                                                                 \
    } while (0)
#define rassert_err(cond, msg, args...) do {                                \
        int rassert_err_errsv = errno;                                      \
        if (!(cond)) {                                                      \
            if (rassert_err_errsv == 0) {                                   \
                crash_or_trap(format_assert_message("Assert", cond) msg);   \
            } else {                                                        \
                char rassert_err_buf[250];                                  \
                const char *errstr = errno_string_maybe_using_buffer(rassert_err_errsv, rassert_err_buf, sizeof(rassert_err_buf)); \
                crash_or_trap(format_assert_message("Assert", cond) " (errno %d - %s) " msg, rassert_err_errsv, errstr, ##args);  \
            }                                                               \
        }                                                                   \
    } while (0)
#endif

void install_generic_crash_handler();

// If you include errors.hpp before including a Boost library, then Boost assertion
// failures will be forwarded to the RethinkDB error mechanism.
#define BOOST_ENABLE_ASSERT_HANDLER
namespace boost {
void assertion_failed(char const * expr, char const * function, char const * file, long line);  // NOLINT(runtime/int)
}

#ifdef MCHECK
void mcheck_init(void);
void mcheck_all(void);
#endif

#define DISABLE_COPYING(T)                      \
    T(const T&) = delete;                       \
    T& operator=(const T&) = delete

/* Put these after functions to indicate what they throw. In release mode, they
turn into noops so that the compiler doesn't have to generate exception-checking
code everywhere. If you need to add an exception specification for compatibility
with e.g. a virtual method, don't use these, or your code won't compile in
release mode. */
#ifdef NDEBUG
#define THROWS_NOTHING
#define THROWS_ONLY(...)
#else
#define THROWS_NOTHING throw ()
#define THROWS_ONLY(...) throw (__VA_ARGS__)
#endif

// This is a workaround for old versions of boost causing a compilation error
#include <boost/version.hpp> // NOLINT(build/include_order)
#if (BOOST_VERSION >= 104200) && (BOOST_VERSION <= 104399)
#include <boost/config.hpp> // NOLINT(build/include_order)
#undef BOOST_HAS_RVALUE_REFS
#endif

#endif /* ERRORS_HPP_ */
<|endoftext|>


<commit_before>#include "logics.hpp"

// получаем контур на поверхности сферы
std::vector<vec3s> cell_contour( cell c, field f, std::size_t npoints ){
    // пока не обрабатываем ячейки у полюсов, хотя там одна из сторон просто
    // вырождается в точку и все точки на этой стороне совпадают
    std::vector<vec3s> contour(npoints);
    std::size_t pos = npoints / 4; // points on side
    std::size_t i = 0;
    // обход из правого нижнего угла против часовой стрелки
    // по меридиану
    for ( ; i < pos; ++i ){
        contour[i].theta = ( c.i + 1 - ( float ) i / pos ) * M_PI / f.height;
        contour[i].phi = ( c.j + 1 ) * 2 * M_PI / f.width;
    }
    // по широте
    for ( ; i < 2 * pos; ++i ){
        contour[i].theta = c.i * M_PI / f.height;
        contour[i].phi = ( c.j + 1 - ( float ) ( i - pos ) / pos )
                         * 2 * M_PI / f.width;
    }
    // снова по меридиану
    for ( ; i < 3 * pos; ++i ){
        contour[i].theta = ( c.i + ( float ) ( i - 2 * pos ) / pos )
                           * M_PI / f.height;
        contour[i].phi = c.j * 2 * M_PI / f.width;
    }
    // и ещё раз по широте
    for ( ; i < npoints; ++i) {
        auto last = npoints - 3 * pos;
        contour[i].theta = ( c.i + 1 ) * M_PI / f.height;
        contour[i].phi = ( c.j + ( float ) ( i - 3 * pos ) / last )
                         * 2 * M_PI / f.width;
    }
    return contour;
}

cell cell_from_point( field & f, vec3s & p ){
    return { ( int )( p.theta / M_PI   * f.height ),
             ( int )( p.phi / 2 / M_PI * f.width  )};
}

void create( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = true;
}

void kill( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = false;
}

void toggle( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = !f[c.i][c.j];
}

void nextStep(field & f){
    // массив для подсчёта соседей
    auto tmp = new char*[f.height];
    for (std::size_t i = 0; i < f.height; ++i)
        tmp[i] = new char[f.width];

    // 1. Обрабатываем первую строку
    //    в ней все соседи друг другу, так как есть общая вершина (полюс)
    //    поэтому проще всего посчитать сумму в этой строке
    std::size_t s = 0;
    for (std::size_t i = 0; i < f.width; ++i)
        s += f[0][i];

    for (std::size_t i = 0; i < f.width; ++i)
        if ( f[0][i] ){
            tmp[0][i] = s - 1;
            tmp[1][( i + f.width - 1 ) % f.width] += 1;
            tmp[1][i] += 1;
            tmp[1][( i + f.width + 1 ) % f.width] += 1;
        } else {
            tmp[0][i] = s;
        }

    // 2. Между первой и последней
    for (std::size_t j = 1; j < f.height - 1; ++j)
        for (std::size_t i = 0; i < f.width; ++i)
            if ( f[j][i] ){
                tmp[j - 1][( i + f.width - 1 ) % f.width] += 1;
                tmp[j - 1][i]                             += 1;
                tmp[j - 1][( i + f.width + 1 ) % f.width] += 1;

                tmp[j][( i + f.width - 1 ) % f.width] += 1;
                tmp[j][i]                             += 1;
                tmp[j][( i + f.width + 1 ) % f.width] += 1;

                tmp[j + 1][( i + f.width - 1 ) % f.width] += 1;
                tmp[j + 1][i]                             += 1;
                tmp[j + 1][( i + f.width + 1 ) % f.width] += 1;
            }

    // 3. Последняя аналогично первой
    std::size_t p = f.height - 1;
    s = 0;
    for (std::size_t i = 0; i < f.width; ++i)
        s += f[p][i];

    for (std::size_t i = 0; i < f.width; ++i)
        if ( f[0][i] ){
            tmp[0][i] += s - 1;

            tmp[p - 1][( i + f.width - 1 ) % f.width] += 1;
            tmp[p - 1][i] += 1;
            tmp[p - 1][( i + f.width + 1 ) % f.width] += 1;
        } else {
            tmp[p][i] += s;
        }

    // 4. Установка нового состояния
    for (std::size_t j = 0; j < f.height; ++j)
        for (std::size_t i = 0; i < f.width; ++i)
            f[j][i] = (tmp[j][i] == 2 && f[j][i]) || (tmp[j][i] == 3);
    delete[] tmp;
}
<commit_msg>[fix] f -> f.f<commit_after>#include "logics.hpp"

// получаем контур на поверхности сферы
std::vector<vec3s> cell_contour( cell c, field f, std::size_t npoints ){
    // пока не обрабатываем ячейки у полюсов, хотя там одна из сторон просто
    // вырождается в точку и все точки на этой стороне совпадают
    std::vector<vec3s> contour(npoints);
    std::size_t pos = npoints / 4; // points on side
    std::size_t i = 0;
    // обход из правого нижнего угла против часовой стрелки
    // по меридиану
    for ( ; i < pos; ++i ){
        contour[i].theta = ( c.i + 1 - ( float ) i / pos ) * M_PI / f.height;
        contour[i].phi = ( c.j + 1 ) * 2 * M_PI / f.width;
    }
    // по широте
    for ( ; i < 2 * pos; ++i ){
        contour[i].theta = c.i * M_PI / f.height;
        contour[i].phi = ( c.j + 1 - ( float ) ( i - pos ) / pos )
                         * 2 * M_PI / f.width;
    }
    // снова по меридиану
    for ( ; i < 3 * pos; ++i ){
        contour[i].theta = ( c.i + ( float ) ( i - 2 * pos ) / pos )
                           * M_PI / f.height;
        contour[i].phi = c.j * 2 * M_PI / f.width;
    }
    // и ещё раз по широте
    for ( ; i < npoints; ++i) {
        auto last = npoints - 3 * pos;
        contour[i].theta = ( c.i + 1 ) * M_PI / f.height;
        contour[i].phi = ( c.j + ( float ) ( i - 3 * pos ) / last )
                         * 2 * M_PI / f.width;
    }
    return contour;
}

cell cell_from_point( field & f, vec3s & p ){
    return { ( int )( p.theta / M_PI   * f.height ),
             ( int )( p.phi / 2 / M_PI * f.width  )};
}

void create( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = true;
}

void kill( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = false;
}

void toggle( field & f, vec3s & p ){
    auto c = cell_from_point(f, p);
    f[c.i][c.j] = !f[c.i][c.j];
}

void nextStep(field & f){
    // массив для подсчёта соседей
    auto tmp = new char*[f.height];
    for (std::size_t i = 0; i < f.height; ++i)
        tmp[i] = new char[f.width];

    // 1. Обрабатываем первую строку
    //    в ней все соседи друг другу, так как есть общая вершина (полюс)
    //    поэтому проще всего посчитать сумму в этой строке
    std::size_t s = 0;
    for (std::size_t i = 0; i < f.width; ++i)
        s += f.f[0][i];

    for (std::size_t i = 0; i < f.width; ++i)
        if ( f.f[0][i] ){
            tmp[0][i] = s - 1;
            tmp[1][( i + f.width - 1 ) % f.width] += 1;
            tmp[1][i] += 1;
            tmp[1][( i + f.width + 1 ) % f.width] += 1;
        } else {
            tmp[0][i] = s;
        }

    // 2. Между первой и последней
    for (std::size_t j = 1; j < f.height - 1; ++j)
        for (std::size_t i = 0; i < f.width; ++i)
            if ( f.f[j][i] ){
                tmp[j - 1][( i + f.width - 1 ) % f.width] += 1;
                tmp[j - 1][i]                             += 1;
                tmp[j - 1][( i + f.width + 1 ) % f.width] += 1;

                tmp[j][( i + f.width - 1 ) % f.width] += 1;
                tmp[j][i]                             += 1;
                tmp[j][( i + f.width + 1 ) % f.width] += 1;

                tmp[j + 1][( i + f.width - 1 ) % f.width] += 1;
                tmp[j + 1][i]                             += 1;
                tmp[j + 1][( i + f.width + 1 ) % f.width] += 1;
            }

    // 3. Последняя аналогично первой
    std::size_t p = f.height - 1;
    s = 0;
    for (std::size_t i = 0; i < f.width; ++i)
        s += f.f[p][i];

    for (std::size_t i = 0; i < f.width; ++i)
        if ( f.f[0][i] ){
            tmp[0][i] += s - 1;

            tmp[p - 1][( i + f.width - 1 ) % f.width] += 1;
            tmp[p - 1][i] += 1;
            tmp[p - 1][( i + f.width + 1 ) % f.width] += 1;
        } else {
            tmp[p][i] += s;
        }

    // 4. Установка нового состояния
    for (std::size_t j = 0; j < f.height; ++j)
        for (std::size_t i = 0; i < f.width; ++i)
            f.f[j][i] = (tmp[j][i] == 2 && f.f[j][i]) || (tmp[j][i] == 3);
    delete[] tmp;
}
<|endoftext|>