<commit_before>/*
 * TTBlue Global Object
 * Copyright © 2008, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTFoundation.h"
#include "TTEnvironment.h"
#include "TTClass.h"

#define thisTTClass TTEnvironment

// The environment object has one instance, which is global in scope.
TTEnvironment*	ttEnvironment = NULL;

/****************************************************************************************************/

TTEnvironment::TTEnvironment()
	: TTObject(*kTTValNONE), debugBasic(false), debugMessaging(false), sr(0)
{
	classes = new TTHash();
	tags = new TTHash();

	registerAttributeSimple(debugBasic,		kTypeBoolean);
	registerAttributeSimple(debugMessaging,	kTypeBoolean);
	registerAttributeSimple(sr,				kTypeUInt32);

	registerMessageWithArgument(getVersion);

	setAttributeValue(TT("sr"), TTUInt32(44100));
}

TTEnvironment::~TTEnvironment()
{
	// If on Windows, we need to call FreeLibrary() on all plug-ins loaded with LoadLibrary()
	// On the Mac, we may need to do the equivalent

	delete tags;
	delete classes;
}

TTErr TTEnvironment::getVersion(TTValue &value)
{
	value = TTFOUNDATION_VERSION_STRING;
	return kTTErrNone;
}

TTErr TTEnvironment::registerClass(const TTSymbolPtr className, const TTString& tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	TTValue		v((TTString&)tagString);	// The tags to be associated with the class we are registering.
	TTValue		tagObjects;					// Contains a TTList of objects in the environment with the given tag.
	TTClassPtr	theClass;
	TTErr		err;
	TTList*		classNamesForTag;			// The TTList contained by tagObjects
	TTUInt16	size;
	TTSymbolPtr	tag;

	// 1. Turn the string into an array of symbols
	v.transformCSVStringToSymbolArray();

	// 2. Create the class and associate it with its name
	theClass = new TTClass(className, v, anInstantiationMethod);

	// 3. For each symbol in the TTValue array...
	size = v.getSize();
	for(TTUInt16 i=0; i<size; i++){
		v.get(i, &tag);

		// 4. Look to see if this tag exists yet
		err = tags->lookup(tag, tagObjects);
		if(!err){
			classNamesForTag = (TTList*)(TTPtr(tagObjects));

			// TODO: The following code demonstrates so extreme lameness that we need to evaluate.
			//	First, we should probably just do this section of code with TTValue instead of TTList (but we needed code to test TTList)
			//	Second, TTList is taking references but keeping things internally as pointers, which leads to lots of confusion
			//	Third, we have to pass objects that are permanent - so no temporaries are allowed unless we make TTList do a copy
			//	etc.

			// TODO: We need to factor out a function to add a tag for a named class (or a given class ptr)

			//classNamesForTag->append(className);
			classNamesForTag->append(*new TTValue(className));
		}
		else{
			classNamesForTag = new TTList;
			tagObjects = TTPtr(classNamesForTag);
			tags->append(tag ,tagObjects);
			classNamesForTag->append(*new TTValue(className));
		}
	}

	// 4. Register it
	return registerClass(theClass);
}

TTErr TTEnvironment::registerClass(TTClassPtr theClass)
{
	return classes->append(theClass->name, TTPtr(theClass));
}

TTErr TTEnvironment::getAllClassNames(TTValue& returnedClassNames)
{
	return classes->getKeys(returnedClassNames);
}

TTErr TTEnvironment::getClassNamesWithTags(TTValue& classNames, const TTValue& searchTags)
{
	// TODO: right now we only look for the first tag, we should look for each and then do a union on the results.
	// Well, that's not what's really happening, but the point is that this really only works if we are searching for one tag.

	TTUInt16	size = searchTags.getSize();
	TTSymbolPtr	tag;
	TTValue		tagObjects;
	TTErr		err = kTTErrGeneric;
	TTList*		classNamesForTag;

	for(TTUInt16 i=0; i<size; i++){
		searchTags.get(i, &tag);

		err = tags->lookup(tag, tagObjects);
		if(!err){
			classNamesForTag = (TTList*)(TTPtr(tagObjects));
			classNamesForTag->assignToValue(classNames);
		}
	}

	return err;
}

TTErr TTEnvironment::createInstance(const TTSymbolPtr className, TTObjectPtr* anObject, const TTValue& anArgument)
{
	return createInstance(className, anObject, (TTValue&)anArgument); // throw away the const (I know, I know...), maybe the non-const constructor shouldn't exist at all?
}

TTErr TTEnvironment::createInstance(const TTSymbolPtr className, TTObjectPtr* anObject, TTValue& anArgument)
{
	TTValue		v;
	TTClassPtr	theClass;
	TTErr		err;
	TTObjectPtr	newObject = NULL;
	TTObjectPtr	oldObject = NULL;

	err = classes->lookup(className, v);
	if(!err){
		theClass = TTClassPtr(TTPtr(v));
		if(theClass)
			err = theClass->createInstance(&newObject, anArgument);
		else
			err = kTTErrGeneric;
	}

	if(!err && newObject){
		if(*anObject)
			oldObject = *anObject;
		*anObject = newObject;
		if(oldObject)
			releaseInstance(&oldObject);

		(*anObject)->classPtr = theClass;
		(*anObject)->valid = true;
	}

	//TODO: Add instance tracking.  For each instance of a class, we push the instance onto a linked list of instances for that class
	// When the object is freed using deleteInstance(), then we pop it.
	// What would this achieve?
	//	- we could check statistics on them or do other logging
	//	- we could access instances remotely, and perhaps then manipulate them remotely in a shared manner
	//	- if an object is referenced by another object, and thus shared, then we need to reference counting here before freeing.
	// THEREFORE: we should have an addReference() and release() method (instead of a deleteInstance() method).
	//	- the reference counting itself should probably be done inside of TTObject though, yes?
	return err;
}

TTObjectPtr TTEnvironment::referenceInstance(TTObjectPtr anObject)
{
	// TODO: make sure that anObject is valid or wrap with an exception?
	anObject->referenceCount++;
	return anObject;
}

TTErr TTEnvironment::releaseInstance(TTObjectPtr* anObject)
{
	TTValue v = **anObject;

	(*anObject)->valid = false;
	(*anObject)->observers->iterateObjectsSendingMessage(TT("objectFreeing"), v);

	// If the object is locked (e.g. in the middle of processing a vector in another thread)
	//	then we spin until the lock is released
	//	TODO: we should also be able to time-out in the event that we have a dead lock.
	while((*anObject)->getlock())
		;

	(*anObject)->referenceCount--;
	if((*anObject)->referenceCount < 1){
		delete *anObject;
		*anObject = NULL;
	}
	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Public Interface
#endif

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, TTValue& arguments)
{
	return ttEnvironment->createInstance(className, returnedObjectPtr, arguments);
}

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, const TTValue& arguments)
{
	return ttEnvironment->createInstance(className, returnedObjectPtr, arguments);
}

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, const TTUInt16 arguments)
{
	TTValue	v(arguments);
	return ttEnvironment->createInstance(className, returnedObjectPtr, v);
}

TTObjectPtr TTObjectReference(TTObjectPtr anObject)
{
	return ttEnvironment->referenceInstance(anObject);
}

TTErr TTObjectRelease(TTObjectPtr* anObject)
{
	if(*anObject)
		return ttEnvironment->releaseInstance(anObject);
	else
		return kTTErrNone;
}

TTErr TTClassRegister(const TTSymbolPtr className, const TTString& tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	return ttEnvironment->registerClass(className, tagString, anInstantiationMethod);
}

TTErr TTClassRegister(const TTSymbolPtr className, TTImmutableCString tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	return ttEnvironment->registerClass(className, TTString(tagString), anInstantiationMethod);
}

TTErr TTGetRegisteredClassNames(TTValue& classNames)
{
	return ttEnvironment->getAllClassNames(classNames);
}

TTErr TTGetRegisteredClassNamesForTags(TTValue& classNames, const TTValue& searchTags)
{
	return ttEnvironment->getClassNamesWithTags(classNames, searchTags);
}

<commit_msg>TTEnvironment::registerClass: now checks for an existing class of the same name before registering a new class with that name.<commit_after>/*
 * TTBlue Global Object
 * Copyright © 2008, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTFoundation.h"
#include "TTEnvironment.h"
#include "TTClass.h"

#define thisTTClass TTEnvironment

// The environment object has one instance, which is global in scope.
TTEnvironment*	ttEnvironment = NULL;

/****************************************************************************************************/

TTEnvironment::TTEnvironment()
	: TTObject(*kTTValNONE), debugBasic(false), debugMessaging(false), sr(0)
{
	classes = new TTHash();
	tags = new TTHash();

	registerAttributeSimple(debugBasic,		kTypeBoolean);
	registerAttributeSimple(debugMessaging,	kTypeBoolean);
	registerAttributeSimple(sr,				kTypeUInt32);

	registerMessageWithArgument(getVersion);

	setAttributeValue(TT("sr"), TTUInt32(44100));
}

TTEnvironment::~TTEnvironment()
{
	// If on Windows, we need to call FreeLibrary() on all plug-ins loaded with LoadLibrary()
	// On the Mac, we may need to do the equivalent

	delete tags;
	delete classes;
}

TTErr TTEnvironment::getVersion(TTValue &value)
{
	value = TTFOUNDATION_VERSION_STRING;
	return kTTErrNone;
}

