<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#define __STDC_LIMIT_MACROS 1

#include <math.h>
#include <new>
#include <stdint.h>
#include <string.h>

#ifdef __SSE__
#    include <xmmintrin.h>
#endif

#include "ingen/URIMap.hpp"
#include "ingen/URIs.hpp"
#include "ingen/World.hpp"
#include "ingen_config.h"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"
#include "ingen/Log.hpp"

#include "Buffer.hpp"
#include "BufferFactory.hpp"
#include "Engine.hpp"

namespace Ingen {
namespace Server {

Buffer::Buffer(BufferFactory& bufs,
               LV2_URID       type,
               LV2_URID       value_type,
               uint32_t       capacity,
               bool           external,
               void*          buf)
	: _factory(bufs)
	, _type(type)
	, _value_type(value_type)
	, _capacity(capacity)
	, _latest_event(0)
	, _next(NULL)
	, _refs(0)
	, _external(external)
{
	if (!external) {
#ifdef HAVE_POSIX_MEMALIGN
		int ret = posix_memalign((void**)&_buf, 16, capacity);
		if (!ret) {
			memset(_buf, 0, capacity);
		}
#else
		_buf = (LV2_buf*)calloc(1, capacity);
		int ret = (_buf != NULL) ? 0 : -1;
#endif

		if (ret) {
			bufs.engine().log().error("Failed to allocate event buffer\n");
			throw std::bad_alloc();
		}
	}

	if (type != bufs.uris().atom_Sound) {
		/* Audio buffers are not atoms, the buffer is the start of a float
		   array which is already silent since the buffer is zeroed.  All other
		   buffers are atoms. */
		if (_buf) {
			LV2_Atom* atom = get<LV2_Atom>();
			atom->size = capacity - sizeof(LV2_Atom);
			atom->type = type;

			clear();
		}

		if (value_type && value_type != type) {
			/* Buffer with a different value type.  These buffers (probably
			   sequences) have a "value" that persists independently of the buffer
			   contents.  This is used to represent things like a Sequence of
			   Float, which acts like an individual float (has a value), but the
			   buffer itself only transmits changes and does not necessarily
			   contain the current value. */
			_value_buffer = bufs.get_buffer(value_type, 0, 0, false);
		}
	}
}

Buffer::~Buffer()
{
	if (!_external) {
		free(_buf);
	}
}

void
Buffer::recycle()
{
	_factory.recycle(this);
}

void
Buffer::set_type(LV2_URID type, LV2_URID value_type)
{
	_type = type;
	if (type == _factory.uris().atom_Sequence && value_type) {
		_value_buffer = _factory.get_buffer(value_type, 0, 0, false);
	}
}

void
Buffer::clear()
{
	if (is_audio() && _buf) {
		memset(_buf, 0, _capacity);
	} else if (is_control()) {
		get<LV2_Atom_Float>()->body = 0;
	} else if (is_sequence()) {
		LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>();
		seq->atom.type = _factory.uris().atom_Sequence;
		seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
		seq->body.unit = 0;
		seq->body.pad  = 0;
		_latest_event  = 0;
	}
}

void
Buffer::render_sequence(const Context& context, const Buffer* src, bool add)
{
	const LV2_URID           atom_Float = _factory.uris().atom_Float;
	const LV2_Atom_Sequence* seq        = src->get<const LV2_Atom_Sequence>();
	const LV2_Atom_Float*    init       = (const LV2_Atom_Float*)src->value();
	float                    value      = init ? init->body : 0.0f;
	SampleCount              offset     = context.offset();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames >= offset && ev->body.type == atom_Float) {
			write_block(value, offset, ev->time.frames, add);
			value  = ((const LV2_Atom_Float*)&ev->body)->body;
			offset = ev->time.frames;
		}
	}
	write_block(value, offset, context.offset() + context.nframes(), add);
}

void
Buffer::copy(const Context& context, const Buffer* src)
{
	if (!_buf) {
		return;
	} else if (is_audio() && src->is_audio()) {
		memcpy(_buf, src->_buf, src->_capacity);
	} else if (_type == src->type()) {
		const LV2_Atom* src_atom = src->get<const LV2_Atom>();
		if (lv2_atom_total_size(src_atom) <= _capacity) {
			memcpy(_buf, src_atom, lv2_atom_total_size(src_atom));
		} else {
			clear();
		}

		if (value() && src->value()) {
			memcpy(value(), src->value(), lv2_atom_total_size(src->value()));
		}
	} else if (src->is_audio() && is_control()) {
		samples()[0] = src->samples()[0];
	} else if (src->is_control() && is_audio()) {
		set_block(src->samples()[0], 0, context.nframes());
	} else if (src->is_sequence() && is_audio() &&
	           src->value_type() == _factory.uris().atom_Float) {
		render_sequence(context, src, false);
	} else {
		clear();
	}
}

void
Buffer::resize(uint32_t capacity)
{
	if (!_external) {
		_buf      = realloc(_buf, capacity);
		_capacity = capacity;
		clear();
	} else {
		_factory.engine().log().error("Attempt to resize external buffer\n");
	}
}

void*
Buffer::port_data(PortType port_type, SampleCount offset)
{
	switch (port_type.id()) {
	case PortType::ID::CONTROL:
	case PortType::ID::CV:
	case PortType::ID::AUDIO:
		if (_type == _factory.uris().atom_Float) {
			return &get<LV2_Atom_Float>()->body;
		} else if (_type == _factory.uris().atom_Sound) {
			return (Sample*)_buf;
		}
		break;
	case PortType::ID::ATOM:
		if (_type != _factory.uris().atom_Sound) {
			return _buf;
		}
	default: break;
	}
	return NULL;
}

const void*
Buffer::port_data(PortType port_type, SampleCount offset) const
{
	return const_cast<void*>(
		const_cast<Buffer*>(this)->port_data(port_type, offset));
}

#ifdef __SSE__
/** Vector fabsf */
static inline __m128
mm_abs_ps(__m128 x)
{
	const __m128 sign_mask = _mm_set1_ps(-0.0f);  // -0.0f = 1 << 31
	return _mm_andnot_ps(sign_mask, x);
}
#endif

float
Buffer::peak(const Context& context) const
{
#ifdef __SSE__
	const __m128* const vbuf    = (const __m128* const)samples();
	__m128              vpeak   = mm_abs_ps(vbuf[0]);
	const SampleCount   nblocks = context.nframes() / 4;

	// First, find the vector absolute max of the buffer
	for (SampleCount i = 1; i < nblocks; ++i) {
		vpeak = _mm_max_ps(vpeak, mm_abs_ps(vbuf[i]));
	}

	// Now we need the single max of vpeak
	// vpeak = ABCD
	// tmp   = CDAB
	__m128 tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(2, 3, 0, 1));

	// vpeak = MAX(A,C) MAX(B,D) MAX(C,A) MAX(D,B)
	vpeak = _mm_max_ps(vpeak, tmp);

	// tmp = BADC of the new vpeak
	// tmp = MAX(B,D) MAX(A,C) MAX(D,B) MAX(C,A)
	tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(1, 0, 3, 2));

	// vpeak = MAX(MAX(A,C), MAX(B,D)), ...
	vpeak = _mm_max_ps(vpeak, tmp);

	// peak = vpeak[0]
	float peak;
	_mm_store_ss(&peak, vpeak);

	return peak;
#else
	const Sample* const buf = samples();
	float peak = 0.0f;
	for (SampleCount i = 0; i < context.nframes(); ++i) {
		peak = fmaxf(peak, fabsf(buf[i]));
	}
	return peak;
#endif
}

void
Buffer::prepare_write(Context& context)
{
	if (_type == _factory.uris().atom_Sequence) {
		LV2_Atom* atom = get<LV2_Atom>();

		atom->type    = (LV2_URID)_factory.uris().atom_Sequence;
		atom->size    = sizeof(LV2_Atom_Sequence_Body);
		_latest_event = 0;
	}
}

void
Buffer::prepare_output_write(Context& context)
{
	if (_type == _factory.uris().atom_Sequence) {
		LV2_Atom* atom = get<LV2_Atom>();

		atom->type    = (LV2_URID)_factory.uris().atom_Chunk;
		atom->size    = _capacity - sizeof(LV2_Atom);
		_latest_event = 0;
	}
}

bool
Buffer::append_event(int64_t        frames,
                     uint32_t       size,
                     uint32_t       type,
                     const uint8_t* data)
{
	assert(frames >= _latest_event);

	LV2_Atom* atom = get<LV2_Atom>();
	if (atom->type == _factory.uris().atom_Chunk) {
		// Chunk initialized with prepare_output_write(), clear
		clear();
	}

	if (sizeof(LV2_Atom) + atom->size + lv2_atom_pad_size(size) > _capacity) {
		return false;
	}

	LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)atom;
	LV2_Atom_Event*    ev  = (LV2_Atom_Event*)(
		(uint8_t*)seq + lv2_atom_total_size(&seq->atom));

	ev->time.frames = frames;
	ev->body.size   = size;
	ev->body.type   = type;
	memcpy(ev + 1, data, size);

	atom->size += sizeof(LV2_Atom_Event) + lv2_atom_pad_size(size);

	_latest_event = frames;

	return true;
}

SampleCount
Buffer::next_value_offset(SampleCount offset, SampleCount end) const
{
	SampleCount              earliest = end;
	const LV2_Atom_Sequence* seq      = get<const LV2_Atom_Sequence>();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames >  offset   &&
		    ev->time.frames <  earliest &&
		    ev->body.type   == _value_type) {
			earliest = ev->time.frames;
			break;
		}
	}
	return earliest;
}

const LV2_Atom*
Buffer::value() const
{
	return _value_buffer ? _value_buffer->get<const LV2_Atom>() : NULL;
}

LV2_Atom*
Buffer::value()
{
	return _value_buffer ? _value_buffer->get<LV2_Atom>() : NULL;
}

void
Buffer::update_value_buffer(SampleCount offset)
{
	if (!_value_buffer) {
		return;
	}

	LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames <= offset && ev->body.type == _value_type) {
			memcpy(_value_buffer->get<LV2_Atom>(),
			       &ev->body,
			       lv2_atom_total_size(&ev->body));
			break;
		}
	}
}

void
intrusive_ptr_add_ref(Buffer* b)
{
	b->ref();
}

