From 2fdb0eafe87b14fba088259c875489df8c6c7896 Mon Sep 17 00:00:00 2001 From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz> Date: Thu, 17 Mar 2016 15:10:11 +0100 Subject: [PATCH] add general alphabet class and tests --- alib2data/src/alphabet/Alphabet.hpp | 365 +++++++++++++++++++ alib2data/test-src/alphabet/AlphabetTest.cpp | 120 ++++++ alib2data/test-src/alphabet/AlphabetTest.h | 20 + 3 files changed, 505 insertions(+) create mode 100644 alib2data/src/alphabet/Alphabet.hpp create mode 100644 alib2data/test-src/alphabet/AlphabetTest.cpp create mode 100644 alib2data/test-src/alphabet/AlphabetTest.h diff --git a/alib2data/src/alphabet/Alphabet.hpp b/alib2data/src/alphabet/Alphabet.hpp new file mode 100644 index 0000000000..ee5a330fc5 --- /dev/null +++ b/alib2data/src/alphabet/Alphabet.hpp @@ -0,0 +1,365 @@ +/* + * Alphabet.hpp + * + * Created on: Mar 16, 2016 + * Author: Jan Travnicek + */ + +#ifndef ALPHABET_HPP_ +#define ALPHABET_HPP_ + +#include <set> +#include <algorithm> +#include <exception/AlibException.h> + +namespace alphabet { + +/** + * Represents an alphabet of symbols. + * @param Derived class representing datatype using this alphabet. + * @param SymbolType underlying type of symbols in the alphabet. + * @param AlphabetType arbirtrary type used to distinguish different alphabets. + */ +template < class Derived, class SymbolType, class AlphabetType > +class Alphabet { + /** + * The alphabet. + */ + std::set < SymbolType > data; + +public: + /* + * Constructs an empty alphabet. + */ + Alphabet ( ) { + } + + /** + * Constructs an alphabet containing given symbols. + * @throw AlibException if symbols are not available in context of datatype where the alphabet is used + */ + Alphabet ( std::set < SymbolType > symbols ) : data ( std::move ( symbols ) ) { + for ( const SymbolType & symbol : data ) { + valid ( symbol ); + + if ( !available ( symbol ) ) + throw exception::AlibException ( "Symbol " + ( std::string ) symbol + " is not available." ); + } + } + + /** + * Adds a symbol to the alphabet. + * @param symbol to add to the alphabet + * @throw AlibException if symbols are not available in context of datatype where the alphabet is used + */ + bool add ( SymbolType symbol ) { + valid ( symbol ); + + if ( !available ( symbol ) ) + throw exception::AlibException ( "Symbol " + ( std::string ) symbol + " is not available." ); + else + return data.insert ( std::move ( symbol ) ).second; + } + + /** + * Changes the alphabet. + * @param symbols by which to replace those currently in the alphabet + * @throw AlibException if symbol is used in context of datatype instance using the alphabet + */ + void set ( std::set < SymbolType > symbols ) { + std::set < SymbolType > removed; + std::set_difference ( data.begin ( ), data.end ( ), symbols.begin ( ), symbols.end ( ), std::inserter ( removed, removed.end ( ) ) ); + + for ( const SymbolType & symbol : removed ) + remove ( symbol ); + + data = std::move ( symbols ); + } + + /** + * @return set of symbols in the alphabet. + */ + const std::set < SymbolType > & get ( ) const { + return data; + } + + /** + * Removes a symbol from alphabet if not used. + * @throw AlibException if symbol is used in context of datatype instance using the alphabet + * @return true if symbol was indeed removed + * false if symbol was not present in the alphabet + */ + bool remove ( const SymbolType & symbol ) { + if ( used ( symbol ) ) + throw exception::AlibException ( "Symbol " + ( std::string ) symbol + " is used." ); + else + return data.erase ( symbol ); + } + + /** + * Alphabet emptines checker. + * @return true if alphabet is an empty + */ + bool empty ( ) const { + return data.empty ( ); + } + + /** + * Checks whether a concrete symbol is used in context of the datatype instance using this alphabet + * + * To be implemented by all template instantiations explicitly + * + * @param symbol symbol to check + * @return true if symbol is used + * false if symbol is not used + */ + bool used ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete symbol is available in context of the datatype instance using this alphabet + * + * To be implemented by all template instantiations explicitly + * + * @param symbol symbol to check + * @return true if symbol is available + * false if symbol is not available + */ + bool available ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete symbol is valid in context of the datatype instance using this alphabet + * + * To be implemented by all template instantiations explicitly + * + * @param symbol symbol to check + * @throw AlibException if the symbol in any way invalid + */ + void valid ( const SymbolType & symbol ) const; + + /** + * Compares alphabets for equality. + * @param other another alphabet + */ + bool operator ==( const Alphabet & other ) const { + return data == other.data ( ); + } + + /** + * Compares alphabets in natural order. + * @param other another alphabet + */ + bool operator <( const Alphabet & other ) const { + return data < other.data ( ); + } + +}; + +/** + * Auxilary class packing multiple alphabets. + */ +template < class Derived, class SymbolType, class ... AlphabetTypes > +class AlphabetPack; + +/** + * Auxilary class packing single alphabets. + */ +template < class Derived, class SymbolType, class AlphabetType1 > +class AlphabetPack < Derived, SymbolType, AlphabetType1 > : public Alphabet < Derived, SymbolType, AlphabetType1 > { +public: + /** + * Construct an alphabet pack from single alphabet. + */ + AlphabetPack ( SymbolType first ) : Alphabet < Derived, SymbolType, AlphabetType1 > ( std::move ( first ) ) { + } + +}; + +/** + * Auxilary class packing two alphabets. + */ +template < class Derived, class SymbolType, class AlphabetType1, class AlphabetType2 > +class AlphabetPack < Derived, SymbolType, AlphabetType1, AlphabetType2 > : public Alphabet < Derived, SymbolType, AlphabetType1 >, public Alphabet < Derived, SymbolType, AlphabetType2 > { +public: + /** + * Construct an alphabet pack from two alphabets. + */ + AlphabetPack ( std::set < SymbolType > first, std::set < SymbolType > second ) : Alphabet < Derived, SymbolType, AlphabetType1 > ( std::move ( first ) ), Alphabet < Derived, SymbolType, AlphabetType2 > ( std::move ( second ) ) { + } + +}; + +/** + * Auxilary class allowing simple access to the alphabets. + */ +template < class Derived, class SymbolType, class ... AlphabetTypes > +class Alphabets : public AlphabetPack < Derived, SymbolType, AlphabetTypes ... > { +public: + /** + * Reuse constructor of the base class. + */ + using AlphabetPack < Derived, SymbolType, AlphabetTypes ... >::AlphabetPack; + + /** + * Allow acces to subalphabet using its type. + * @param AlphabetType alphabet type used to distinguish different sub alphabets + * @return subalphabet + */ + template < class AlphabetType > + const Alphabet < Derived, SymbolType, AlphabetType > & getAlphabet ( ) const { + return static_cast < const Alphabet < Derived, SymbolType, AlphabetType > & > ( * this ); + } + + /** + * Allow acces to subalphabet using its type. + * @param AlphabetType alphabet type used to distinguish different sub alphabets + * @return subalphabet + */ + template < class AlphabetType > + Alphabet < Derived, SymbolType, AlphabetType > & getAlphabet ( ) { + return static_cast < Alphabet < Derived, SymbolType, AlphabetType > & > ( * this ); + } + +}; + +/** + * Represents an notable alphabet element. + * @param Derived class representing datatype using this alphabet. + * @param SymbolType underlying type of symbols in the alphabet. + * @param ElementType arbirtrary type used to distinguish different alphabet elements. + */ +template < class Derived, class SymbolType, class ElementType > +class AlphabetElement { + /** + * The element. + */ + SymbolType data; + +public: + /** + * Constructs an alphabet element from symbol. + * @throw AlibException if symbol is not available in context of datatype where the alphabet element is used + */ + AlphabetElement ( SymbolType symbol ) : data ( std::move ( symbol ) ) { + valid ( data ); + + if ( !available ( data ) ) + throw exception::AlibException ( "Symbol " + ( std::string ) symbol + " is not available." ); + } + + /** + * Changes the alphabet element. + * @param new value of the symbol element + * @throw AlibException if the new symbol is not available in context of datatype instance using the alphabet + */ + bool set ( SymbolType symbol ) { + valid ( symbol ); + + if ( !available ( symbol ) ) { + throw exception::AlibException ( "Symbol " + ( std::string ) symbol + " is used." ); + } else { + bool res = data == symbol; + data = std::move ( symbol ); + return res; + } + } + + /** + * Returns the current alphabet element of ElementType. + * @return the notable symbol from the alphabet + */ + const SymbolType & get ( ) const { + return data; + } + + /** + * Checks whether a concrete symbol is available in context of the datatype instance using this alphabet + * + * To be implemented by all template instantiations explicitly + * + * @param symbol symbol to check + * @return true if symbol is available + * false if symbol is not available + */ + bool available ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete symbol is valid in context of the datatype instance using this alphabet + * + * To be implemented by all template instantiations explicitly + * + * @param symbol symbol to check + * @throw AlibException if the symbol in any way invalid + */ + void valid ( const SymbolType & symbol ) const; +}; + +/** + * Auxilary class packing multiple alphabet elements. + */ +template < class Derived, class SymbolType, class ... AlphabetElementTypes > +class AlphabetElementPack; + +/** + * Auxilary class packing single alphabet element. + */ +template < class Derived, class SymbolType, class AlphabetElementType1 > +class AlphabetElementPack < Derived, SymbolType, AlphabetElementType1 > : public AlphabetElement < Derived, SymbolType, AlphabetElementType1 > { +public: + /** + * Construct an alphabet element pack from single alphabet element. + */ + AlphabetElementPack ( SymbolType first ) : AlphabetElement < Derived, SymbolType, AlphabetElementType1 > ( std::move ( first ) ) { + } + +}; + +/** + * Auxilary class packing two alphabet elements. + */ +template < class Derived, class SymbolType, class AlphabetElementType1, class AlphabetElementType2 > +class AlphabetElementPack < Derived, SymbolType, AlphabetElementType1, AlphabetElementType2 > : public AlphabetElement < Derived, SymbolType, AlphabetElementType1 >, public AlphabetElement < Derived, SymbolType, AlphabetElementType2 > { +public: + /** + * Construct an alphabet element pack from two alphabet elements. + */ + AlphabetElementPack ( SymbolType first, SymbolType second ) : AlphabetElement < Derived, SymbolType, AlphabetElementType1 > ( std::move ( first ) ), AlphabetElement < Derived, SymbolType, AlphabetElementType2 > ( std::move ( second ) ) { + } + +}; + +/** + * Auxilary class allowing simple access to the alphabet elements. + */ +template < class Derived, class SymbolType, class ... AlphabetElementTypes > +class AlphabetElements : public AlphabetElementPack < Derived, SymbolType, AlphabetElementTypes ... > { +public: + /** + * Reuse constructor of the base class. + */ + using AlphabetElementPack < Derived, SymbolType, AlphabetElementTypes ... >::AlphabetElementPack; + + /** + * Allow acces to subalphabet element using its type. + * @param AlphabetElementType alphabet type used to distinguish different sub alphabet elements + * @return subalphabet element + */ + template < class AlphabetElementType > + const AlphabetElement < Derived, SymbolType, AlphabetElementType > & getAlphabetElement ( ) const { + return static_cast < const AlphabetElement < Derived, SymbolType, AlphabetElementType > & > ( * this ); + } + + /** + * Allow acces to subalphabet element using its type. + * @param AlphabetElementType alphabet type used to distinguish different sub alphabet elements + * @return subalphabet element + */ + template < class AlphabetElementType > + AlphabetElement < Derived, SymbolType, AlphabetElementType > & getAlphabetElement ( ) { + return static_cast < AlphabetElement < Derived, SymbolType, AlphabetElementType > & > ( * this ); + } + +}; + +} /* namespace alphabet */ + +#endif /* ALPHABET_HPP_ */ diff --git a/alib2data/test-src/alphabet/AlphabetTest.cpp b/alib2data/test-src/alphabet/AlphabetTest.cpp new file mode 100644 index 0000000000..77300283cb --- /dev/null +++ b/alib2data/test-src/alphabet/AlphabetTest.cpp @@ -0,0 +1,120 @@ +#include "AlphabetTest.h" +#include <alphabet/Alphabet.hpp> + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION ( AlphabetTest, "alphabet" ); +CPPUNIT_TEST_SUITE_REGISTRATION ( AlphabetTest ); + +struct GeneralAlphabet { +}; + +struct NonlinearAlphabet { +}; + +struct SubtreeWildcard { +}; + +class A : public alphabet::Alphabets < A, std::string, GeneralAlphabet, NonlinearAlphabet >, public alphabet::AlphabetElements < A, std::string, SubtreeWildcard > { +public: + A ( std::string string ) : Alphabets < A, std::string, GeneralAlphabet, NonlinearAlphabet > ( { string }, { } ), AlphabetElements < A, std::string, SubtreeWildcard > ( string ) { + } +}; + +namespace alphabet { + +template < > +bool A::Alphabet < A, std::string, GeneralAlphabet >::used ( const std::string & string ) const { + return static_cast < const A * > ( this )->getAlphabet < NonlinearAlphabet > ( ).get ( ).count ( string ) || static_cast < const A * > ( this )->getAlphabetElement < SubtreeWildcard > ( ).get ( ) == string; +} + +template < > +bool A::Alphabet < A, std::string, NonlinearAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool A::Alphabet < A, std::string, GeneralAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +bool A::Alphabet < A, std::string, NonlinearAlphabet >::available ( const std::string & string ) const { + return static_cast < const A * > ( this )->getAlphabet < GeneralAlphabet > ( ).get ( ).count ( string ); +} + +template < > +bool A::AlphabetElement < A, std::string, SubtreeWildcard >::available ( const std::string & string ) const { + return static_cast < const A * > ( this )->getAlphabet < GeneralAlphabet > ( ).get ( ).count ( string ); +} + +template < > +void A::Alphabet < A, std::string, GeneralAlphabet >::valid ( const std::string & ) const { +} + +template < > +void A::Alphabet < A, std::string, NonlinearAlphabet >::valid ( const std::string & ) const { +} + +template < > +void A::AlphabetElement < A, std::string, SubtreeWildcard >::valid ( const std::string & ) const { +} + +} + +class B : public alphabet::Alphabets < B, std::string, GeneralAlphabet, NonlinearAlphabet > { +}; + +namespace alphabet { + +template < > +bool B::Alphabet < B, std::string, GeneralAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool B::Alphabet < B, std::string, NonlinearAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool B::Alphabet < B, std::string, GeneralAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +bool B::Alphabet < B, std::string, NonlinearAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +void B::Alphabet < B, std::string, GeneralAlphabet >::valid ( const std::string & ) const { +} + +template < > +void B::Alphabet < B, std::string, NonlinearAlphabet >::valid ( const std::string & ) const { +} + +} + +void AlphabetTest::setUp ( ) { +} + +void AlphabetTest::tearDown ( ) { +} + +void AlphabetTest::testAdd ( ) { + A tmp ( "2" ); + + CPPUNIT_ASSERT_THROW ( tmp.getAlphabet < NonlinearAlphabet > ( ).add ( "1" ), exception::AlibException ); + tmp.getAlphabet < GeneralAlphabet > ( ).add ( "1" ); + tmp.getAlphabet < NonlinearAlphabet > ( ).add ( "1" ); +} + +void AlphabetTest::testRemove ( ) { + A tmp ( "2" ); + + tmp.getAlphabet < GeneralAlphabet > ( ).add ( "1" ); + tmp.getAlphabet < NonlinearAlphabet > ( ).add ( "1" ); + CPPUNIT_ASSERT_THROW ( tmp.getAlphabet < GeneralAlphabet > ( ).remove ( "1" ), exception::AlibException ); + tmp.getAlphabet < NonlinearAlphabet > ( ).remove ( "1" ); + tmp.getAlphabet < GeneralAlphabet > ( ).remove ( "1" ); +} diff --git a/alib2data/test-src/alphabet/AlphabetTest.h b/alib2data/test-src/alphabet/AlphabetTest.h new file mode 100644 index 0000000000..d42a0cd132 --- /dev/null +++ b/alib2data/test-src/alphabet/AlphabetTest.h @@ -0,0 +1,20 @@ +#ifndef ALPHABET_TEST_H_ +#define ALPHABET_TEST_H_ + +#include <cppunit/extensions/HelperMacros.h> + +class AlphabetTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE ( AlphabetTest ); + CPPUNIT_TEST ( testAdd ); + CPPUNIT_TEST ( testRemove ); + CPPUNIT_TEST_SUITE_END ( ); + +public: + void setUp ( ); + void tearDown ( ); + + void testAdd ( ); + void testRemove ( ); +}; + +#endif // ALPHABET_TEST_H_ -- GitLab