TTErr TTEnvironment::registerClass(const TTSymbolPtr className, const TTString& tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	TTValue		v((TTString&)tagString);	// The tags to be associated with the class we are registering.
	TTValue		tagObjects;					// Contains a TTList of objects in the environment with the given tag.
	TTClassPtr	theClass;
	TTErr		err;
	TTList*		classNamesForTag;			// The TTList contained by tagObjects
	TTUInt16	size;
	TTSymbolPtr	tag;
	TTValue		result;

	err = classes->lookup(className, result);

	// If a class is already registered with this name, then we do not want to register another class with the same name!
	if (err == kTTErrValueNotFound) {

		// 1. Turn the string into an array of symbols
		v.transformCSVStringToSymbolArray();

		// 2. Create the class and associate it with its name
		theClass = new TTClass(className, v, anInstantiationMethod);

		// 3. For each symbol in the TTValue array...
		size = v.getSize();
		for (TTUInt16 i=0; i<size; i++) {
			v.get(i, &tag);

			// 4. Look to see if this tag exists yet
			err = tags->lookup(tag, tagObjects);
			if (!err) {
				classNamesForTag = (TTList*)(TTPtr(tagObjects));

				// TODO: The following code demonstrates so extreme lameness that we need to evaluate.
				//	First, we should probably just do this section of code with TTValue instead of TTList (but we needed code to test TTList)
				//	Second, TTList is taking references but keeping things internally as pointers, which leads to lots of confusion
				//	Third, we have to pass objects that are permanent - so no temporaries are allowed unless we make TTList do a copy
				//	etc.

				// TODO: We need to factor out a function to add a tag for a named class (or a given class ptr)

				//classNamesForTag->append(className);
				classNamesForTag->append(*new TTValue(className));
			}
			else {
				classNamesForTag = new TTList;
				tagObjects = TTPtr(classNamesForTag);
				tags->append(tag ,tagObjects);
				classNamesForTag->append(*new TTValue(className));
			}
		}

		// 4. Register it
		err = registerClass(theClass);
	}
	return err;
}

TTErr TTEnvironment::registerClass(TTClassPtr theClass)
{
	return classes->append(theClass->name, TTPtr(theClass));
}

TTErr TTEnvironment::getAllClassNames(TTValue& returnedClassNames)
{
	return classes->getKeys(returnedClassNames);
}

TTErr TTEnvironment::getClassNamesWithTags(TTValue& classNames, const TTValue& searchTags)
{
	// TODO: right now we only look for the first tag, we should look for each and then do a union on the results.
	// Well, that's not what's really happening, but the point is that this really only works if we are searching for one tag.

	TTUInt16	size = searchTags.getSize();
	TTSymbolPtr	tag;
	TTValue		tagObjects;
	TTErr		err = kTTErrGeneric;
	TTList*		classNamesForTag;

	for(TTUInt16 i=0; i<size; i++){
		searchTags.get(i, &tag);

		err = tags->lookup(tag, tagObjects);
		if(!err){
			classNamesForTag = (TTList*)(TTPtr(tagObjects));
			classNamesForTag->assignToValue(classNames);
		}
	}

	return err;
}

TTErr TTEnvironment::createInstance(const TTSymbolPtr className, TTObjectPtr* anObject, const TTValue& anArgument)
{
	return createInstance(className, anObject, (TTValue&)anArgument); // throw away the const (I know, I know...), maybe the non-const constructor shouldn't exist at all?
}

TTErr TTEnvironment::createInstance(const TTSymbolPtr className, TTObjectPtr* anObject, TTValue& anArgument)
{
	TTValue		v;
	TTClassPtr	theClass;
	TTErr		err;
	TTObjectPtr	newObject = NULL;
	TTObjectPtr	oldObject = NULL;

	err = classes->lookup(className, v);
	if(!err){
		theClass = TTClassPtr(TTPtr(v));
		if(theClass)
			err = theClass->createInstance(&newObject, anArgument);
		else
			err = kTTErrGeneric;
	}

	if(!err && newObject){
		if(*anObject)
			oldObject = *anObject;
		*anObject = newObject;
		if(oldObject)
			releaseInstance(&oldObject);

		(*anObject)->classPtr = theClass;
		(*anObject)->valid = true;
	}

	//TODO: Add instance tracking.  For each instance of a class, we push the instance onto a linked list of instances for that class
	// When the object is freed using deleteInstance(), then we pop it.
	// What would this achieve?
	//	- we could check statistics on them or do other logging
	//	- we could access instances remotely, and perhaps then manipulate them remotely in a shared manner
	//	- if an object is referenced by another object, and thus shared, then we need to reference counting here before freeing.
	// THEREFORE: we should have an addReference() and release() method (instead of a deleteInstance() method).
	//	- the reference counting itself should probably be done inside of TTObject though, yes?
	return err;
}

TTObjectPtr TTEnvironment::referenceInstance(TTObjectPtr anObject)
{
	// TODO: make sure that anObject is valid or wrap with an exception?
	anObject->referenceCount++;
	return anObject;
}

TTErr TTEnvironment::releaseInstance(TTObjectPtr* anObject)
{
	TTValue v = **anObject;

	(*anObject)->valid = false;
	(*anObject)->observers->iterateObjectsSendingMessage(TT("objectFreeing"), v);

	// If the object is locked (e.g. in the middle of processing a vector in another thread)
	//	then we spin until the lock is released
	//	TODO: we should also be able to time-out in the event that we have a dead lock.
	while((*anObject)->getlock())
		;

	(*anObject)->referenceCount--;
	if((*anObject)->referenceCount < 1){
		delete *anObject;
		*anObject = NULL;
	}
	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Public Interface
#endif

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, TTValue& arguments)
{
	return ttEnvironment->createInstance(className, returnedObjectPtr, arguments);
}

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, const TTValue& arguments)
{
	return ttEnvironment->createInstance(className, returnedObjectPtr, arguments);
}

TTErr TTObjectInstantiate(const TTSymbolPtr className, TTObjectPtr* returnedObjectPtr, const TTUInt16 arguments)
{
	TTValue	v(arguments);
	return ttEnvironment->createInstance(className, returnedObjectPtr, v);
}

TTObjectPtr TTObjectReference(TTObjectPtr anObject)
{
	return ttEnvironment->referenceInstance(anObject);
}

TTErr TTObjectRelease(TTObjectPtr* anObject)
{
	if(*anObject)
		return ttEnvironment->releaseInstance(anObject);
	else
		return kTTErrNone;
}

TTErr TTClassRegister(const TTSymbolPtr className, const TTString& tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	return ttEnvironment->registerClass(className, tagString, anInstantiationMethod);
}

TTErr TTClassRegister(const TTSymbolPtr className, TTImmutableCString tagString, const TTObjectInstantiationMethod anInstantiationMethod)
{
	return ttEnvironment->registerClass(className, TTString(tagString), anInstantiationMethod);
}

TTErr TTGetRegisteredClassNames(TTValue& classNames)
{
	return ttEnvironment->getAllClassNames(classNames);
}

TTErr TTGetRegisteredClassNamesForTags(TTValue& classNames, const TTValue& searchTags)
{
	return ttEnvironment->getClassNamesWithTags(classNames, searchTags);
}

<|endoftext|>


<commit_before>#ifndef ITER_FILTER_H_
#define ITER_FILTER_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <initializer_list>

namespace iter {

    //Forward declarations of Filter and filter
    template <typename FilterFunc, typename Container>
    class Filter;

    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(FilterFunc, Container&&);

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc, std::initializer_list<T>);

    template <typename FilterFunc, typename Container>
    class Filter {
        private:
            Container container;
            FilterFunc filter_func;

            // The filter function is the only thing allowed to create a Filter
            friend Filter filter<FilterFunc, Container>(
                    FilterFunc, Container&&);

            template <typename FF, typename T>
            friend Filter<FF, std::initializer_list<T>> filter(
                    FF, std::initializer_list<T>);

            // Value constructor for use only in the filter function
            Filter(FilterFunc filter_func, Container container)
                : container(std::forward<Container>(container)),
                filter_func(filter_func)
            { }
            Filter() = delete;
            Filter& operator=(const Filter&) = delete;

        public:
            Filter(const Filter&) = default;

            class Iterator
                : public std::iterator<std::input_iterator_tag,
                        iterator_traits_deref<Container>>
            {
                protected:
                    iterator_type<Container> sub_iter;
                    const iterator_type<Container> sub_end;
                    FilterFunc filter_func;

                    // increment until the iterator points to is true on the
                    // predicate.  Called by constructor and operator++
                    void skip_failures() {
                        while (this->sub_iter != this->sub_end
                                && !this->filter_func(*this->sub_iter)) {
                            ++this->sub_iter;
                        }
                    }

                public:
                    Iterator (iterator_type<Container> iter,
                            iterator_type<Container> end,
                            FilterFunc filter_func)
                        : sub_iter{iter},
                        sub_end{end},
                        filter_func(filter_func)
                    {
                        this->skip_failures();
                    }

                    iterator_deref<Container> operator*() {
                        return *this->sub_iter;
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        this->skip_failures();
                        return *this;
                    }

                    bool operator!=(const Iterator& other) const {
                        return this->sub_iter != other.sub_iter;
                    }
            };

            Iterator begin() {
                return {std::begin(this->container),
                        std::end(this->container),
                        this->filter_func};
            }

            Iterator end() {
                return {std::end(this->container),
                        std::end(this->container),
                        this->filter_func};
            }

    };

    // Helper function to instantiate a Filter
    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(
            FilterFunc filter_func, Container&& container) {
        return {filter_func, std::forward<Container>(container)};
    }

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc filter_func,
            std::initializer_list<T> il)
    {
        return {filter_func, std::move(il)};
    }

    namespace detail {

        template <typename T>
        bool boolean_cast(const T& t) {
            return bool(t);
        }

        template <typename Container>
        class BoolTester {
            public:
                bool operator() (const iterator_deref<Container> item) const {
                    return bool(item);
                }
        };
    }

    template <typename Container>
    auto filter(Container&& container) ->
            decltype(filter(
                        detail::BoolTester<Container>(),
                        std::forward<Container>(container))) {
        return filter(
                detail::BoolTester<Container>(),
                std::forward<Container>(container));
    }

    template <typename T>
    auto filter(std::initializer_list<T> il) ->
            decltype(filter(
                        detail::BoolTester<std::initializer_list<T>>(),
                        std::move(il))) {
           return filter(
                   detail::BoolTester<std::initializer_list<T>>(),
                   std::move(il));
    }

}