void
intrusive_ptr_release(Buffer* b)
{
	b->deref();
}

} // namespace Server
} // namespace Ingen
<commit_msg>Fix uninitialized buffer pointer<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#define __STDC_LIMIT_MACROS 1

#include <math.h>
#include <new>
#include <stdint.h>
#include <string.h>

#ifdef __SSE__
#    include <xmmintrin.h>
#endif

#include "ingen/URIMap.hpp"
#include "ingen/URIs.hpp"
#include "ingen/World.hpp"
#include "ingen_config.h"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"
#include "ingen/Log.hpp"

#include "Buffer.hpp"
#include "BufferFactory.hpp"
#include "Engine.hpp"

namespace Ingen {
namespace Server {

Buffer::Buffer(BufferFactory& bufs,
               LV2_URID       type,
               LV2_URID       value_type,
               uint32_t       capacity,
               bool           external,
               void*          buf)
	: _factory(bufs)
	, _buf(NULL)
	, _type(type)
	, _value_type(value_type)
	, _capacity(capacity)
	, _latest_event(0)
	, _next(NULL)
	, _refs(0)
	, _external(external)
{
	if (!external) {
#ifdef HAVE_POSIX_MEMALIGN
		int ret = posix_memalign((void**)&_buf, 16, capacity);
		if (!ret) {
			memset(_buf, 0, capacity);
		}
#else
		_buf = (LV2_buf*)calloc(1, capacity);
		int ret = (_buf != NULL) ? 0 : -1;
#endif

		if (ret) {
			bufs.engine().log().error("Failed to allocate event buffer\n");
			throw std::bad_alloc();
		}
	}

	if (type != bufs.uris().atom_Sound) {
		/* Audio buffers are not atoms, the buffer is the start of a float
		   array which is already silent since the buffer is zeroed.  All other
		   buffers are atoms. */
		if (_buf) {
			LV2_Atom* atom = get<LV2_Atom>();
			atom->size = capacity - sizeof(LV2_Atom);
			atom->type = type;

			clear();
		}

		if (value_type && value_type != type) {
			/* Buffer with a different value type.  These buffers (probably
			   sequences) have a "value" that persists independently of the buffer
			   contents.  This is used to represent things like a Sequence of
			   Float, which acts like an individual float (has a value), but the
			   buffer itself only transmits changes and does not necessarily
			   contain the current value. */
			_value_buffer = bufs.get_buffer(value_type, 0, 0, false);
		}
	}
}

Buffer::~Buffer()
{
	if (!_external) {
		free(_buf);
	}
}

void
Buffer::recycle()
{
	_factory.recycle(this);
}

void
Buffer::set_type(LV2_URID type, LV2_URID value_type)
{
	_type = type;
	if (type == _factory.uris().atom_Sequence && value_type) {
		_value_buffer = _factory.get_buffer(value_type, 0, 0, false);
	}
}

void
Buffer::clear()
{
	if (is_audio() && _buf) {
		memset(_buf, 0, _capacity);
	} else if (is_control()) {
		get<LV2_Atom_Float>()->body = 0;
	} else if (is_sequence()) {
		LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>();
		seq->atom.type = _factory.uris().atom_Sequence;
		seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
		seq->body.unit = 0;
		seq->body.pad  = 0;
		_latest_event  = 0;
	}
}

void
Buffer::render_sequence(const Context& context, const Buffer* src, bool add)
{
	const LV2_URID           atom_Float = _factory.uris().atom_Float;
	const LV2_Atom_Sequence* seq        = src->get<const LV2_Atom_Sequence>();
	const LV2_Atom_Float*    init       = (const LV2_Atom_Float*)src->value();
	float                    value      = init ? init->body : 0.0f;
	SampleCount              offset     = context.offset();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames >= offset && ev->body.type == atom_Float) {
			write_block(value, offset, ev->time.frames, add);
			value  = ((const LV2_Atom_Float*)&ev->body)->body;
			offset = ev->time.frames;
		}
	}
	write_block(value, offset, context.offset() + context.nframes(), add);
}

void
Buffer::copy(const Context& context, const Buffer* src)
{
	if (!_buf) {
		return;
	} else if (is_audio() && src->is_audio()) {
		memcpy(_buf, src->_buf, src->_capacity);
	} else if (_type == src->type()) {
		const LV2_Atom* src_atom = src->get<const LV2_Atom>();
		if (lv2_atom_total_size(src_atom) <= _capacity) {
			memcpy(_buf, src_atom, lv2_atom_total_size(src_atom));
		} else {
			clear();
		}

		if (value() && src->value()) {
			memcpy(value(), src->value(), lv2_atom_total_size(src->value()));
		}
	} else if (src->is_audio() && is_control()) {
		samples()[0] = src->samples()[0];
	} else if (src->is_control() && is_audio()) {
		set_block(src->samples()[0], 0, context.nframes());
	} else if (src->is_sequence() && is_audio() &&
	           src->value_type() == _factory.uris().atom_Float) {
		render_sequence(context, src, false);
	} else {
		clear();
	}
}

void
Buffer::resize(uint32_t capacity)
{
	if (!_external) {
		_buf      = realloc(_buf, capacity);
		_capacity = capacity;
		clear();
	} else {
		_factory.engine().log().error("Attempt to resize external buffer\n");
	}
}

void*
Buffer::port_data(PortType port_type, SampleCount offset)
{
	switch (port_type.id()) {
	case PortType::ID::CONTROL:
	case PortType::ID::CV:
	case PortType::ID::AUDIO:
		if (_type == _factory.uris().atom_Float) {
			return &get<LV2_Atom_Float>()->body;
		} else if (_type == _factory.uris().atom_Sound) {
			return (Sample*)_buf;
		}
		break;
	case PortType::ID::ATOM:
		if (_type != _factory.uris().atom_Sound) {
			return _buf;
		}
	default: break;
	}
	return NULL;
}

const void*
Buffer::port_data(PortType port_type, SampleCount offset) const
{
	return const_cast<void*>(
		const_cast<Buffer*>(this)->port_data(port_type, offset));
}

#ifdef __SSE__
/** Vector fabsf */
static inline __m128
mm_abs_ps(__m128 x)
{
	const __m128 sign_mask = _mm_set1_ps(-0.0f);  // -0.0f = 1 << 31
	return _mm_andnot_ps(sign_mask, x);
}
#endif

float
Buffer::peak(const Context& context) const
{
#ifdef __SSE__
	const __m128* const vbuf    = (const __m128* const)samples();
	__m128              vpeak   = mm_abs_ps(vbuf[0]);
	const SampleCount   nblocks = context.nframes() / 4;

	// First, find the vector absolute max of the buffer
	for (SampleCount i = 1; i < nblocks; ++i) {
		vpeak = _mm_max_ps(vpeak, mm_abs_ps(vbuf[i]));
	}

	// Now we need the single max of vpeak
	// vpeak = ABCD
	// tmp   = CDAB
	__m128 tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(2, 3, 0, 1));

	// vpeak = MAX(A,C) MAX(B,D) MAX(C,A) MAX(D,B)
	vpeak = _mm_max_ps(vpeak, tmp);

	// tmp = BADC of the new vpeak
	// tmp = MAX(B,D) MAX(A,C) MAX(D,B) MAX(C,A)
	tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(1, 0, 3, 2));

	// vpeak = MAX(MAX(A,C), MAX(B,D)), ...
	vpeak = _mm_max_ps(vpeak, tmp);

	// peak = vpeak[0]
	float peak;
	_mm_store_ss(&peak, vpeak);

	return peak;
#else
	const Sample* const buf = samples();
	float peak = 0.0f;
	for (SampleCount i = 0; i < context.nframes(); ++i) {
		peak = fmaxf(peak, fabsf(buf[i]));
	}
	return peak;
#endif
}

void
Buffer::prepare_write(Context& context)
{
	if (_type == _factory.uris().atom_Sequence) {
		LV2_Atom* atom = get<LV2_Atom>();

		atom->type    = (LV2_URID)_factory.uris().atom_Sequence;
		atom->size    = sizeof(LV2_Atom_Sequence_Body);
		_latest_event = 0;
	}
}

void
Buffer::prepare_output_write(Context& context)
{
	if (_type == _factory.uris().atom_Sequence) {
		LV2_Atom* atom = get<LV2_Atom>();

		atom->type    = (LV2_URID)_factory.uris().atom_Chunk;
		atom->size    = _capacity - sizeof(LV2_Atom);
		_latest_event = 0;
	}
}

bool
Buffer::append_event(int64_t        frames,
                     uint32_t       size,
                     uint32_t       type,
                     const uint8_t* data)
{
	assert(frames >= _latest_event);

	LV2_Atom* atom = get<LV2_Atom>();
	if (atom->type == _factory.uris().atom_Chunk) {
		// Chunk initialized with prepare_output_write(), clear
		clear();
	}

	if (sizeof(LV2_Atom) + atom->size + lv2_atom_pad_size(size) > _capacity) {
		return false;
	}

	LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)atom;
	LV2_Atom_Event*    ev  = (LV2_Atom_Event*)(
		(uint8_t*)seq + lv2_atom_total_size(&seq->atom));

	ev->time.frames = frames;
	ev->body.size   = size;
	ev->body.type   = type;
	memcpy(ev + 1, data, size);

	atom->size += sizeof(LV2_Atom_Event) + lv2_atom_pad_size(size);

	_latest_event = frames;

	return true;
}

SampleCount
Buffer::next_value_offset(SampleCount offset, SampleCount end) const
{
	SampleCount              earliest = end;
	const LV2_Atom_Sequence* seq      = get<const LV2_Atom_Sequence>();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames >  offset   &&
		    ev->time.frames <  earliest &&
		    ev->body.type   == _value_type) {
			earliest = ev->time.frames;
			break;
		}
	}
	return earliest;
}

const LV2_Atom*
Buffer::value() const
{
	return _value_buffer ? _value_buffer->get<const LV2_Atom>() : NULL;
}

LV2_Atom*
Buffer::value()
{
	return _value_buffer ? _value_buffer->get<LV2_Atom>() : NULL;
}

void
Buffer::update_value_buffer(SampleCount offset)
{
	if (!_value_buffer) {
		return;
	}

	LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>();
	LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
		if (ev->time.frames <= offset && ev->body.type == _value_type) {
			memcpy(_value_buffer->get<LV2_Atom>(),
			       &ev->body,
			       lv2_atom_total_size(&ev->body));
			break;
		}
	}
}