#endif // #ifndef ITER_FILTER_H_
<commit_msg>adds filter iter postfix ++<commit_after>#ifndef ITER_FILTER_H_
#define ITER_FILTER_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <initializer_list>

namespace iter {

    //Forward declarations of Filter and filter
    template <typename FilterFunc, typename Container>
    class Filter;

    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(FilterFunc, Container&&);

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc, std::initializer_list<T>);

    template <typename FilterFunc, typename Container>
    class Filter {
        private:
            Container container;
            FilterFunc filter_func;

            // The filter function is the only thing allowed to create a Filter
            friend Filter filter<FilterFunc, Container>(
                    FilterFunc, Container&&);

            template <typename FF, typename T>
            friend Filter<FF, std::initializer_list<T>> filter(
                    FF, std::initializer_list<T>);

            // Value constructor for use only in the filter function
            Filter(FilterFunc filter_func, Container container)
                : container(std::forward<Container>(container)),
                filter_func(filter_func)
            { }
            Filter() = delete;
            Filter& operator=(const Filter&) = delete;

        public:
            Filter(const Filter&) = default;

            class Iterator
                : public std::iterator<std::input_iterator_tag,
                        iterator_traits_deref<Container>>
            {
                protected:
                    iterator_type<Container> sub_iter;
                    const iterator_type<Container> sub_end;
                    FilterFunc filter_func;

                    // increment until the iterator points to is true on the
                    // predicate.  Called by constructor and operator++
                    void skip_failures() {
                        while (this->sub_iter != this->sub_end
                                && !this->filter_func(*this->sub_iter)) {
                            ++this->sub_iter;
                        }
                    }

                public:
                    Iterator (iterator_type<Container> iter,
                            iterator_type<Container> end,
                            FilterFunc filter_func)
                        : sub_iter{iter},
                        sub_end{end},
                        filter_func(filter_func)
                    {
                        this->skip_failures();
                    }

                    iterator_deref<Container> operator*() {
                        return *this->sub_iter;
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        this->skip_failures();
                        return *this;
                    }

                    Iterator operator++(int) {
                        auto ret = *this;
                        ++*this;
                        return ret;
                    }

                    bool operator!=(const Iterator& other) const {
                        return this->sub_iter != other.sub_iter;
                    }
            };

            Iterator begin() {
                return {std::begin(this->container),
                        std::end(this->container),
                        this->filter_func};
            }

            Iterator end() {
                return {std::end(this->container),
                        std::end(this->container),
                        this->filter_func};
            }

    };

    // Helper function to instantiate a Filter
    template <typename FilterFunc, typename Container>
    Filter<FilterFunc, Container> filter(
            FilterFunc filter_func, Container&& container) {
        return {filter_func, std::forward<Container>(container)};
    }

    template <typename FilterFunc, typename T>
    Filter<FilterFunc, std::initializer_list<T>> filter(
            FilterFunc filter_func,
            std::initializer_list<T> il)
    {
        return {filter_func, std::move(il)};
    }

    namespace detail {

        template <typename T>
        bool boolean_cast(const T& t) {
            return bool(t);
        }

        template <typename Container>
        class BoolTester {
            public:
                bool operator() (const iterator_deref<Container> item) const {
                    return bool(item);
                }
        };
    }

    template <typename Container>
    auto filter(Container&& container) ->
            decltype(filter(
                        detail::BoolTester<Container>(),
                        std::forward<Container>(container))) {
        return filter(
                detail::BoolTester<Container>(),
                std::forward<Container>(container));
    }

    template <typename T>
    auto filter(std::initializer_list<T> il) ->
            decltype(filter(
                        detail::BoolTester<std::initializer_list<T>>(),
                        std::move(il))) {
           return filter(
                   detail::BoolTester<std::initializer_list<T>>(),
                   std::move(il));
    }

}

#endif // #ifndef ITER_FILTER_H_
<|endoftext|>