void
intrusive_ptr_add_ref(Buffer* b)
{
	b->ref();
}

void
intrusive_ptr_release(Buffer* b)
{
	b->deref();
}

} // namespace Server
} // namespace Ingen
<|endoftext|>


<commit_before>#include "Server.hpp"

#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

#include <iostream>
#include <cstdlib>  // for random
#include <cmath>    // for sin,cos in the server
#include <list>

#include "../functions.h"

namespace ap {
namespace server {

using namespace std;

Server::Server(unsigned int port) :
  mPort(port)
{
    std::cout << "Created a server."<< std::endl;
}

void Server::start() {
    std::cout << "Started the server."<<std::endl;
    netdata = new net::NetData(net::NetData::SERVER, mPort);

    std::cout << "Serving "<< ap::net::netobjectprototypes().size()<<" objects."<<std::endl;

    newticks = nextupdate = getTicks();

    while (1) {             // Server main loop
        netdata->receiveChanges();
        oldticks = newticks; newticks = getTicks(); dt = float(newticks - oldticks) * 0.001;

        p = netdata->netobjects.begin();
        while (p != netdata->netobjects.end()) {
            iUser = netdata->users.find(p->second->uid);

/*            NetGameObject *ptrObj = dynamic_cast<NetGameObject *>(p->second);
            if (ptrObj) // if the dynamic cast succeeded, then p->second is a NetGameObject!
            {
                if (iUser != netdata->users.end()) // That means the object's owner is still logged in
                {
                    list<NetObject *> *lObjs = ptrObj->control(iUser->second, true);
                    if (lObjs != NULL) {            // if control returns objects, insert them to netobjects map
                        list<NetObject *>::iterator ilObj;
                        for (ilObj = lObjs->begin(); ilObj != lObjs->end(); ilObj++)
                        {
                            int newid = netdata->getUniqueObjectID();
                            (*ilObj)->id = newid;
                            netdata->netobjects.insert(make_pair(newid, *ilObj));
                        }
                    }
                }

                pProj = netdata->netobjects.begin();
                while (pProj != netdata->netobjects.end())
                {
                    Projectile *proj = dynamic_cast<Projectile *>(pProj->second);
                    if ((proj) && (proj->loc.collision(ptrObj->loc))) {
                        ptrObj->loc.x = 500 + (rand()%4)*1000;
                        ptrObj->loc.y = 500 + (rand()%4)*1000;
                        ptrObj->loc.xvel = ptrObj->loc.yvel = 0.0;
                        netdata->delObject(pProj->second->id);  // This erases a pair from netobjects map,
                    }                                           // (hope this doesn't do harm),
                    pProj++;                                    // and this still uses an iterator to the one.
                }
            }
*/
            if (p->second->advance(dt) == -1) {
                delete p->second;
                netdata->netobjects.erase(p);
            }

            p++;
        }

        if (newticks >= nextupdate) {
            netdata->sendChanges();
            nextupdate = newticks + 40; // 40 ms between updates, that's 25 network fps.
        }                               // Seems to me that up to 100 is still okay!
        SDL_Delay(1);       // sleep even a little bit so that dt is never 0

        while (netdata->pollEvent(&event)) {
            switch (event.type)
            {
             case net::NetData::EVENT_CONNECT:
             {
                cout << "Received a connection from "<< uint2ipv4(netdata->users[event.uid].peer->address.host) <<", uid "<<event.uid;
                cout << ". We now have "<<netdata->users.size()<<" users."<<endl;

                int newid = netdata->getUniqueObjectID();   // and an unused object id
//                netdata->netobjects.insert(make_pair(newid, new NetGameObject(newid, event.uid)));

                netdata->setAvatarID(event.uid, newid);
                break;

             }
             case net::NetData::EVENT_DISCONNECT:
                cout << "Client "<<event.uid<<" disconnected."<<endl;
                break;
             default:
                break;
            }
        }

    } // Main loop
} // void Server::start()

} // namespace server
} // namespace ap
<commit_msg>Removed dependency on SDL by changing [SDL.Delay(1) >>> usleep(1)]<commit_after>#include "Server.hpp"

#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif

#include <iostream>
#include <cstdlib>  // for random
#include <cmath>    // for sin,cos in the server
#include <list>

#include "../functions.h"

namespace ap {
namespace server {

using namespace std;

Server::Server(unsigned int port) :
  mPort(port)
{
    std::cout << "Created a server."<< std::endl;
}

void Server::start() {
    std::cout << "Started the server."<<std::endl;
    netdata = new net::NetData(net::NetData::SERVER, mPort);

    std::cout << "Serving "<< ap::net::netobjectprototypes().size()<<" objects."<<std::endl;

    newticks = nextupdate = getTicks();

    while (1) {             // Server main loop
        netdata->receiveChanges();
        oldticks = newticks; newticks = getTicks(); dt = float(newticks - oldticks) * 0.001;

        p = netdata->netobjects.begin();
        while (p != netdata->netobjects.end()) {
            iUser = netdata->users.find(p->second->uid);

/*            NetGameObject *ptrObj = dynamic_cast<NetGameObject *>(p->second);
            if (ptrObj) // if the dynamic cast succeeded, then p->second is a NetGameObject!
            {
                if (iUser != netdata->users.end()) // That means the object's owner is still logged in
                {
                    list<NetObject *> *lObjs = ptrObj->control(iUser->second, true);
                    if (lObjs != NULL) {            // if control returns objects, insert them to netobjects map
                        list<NetObject *>::iterator ilObj;
                        for (ilObj = lObjs->begin(); ilObj != lObjs->end(); ilObj++)
                        {
                            int newid = netdata->getUniqueObjectID();
                            (*ilObj)->id = newid;
                            netdata->netobjects.insert(make_pair(newid, *ilObj));
                        }
                    }
                }

                pProj = netdata->netobjects.begin();
                while (pProj != netdata->netobjects.end())
                {
                    Projectile *proj = dynamic_cast<Projectile *>(pProj->second);
                    if ((proj) && (proj->loc.collision(ptrObj->loc))) {
                        ptrObj->loc.x = 500 + (rand()%4)*1000;
                        ptrObj->loc.y = 500 + (rand()%4)*1000;
                        ptrObj->loc.xvel = ptrObj->loc.yvel = 0.0;
                        netdata->delObject(pProj->second->id);  // This erases a pair from netobjects map,
                    }                                           // (hope this doesn't do harm),
                    pProj++;                                    // and this still uses an iterator to the one.
                }
            }
*/
            if (p->second->advance(dt) == -1) {
                delete p->second;
                netdata->netobjects.erase(p);
            }

            p++;
        }

        if (newticks >= nextupdate) {
            netdata->sendChanges();
            nextupdate = newticks + 40; // 40 ms between updates, that's 25 network fps.
        }                               // Seems to me that up to 100 is still okay!
        usleep(1);       // sleep even a little bit so that dt is never 0

        while (netdata->pollEvent(&event)) {
            switch (event.type)
            {
             case net::NetData::EVENT_CONNECT:
             {
                cout << "Received a connection from "<< uint2ipv4(netdata->users[event.uid].peer->address.host) <<", uid "<<event.uid;
                cout << ". We now have "<<netdata->users.size()<<" users."<<endl;

                int newid = netdata->getUniqueObjectID();   // and an unused object id
//                netdata->netobjects.insert(make_pair(newid, new NetGameObject(newid, event.uid)));

                netdata->setAvatarID(event.uid, newid);
                break;

             }
             case net::NetData::EVENT_DISCONNECT:
                cout << "Client "<<event.uid<<" disconnected."<<endl;
                break;
             default:
                break;
            }
        }

    } // Main loop
} // void Server::start()

} // namespace server
} // namespace ap
<|endoftext|>


<commit_before>
/*
    pbrt source code is Copyright(c) 1998-2016
                        Matt Pharr, Greg Humphreys, and Wenzel Jakob.

    This file is part of pbrt.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

    - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    - Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 */

// shapes/sphere.cpp*
#include "shapes/sphere.h"
#include "sampling.h"
#include "paramset.h"
#include "efloat.h"
#include "stats.h"

// Sphere Method Definitions
Bounds3f Sphere::ObjectBound() const {
    return Bounds3f(Point3f(-radius, -radius, zMin),
                    Point3f(radius, radius, zMax));
}

bool Sphere::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect,
                       bool testAlphaTexture) const {
    ProfilePhase p(Prof::ShapeIntersect);
    Float phi;
    Point3f pHit;
    // Transform _Ray_ to object space
    Vector3f oErr, dErr;
    Ray ray = (*WorldToObject)(r, &oErr, &dErr);

    // Compute quadratic sphere coefficients

    // Initialize _EFloat_ ray coordinate values
    EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z);
    EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z);
    EFloat a = dx * dx + dy * dy + dz * dz;
    EFloat b = 2 * (dx * ox + dy * oy + dz * oz);
    EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius);

    // Solve quadratic equation for _t_ values
    EFloat t0, t1;
    if (!Quadratic(a, b, c, &t0, &t1)) return false;

    // Check quadric shape _t0_ and _t1_ for nearest intersection
    if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
    EFloat tShapeHit = t0;
    if (tShapeHit.LowerBound() <= 0) {
        tShapeHit = t1;
        if (tShapeHit.UpperBound() > ray.tMax) return false;
    }

    // Compute sphere hit position and $\phi$
    pHit = ray((Float)tShapeHit);

    // Refine sphere intersection point
    pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
    if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
    phi = std::atan2(pHit.y, pHit.x);
    if (phi < 0) phi += 2 * Pi;

    // Test sphere intersection against clipping parameters
    if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) ||
        phi > phiMax) {
        if (tShapeHit == t1) return false;
        if (t1.UpperBound() > ray.tMax) return false;
        tShapeHit = t1;
        // Compute sphere hit position and $\phi$
        pHit = ray((Float)tShapeHit);

        // Refine sphere intersection point
        pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
        if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
        phi = std::atan2(pHit.y, pHit.x);
        if (phi < 0) phi += 2 * Pi;
        if ((zMin > -radius && pHit.z < zMin) ||
            (zMax < radius && pHit.z > zMax) || phi > phiMax)
            return false;
    }

    // Find parametric representation of sphere hit
    Float u = phi / phiMax;
    Float theta = std::acos(Clamp(pHit.z / radius, -1, 1));
    Float v = (theta - thetaMin) / (thetaMax - thetaMin);

    // Compute sphere $\dpdu$ and $\dpdv$
    Float zRadius = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y);
    Float invZRadius = 1 / zRadius;
    Float cosPhi = pHit.x * invZRadius;
    Float sinPhi = pHit.y * invZRadius;
    Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0);
    Vector3f dpdv =
        (thetaMax - thetaMin) *
        Vector3f(pHit.z * cosPhi, pHit.z * sinPhi, -radius * std::sin(theta));

    // Compute sphere $\dndu$ and $\dndv$
    Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0);
    Vector3f d2Pduv =
        (thetaMax - thetaMin) * pHit.z * phiMax * Vector3f(-sinPhi, cosPhi, 0.);
    Vector3f d2Pdvv = -(thetaMax - thetaMin) * (thetaMax - thetaMin) *
                      Vector3f(pHit.x, pHit.y, pHit.z);

    // Compute coefficients for fundamental forms
    Float E = Dot(dpdu, dpdu);
    Float F = Dot(dpdu, dpdv);
    Float G = Dot(dpdv, dpdv);
    Vector3f N = Normalize(Cross(dpdu, dpdv));
    Float e = Dot(N, d2Pduu);
    Float f = Dot(N, d2Pduv);
    Float g = Dot(N, d2Pdvv);

    // Compute $\dndu$ and $\dndv$ from fundamental form coefficients
    Float invEGF2 = 1 / (E * G - F * F);
    Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu +
                             (e * F - f * E) * invEGF2 * dpdv);
    Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu +
                             (f * F - g * E) * invEGF2 * dpdv);

    // Compute error bounds for sphere intersection
    Vector3f pError = gamma(5) * Abs((Vector3f)pHit);

    // Initialize _SurfaceInteraction_ from parametric information
    *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v),
                                                 -ray.d, dpdu, dpdv, dndu, dndv,
                                                 ray.time, this));

    // Update _tHit_ for quadric intersection
    *tHit = (Float)tShapeHit;
    return true;
}

bool Sphere::IntersectP(const Ray &r, bool testAlphaTexture) const {
    ProfilePhase p(Prof::ShapeIntersectP);
    Float phi;
    Point3f pHit;
    // Transform _Ray_ to object space
    Vector3f oErr, dErr;
    Ray ray = (*WorldToObject)(r, &oErr, &dErr);

    // Compute quadratic sphere coefficients

    // Initialize _EFloat_ ray coordinate values
    EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z);
    EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z);
    EFloat a = dx * dx + dy * dy + dz * dz;
    EFloat b = 2 * (dx * ox + dy * oy + dz * oz);
    EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius);

    // Solve quadratic equation for _t_ values
    EFloat t0, t1;
    if (!Quadratic(a, b, c, &t0, &t1)) return false;

    // Check quadric shape _t0_ and _t1_ for nearest intersection
    if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
    EFloat tShapeHit = t0;
    if (tShapeHit.LowerBound() <= 0) {
        tShapeHit = t1;
        if (tShapeHit.UpperBound() > ray.tMax) return false;
    }

    // Compute sphere hit position and $\phi$
    pHit = ray((Float)tShapeHit);

    // Refine sphere intersection point
    pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
    if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
    phi = std::atan2(pHit.y, pHit.x);
    if (phi < 0) phi += 2 * Pi;

    // Test sphere intersection against clipping parameters
    if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) ||
        phi > phiMax) {
        if (tShapeHit == t1) return false;
        if (t1.UpperBound() > ray.tMax) return false;
        tShapeHit = t1;
        // Compute sphere hit position and $\phi$
        pHit = ray((Float)tShapeHit);

        // Refine sphere intersection point
        pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
        if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
        phi = std::atan2(pHit.y, pHit.x);
        if (phi < 0) phi += 2 * Pi;
        if ((zMin > -radius && pHit.z < zMin) ||
            (zMax < radius && pHit.z > zMax) || phi > phiMax)
            return false;
    }
    return true;
}

Float Sphere::Area() const { return phiMax * radius * (zMax - zMin); }

Interaction Sphere::Sample(const Point2f &u) const {
    Point3f pObj = Point3f(0, 0, 0) + radius * UniformSampleSphere(u);
    Interaction it;
    it.n = Normalize((*ObjectToWorld)(Normal3f(pObj.x, pObj.y, pObj.z)));
    if (reverseOrientation) it.n *= -1;
    // Reproject _pObj_ to sphere surface and compute _pObjError_
    pObj *= radius / Distance(pObj, Point3f(0, 0, 0));
    Vector3f pObjError = gamma(5) * Abs((Vector3f)pObj);
    it.p = (*ObjectToWorld)(pObj, pObjError, &it.pError);
    return it;
}

Interaction Sphere::Sample(const Interaction &ref, const Point2f &u) const {
    // Compute coordinate system for sphere sampling
    Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0));
    Vector3f wc = Normalize(pCenter - ref.p);
    Vector3f wcX, wcY;
    CoordinateSystem(wc, &wcX, &wcY);

    // Sample uniformly on sphere if $\pt{}$ is inside it
    Point3f pOrigin =
        OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p);
    if (DistanceSquared(pOrigin, pCenter) <= radius * radius) return Sample(u);

    // Sample sphere uniformly inside subtended cone

    // Compute $\theta$ and $\phi$ values for sample in cone
    Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter);
    Float cosThetaMax = std::sqrt(std::max((Float)0, 1 - sinThetaMax2));
    Float cosTheta = (1 - u[0]) + u[0] * cosThetaMax;
    Float sinTheta = std::sqrt(std::max((Float)0, 1 - cosTheta * cosTheta));
    Float phi = u[1] * 2 * Pi;

    // Compute angle $\alpha$ from center of sphere to sampled point on surface
    Float dc = Distance(ref.p, pCenter);
    Float ds = dc * cosTheta -
               std::sqrt(std::max(
                   (Float)0, radius * radius - dc * dc * sinTheta * sinTheta));
    Float cosAlpha = (dc * dc + radius * radius - ds * ds) / (2 * dc * radius);
    Float sinAlpha = std::sqrt(std::max((Float)0, 1 - cosAlpha * cosAlpha));

    // Compute surface normal and sampled point on sphere
    Vector3f nObj =
        SphericalDirection(sinAlpha, cosAlpha, phi, -wcX, -wcY, -wc);
    Point3f pObj = radius * Point3f(nObj.x, nObj.y, nObj.z);

    // Return _Interaction_ for sampled point on sphere
    Interaction it;

    // Reproject _pObj_ to sphere surface and compute _pObjError_
    pObj *= radius / Distance(pObj, Point3f(0, 0, 0));
    Vector3f pObjError = gamma(5) * Abs((Vector3f)pObj);
    it.p = (*ObjectToWorld)(pObj, pObjError, &it.pError);
    it.n = (*ObjectToWorld)(Normal3f(nObj));
    if (reverseOrientation) it.n *= -1;
    return it;
}

Float Sphere::Pdf(const Interaction &ref, const Vector3f &wi) const {
    Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0));
    // Return uniform PDF if point is inside sphere
    Point3f pOrigin =
        OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p);
    if (DistanceSquared(pOrigin, pCenter) <= radius * radius)
        return Shape::Pdf(ref, wi);

    // Compute general sphere PDF
    Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter);
    Float cosThetaMax = std::sqrt(std::max((Float)0, 1 - sinThetaMax2));
    return UniformConePdf(cosThetaMax);
}

std::shared_ptr<Shape> CreateSphereShape(const Transform *o2w,
                                         const Transform *w2o,
                                         bool reverseOrientation,
                                         const ParamSet &params) {
    Float radius = params.FindOneFloat("radius", 1.f);
    Float zmin = params.FindOneFloat("zmin", -radius);
    Float zmax = params.FindOneFloat("zmax", radius);
    Float phimax = params.FindOneFloat("phimax", 360.f);
    return std::make_shared<Sphere>(o2w, w2o, reverseOrientation, radius, zmin,
                                    zmax, phimax);
}
<commit_msg>Fix crash due to normalize of (0,0,0) vector.<commit_after>
/*
    pbrt source code is Copyright(c) 1998-2016
                        Matt Pharr, Greg Humphreys, and Wenzel Jakob.

    This file is part of pbrt.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

    - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    - Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 */

// shapes/sphere.cpp*
#include "shapes/sphere.h"
#include "sampling.h"
#include "paramset.h"
#include "efloat.h"
#include "stats.h"

// Sphere Method Definitions
Bounds3f Sphere::ObjectBound() const {
    return Bounds3f(Point3f(-radius, -radius, zMin),
                    Point3f(radius, radius, zMax));
}