<commit_before>/*
 * Jamoma Asynchronous Object Graph Layer
 * Creates a wrapper for TTObjects that can be used to build a control graph for asynchronous message passing.
 * Copyright © 2010, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTGraphObject.h"
#include "TTGraphInlet.h"
#include "TTGraphOutlet.h"

#define thisTTClass			TTGraphObject
#define thisTTClassName		"graph.object"
#define thisTTClassTags		"audio, multicore, wrapper"

//	Arguments
//	1. (required) The name of the Jamoma DSP object you want to wrap
//	2. (optional) Number of inlets, default = 1
//	3. (optional) Number of outlets, default = 1

TT_OBJECT_CONSTRUCTOR,
//	mFlags(kTTGraphProcessor),
	mKernel(NULL)
{
	TTErr		err = kTTErrNone;
	TTSymbolPtr	wrappedObjectName = NULL;
	TTUInt16	initialNumChannels = 1;
	TTUInt16	numInlets = 1;
	TTUInt16	numOutlets = 1;

	TT_ASSERT(graph_correct_instantiation_args, arguments.getSize() > 0);

	arguments.get(0, &wrappedObjectName);
	if (arguments.getSize() > 1)
		arguments.get(1, numInlets);
	if (arguments.getSize() > 2)
		arguments.get(2, numOutlets);

	err = TTObjectInstantiate(wrappedObjectName, &mKernel, initialNumChannels);
	mDictionary = new TTDictionary;

	mInlets.resize(numInlets);
	mOutlets.resize(numOutlets);
}

TTGraphObject::~TTGraphObject()
{
	TTObjectRelease(&mKernel);
	delete mDictionary;
}

void TTGraphObject::getDescription(TTGraphDescription& desc)
{
	desc.mClassName = mKernel->getName();
	desc.mInputDescriptions.clear();
	for (TTGraphInletIter inlet = mInlets.begin(); inlet != mInlets.end(); inlet++)
		inlet->getDescriptions(desc.mInputDescriptions);
}

TTErr TTGraphObject::reset()
{
	for_each(mInlets.begin(), mInlets.end(), mem_fun_ref(&TTGraphInlet::reset));
	for_each(mOutlets.begin(), mOutlets.end(), mem_fun_ref(&TTGraphOutlet::reset));
	return kTTErrNone;
}

TTErr TTGraphObject::handshake(TTGraphObjectPtr objectWhichIsBeingConnected, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err;

	err = mOutlets[fromOutletNumber].connect(objectWhichIsBeingConnected, toInletNumber);
	return err;
}

TTErr TTGraphObject::connect(TTGraphObjectPtr anObject, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err;

	err = mInlets[toInletNumber].connect(anObject, fromOutletNumber);
	anObject->handshake(this, fromOutletNumber, toInletNumber);

	return err;
}

TTErr TTGraphObject::drop(TTGraphObjectPtr anObject, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err = kTTErrInvalidValue;

	if (toInletNumber < mInlets.size())
		err = mInlets[toInletNumber].drop(anObject, fromOutletNumber);
	return err;
}

TTErr TTGraphObject::push(const TTDictionary& aDictionary)
{
	TTSymbolPtr		schema = aDictionary.getSchema();
	TTValue			v;
	TTErr			err = kTTErrMethodNotFound;
	TTMessagePtr	message = NULL;

	// If an object defines a 'dictionary' message then this trumps all the others
	err = mKernel->findMessage(TT("dictionary"), &message);
	if (!err && message) {
		(*mDictionary) = aDictionary;
		v.set(0, TTPtr(mDictionary));
		err = mKernel->sendMessage(TT("dictionary"), v);
	}
	else if (schema == TT("number")) {
		aDictionary.getValue(v);
		// TODO: maybe try seeing if there is a "number" message first and then prefer that if it exists?
		err = mKernel->sendMessage(TT("calculate"), v);

		mDictionary->setSchema(TT("number"));
		mDictionary->setValue(v);
		// NOTE: doesn't have inlet/outlet info at this point
	}
	else {
		// not sure what to do with other dictionary schemas yet...
	}

	for (TTGraphOutletIter outlet = mOutlets.begin(); outlet != mOutlets.end(); outlet++)
		outlet->push(*mDictionary);

	return err;
}

<commit_msg>lib : capitalization fixes<commit_after>/*
 * Jamoma Asynchronous Object Graph Layer
 * Creates a wrapper for TTObjects that can be used to build a control graph for asynchronous message passing.
 * Copyright © 2010, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTGraphObject.h"
#include "TTGraphInlet.h"
#include "TTGraphOutlet.h"

#define thisTTClass			TTGraphObject
#define thisTTClassName		"graph.object"
#define thisTTClassTags		"audio, multicore, wrapper"

//	Arguments
//	1. (required) The name of the Jamoma DSP object you want to wrap
//	2. (optional) Number of inlets, default = 1
//	3. (optional) Number of outlets, default = 1

TT_OBJECT_CONSTRUCTOR,
//	mFlags(kTTGraphProcessor),
	mKernel(NULL)
{
	TTErr		err = kTTErrNone;
	TTSymbolPtr	wrappedObjectName = NULL;
	TTUInt16	initialNumChannels = 1;
	TTUInt16	numInlets = 1;
	TTUInt16	numOutlets = 1;

	TT_ASSERT(graph_correct_instantiation_args, arguments.getSize() > 0);

	arguments.get(0, &wrappedObjectName);
	if (arguments.getSize() > 1)
		arguments.get(1, numInlets);
	if (arguments.getSize() > 2)
		arguments.get(2, numOutlets);

	err = TTObjectInstantiate(wrappedObjectName, &mKernel, initialNumChannels);
	mDictionary = new TTDictionary;

	mInlets.resize(numInlets);
	mOutlets.resize(numOutlets);
}

TTGraphObject::~TTGraphObject()
{
	TTObjectRelease(&mKernel);
	delete mDictionary;
}

void TTGraphObject::getDescription(TTGraphDescription& desc)
{
	desc.mClassName = mKernel->getName();
	desc.mInputDescriptions.clear();
	for (TTGraphInletIter inlet = mInlets.begin(); inlet != mInlets.end(); inlet++)
		inlet->getDescriptions(desc.mInputDescriptions);
}

TTErr TTGraphObject::reset()
{
	for_each(mInlets.begin(), mInlets.end(), mem_fun_ref(&TTGraphInlet::reset));
	for_each(mOutlets.begin(), mOutlets.end(), mem_fun_ref(&TTGraphOutlet::reset));
	return kTTErrNone;
}

TTErr TTGraphObject::handshake(TTGraphObjectPtr objectWhichIsBeingConnected, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err;

	err = mOutlets[fromOutletNumber].connect(objectWhichIsBeingConnected, toInletNumber);
	return err;
}

TTErr TTGraphObject::connect(TTGraphObjectPtr anObject, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err;

	err = mInlets[toInletNumber].connect(anObject, fromOutletNumber);
	anObject->handshake(this, fromOutletNumber, toInletNumber);

	return err;
}

TTErr TTGraphObject::drop(TTGraphObjectPtr anObject, TTUInt16 fromOutletNumber, TTUInt16 toInletNumber)
{
	TTErr err = kTTErrInvalidValue;

	if (toInletNumber < mInlets.size())
		err = mInlets[toInletNumber].drop(anObject, fromOutletNumber);
	return err;
}

TTErr TTGraphObject::push(const TTDictionary& aDictionary)
{
	TTSymbolPtr		schema = aDictionary.getSchema();
	TTValue			v;
	TTErr			err = kTTErrMethodNotFound;
	TTMessagePtr	message = NULL;

	// If an object defines a 'dictionary' message then this trumps all the others
	err = mKernel->findMessage(TT("dictionary"), &message);
	if (!err && message) {
		(*mDictionary) = aDictionary;
		v.set(0, TTPtr(mDictionary));
		err = mKernel->sendMessage(TT("dictionary"), v);
	}
	else if (schema == TT("number")) {
		aDictionary.getValue(v);
		// TODO: maybe try seeing if there is a "number" message first and then prefer that if it exists?
		err = mKernel->sendMessage(TT("Calculate"), v);

		mDictionary->setSchema(TT("number"));
		mDictionary->setValue(v);
		// NOTE: doesn't have inlet/outlet info at this point
	}
	else {
		// not sure what to do with other dictionary schemas yet...
	}

	for (TTGraphOutletIter outlet = mOutlets.begin(); outlet != mOutlets.end(); outlet++)
		outlet->push(*mDictionary);

	return err;
}

<|endoftext|>


<commit_before>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// 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 <fstream>
#include <map>
#include "wx/toolbar.h"
#include "wx/frame.h"
#include "wx/html/htmlwin.h"
#include "wx/splitter.h"
#include "wx/treectrl.h"
#include "app/resource-id.hh"
#include "gui/art.hh"
#include "gui/help-frame.hh"
#include "util-wx/bind-event.hh"
#include "util-wx/convert-wx.hh"
#include "util-wx/fwd-bind.hh"
#include "util-wx/gui-util.hh"
#include "util-wx/make-event.hh"
#include "util/distinct.hh"
#include "util/optional.hh"

namespace faint{

MAKE_FAINT_COMMAND_EVENT(FAINT_CLOSE_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_MAXIMIZE_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_BACK_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_END);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_HOME);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_LINE_UP);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_LINE_DOWN);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_PAGE_DOWN);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_PAGE_UP);

static bool send_event(wxWindow* window, int eventId){
  wxCommandEvent newEvent(eventId);
  window->GetEventHandler()->ProcessEvent(newEvent);
  return true;
}

static bool is_web_link(const wxHtmlLinkInfo& link){
  auto href = link.GetHref();
  return href.StartsWith("http://") || href.StartsWith("https://");
}

static bool is_help_link(const wxHtmlLinkInfo& link){
  return !is_web_link(link);
}

bool common_help_key(wxWindow* window, const wxKeyEvent& event){
  // Handle key-presses common to windows within the HelpFrame. The
  // window parameter is the window that received the event, and is used
  // for propagating new events to the frame
  if (event.GetKeyCode() == WXK_ESCAPE){
    return send_event(window, FAINT_CLOSE_HELP);
  }
  else if (event.GetKeyCode() == WXK_RETURN && event.AltDown()){
    return send_event(window, FAINT_MAXIMIZE_HELP);
  }
  else if (event.GetKeyCode() == WXK_BACK){
    return send_event(window, FAINT_BACK_HELP);
  }
  else if (event.GetKeyCode() == WXK_SPACE || event.GetKeyCode() == WXK_PAGEDOWN){
    return send_event(window, FAINT_SCROLL_HELP_PAGE_DOWN);
  }
  else if (event.GetKeyCode() == WXK_PAGEUP){
    return send_event(window, FAINT_SCROLL_HELP_PAGE_UP);
  }
  else if (event.GetKeyCode() == WXK_UP && event.ControlDown()){
    return send_event(window, FAINT_SCROLL_HELP_LINE_UP);
  }
  else if (event.GetKeyCode() == WXK_DOWN && event.ControlDown()){
    return send_event(window, FAINT_SCROLL_HELP_LINE_DOWN);
  }
  else if (event.GetKeyCode() == WXK_HOME){
    return send_event(window, FAINT_SCROLL_HELP_HOME);
  }
  else if (event.GetKeyCode() == WXK_END){
    return send_event(window, FAINT_SCROLL_HELP_END);
  }
  return false;
}

wxSplitterWindow* create_help_splitter(wxWindow* parent){
  wxSplitterWindow* splitter = new wxSplitterWindow(parent);

  // Prevent unsplitting
  splitter->SetMinimumPaneSize(20);

  // Only grow the right window when rescaling
  splitter->SetSashGravity(0.0);
  return splitter;
}

class HelpWindow : public wxHtmlWindow{
// The html area for the help-text. Uses the basic wxHtmlWindow to
// avoid heavier dependencies (like wxWebView), this should be enough
// for the help system.
public:
  explicit HelpWindow(wxWindow* parent)
    : wxHtmlWindow(parent,
      wxID_ANY,
      wxDefaultPosition,
      wxDefaultSize,
      wxHW_DEFAULT_STYLE | wxBORDER_THEME)
  {
    bind_fwd(this, wxEVT_KEY_DOWN,
      [this](wxKeyEvent& event){
        bool handled = common_help_key(this, event);
        if (!handled){
          event.Skip();
        }
      });
  }

  void FaintLineDown(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y + 1);
  }

  void FaintLineUp(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y - 1);
  }

  void FaintPageDown(){
    int steps = GetScrollPageSize(wxVERTICAL);
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y + steps - m_keep);
  }

  void FaintPageUp(){
    int steps = GetScrollPageSize(wxVERTICAL);
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y - steps + m_keep);
  }

  void FaintHome(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, 0);
  }

  void FaintEnd(){
    wxPoint pos = GetViewStart();
    int x, y;
    GetVirtualSize(&x, &y);
    Scroll(pos.x, y);
  }
private:
  void OnLinkClicked(const wxHtmlLinkInfo& link) override{
    if (is_web_link(link)){
      // External links should open in the default browser, not the
      // help window.
      wxLaunchDefaultBrowser(link.GetHref());
    }
    else{
      wxHtmlWindow::OnLinkClicked(link);
    }
  }

  // (Approximate-) lines to keep from the current page while
  // scrolling a page, to make it easier to follow scrolling with pgdn, pgup
  static const int m_keep = 2;
};

class HelpTree;
using page_filename = Distinct<wxString, HelpTree, 0>;
using page_map_t = std::map<wxTreeItemId, page_filename>;

class HelpTree : public wxTreeCtrl{
// The tree-based table of contents for the HelpFrame.
public:
  HelpTree(wxWindow* parent, const wxString& contentsFile) :
    wxTreeCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
      wxTR_DEFAULT_STYLE|wxTR_HIDE_ROOT|wxTR_NO_LINES|wxTR_TWIST_BUTTONS)
  {
    m_root = AddRoot("");
    CreateFromFile(contentsFile);

    bind_fwd(this, wxEVT_KEY_DOWN,
      [this](wxKeyEvent& event){

        bool handled = common_help_key(this, event);
        if (!handled){
          event.Skip();
        }
      });
  }

  wxTreeItemId AddPage(const wxString& title, const page_filename& page){
    return AddChildPage(m_root, title, page);
  }

  wxTreeItemId AddChildPage(const wxTreeItemId& parent,
    const wxString& title,
    const page_filename& page)
  {
    wxTreeItemId id = AppendItem(parent, title);
    m_idToPage.insert(std::make_pair(id, page));
    return id;
  }

  page_filename GetPage(const wxTreeItemId& id){
    return m_idToPage.at(id);
  }

  void Next(){
    wxTreeItemId selected = GetSelection();
    if (!selected.IsOk()){
      return;
    }
    wxTreeItemId next = GetNextSibling(selected);
    if (!next.IsOk()){
      return;
    }
    SelectItem(next);
  }

  void Prev(){
    wxTreeItemId selected = GetSelection();
    if (!selected.IsOk()){
      return;
    }
    wxTreeItemId prev = GetPrevSibling(selected);
    if (!prev.IsOk()){
      return;
    }
    SelectItem(prev);
  }

  void SelectPage(const page_filename& name){
    for (const auto& mapItem : m_idToPage){
      if (mapItem.second == name){
        SelectItem(mapItem.first);
      }
    }
  }
private:
  void CreateFromFile(const wxString& contentsFile){
    std::ifstream f(iostream_friendly(contentsFile));
    std::string s;
    wxTreeItemId currentParent = m_root; // Note: This is too simplistic for deeper nesting
    while (std::getline(f,s)){
      if (s.empty()){
        break;
      }

      size_t sep = s.find(";");
      assert(sep != wxString::npos);
      page_filename filename(s.substr(sep + 1));
      wxString name = s.substr(0, sep);
      bool child = name[0] == '>';
      if (child){
        name = name.substr(1);
      }
      if (child){
        AddChildPage(currentParent, name, filename);
      }
      else {
        currentParent = AddPage(name, filename);
      }
    }
  }

  page_map_t m_idToPage;
  wxTreeItemId m_root;
};

const int help_toolbar_back=0;
const int help_toolbar_forward=1;

static page_filename parse_page_filename(const wxString& str){
  size_t pos = str.rfind("/");
  if (pos == wxString::npos){
    pos = str.rfind("\\");
  }
  if (pos == wxString::npos){
    return page_filename("");
  }
  return page_filename(str.substr(pos + 1));
}

static page_filename link_to_filename(const wxString& str){
  size_t pos = str.rfind("#");
  if (pos == wxString::npos){
    return page_filename(wxString(str));
  }
  return page_filename(str.substr(0, pos));
}

class HelpFrame::HelpFrameImpl : public wxFrame {
public:
  HelpFrameImpl(const wxString& rootDir, const Art& art)
    : wxFrame(null_parent(), wxID_ANY, "Faint Help"),
      m_html(nullptr),
      m_tree(nullptr),
      m_rootDir(rootDir),
      m_initialized(false),
      m_updateOnTree(true)
  {
    SetInitialSize(wxSize(800,600));
    wxSplitterWindow* splitter = create_help_splitter(this);
    m_tree = make_dumb<HelpTree>(splitter, m_rootDir + "/contents.dat");
    m_html = make_dumb<HelpWindow>(splitter);
    splitter->SplitVertically(m_tree.get(), m_html.get());
    splitter->SetSashPosition(200);
    auto toolbar = new wxToolBar(this, wxID_ANY);
    toolbar->AddTool(help_toolbar_back, "Back", art.Get(Icon::HELP_BACK));
    toolbar->AddTool(help_toolbar_forward, "Forward", art.Get(Icon::HELP_FORWARD));
    toolbar->Realize();
    SetToolBar(toolbar);

    bind_fwd(this, wxEVT_CLOSE_WINDOW,
      [this](wxCloseEvent& event){
        if (event.CanVeto()){
          // Hide instead of close if possible
          event.Veto();
          Hide();
        }
        else {
          Destroy();
        }
      });

    bind(this, EVT_FAINT_CLOSE_HELP,
      // Handles custom EVT_CLOSE_HELP from contained windows.
      [this](){
        // Non-forcing close
        Close();
      });

    bind(this, EVT_FAINT_MAXIMIZE_HELP,
      [this](){
        Maximize(!IsMaximized());
      });

    bind_fwd(this, wxEVT_HTML_LINK_CLICKED,
      [this](wxHtmlLinkEvent& evt){
        m_updateOnTree = false;
        wxHtmlLinkInfo link(evt.GetLinkInfo());
        assert(is_help_link(link));
        m_tree->SelectPage(link_to_filename(link.GetHref()));
        evt.Skip();
        m_updateOnTree = true;
      });

    bind(this, EVT_FAINT_SCROLL_HELP_END,
      [this](){
        m_html->FaintEnd();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_HOME,
      [this](){
        m_html->FaintHome();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_LINE_DOWN,
      [this](){
        if (m_tree->HasFocus()){
          m_html->FaintLineDown();
        }
        else {
          m_tree->Next();
        }
      });

    bind(this, EVT_FAINT_SCROLL_HELP_LINE_UP,
      [this](){
        if (m_tree->HasFocus()){
          m_html->FaintLineUp();
        }
        else {
          m_tree->Prev();
        }
      });

    bind(this, EVT_FAINT_SCROLL_HELP_PAGE_DOWN,
      [this](){
        m_html->FaintPageDown();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_PAGE_UP,
      [this](){
        m_html->FaintPageUp();
      });

    bind_fwd(this, wxEVT_TOOL,
      [this](wxCommandEvent& evt){
        if (evt.GetId() == help_toolbar_back){
          GoBack();
        }
        else if (evt.GetId() == help_toolbar_forward){
          GoForward();
        }
      });

    bind_fwd(this, wxEVT_TREE_SEL_CHANGED,
      [this](wxTreeEvent& evt){
        evt.Skip();
        wxTreeItemId newItem = evt.GetItem();
        wxTreeItemId old = evt.GetOldItem();
        if (old.IsOk()){
          m_tree->SetItemBold(evt.GetOldItem(), false);
        }
        m_tree->SetItemBold(newItem);
        if (m_updateOnTree){
          page_filename page(m_tree->GetPage(evt.GetItem()));
          wxFileName htmlFile(m_rootDir + "/" + page.Get());
          m_html->LoadFile(htmlFile);
        }
      });
  }

  bool FaintHasFocus(){
    return HasFocus() || m_html->HasFocus() || m_tree->HasFocus();
  }

  void FaintShow(){
    if (!m_initialized){
      m_html->LoadFile(wxFileName(m_rootDir + "/main.html"));
      m_initialized = true;
    }
    Show();
  }
private:
  void GoBack(){
    m_html->HistoryBack();
    UpdateTreeSelection();
  }
  void GoForward(){
    m_html->HistoryForward();
    UpdateTreeSelection();
  }

  void UpdateTreeSelection(){
    m_tree->SelectPage(parse_page_filename(m_html->GetOpenedPage()));
  }

  dumb_ptr<HelpWindow> m_html;
  dumb_ptr<HelpTree> m_tree;
  const wxString m_rootDir;
  bool m_initialized;
  bool m_updateOnTree;
};

HelpFrame::HelpFrame(const DirPath& rootDir, const Art& art)
  : m_impl(make_dumb<HelpFrameImpl>(to_wx(rootDir.Str()), art))
{
  restore_persisted_state(m_impl.get(), storage_name("HelpFrame"));
}

HelpFrame::~HelpFrame(){
  if (m_impl != nullptr){
    Close();
  }
 }

void HelpFrame::Close(){
  m_impl->Close(true);
  m_impl = nullptr;
}

void HelpFrame::Hide(){
  m_impl->Hide();
}

bool HelpFrame::HasFocus() const{
  return m_impl->FaintHasFocus();
}

bool HelpFrame::IsHidden() const{
  return !IsShown();
}

bool HelpFrame::IsIconized() const{
  return m_impl->IsIconized();
}

bool HelpFrame::IsShown() const{
  return m_impl->IsShown();
}

void HelpFrame::Raise(){
  m_impl->Raise();
}

void HelpFrame::Restore(){
  m_impl->Restore();
}

void HelpFrame::SetIcons(const wxIcon& icon16, const wxIcon& icon32){
  m_impl->SetIcons(bundle_icons(icon16, icon32));
}

void HelpFrame::Show(){
  m_impl->FaintShow();
}

} // namespace
<commit_msg>Using slice instead of substr.<commit_after>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// 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 <fstream>
#include <map>
#include "wx/toolbar.h"
#include "wx/frame.h"
#include "wx/html/htmlwin.h"
#include "wx/splitter.h"
#include "wx/treectrl.h"
#include "app/resource-id.hh"
#include "gui/art.hh"
#include "gui/help-frame.hh"
#include "util-wx/bind-event.hh"
#include "util-wx/convert-wx.hh"
#include "util-wx/fwd-bind.hh"
#include "util-wx/gui-util.hh"
#include "util-wx/make-event.hh"
#include "util-wx/slice-wx.hh"
#include "util/distinct.hh"
#include "util/optional.hh"

namespace faint{

MAKE_FAINT_COMMAND_EVENT(FAINT_CLOSE_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_MAXIMIZE_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_BACK_HELP);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_END);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_HOME);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_LINE_UP);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_LINE_DOWN);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_PAGE_DOWN);
MAKE_FAINT_COMMAND_EVENT(FAINT_SCROLL_HELP_PAGE_UP);

static bool send_event(wxWindow* window, int eventId){
  wxCommandEvent newEvent(eventId);
  window->GetEventHandler()->ProcessEvent(newEvent);
  return true;
}

static bool is_web_link(const wxHtmlLinkInfo& link){
  auto href = link.GetHref();
  return href.StartsWith("http://") || href.StartsWith("https://");
}

static bool is_help_link(const wxHtmlLinkInfo& link){
  return !is_web_link(link);
}

bool common_help_key(wxWindow* window, const wxKeyEvent& event){
  // Handle key-presses common to windows within the HelpFrame. The
  // window parameter is the window that received the event, and is used
  // for propagating new events to the frame
  if (event.GetKeyCode() == WXK_ESCAPE){
    return send_event(window, FAINT_CLOSE_HELP);
  }
  else if (event.GetKeyCode() == WXK_RETURN && event.AltDown()){
    return send_event(window, FAINT_MAXIMIZE_HELP);
  }
  else if (event.GetKeyCode() == WXK_BACK){
    return send_event(window, FAINT_BACK_HELP);
  }
  else if (event.GetKeyCode() == WXK_SPACE || event.GetKeyCode() == WXK_PAGEDOWN){
    return send_event(window, FAINT_SCROLL_HELP_PAGE_DOWN);
  }
  else if (event.GetKeyCode() == WXK_PAGEUP){
    return send_event(window, FAINT_SCROLL_HELP_PAGE_UP);
  }
  else if (event.GetKeyCode() == WXK_UP && event.ControlDown()){
    return send_event(window, FAINT_SCROLL_HELP_LINE_UP);
  }
  else if (event.GetKeyCode() == WXK_DOWN && event.ControlDown()){
    return send_event(window, FAINT_SCROLL_HELP_LINE_DOWN);
  }
  else if (event.GetKeyCode() == WXK_HOME){
    return send_event(window, FAINT_SCROLL_HELP_HOME);
  }
  else if (event.GetKeyCode() == WXK_END){
    return send_event(window, FAINT_SCROLL_HELP_END);
  }
  return false;
}

wxSplitterWindow* create_help_splitter(wxWindow* parent){
  wxSplitterWindow* splitter = new wxSplitterWindow(parent);

  // Prevent unsplitting
  splitter->SetMinimumPaneSize(20);

  // Only grow the right window when rescaling
  splitter->SetSashGravity(0.0);
  return splitter;
}

class HelpWindow : public wxHtmlWindow{
// The html area for the help-text. Uses the basic wxHtmlWindow to
// avoid heavier dependencies (like wxWebView), this should be enough
// for the help system.
public:
  explicit HelpWindow(wxWindow* parent)
    : wxHtmlWindow(parent,
      wxID_ANY,
      wxDefaultPosition,
      wxDefaultSize,
      wxHW_DEFAULT_STYLE | wxBORDER_THEME)
  {
    bind_fwd(this, wxEVT_KEY_DOWN,
      [this](wxKeyEvent& event){
        bool handled = common_help_key(this, event);
        if (!handled){
          event.Skip();
        }
      });
  }

  void FaintLineDown(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y + 1);
  }

  void FaintLineUp(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y - 1);
  }

  void FaintPageDown(){
    int steps = GetScrollPageSize(wxVERTICAL);
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y + steps - m_keep);
  }

  void FaintPageUp(){
    int steps = GetScrollPageSize(wxVERTICAL);
    wxPoint pos = GetViewStart();
    Scroll(pos.x, pos.y - steps + m_keep);
  }

  void FaintHome(){
    wxPoint pos = GetViewStart();
    Scroll(pos.x, 0);
  }

  void FaintEnd(){
    wxPoint pos = GetViewStart();
    int x, y;
    GetVirtualSize(&x, &y);
    Scroll(pos.x, y);
  }
private:
  void OnLinkClicked(const wxHtmlLinkInfo& link) override{
    if (is_web_link(link)){
      // External links should open in the default browser, not the
      // help window.
      wxLaunchDefaultBrowser(link.GetHref());
    }
    else{
      wxHtmlWindow::OnLinkClicked(link);
    }
  }

  // (Approximate-) lines to keep from the current page while
  // scrolling a page, to make it easier to follow scrolling with pgdn, pgup
  static const int m_keep = 2;
};

class HelpTree;
using page_filename = Distinct<wxString, HelpTree, 0>;
using page_map_t = std::map<wxTreeItemId, page_filename>;

class HelpTree : public wxTreeCtrl{
// The tree-based table of contents for the HelpFrame.
public:
  HelpTree(wxWindow* parent, const wxString& contentsFile) :
    wxTreeCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
      wxTR_DEFAULT_STYLE|wxTR_HIDE_ROOT|wxTR_NO_LINES|wxTR_TWIST_BUTTONS)
  {
    m_root = AddRoot("");
    CreateFromFile(contentsFile);

    bind_fwd(this, wxEVT_KEY_DOWN,
      [this](wxKeyEvent& event){

        bool handled = common_help_key(this, event);
        if (!handled){
          event.Skip();
        }
      });
  }

  wxTreeItemId AddPage(const wxString& title, const page_filename& page){
    return AddChildPage(m_root, title, page);
  }

  wxTreeItemId AddChildPage(const wxTreeItemId& parent,
    const wxString& title,
    const page_filename& page)
  {
    wxTreeItemId id = AppendItem(parent, title);
    m_idToPage.insert(std::make_pair(id, page));
    return id;
  }

  page_filename GetPage(const wxTreeItemId& id){
    return m_idToPage.at(id);
  }

  void Next(){
    wxTreeItemId selected = GetSelection();
    if (!selected.IsOk()){
      return;
    }
    wxTreeItemId next = GetNextSibling(selected);
    if (!next.IsOk()){
      return;
    }
    SelectItem(next);
  }

  void Prev(){
    wxTreeItemId selected = GetSelection();
    if (!selected.IsOk()){
      return;
    }
    wxTreeItemId prev = GetPrevSibling(selected);
    if (!prev.IsOk()){
      return;
    }
    SelectItem(prev);
  }

  void SelectPage(const page_filename& name){
    for (const auto& mapItem : m_idToPage){
      if (mapItem.second == name){
        SelectItem(mapItem.first);
      }
    }
  }
private:
  void CreateFromFile(const wxString& contentsFile){
    std::ifstream f(iostream_friendly(contentsFile));
    std::string s;
    wxTreeItemId currentParent = m_root; // Note: This is too simplistic for deeper nesting
    while (std::getline(f,s)){
      if (s.empty()){
        break;
      }

      size_t sep = s.find(";");
      assert(sep != wxString::npos);
      wxString name = slice_up_to(s, sep);
      page_filename filename(slice_from(s, sep + 1));
      bool child = name[0] == '>';
      if (child){
        name = slice_from(name, 1);
      }
      if (child){
        AddChildPage(currentParent, name, filename);
      }
      else {
        currentParent = AddPage(name, filename);
      }
    }
  }

  page_map_t m_idToPage;
  wxTreeItemId m_root;
};

const int help_toolbar_back=0;
const int help_toolbar_forward=1;

static page_filename parse_page_filename(const wxString& str){
  size_t pos = str.rfind("/");
  if (pos == wxString::npos){
    pos = str.rfind("\\");
  }
  if (pos == wxString::npos){
    return page_filename("");
  }
  return page_filename(slice_from(str, pos + 1));
}

static page_filename link_to_filename(const wxString& str){
  size_t pos = str.rfind("#");
  if (pos == wxString::npos){
    return page_filename(wxString(str));
  }
  return page_filename(slice_up_to(str, pos));
}

class HelpFrame::HelpFrameImpl : public wxFrame {
public:
  HelpFrameImpl(const wxString& rootDir, const Art& art)
    : wxFrame(null_parent(), wxID_ANY, "Faint Help"),
      m_html(nullptr),
      m_tree(nullptr),
      m_rootDir(rootDir),
      m_initialized(false),
      m_updateOnTree(true)
  {
    SetInitialSize(wxSize(800,600));
    wxSplitterWindow* splitter = create_help_splitter(this);
    m_tree = make_dumb<HelpTree>(splitter, m_rootDir + "/contents.dat");
    m_html = make_dumb<HelpWindow>(splitter);
    splitter->SplitVertically(m_tree.get(), m_html.get());
    splitter->SetSashPosition(200);
    auto toolbar = new wxToolBar(this, wxID_ANY);
    toolbar->AddTool(help_toolbar_back, "Back", art.Get(Icon::HELP_BACK));
    toolbar->AddTool(help_toolbar_forward, "Forward", art.Get(Icon::HELP_FORWARD));
    toolbar->Realize();
    SetToolBar(toolbar);

    bind_fwd(this, wxEVT_CLOSE_WINDOW,
      [this](wxCloseEvent& event){
        if (event.CanVeto()){
          // Hide instead of close if possible
          event.Veto();
          Hide();
        }
        else {
          Destroy();
        }
      });

    bind(this, EVT_FAINT_CLOSE_HELP,
      // Handles custom EVT_CLOSE_HELP from contained windows.
      [this](){
        // Non-forcing close
        Close();
      });

    bind(this, EVT_FAINT_MAXIMIZE_HELP,
      [this](){
        Maximize(!IsMaximized());
      });

    bind_fwd(this, wxEVT_HTML_LINK_CLICKED,
      [this](wxHtmlLinkEvent& evt){
        m_updateOnTree = false;
        wxHtmlLinkInfo link(evt.GetLinkInfo());
        assert(is_help_link(link));
        m_tree->SelectPage(link_to_filename(link.GetHref()));
        evt.Skip();
        m_updateOnTree = true;
      });

    bind(this, EVT_FAINT_SCROLL_HELP_END,
      [this](){
        m_html->FaintEnd();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_HOME,
      [this](){
        m_html->FaintHome();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_LINE_DOWN,
      [this](){
        if (m_tree->HasFocus()){
          m_html->FaintLineDown();
        }
        else {
          m_tree->Next();
        }
      });

    bind(this, EVT_FAINT_SCROLL_HELP_LINE_UP,
      [this](){
        if (m_tree->HasFocus()){
          m_html->FaintLineUp();
        }
        else {
          m_tree->Prev();
        }
      });

    bind(this, EVT_FAINT_SCROLL_HELP_PAGE_DOWN,
      [this](){
        m_html->FaintPageDown();
      });

    bind(this, EVT_FAINT_SCROLL_HELP_PAGE_UP,
      [this](){
        m_html->FaintPageUp();
      });

    bind_fwd(this, wxEVT_TOOL,
      [this](wxCommandEvent& evt){
        if (evt.GetId() == help_toolbar_back){
          GoBack();
        }
        else if (evt.GetId() == help_toolbar_forward){
          GoForward();
        }
      });

    bind_fwd(this, wxEVT_TREE_SEL_CHANGED,
      [this](wxTreeEvent& evt){
        evt.Skip();
        wxTreeItemId newItem = evt.GetItem();
        wxTreeItemId old = evt.GetOldItem();
        if (old.IsOk()){
          m_tree->SetItemBold(evt.GetOldItem(), false);
        }
        m_tree->SetItemBold(newItem);
        if (m_updateOnTree){
          page_filename page(m_tree->GetPage(evt.GetItem()));
          wxFileName htmlFile(m_rootDir + "/" + page.Get());
          m_html->LoadFile(htmlFile);
        }
      });
  }

  bool FaintHasFocus(){
    return HasFocus() || m_html->HasFocus() || m_tree->HasFocus();
  }

  void FaintShow(){
    if (!m_initialized){
      m_html->LoadFile(wxFileName(m_rootDir + "/main.html"));
      m_initialized = true;
    }
    Show();
  }
private:
  void GoBack(){
    m_html->HistoryBack();
    UpdateTreeSelection();
  }
  void GoForward(){
    m_html->HistoryForward();
    UpdateTreeSelection();
  }

  void UpdateTreeSelection(){
    m_tree->SelectPage(parse_page_filename(m_html->GetOpenedPage()));
  }

  dumb_ptr<HelpWindow> m_html;
  dumb_ptr<HelpTree> m_tree;
  const wxString m_rootDir;
  bool m_initialized;
  bool m_updateOnTree;
};

HelpFrame::HelpFrame(const DirPath& rootDir, const Art& art)
  : m_impl(make_dumb<HelpFrameImpl>(to_wx(rootDir.Str()), art))
{
  restore_persisted_state(m_impl.get(), storage_name("HelpFrame"));
}

HelpFrame::~HelpFrame(){
  if (m_impl != nullptr){
    Close();
  }
 }

void HelpFrame::Close(){
  m_impl->Close(true);
  m_impl = nullptr;
}

void HelpFrame::Hide(){
  m_impl->Hide();
}

bool HelpFrame::HasFocus() const{
  return m_impl->FaintHasFocus();
}

bool HelpFrame::IsHidden() const{
  return !IsShown();
}

bool HelpFrame::IsIconized() const{
  return m_impl->IsIconized();
}

bool HelpFrame::IsShown() const{
  return m_impl->IsShown();
}

void HelpFrame::Raise(){
  m_impl->Raise();
}

void HelpFrame::Restore(){
  m_impl->Restore();
}

void HelpFrame::SetIcons(const wxIcon& icon16, const wxIcon& icon32){
  m_impl->SetIcons(bundle_icons(icon16, icon32));
}

void HelpFrame::Show(){
  m_impl->FaintShow();
}

} // namespace
<|endoftext|>


<commit_before>// Extended Euclid: gcd(a, b) = x*a + y*b
void euclid(int a, int b, int &x, int &y, int &d) {
  if (b) euclid(b, a%b, y, x, d), y -= x*(a/b);
  else x = 1, y = 0, d = a;
}

// Solves a*x + b*y = c
bool find_any_solution(int a, int b, int c, int &x0, int &y0, int &g) {
  euclid(abs(a), abs(b), x0, y0, g);
  if (c % g) {
    return false;
  }

  x0 *= c / g;
  y0 *= c / g;
  if (a < 0) x0 = -x0;
  if (b < 0) y0 = -y0;
  return true;
}

// all x' and y' are a valid solution for any integer k
// x' = x + k*b/gcd
// y' = y - k*a/gcd
// Here a and b are actually a/gcd and b/gcd
void shift_solution (int & x, int & y, int a, int b, int cnt) {
  x += cnt * b;
  y -= cnt * a;
}

// Find the amount of solutions in a interval of x and y
int find_all_solutions (int a, int b, int c, int minx, int maxx, int miny, int maxy) {
  int x, y, g;
  if (! find_any_solution (a, b, c, x, y, g))
    return 0;
  a /= g;  b /= g;

  int sign_a = a>0 ? +1 : -1;
  int sign_b = b>0 ? +1 : -1;

  shift_solution (x, y, a, b, (minx - x) / b);
  if (x < minx)
    shift_solution (x, y, a, b, sign_b);
  if (x > maxx)
    return 0;
  int lx1 = x;

  shift_solution (x, y, a, b, (maxx - x) / b);
  if (x > maxx)
    shift_solution (x, y, a, b, -sign_b);
  int rx1 = x;

  shift_solution (x, y, a, b, - (miny - y) / a);
  if (y < miny)
    shift_solution (x, y, a, b, -sign_a);
  if (y > maxy)
    return 0;
  int lx2 = x;

  shift_solution (x, y, a, b, - (maxy - y) / a);
  if (y > maxy)
    shift_solution (x, y, a, b, sign_a);
  int rx2 = x;

  if (lx2 > rx2)
    swap (lx2, rx2);
  int lx = max (lx1, lx2);
  int rx = min (rx1, rx2);

  if (lx > rx) return 0;
  return (rx - lx) / abs(b) + 1;
}

//Solves
//t = a mod m1
//t = b mod m2
//ans = t mod lcm(m1, m2)
bool chinese_remainder(ll a, ll b, ll m1, ll m2, ll &ans, ll &lcm){
  ll x, y, g, c = b - a;
  euclid(m1, m2, x, y, g);
  if(c%g) return false;

  lcm = m1/g*m2;
  ans = ((a + m1*((x*c/g)%(m2/g)))%lcm + lcm)%lcm;
  return true;
}
<commit_msg>Update extended-euclid.cpp<commit_after>// Extended Euclid: gcd(a, b) = x*a + y*b
void euclid(int a, int b, int &x, int &y, int &d) {
  if (b) euclid(b, a%b, y, x, d), y -= x*(a/b);
  else x = 1, y = 0, d = a;
}

// Solves a*x + b*y = c
bool find_any_solution(int a, int b, int c, int &x0, int &y0, int &g) {
  euclid(abs(a), abs(b), x0, y0, g);
  if (c % g) {
    return false;
  }

  x0 *= c / g;
  y0 *= c / g;
  if (a < 0) x0 = -x0;
  if (b < 0) y0 = -y0;
  return true;
}

// all x' and y' are a valid solution for any integer k
// x' = x + k*b/gcd
// y' = y - k*a/gcd
// Here a and b are actually a/gcd and b/gcd
void shift_solution (int & x, int & y, int a, int b, int cnt) {
  x += cnt * b;
  y -= cnt * a;
}

// Find the amount of solutions in a interval of x and y
int find_all_solutions (int a, int b, int c, int minx, int maxx, int miny, int maxy) {
  int x, y, g;
  if (! find_any_solution (a, b, c, x, y, g))
    return 0;
  a /= g;  b /= g;

  int sign_a = a>0 ? +1 : -1;
  int sign_b = b>0 ? +1 : -1;

  shift_solution (x, y, a, b, (minx - x) / b);
  if (x < minx)
    shift_solution (x, y, a, b, sign_b);
  if (x > maxx)
    return 0;
  int lx1 = x;

  shift_solution (x, y, a, b, (maxx - x) / b);
  if (x > maxx)
    shift_solution (x, y, a, b, -sign_b);
  int rx1 = x;

  shift_solution (x, y, a, b, - (miny - y) / a);
  if (y < miny)
    shift_solution (x, y, a, b, -sign_a);
  if (y > maxy)
    return 0;
  int lx2 = x;

  shift_solution (x, y, a, b, - (maxy - y) / a);
  if (y > maxy)
    shift_solution (x, y, a, b, sign_a);
  int rx2 = x;

  if (lx2 > rx2)
    swap (lx2, rx2);
  int lx = max (lx1, lx2);
  int rx = min (rx1, rx2);

  if (lx > rx) return 0;
  return (rx - lx) / abs(b) + 1;
}

//Solves
//t = a mod m1
//t = b mod m2
//ans = t mod lcm(m1, m2)
bool chinese_remainder(ll a, ll b, ll m1, ll m2, ll &ans, ll &lcm){
  ll x, y, g, c = b - a;
  euclid(m1, m2, x, y, g);
  if(c%g) return false;

  lcm = m1/g*m2;
  ans = ((a + m1*((c/g*x)%(m2/g)))%lcm + lcm)%lcm;
  return true;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <chrono>
#include <map>
#include <optional>
#include <seastar/core/byteorder.hh>
#include <seastar/core/sstring.hh>
#include "seastarx.hh"

//
// This hashing differs from std::hash<> in that it decouples knowledge about
// type structure from the way the hash value is calculated:
//  * appending_hash<T> instantiation knows about what data should be included in the hash for type T.
//  * Hasher object knows how to combine the data into the final hash.
//
// The appending_hash<T> should always feed some data into the hasher, regardless of the state the object is in,
// in order for the hash to be highly sensitive for value changes. For example, vector<optional<T>> should
// ideally feed different values for empty vector and a vector with a single empty optional.
//
// appending_hash<T> is machine-independent.
//

// The Hasher concept
struct Hasher {
    void update(const char* ptr, size_t size);
};

template<typename T, typename Enable = void>
struct appending_hash;

template<typename Hasher, typename T, typename... Args>
inline
void feed_hash(Hasher& h, const T& value, Args&&... args) {
    appending_hash<T>()(h, value, std::forward<Args>(args)...);
};

template<typename T>
struct appending_hash<T, std::enable_if_t<std::is_arithmetic<T>::value>> {
    template<typename Hasher>
    void operator()(Hasher& h, T value) const {
        auto value_le = cpu_to_le(value);
        h.update(reinterpret_cast<const char*>(&value_le), sizeof(T));
    }
};

template<>
struct appending_hash<bool> {
    template<typename Hasher>
    void operator()(Hasher& h, bool value) const {
        feed_hash(h, static_cast<uint8_t>(value));
    }
};

template<typename T>
struct appending_hash<T, std::enable_if_t<std::is_enum<T>::value>> {
    template<typename Hasher>
    void operator()(Hasher& h, const T& value) const {
        feed_hash(h, static_cast<std::underlying_type_t<T>>(value));
    }
};

template<typename T>
struct appending_hash<std::optional<T>>  {
    template<typename Hasher>
    void operator()(Hasher& h, const std::optional<T>& value) const {
        if (value) {
            feed_hash(h, true);
            feed_hash(h, *value);
        } else {
            feed_hash(h, false);
        }
    }
};

template<size_t N>
struct appending_hash<char[N]>  {
    template<typename Hasher>
    void operator()(Hasher& h, const char (&value) [N]) const {
        feed_hash(h, N);
        h.update(value, N);
    }
};

template<typename T>
struct appending_hash<std::vector<T>> {
    template<typename Hasher>
    void operator()(Hasher& h, const std::vector<T>& value) const {
        feed_hash(h, value.size());
        for (auto&& v : value) {
            appending_hash<T>()(h, v);
        }
    }
};

template<typename K, typename V>
struct appending_hash<std::map<K, V>> {
    template<typename Hasher>
    void operator()(Hasher& h, const std::map<K, V>& value) const {
        feed_hash(h, value.size());
        for (auto&& e : value) {
            appending_hash<K>()(h, e.first);
            appending_hash<V>()(h, e.second);
        }
    }
};

template<>
struct appending_hash<sstring> {
    template<typename Hasher>
    void operator()(Hasher& h, const sstring& v) const {
        feed_hash(h, v.size());
        h.update(reinterpret_cast<const char*>(v.cbegin()), v.size() * sizeof(sstring::value_type));
    }
};

template<>
struct appending_hash<std::string> {
    template<typename Hasher>
    void operator()(Hasher& h, const std::string& v) const {
        feed_hash(h, v.size());
        h.update(reinterpret_cast<const char*>(v.data()), v.size() * sizeof(std::string::value_type));
    }
};

template<typename T, typename R>
struct appending_hash<std::chrono::duration<T, R>> {
    template<typename Hasher>
    void operator()(Hasher& h, std::chrono::duration<T, R> v) const {
        feed_hash(h, v.count());
    }
};

template<typename Clock, typename Duration>
struct appending_hash<std::chrono::time_point<Clock, Duration>> {
    template<typename Hasher>
    void operator()(Hasher& h, std::chrono::time_point<Clock, Duration> v) const {
        feed_hash(h, v.time_since_epoch().count());
    }
};
<commit_msg>hashing: Introduce C++ concept for the hasher<commit_after>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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
 * (at your option) any later version.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <chrono>
#include <map>
#include <optional>
#include <seastar/core/byteorder.hh>
#include <seastar/core/sstring.hh>
#include "seastarx.hh"
#include <seastar/util/gcc6-concepts.hh>

//
// This hashing differs from std::hash<> in that it decouples knowledge about
// type structure from the way the hash value is calculated:
//  * appending_hash<T> instantiation knows about what data should be included in the hash for type T.
//  * Hasher object knows how to combine the data into the final hash.
//
// The appending_hash<T> should always feed some data into the hasher, regardless of the state the object is in,
// in order for the hash to be highly sensitive for value changes. For example, vector<optional<T>> should
// ideally feed different values for empty vector and a vector with a single empty optional.
//
// appending_hash<T> is machine-independent.
//

GCC6_CONCEPT(
    template<typename H>
    concept bool Hasher() {
        return requires(H& h, const char* ptr, size_t size) {
            { h.update(ptr, size) } -> void
        };
    }
)

template<typename T, typename Enable = void>
struct appending_hash;

template<typename H, typename T, typename... Args>
GCC6_CONCEPT(requires Hasher<H>())
inline
void feed_hash(H& h, const T& value, Args&&... args) {
    appending_hash<T>()(h, value, std::forward<Args>(args)...);
};

template<typename T>
struct appending_hash<T, std::enable_if_t<std::is_arithmetic<T>::value>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, T value) const {
        auto value_le = cpu_to_le(value);
        h.update(reinterpret_cast<const char*>(&value_le), sizeof(T));
    }
};

template<>
struct appending_hash<bool> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, bool value) const {
        feed_hash(h, static_cast<uint8_t>(value));
    }
};

template<typename T>
struct appending_hash<T, std::enable_if_t<std::is_enum<T>::value>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const T& value) const {
        feed_hash(h, static_cast<std::underlying_type_t<T>>(value));
    }
};

template<typename T>
struct appending_hash<std::optional<T>>  {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const std::optional<T>& value) const {
        if (value) {
            feed_hash(h, true);
            feed_hash(h, *value);
        } else {
            feed_hash(h, false);
        }
    }
};

template<size_t N>
struct appending_hash<char[N]>  {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const char (&value) [N]) const {
        feed_hash(h, N);
        h.update(value, N);
    }
};

template<typename T>
struct appending_hash<std::vector<T>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const std::vector<T>& value) const {
        feed_hash(h, value.size());
        for (auto&& v : value) {
            appending_hash<T>()(h, v);
        }
    }
};

template<typename K, typename V>
struct appending_hash<std::map<K, V>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const std::map<K, V>& value) const {
        feed_hash(h, value.size());
        for (auto&& e : value) {
            appending_hash<K>()(h, e.first);
            appending_hash<V>()(h, e.second);
        }
    }
};

template<>
struct appending_hash<sstring> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const sstring& v) const {
        feed_hash(h, v.size());
        h.update(reinterpret_cast<const char*>(v.cbegin()), v.size() * sizeof(sstring::value_type));
    }
};

template<>
struct appending_hash<std::string> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, const std::string& v) const {
        feed_hash(h, v.size());
        h.update(reinterpret_cast<const char*>(v.data()), v.size() * sizeof(std::string::value_type));
    }
};

template<typename T, typename R>
struct appending_hash<std::chrono::duration<T, R>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, std::chrono::duration<T, R> v) const {
        feed_hash(h, v.count());
    }
};

template<typename Clock, typename Duration>
struct appending_hash<std::chrono::time_point<Clock, Duration>> {
    template<typename H>
    GCC6_CONCEPT(requires Hasher<H>())
    void operator()(H& h, std::chrono::time_point<Clock, Duration> v) const {
        feed_hash(h, v.time_since_epoch().count());
    }
};
<|endoftext|>


<commit_before>//
// Created by michael on 09.11.15.
//

#include <string>
#include <memory>
#include "catch.hpp"
#include "../Core/WeaverUtils.h"
#include "../Core/GraphAnalyzer.h"

using namespace weave;
using namespace std;

TEST_CASE("Graph analyzer", "[graph analyzer]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    WeaverGraph graph;

    SECTION("Solve empty graph") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 0);
    }

    string groupA = "entityA";
    graph.CreateNodeGroup(groupA, true);
    Node node1(groupA, 1);
    graph.AddNode(node1);

    SECTION("Solve graph with single node") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 1);
        REQUIRE(properties[groupA] == node1);
    }

    Node node2(groupA, 2);
    graph.AddNode(node2);

    SECTION("Solve graph with two nodes / one group") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 1);
        REQUIRE(((properties[groupA] == node1) || (properties[groupA] == node2)));
    }

    string groupB = "entityB";
    graph.CreateNodeGroup(groupB, true);

    SECTION("Error on missing required node") {
        REQUIRE_THROWS_AS(GraphAnalyzer::SolveGraph(&graph, rs), ContractFailedException);
    }

    Node node3(groupB, 3);
    graph.AddNode(node3);

    SECTION("Solve graph with three nodes / two groups") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE(((properties[groupA] == node1) || (properties[groupA] == node2)));
        REQUIRE((properties[groupB] == node3));
    }

    SECTION("Solve graph with three nodes / two groups / edge1-3") {
        Edge edge13(1, 3, EdgeType::DIRECT);
        graph.AddEdge(edge13);
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node1));
        REQUIRE((properties[groupB] == node3));
    }

    SECTION("Solve graph with three nodes / two groups / edge2-3") {
        Edge edge23(2, 3, EdgeType::DIRECT);
        graph.AddEdge(edge23);
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node2));
        REQUIRE((properties[groupB] == node3));
    }
}
<commit_msg>Extended the graph analyzer tests<commit_after>//
// Created by michael on 09.11.15.
//

#include <string>
#include <memory>
#include "catch.hpp"
#include "../Core/WeaverUtils.h"
#include "../Core/GraphAnalyzer.h"

using namespace weave;
using namespace std;

TEST_CASE("Graph analyzer", "[graph analyzer]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    WeaverGraph graph;

    SECTION("Solve empty graph") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 0);
    }

    string groupA = "entityA";
    graph.CreateNodeGroup(groupA, true);
    Node node1(groupA, 1);
    graph.AddNode(node1);

    SECTION("Solve graph with single node") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 1);
        REQUIRE(properties[groupA] == node1);
    }

    Node node2(groupA, 2);
    graph.AddNode(node2);

    SECTION("Solve graph with two nodes / one group") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 1);
        REQUIRE(((properties[groupA] == node1) || (properties[groupA] == node2)));
    }

    string groupB = "entityB";
    graph.CreateNodeGroup(groupB, true);

    SECTION("Error on missing required node") {
        REQUIRE_THROWS_AS(GraphAnalyzer::SolveGraph(&graph, rs), ContractFailedException);
    }

    Node node3(groupB, 3);
    graph.AddNode(node3);

    SECTION("Solve graph with three nodes / two groups") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE(((properties[groupA] == node1) || (properties[groupA] == node2)));
        REQUIRE((properties[groupB] == node3));
    }

    SECTION("Solve graph with three nodes / two groups / edge1-3") {
        Edge edge13(1, 3, EdgeType::DIRECT);
        graph.AddEdge(edge13);
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node1));
        REQUIRE((properties[groupB] == node3));
    }

    SECTION("Solve graph with three nodes / two groups / edge2-3") {
        Edge edge23(2, 3, EdgeType::DIRECT);
        graph.AddEdge(edge23);
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node2));
        REQUIRE((properties[groupB] == node3));
    }

    Edge edge13(1, 3, EdgeType::TRANSITIVE);
    string groupC = "entityC";
    Node node4(groupC, 4);
    graph.AddEdge(edge13).CreateNodeGroup(groupC, false).AddNode(node4);

    SECTION("Solve graph with four nodes / one optional") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node1));
        REQUIRE((properties[groupB] == node3));
    }

    Edge edge23(2, 3, EdgeType::DIRECT);
    graph.AddEdge(edge23);

    SECTION("Solve graph with two edges / direct edge takes precedence") {
        auto properties = GraphAnalyzer::SolveGraph(&graph, rs);
        REQUIRE(properties.size() == 2);
        REQUIRE((properties[groupA] == node2));
        REQUIRE((properties[groupB] == node3));
    }
}
<|endoftext|>