bool Sphere::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect,
                       bool testAlphaTexture) const {
    ProfilePhase p(Prof::ShapeIntersect);
    Float phi;
    Point3f pHit;
    // Transform _Ray_ to object space
    Vector3f oErr, dErr;
    Ray ray = (*WorldToObject)(r, &oErr, &dErr);

    // Compute quadratic sphere coefficients

    // Initialize _EFloat_ ray coordinate values
    EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z);
    EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z);
    EFloat a = dx * dx + dy * dy + dz * dz;
    EFloat b = 2 * (dx * ox + dy * oy + dz * oz);
    EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius);

    // Solve quadratic equation for _t_ values
    EFloat t0, t1;
    if (!Quadratic(a, b, c, &t0, &t1)) return false;

    // Check quadric shape _t0_ and _t1_ for nearest intersection
    if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
    EFloat tShapeHit = t0;
    if (tShapeHit.LowerBound() <= 0) {
        tShapeHit = t1;
        if (tShapeHit.UpperBound() > ray.tMax) return false;
    }

    // Compute sphere hit position and $\phi$
    pHit = ray((Float)tShapeHit);

    // Refine sphere intersection point
    pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
    if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
    phi = std::atan2(pHit.y, pHit.x);
    if (phi < 0) phi += 2 * Pi;

    // Test sphere intersection against clipping parameters
    if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) ||
        phi > phiMax) {
        if (tShapeHit == t1) return false;
        if (t1.UpperBound() > ray.tMax) return false;
        tShapeHit = t1;
        // Compute sphere hit position and $\phi$
        pHit = ray((Float)tShapeHit);

        // Refine sphere intersection point
        pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
        if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
        phi = std::atan2(pHit.y, pHit.x);
        if (phi < 0) phi += 2 * Pi;
        if ((zMin > -radius && pHit.z < zMin) ||
            (zMax < radius && pHit.z > zMax) || phi > phiMax)
            return false;
    }

    // Find parametric representation of sphere hit
    Float u = phi / phiMax;
    Float theta = std::acos(Clamp(pHit.z / radius, -1, 1));
    Float v = (theta - thetaMin) / (thetaMax - thetaMin);

    // Compute sphere $\dpdu$ and $\dpdv$
    Float zRadius = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y);
    Float invZRadius = 1 / zRadius;
    Float cosPhi = pHit.x * invZRadius;
    Float sinPhi = pHit.y * invZRadius;
    Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0);
    Vector3f dpdv =
        (thetaMax - thetaMin) *
        Vector3f(pHit.z * cosPhi, pHit.z * sinPhi, -radius * std::sin(theta));

    // Compute sphere $\dndu$ and $\dndv$
    Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0);
    Vector3f d2Pduv =
        (thetaMax - thetaMin) * pHit.z * phiMax * Vector3f(-sinPhi, cosPhi, 0.);
    Vector3f d2Pdvv = -(thetaMax - thetaMin) * (thetaMax - thetaMin) *
                      Vector3f(pHit.x, pHit.y, pHit.z);

    // Compute coefficients for fundamental forms
    Float E = Dot(dpdu, dpdu);
    Float F = Dot(dpdu, dpdv);
    Float G = Dot(dpdv, dpdv);
    Vector3f N = Normalize(Cross(dpdu, dpdv));
    Float e = Dot(N, d2Pduu);
    Float f = Dot(N, d2Pduv);
    Float g = Dot(N, d2Pdvv);

    // Compute $\dndu$ and $\dndv$ from fundamental form coefficients
    Float invEGF2 = 1 / (E * G - F * F);
    Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu +
                             (e * F - f * E) * invEGF2 * dpdv);
    Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu +
                             (f * F - g * E) * invEGF2 * dpdv);

    // Compute error bounds for sphere intersection
    Vector3f pError = gamma(5) * Abs((Vector3f)pHit);

    // Initialize _SurfaceInteraction_ from parametric information
    *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v),
                                                 -ray.d, dpdu, dpdv, dndu, dndv,
                                                 ray.time, this));

    // Update _tHit_ for quadric intersection
    *tHit = (Float)tShapeHit;
    return true;
}

bool Sphere::IntersectP(const Ray &r, bool testAlphaTexture) const {
    ProfilePhase p(Prof::ShapeIntersectP);
    Float phi;
    Point3f pHit;
    // Transform _Ray_ to object space
    Vector3f oErr, dErr;
    Ray ray = (*WorldToObject)(r, &oErr, &dErr);

    // Compute quadratic sphere coefficients

    // Initialize _EFloat_ ray coordinate values
    EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z);
    EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z);
    EFloat a = dx * dx + dy * dy + dz * dz;
    EFloat b = 2 * (dx * ox + dy * oy + dz * oz);
    EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius);

    // Solve quadratic equation for _t_ values
    EFloat t0, t1;
    if (!Quadratic(a, b, c, &t0, &t1)) return false;

    // Check quadric shape _t0_ and _t1_ for nearest intersection
    if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
    EFloat tShapeHit = t0;
    if (tShapeHit.LowerBound() <= 0) {
        tShapeHit = t1;
        if (tShapeHit.UpperBound() > ray.tMax) return false;
    }

    // Compute sphere hit position and $\phi$
    pHit = ray((Float)tShapeHit);

    // Refine sphere intersection point
    pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
    if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
    phi = std::atan2(pHit.y, pHit.x);
    if (phi < 0) phi += 2 * Pi;

    // Test sphere intersection against clipping parameters
    if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) ||
        phi > phiMax) {
        if (tShapeHit == t1) return false;
        if (t1.UpperBound() > ray.tMax) return false;
        tShapeHit = t1;
        // Compute sphere hit position and $\phi$
        pHit = ray((Float)tShapeHit);

        // Refine sphere intersection point
        pHit *= radius / Distance(pHit, Point3f(0, 0, 0));
        if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius;
        phi = std::atan2(pHit.y, pHit.x);
        if (phi < 0) phi += 2 * Pi;
        if ((zMin > -radius && pHit.z < zMin) ||
            (zMax < radius && pHit.z > zMax) || phi > phiMax)
            return false;
    }
    return true;
}

Float Sphere::Area() const { return phiMax * radius * (zMax - zMin); }

Interaction Sphere::Sample(const Point2f &u) const {
    Point3f pObj = Point3f(0, 0, 0) + radius * UniformSampleSphere(u);
    Interaction it;
    it.n = Normalize((*ObjectToWorld)(Normal3f(pObj.x, pObj.y, pObj.z)));
    if (reverseOrientation) it.n *= -1;
    // Reproject _pObj_ to sphere surface and compute _pObjError_
    pObj *= radius / Distance(pObj, Point3f(0, 0, 0));
    Vector3f pObjError = gamma(5) * Abs((Vector3f)pObj);
    it.p = (*ObjectToWorld)(pObj, pObjError, &it.pError);
    return it;
}

Interaction Sphere::Sample(const Interaction &ref, const Point2f &u) const {
    Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0));

    // Sample uniformly on sphere if $\pt{}$ is inside it
    Point3f pOrigin =
        OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p);
    if (DistanceSquared(pOrigin, pCenter) <= radius * radius) return Sample(u);

    // Compute coordinate system for sphere sampling
    Vector3f wc = Normalize(pCenter - ref.p);
    Vector3f wcX, wcY;
    CoordinateSystem(wc, &wcX, &wcY);

    // Sample sphere uniformly inside subtended cone

    // Compute $\theta$ and $\phi$ values for sample in cone
    Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter);
    Float cosThetaMax = std::sqrt(std::max((Float)0, 1 - sinThetaMax2));
    Float cosTheta = (1 - u[0]) + u[0] * cosThetaMax;
    Float sinTheta = std::sqrt(std::max((Float)0, 1 - cosTheta * cosTheta));
    Float phi = u[1] * 2 * Pi;

    // Compute angle $\alpha$ from center of sphere to sampled point on surface
    Float dc = Distance(ref.p, pCenter);
    Float ds = dc * cosTheta -
               std::sqrt(std::max(
                   (Float)0, radius * radius - dc * dc * sinTheta * sinTheta));
    Float cosAlpha = (dc * dc + radius * radius - ds * ds) / (2 * dc * radius);
    Float sinAlpha = std::sqrt(std::max((Float)0, 1 - cosAlpha * cosAlpha));

    // Compute surface normal and sampled point on sphere
    Vector3f nObj =
        SphericalDirection(sinAlpha, cosAlpha, phi, -wcX, -wcY, -wc);
    Point3f pObj = radius * Point3f(nObj.x, nObj.y, nObj.z);

    // Return _Interaction_ for sampled point on sphere
    Interaction it;

    // Reproject _pObj_ to sphere surface and compute _pObjError_
    pObj *= radius / Distance(pObj, Point3f(0, 0, 0));
    Vector3f pObjError = gamma(5) * Abs((Vector3f)pObj);
    it.p = (*ObjectToWorld)(pObj, pObjError, &it.pError);
    it.n = (*ObjectToWorld)(Normal3f(nObj));
    if (reverseOrientation) it.n *= -1;
    return it;
}

Float Sphere::Pdf(const Interaction &ref, const Vector3f &wi) const {
    Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0));
    // Return uniform PDF if point is inside sphere
    Point3f pOrigin =
        OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p);
    if (DistanceSquared(pOrigin, pCenter) <= radius * radius)
        return Shape::Pdf(ref, wi);

    // Compute general sphere PDF
    Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter);
    Float cosThetaMax = std::sqrt(std::max((Float)0, 1 - sinThetaMax2));
    return UniformConePdf(cosThetaMax);
}

std::shared_ptr<Shape> CreateSphereShape(const Transform *o2w,
                                         const Transform *w2o,
                                         bool reverseOrientation,
                                         const ParamSet &params) {
    Float radius = params.FindOneFloat("radius", 1.f);
    Float zmin = params.FindOneFloat("zmin", -radius);
    Float zmax = params.FindOneFloat("zmax", radius);
    Float phimax = params.FindOneFloat("phimax", 360.f);
    return std::make_shared<Sphere>(o2w, w2o, reverseOrientation, radius, zmin,
                                    zmax, phimax);
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <iostream>
#include "kernel/inductive/inductive.h"
#include "kernel/quotient/quotient.h"
#include "library/documentation.h"
#include "library/io_state.h"
#include "library/type_context.h"
#include "library/pp_options.h"
#include "library/protected.h"
#include "library/private.h"
#include "library/class.h"
#include "library/util.h"
#include "library/noncomputable.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/structure_cmd.h"

namespace lean {
static char const * get_decl_kind(environment const & env, name const & id) {
    declaration const & d = env.get(id);
    if (d.is_theorem()) return "Theorem";

    if (d.is_axiom() || d.is_constant_assumption()) {
        if (inductive::is_inductive_decl(env, id)) {
            if (is_structure(env, id) && is_class(env, id)) return "Class";
            if (is_structure(env, id) && !is_class(env, id))  return "Structure";
            if (is_class(env, id)) return "Inductive class";
            return "Inductive";
        }

        if (inductive::is_intro_rule(env, id)) return "Constructor";

        if (inductive::is_elim_rule(env, id)) return "Eliminator";

        if (is_quotient_decl(env, id)) return "Quotient type builtin";

        if (d.is_axiom()) return "Axiom";

        return "Constant";
    } else if (d.is_definition()) {
        if (is_instance(env, id)) return "Instance";
        if (is_class(env, id)) return "Class";
        return "Definition";
    }
    return "";
}

static name to_user_name(environment const & env, name const & n) {
    if (auto r = hidden_to_user_name(env, n))
        return *r;
    else
        return n;
}

static void add_string(format & r, char const * s) {
    r += format(s) + space();
}

static void print_constant(std::ostream & out, environment const & env, formatter const & fmt, char const * kind, declaration const & d) {
    options const & o = fmt.get_options();
    out << "```lean\n";
    format r;
    if (is_protected(env, d.get_name()))
        add_string(r, "protected");
    if (!d.is_trusted())
        add_string(r, "meta");
    if (d.is_definition() && is_marked_noncomputable(env, d.get_name()))
        add_string(r, "noncomputable");
    add_string(r, kind);
    name id = to_user_name(env, d.get_name());
    r += format(id);
    r += space() + colon() + space();
    r += nest(get_pp_indent(o), line() + fmt(d.get_type()));
    out << mk_pair(group(r), o);
    out << "\n";
    out << "```\n";
}

static void print_fields(std::ostream & out, environment const & env, formatter const & fmt, name const & S) {
    lean_assert(is_structure(env, S));
    options const & o = fmt.get_options();
    buffer<name> field_names;
    get_structure_fields(env, S, field_names);
    out << "```lean\n";
    for (name const & field_name : field_names) {
        declaration d = env.get(field_name);
        format r;
        r += format(d.get_name());
        r += space() + colon() + space();
        r += nest(get_pp_indent(o), line() + fmt(d.get_type()));
        out << mk_pair(group(r), fmt.get_options()) << "\n";
    }
    out << "```\n";
}

static void print_inductive(std::ostream & out, environment const & env, formatter const & fmt, name const & n) {
    options const & o = fmt.get_options();
    auto idecl = inductive::is_inductive_decl(env, n);
    lean_assert(idecl);
    out << "```lean\n";
    format r;
    if (is_structure(env, n)) {
        if (is_class(env, n))
            add_string(r, "class");
        else
            add_string(r, "structure");
    } else {
        if (is_class(env, n))
            add_string(r, "inductive class");
        else
            add_string(r, "inductive");
    }
    r += space() + format(n);
    r += space() + colon() + space();
    r += nest(get_pp_indent(o), line() + fmt(env.get(n).get_type()));
    out << mk_pair(group(r), o) << "\n";
    out << "```\n";
    if (is_structure(env, n)) {
        out << "**Fields:**\n";
        print_fields(out, env, fmt, n);
    } else {
        out << "**Constructors:**\n";
        buffer<name> constructors;
        get_intro_rule_names(env, n, constructors);
        out << "```lean\n";
        for (name const & c : constructors) {
            format r   = format(c);
            r += space() + colon() + space();
            r += nest(get_pp_indent(o), line() + fmt(env.get(c).get_type()));
            out << mk_pair(group(r), o) << "\n";
        }
        out << "```\n";
    }
}

static void print_id_info(std::ostream & out, environment const & env, formatter const & fmt, name const & id) {
    declaration const & d = env.get(id);
    if (d.is_theorem()) {
        print_constant(out, env, fmt, "theorem", d);
    } else if (d.is_axiom() || d.is_constant_assumption()) {
        if (inductive::is_inductive_decl(env, id)) {
            print_inductive(out, env, fmt, id);
        } else if (inductive::is_intro_rule(env, id)) {
            print_constant(out, env, fmt, "constructor", d);
                } else if (inductive::is_elim_rule(env, id)) {
            print_constant(out, env, fmt, "eliminator", d);
        } else if (is_quotient_decl(env, id)) {
            print_constant(out, env, fmt, "builtin-quotient-type-constant", d);
        } else if (d.is_axiom()) {
            print_constant(out, env, fmt, "axiom", d);
        } else {
            print_constant(out, env, fmt, "constant", d);
        }
    } else if (d.is_definition()) {
        if (is_instance(env, id))
            print_constant(out, env, fmt, "instance", d);
        else if (is_class(env, id))
            print_constant(out, env, fmt, "class", d);
        else
            print_constant(out, env, fmt, "def", d);
    }
}

static void consume_spaces(std::string const & doc, unsigned & i) {
    unsigned sz = doc.size();
    for (; i < sz; i++) {
        if (!isspace(doc[i]))
            break;
    }
}

static bool has_brief(std::string const & doc) {
    unsigned i = 0;
    consume_spaces(doc, i);
    return doc.substr(i, i+6) == "#brief";
}

static pair<std::string, std::string> split_brief_body(std::string const & doc) {
    lean_assert(has_brief(doc));
    unsigned i = 0;
    consume_spaces(doc, i);
    lean_assert(doc.substr(i, i+6) == "#brief");
    i += 6; // skip #brief
    unsigned sz = doc.size();
    consume_spaces(doc, i);
    std::string brief;
    std::string rest;
    bool found_non_space = false;
    for (; i < sz; i++) {
        if (!isspace(doc[i]))
            found_non_space = true;
        if (doc[i] == '\n') {
            if (!found_non_space) {
                // blank line... end of brief section
                i++;
                break;
            }
            brief += doc[i];
            found_non_space = false;
        } else {
            brief += doc[i];
        }
    }
    for (; i < sz; i++)
        rest += doc[i];
    return mk_pair(brief, rest);
}

static void print_block(std::ostream & out, std::string const & s) {
    if (s.empty())
        return;
    out << s;
    if (s.back() != '\n')
        out << "\n";
}

static void gen_decl_doc(std::ostream & out, environment const & env, formatter const & fmt, name const & decl_name, optional<std::string> const & brief) {
    out << "<a name=\"" << decl_name << "\">**" << get_decl_kind(env, decl_name) << "** " << decl_name << "\n";
    if (brief)
        print_block(out, *brief);
    print_id_info(out, env, fmt, decl_name);
}

void gen_doc(environment const & env, options const & _opts, std::ostream & out) {
    type_context ctx(env);
    options opts     = _opts.update_if_undef(name{"pp", "width"}, 100);
    auto fmt_factory = lean::mk_pretty_formatter_factory();
    auto fmt         = fmt_factory(env, opts, ctx);
    buffer<doc_entry> entries;
    get_module_doc_strings(env, entries);
    for (doc_entry const & entry : entries) {
        if (auto decl_name = entry.get_decl_name()) {
            if (has_brief(entry.get_doc())) {
                std::string brief, body;
                std::tie(brief, body) = split_brief_body(entry.get_doc());
                gen_decl_doc(out, env, fmt, *decl_name, optional<std::string>(brief));
                print_block(out, body);
            } else {
                gen_decl_doc(out, env, fmt, *decl_name, optional<std::string>());
                print_block(out, entry.get_doc());
            }
        } else {
            print_block(out, entry.get_doc());
        }
        out << "\n";
    }
}
}
<commit_msg>chore(shell/leandoc): fix style<commit_after>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <string>
#include <iostream>
#include "kernel/inductive/inductive.h"
#include "kernel/quotient/quotient.h"
#include "library/documentation.h"
#include "library/io_state.h"
#include "library/type_context.h"
#include "library/pp_options.h"
#include "library/protected.h"
#include "library/private.h"
#include "library/class.h"
#include "library/util.h"
#include "library/noncomputable.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/structure_cmd.h"

namespace lean {
static char const * get_decl_kind(environment const & env, name const & id) {
    declaration const & d = env.get(id);
    if (d.is_theorem()) return "Theorem";

    if (d.is_axiom() || d.is_constant_assumption()) {
        if (inductive::is_inductive_decl(env, id)) {
            if (is_structure(env, id) && is_class(env, id)) return "Class";
            if (is_structure(env, id) && !is_class(env, id))  return "Structure";
            if (is_class(env, id)) return "Inductive class";
            return "Inductive";
        }

        if (inductive::is_intro_rule(env, id)) return "Constructor";

        if (inductive::is_elim_rule(env, id)) return "Eliminator";

        if (is_quotient_decl(env, id)) return "Quotient type builtin";

        if (d.is_axiom()) return "Axiom";

        return "Constant";
    } else if (d.is_definition()) {
        if (is_instance(env, id)) return "Instance";
        if (is_class(env, id)) return "Class";
        return "Definition";
    }
    return "";
}

static name to_user_name(environment const & env, name const & n) {
    if (auto r = hidden_to_user_name(env, n))
        return *r;
    else
        return n;
}

static void add_string(format & r, char const * s) {
    r += format(s) + space();
}

static void print_constant(std::ostream & out, environment const & env, formatter const & fmt, char const * kind, declaration const & d) {
    options const & o = fmt.get_options();
    out << "```lean\n";
    format r;
    if (is_protected(env, d.get_name()))
        add_string(r, "protected");
    if (!d.is_trusted())
        add_string(r, "meta");
    if (d.is_definition() && is_marked_noncomputable(env, d.get_name()))
        add_string(r, "noncomputable");
    add_string(r, kind);
    name id = to_user_name(env, d.get_name());
    r += format(id);
    r += space() + colon() + space();
    r += nest(get_pp_indent(o), line() + fmt(d.get_type()));
    out << mk_pair(group(r), o);
    out << "\n";
    out << "```\n";
}

static void print_fields(std::ostream & out, environment const & env, formatter const & fmt, name const & S) {
    lean_assert(is_structure(env, S));
    options const & o = fmt.get_options();
    buffer<name> field_names;
    get_structure_fields(env, S, field_names);
    out << "```lean\n";
    for (name const & field_name : field_names) {
        declaration d = env.get(field_name);
        format r;
        r += format(d.get_name());
        r += space() + colon() + space();
        r += nest(get_pp_indent(o), line() + fmt(d.get_type()));
        out << mk_pair(group(r), fmt.get_options()) << "\n";
    }
    out << "```\n";
}

static void print_inductive(std::ostream & out, environment const & env, formatter const & fmt, name const & n) {
    options const & o = fmt.get_options();
    auto idecl = inductive::is_inductive_decl(env, n);
    lean_assert(idecl);
    out << "```lean\n";
    format r;
    if (is_structure(env, n)) {
        if (is_class(env, n))
            add_string(r, "class");
        else
            add_string(r, "structure");
    } else {
        if (is_class(env, n))
            add_string(r, "inductive class");
        else
            add_string(r, "inductive");
    }
    r += space() + format(n);
    r += space() + colon() + space();
    r += nest(get_pp_indent(o), line() + fmt(env.get(n).get_type()));
    out << mk_pair(group(r), o) << "\n";
    out << "```\n";
    if (is_structure(env, n)) {
        out << "**Fields:**\n";
        print_fields(out, env, fmt, n);
    } else {
        out << "**Constructors:**\n";
        buffer<name> constructors;
        get_intro_rule_names(env, n, constructors);
        out << "```lean\n";
        for (name const & c : constructors) {
            format r   = format(c);
            r += space() + colon() + space();
            r += nest(get_pp_indent(o), line() + fmt(env.get(c).get_type()));
            out << mk_pair(group(r), o) << "\n";
        }
        out << "```\n";
    }
}

static void print_id_info(std::ostream & out, environment const & env, formatter const & fmt, name const & id) {
    declaration const & d = env.get(id);
    if (d.is_theorem()) {
        print_constant(out, env, fmt, "theorem", d);
    } else if (d.is_axiom() || d.is_constant_assumption()) {
        if (inductive::is_inductive_decl(env, id)) {
            print_inductive(out, env, fmt, id);
        } else if (inductive::is_intro_rule(env, id)) {
            print_constant(out, env, fmt, "constructor", d);
                } else if (inductive::is_elim_rule(env, id)) {
            print_constant(out, env, fmt, "eliminator", d);
        } else if (is_quotient_decl(env, id)) {
            print_constant(out, env, fmt, "builtin-quotient-type-constant", d);
        } else if (d.is_axiom()) {
            print_constant(out, env, fmt, "axiom", d);
        } else {
            print_constant(out, env, fmt, "constant", d);
        }
    } else if (d.is_definition()) {
        if (is_instance(env, id))
            print_constant(out, env, fmt, "instance", d);
        else if (is_class(env, id))
            print_constant(out, env, fmt, "class", d);
        else
            print_constant(out, env, fmt, "def", d);
    }
}

static void consume_spaces(std::string const & doc, unsigned & i) {
    unsigned sz = doc.size();
    for (; i < sz; i++) {
        if (!isspace(doc[i]))
            break;
    }
}

static bool has_brief(std::string const & doc) {
    unsigned i = 0;
    consume_spaces(doc, i);
    return doc.substr(i, i+6) == "#brief";
}

static pair<std::string, std::string> split_brief_body(std::string const & doc) {
    lean_assert(has_brief(doc));
    unsigned i = 0;
    consume_spaces(doc, i);
    lean_assert(doc.substr(i, i+6) == "#brief");
    i += 6; // skip #brief
    unsigned sz = doc.size();
    consume_spaces(doc, i);
    std::string brief;
    std::string rest;
    bool found_non_space = false;
    for (; i < sz; i++) {
        if (!isspace(doc[i]))
            found_non_space = true;
        if (doc[i] == '\n') {
            if (!found_non_space) {
                // blank line... end of brief section
                i++;
                break;
            }
            brief += doc[i];
            found_non_space = false;
        } else {
            brief += doc[i];
        }
    }
    for (; i < sz; i++)
        rest += doc[i];
    return mk_pair(brief, rest);
}

static void print_block(std::ostream & out, std::string const & s) {
    if (s.empty())
        return;
    out << s;
    if (s.back() != '\n')
        out << "\n";
}

static void gen_decl_doc(std::ostream & out, environment const & env, formatter const & fmt, name const & decl_name, optional<std::string> const & brief) {
    out << "<a name=\"" << decl_name << "\">**" << get_decl_kind(env, decl_name) << "** " << decl_name << "\n";
    if (brief)
        print_block(out, *brief);
    print_id_info(out, env, fmt, decl_name);
}

void gen_doc(environment const & env, options const & _opts, std::ostream & out) {
    type_context ctx(env);
    options opts     = _opts.update_if_undef(name{"pp", "width"}, 100);
    auto fmt_factory = lean::mk_pretty_formatter_factory();
    auto fmt         = fmt_factory(env, opts, ctx);
    buffer<doc_entry> entries;
    get_module_doc_strings(env, entries);
    for (doc_entry const & entry : entries) {
        if (auto decl_name = entry.get_decl_name()) {
            if (has_brief(entry.get_doc())) {
                std::string brief, body;
                std::tie(brief, body) = split_brief_body(entry.get_doc());
                gen_decl_doc(out, env, fmt, *decl_name, optional<std::string>(brief));
                print_block(out, body);
            } else {
                gen_decl_doc(out, env, fmt, *decl_name, optional<std::string>());
                print_block(out, entry.get_doc());
            }
        } else {
            print_block(out, entry.get_doc());
        }
        out << "\n";
    }
}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <vector>
#include <limits>

// Card lookup values
const std::string number_lookup[] = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
const std::string suit_lookup[] = {"D", "C", "H", "S"};
const int face_value[] = { 11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10};

// Test case constants
const int test_case_number[] = {1, 1, 2, 6, 4, 8, 9, 1, 3, 2, 3, 10};
const int test_case_suit[] = {0, 2, 1, 0, 1, 0, 2, 1, 0, 3, 2, 0};
bool test_case = false;
int test_case_int = 0;

class Card {
public:
  int number;
  int suit;

  Card() {
    if (test_case && test_case_int < 12) {
      number = test_case_number[test_case_int];
      suit = test_case_suit[test_case_int];
      test_case_int++;
    }
    else {
      number = (double) rand()/RAND_MAX*13;
      suit = (double) rand()/RAND_MAX*4;
    }
  }

  Card(int n, int s) {
    number = n;
    suit = s;
  }

  std::string getString() {
    std::string s;
    s = number_lookup[number];
    s.append(suit_lookup[suit]);
    return s;
  }

  int getValue() {
    return face_value[number];
  }
};

class Hand {
public:
  std::vector<Card> hand;
  int hand_count;

  Hand() {
    hand_count = 0;
  }

  int getScore() {
    int score = 0;
    int hasAce = 0;
    for (std::vector<Card>::iterator it = hand.begin() ; it != hand.end(); ++it) {
      if (it->number == 0) {
	hasAce++;
      }
      score += it->getValue();
    }

    while (hasAce && score > 21) {
      hasAce--;
      score -= 10;
    }
    return score;
  }

  std::string getHand(bool ignoreFirst) {
    std::string s = "";
    for (std::vector<Card>::iterator it = hand.begin() ; it != hand.end(); ++it) {
      if (!ignoreFirst) {
	s.append(it->getString());
	s.append(" ");
      }
      ignoreFirst = false;
    }
    return s;
  }

  void drawCard() {
    Card card;
    hand.push_back(card);
    hand_count++;
  }

  bool isDoubles() {
    if (hand_count > 0) {
      std::vector<Card>::iterator it = hand.begin();
      int i = it->number;
      it++;
      return (i == it->number);
    }
    else {
      return false;
    }
  }
};

class Player {
public:
  Hand hand;
  Hand split;
  Hand split2;
  Hand split3;
  int splitLevel;

  Player() {
    splitLevel = 0;
  }

  void checkSplit() {
    if (hand.isDoubles() && askSplit()) {
      splitHandler(hand, split);
      splitLevel = 1;
      if (split.isDoubles() && askSplit()) {
	splitHandler(split, split2);
	splitLevel = 2;
	if (split2.isDoubles() && askSplit()) {
	  splitHandler(split2, split3);
	  splitLevel = 3;
	}
      }
    }
  }

  bool askSplit() {
    if (splitLevel < 1) {
      std::cout << "Split? (y/n): ";
    }
    else {
      std::cout << "Split again? (y/n): ";
    }

    char ch;
    while (true) { // Loop input until valid response
      std::cin >> ch;
      ch = tolower(ch);
      if (ch == 'y') {
	return true;
      }
      else if (ch == 'n') {
	return false;
      }
    }
  }

  void splitHandler(Hand& h1, Hand& h2) {
    std::vector<Card>::iterator it = h1.hand.end();
    it--; // Get last card

    Card card (it->number, it-> suit);

    std::cout << card.number << std::endl;
    std::cout << card.suit << std::endl;

    h2.hand.push_back(card); // Push new card to new hand, and delete from old hand
    h1.hand.pop_back();

    h1.drawCard(); // Draw new cards for both split hands
    h2.drawCard();
  }

  void printHand() {
    std::cout << "Player : " << hand.getHand(false) << "\n";
    if (splitLevel >= 1) {
      std::cout << "Split : " << split.getHand(false) << "\n";
    }
    if (splitLevel >= 2) {
      std::cout << "Split 2 : " << split2.getHand(false) << "\n";
    }
    if (splitLevel >= 3) {
      std::cout << "Split 3 : " << split3.getHand(false) << "\n";
    }
  }
};

class Game {
public:
  int player_bet;
  int player_cash;

  Game() {
    player_cash = 100;
  }

  void newGame() {
    Player player;
    Hand dealer;
    char ch;

    player_bet = 0; // Reset player bet to known value

    std::cout << "Player: $" << player_cash << "\nRound bet: "; // Get player bet amount and deduct it
    std::cin >> player_bet;

    while (player_cash < player_bet || player_bet == 0) { // Ensure valid bet amount
      std::cin.clear(); // Fix looping error with invalid input
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Clear read buffer
      std::cout << "Invalid bet. Round bet: ";
      std::cin >> player_bet;
    }

    player_cash -= player_bet;

    std::cout << "Test case? (y/n): ";
    while (true) {
      std::cin >> ch;
      ch = tolower(ch);

      if (ch == 'y') {
	test_case = true;
	break;
      }
      else if (ch == 'n') {
	break;
      }
    }

    player.hand.drawCard(); // Draw cards for player and dealer
    player.hand.drawCard();
    dealer.drawCard();
    dealer.drawCard();

    player.checkSplit();
    playRound(player, dealer);
  }

  void playRound(Player player, Hand dealer) {
    char ch;

    if (player.hand.getScore() >= 21 || player.hand.hand_count >= 5) { // End game if player gets 21 or over
      endGame(player, dealer);
      return;
    }

    std::cout << "Dealer : * " << dealer.getHand(true) << "\n";
    player.printHand();
    std::cout << "Draw? (y/n) ";

    while (true) {
      std::cin >> ch;
      ch = tolower(ch);

      if (ch == 'y') {
	player.hand.drawCard();
	playRound(player, dealer);
	break;
      }
      else if (ch == 'n') {
	endGame(player, dealer);
	break;
      }
    }
  }

  void endGame(Player player, Hand dealer) {

    while (dealer.getScore() < 17 && dealer.hand_count < 5) {
      dealer.drawCard();
    }

    std::cout << "Dealer : " << dealer.getHand(false) << "\nPlayer : " << player.hand.getHand(false) // Display hands
	      << "\nDealer: " << dealer.getScore() << "\tPlayer: "  << player.hand.getScore() << "\n"; // Display points

    switch (checkWin(player.hand, dealer)) {
      case 0:
	std::cout << "Dealer wins!\n";
	break;
      case 1:
	std::cout << "Tie game!\n";
	player_cash += player_bet;
	break;
      case 2:
	std::cout << "Player Wins!\n";
	if (player.hand.getScore() == 21) {
	  player_cash += player_bet*2.5;
	}
	else {
	  player_cash += player_bet*2;
	}
	break;
      }
  }

  int checkWin(Hand player, Hand dealer) { // 0 = loss, 1 = tie, 2 = win
    if (player.getScore() > 21) {
      return 0;
    }
    else if (dealer.getScore() > 21) {
      return 2;
    }
    else if (dealer.getScore() == player.getScore()) {
      return 1;
    }
    else if (dealer.getScore() > player.getScore()) {
      return 0;
    }
    else {
      return 2;
    }
  }
};

int main() {
  Game game;
  char ch;

  srand(time(0));  // initialize random number generator

  game.newGame(); // start new game

  while (true) { // Wait for valid input
    if (game.player_cash <= 0) { // Make sure the player has cash to continue playing
      std::cout << "Sorry, You have no more money left.\n";
      break;
    }
    std::cout << "Again? (y/n): ";
    std::cin >> ch;
    ch = tolower(ch);
    if (ch == 'y') {
      game.newGame(); // Start new game
    }
    else if (ch == 'n') {
      break; // Exit loop
    }
  }
  return 0;
}
<commit_msg>added split handler, need to ensure bet amounts<commit_after>#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <vector>
#include <limits>

// Card lookup values
const std::string number_lookup[] = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
const std::string suit_lookup[] = {"D", "C", "H", "S"};
const int face_value[] = { 11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10};

// Split lookup values
const std::string split_string[] = {"Player", "Split", "Split 2", "Split 3"};

// Test case constants
const int test_case_number[] = {1, 1, 2, 6, 4, 8, 9, 1, 3, 2, 3, 10};
const int test_case_suit[] = {0, 2, 1, 0, 1, 0, 2, 1, 0, 3, 2, 0};
bool test_case = false;
int test_case_int = 0;

class Card {
public:
  int number;
  int suit;

  Card() {
    if (test_case) {
      number = test_case_number[test_case_int];
      suit = test_case_suit[test_case_int];
      test_case_int++;
    }
    else {
      number = (double) rand()/RAND_MAX*13;
      suit = (double) rand()/RAND_MAX*4;
    }
  }

  Card(int n, int s) {
    number = n;
    suit = s;
  }

  std::string getString() {
    std::string s;
    s = number_lookup[number];
    s.append(suit_lookup[suit]);
    return s;
  }

  int getValue() {
    return face_value[number];
  }
};

class Hand {
public:
  std::vector<Card> hand;
  int hand_count;

  Hand() {
    hand_count = 0;
  }

  int getScore() {
    int score = 0;
    int hasAce = 0;
    for (std::vector<Card>::iterator it = hand.begin() ; it != hand.end(); ++it) {
      if (it->number == 0) {
	hasAce++;
      }
      score += it->getValue();
    }

    while (hasAce && score > 21) {
      hasAce--;
      score -= 10;
    }
    return score;
  }

  std::string getHand(bool ignoreFirst) {
    std::string s = "";
    for (std::vector<Card>::iterator it = hand.begin() ; it != hand.end(); ++it) {
      if (!ignoreFirst) {
	s.append(it->getString());
	s.append(" ");
      }
      ignoreFirst = false;
    }
    return s;
  }

  void drawCard() {
    Card card;
    hand.push_back(card);
    hand_count++;
  }

  bool isDoubles() {
    if (hand_count > 0) {
      std::vector<Card>::iterator it = hand.begin();
      int i = it->number;
      it++;
      return (i == it->number);
    }
    else {
      return false;
    }
  }
};

class Player {
public:
  std::vector<Hand> deck;
  int splitLevel, currentLevel;

  Player() {
    splitLevel = 0;
    currentLevel = 0;
    for (int i = 0; i < 4; i++) {
      Hand hand;
      deck.push_back(hand);
    }
  }

  void checkSplit() {
    if (deck[0].isDoubles() && askSplit()) {
      splitHandler(deck[0], deck[1]);
      splitLevel = 1;
      if (deck[1].isDoubles() && askSplit()) {
	splitHandler(deck[1], deck[2]);
	splitLevel = 2;
	if (deck[0].isDoubles() && askSplit()) {
	  splitHandler(deck[2], deck[3]);
	  splitLevel = 3;
	}
      }
    }
  }

  bool askSplit() {
    if (splitLevel < 1) {
      std::cout << "Split? (y/n): ";
    }
    else {
      std::cout << "Split again? (y/n): ";
    }

    char ch;
    while (true) { // Loop input until valid response
      std::cin >> ch;
      ch = tolower(ch);
      if (ch == 'y') {
	return true;
      }
      else if (ch == 'n') {
	return false;
      }
    }
  }

  void splitHandler(Hand& h1, Hand& h2) {
    printHand();
    std::vector<Card>::iterator it = h1.hand.end();
    it--; // Get last card

    Card card (h1.hand[1].number, h1.hand[1].suit);

    h2.hand.push_back(card); // Push new card to new hand, and delete from old hand
    h1.hand.pop_back();
    h1.hand_count--;

    h1.drawCard(); // Draw new cards for both split hands
    h2.drawCard();
  }

  void printHand() {
    std::cout << "Player : " << deck[0].getHand(false) << "\n";
    if (splitLevel >= 1) {
      std::cout << "Split : " << deck[1].getHand(false) << "\n";
    }
    if (splitLevel >= 2) {
      std::cout << "Split 2 : " << deck[2].getHand(false) << "\n";
    }
    if (splitLevel >= 3) {
      std::cout << "Split 3 : " << deck[3].getHand(false) << "\n";
    }
  }

  void drawCard() {
    deck[currentLevel].drawCard();
  }
};

class Game {
public:
  int player_bet;
  int player_cash;

  Game() {
    player_cash = 100;
  }

  void newGame() {
    Player player;
    Hand dealer;
    char ch;

    player_bet = 0; // Reset player bet to known value

    std::cout << "Player: $" << player_cash << "\nRound bet: "; // Get player bet amount and deduct it
    std::cin >> player_bet;

    while (player_cash < player_bet || player_bet == 0) { // Ensure valid bet amount
      std::cin.clear(); // Fix looping error with invalid input
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Clear read buffer
      std::cout << "Invalid bet. Round bet: ";
      std::cin >> player_bet;
    }

    player_cash -= player_bet;

    std::cout << "Test case? (y/n): ";
    while (true) {
      std::cin >> ch;
      ch = tolower(ch);

      if (ch == 'y') {
	test_case = true;
	break;
      }
      else if (ch == 'n') {
	break;
      }
    }

    player.drawCard(); // Draw cards for player and dealer
    player.drawCard();
    player.printHand();

    dealer.drawCard();
    dealer.drawCard();

    player.checkSplit();
    playRound(player, dealer);
  }

  void playRound(Player player, Hand dealer) {
    char ch;

    if (player.deck[player.currentLevel].getScore() >= 21 || player.deck[player.currentLevel].hand_count >= 5) { // End game if player gets 21 or over
      if (player.currentLevel == player.splitLevel) {
	endGame(player, dealer);
      }
      else {
	player.currentLevel++;
	playRound(player, dealer);
      }
    }

    std::cout << "Dealer : * " << dealer.getHand(true) << "\n";
    player.printHand();
    if (player.currentLevel == 0) {
      std::cout << "Draw? (y/n) ";
    }
    else {
      std::cout << "Draw for " << split_string[player.currentLevel] << "? (y/n) ";
    }

    while (true) {
      std::cin >> ch;
      ch = tolower(ch);

      if (ch == 'y') {
	player.drawCard();
	playRound(player, dealer);
	break;
      }
      else if (ch == 'n') {
	if (player.currentLevel == player.splitLevel) {
	  endGame(player, dealer);
	}
	else {
	  player.currentLevel++;
	  playRound(player, dealer);
	}
	break;
      }
    }
  }

  void endGame(Player player, Hand dealer) {

    while (dealer.getScore() < 17 && dealer.hand_count < 5) {
      dealer.drawCard();
    }

    std::cout << "Dealer : " << dealer.getHand(false) << "\n";
    player.printHand();

    std::cout << "Dealer: " << dealer.getScore() << "\tPlayer: ";
    for (int i = 0; i < player.splitLevel; i++) {
      std::cout << player.deck[i].getScore() << " and "; // Display points
    }
    std::cout << player.deck[player.splitLevel].getScore() << " points\n";

    for (int i = 0; i <= player.splitLevel; i++) {
      std::cout << split_string[i] << ": ";

      switch (checkWin(player.deck[i], dealer)) {
      case 0:
	std::cout << "Dealer wins!\n";
	break;
      case 1:
	std::cout << "Tie game!\n";
	player_cash += player_bet;
	break;
      case 2:
	std::cout << "Player Wins!\n";
	if (player.deck[i].getScore() == 21) {
	  player_cash += player_bet*2.5;
	}
	else {
	  player_cash += player_bet*2;
	}
	break;
      }
    }
  }

  int checkWin(Hand player, Hand dealer) { // 0 = loss, 1 = tie, 2 = win
    if (player.getScore() > 21) {
      return 0;
    }
    else if (dealer.getScore() > 21) {
      return 2;
    }
    else if (dealer.getScore() == player.getScore()) {
      return 1;
    }
    else if (dealer.getScore() > player.getScore()) {
      return 0;
    }
    else {
      return 2;
    }
  }
};

int main() {
  Game game;
  char ch;

  srand(time(0));  // initialize random number generator

  game.newGame(); // start new game

  while (true) { // Wait for valid input
    if (game.player_cash <= 0) { // Make sure the player has cash to continue playing
      std::cout << "Sorry, You have no more money left.\n";
      break;
    }
    std::cout << "Again? (y/n): ";
    std::cin >> ch;
    ch = tolower(ch);
    if (ch == 'y') {
      game.newGame(); // Start new game
    }
    else if (ch == 'n') {
      break; // Exit loop
    }
  }
  return 0;
}
<|endoftext|>