2 // part of libkombilo, http://www.u-go.net/kombilo/
4 // Copyright (c) 2006 Ulrich Goertz <u@g0ertz.de>
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of
7 // this software and associated documentation files (the "Software"), to deal in
8 // the Software without restriction, including without limitation the rights to
9 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 // of the Software, and to permit persons to whom the Software is furnished to do
11 // so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 #include "abstractboard.h"
32 #include "sgfparser.h"
35 #if (defined(__BORLANDC__) || defined(_MSC_VER))
36 typedef __int64 hashtype
;
37 const hashtype NOT_HASHABLE
= 9223372036854775807i64
;
39 typedef long long hashtype
;
40 const hashtype NOT_HASHABLE
= 9223372036854775807LL;
43 const char NO_CONT
= 255;
45 class SnapshotVector
: public std::vector
<unsigned char> {
48 SnapshotVector(char* c
, int size
);
51 void pb_charp(char* c
, int size
);
53 void pb_string(std::string s
);
54 void pb_intp(int* p
, int size
);
59 char* retrieve_charp();
60 std::string
retrieve_string();
65 SnapshotVector::iterator current
;
86 Symmetries(char sX
=0, char sY
=0);
88 Symmetries(const Symmetries
& s
);
89 Symmetries
& operator=(const Symmetries
& s
);
90 void set(char i
, char j
, char k
, char l
, char cs
) throw(PatternError
);
91 char getX(char i
, char j
) throw(PatternError
);
92 char getY(char i
, char j
) throw(PatternError
);
93 char getCS(char i
, char j
) throw(PatternError
);
94 char has_key(char i
, char j
) throw(PatternError
);
97 const int CORNER_NW_PATTERN
= 0;
98 const int CORNER_NE_PATTERN
= 1;
99 const int CORNER_SW_PATTERN
= 2;
100 const int CORNER_SE_PATTERN
= 3;
101 const int SIDE_N_PATTERN
= 4;
102 const int SIDE_W_PATTERN
= 5;
103 const int SIDE_E_PATTERN
= 6;
104 const int SIDE_S_PATTERN
= 7;
105 const int CENTER_PATTERN
= 8;
106 const int FULLBOARD_PATTERN
= 9;
110 int left
; // left, right, top, bottom "==" anchors
119 int flip
; // used for elements of a patternList
120 int colorSwitch
; // dito
124 std::vector
<MoveNC
> contList
;
126 // Pattern constructors
128 // the char* contLabels, if != 0, should have the same size as the pattern, and should
129 // contain pre-fixed label (which should be re-used when presenting the search results)
130 // Positions without a given label should contain '.'
132 // Note: the char*'s iPos and CONTLABELS will NOT be free'ed by the Pattern class.
135 Pattern(int le
, int ri
, int to
, int bo
, int BOARDSIZE
, int sX
, int sY
, char* iPos
, const std::vector
<MoveNC
>& CONTLIST
, char* CONTLABELS
= 0) throw(PatternError
);
136 Pattern(int type
, int BOARDSIZE
, int sX
, int sY
, char* iPos
, std::vector
<MoveNC
> CONTLIST
, char* CONTLABELS
= 0);
137 Pattern(int type
, int BOARDSIZE
, int sX
, int sY
, char* iPos
, char* CONTLABELS
= 0);
138 Pattern(const Pattern
& p
);
139 Pattern(SnapshotVector
& snv
);
141 Pattern
& operator=(const Pattern
& p
);
142 Pattern
& copy(const Pattern
& p
);
144 char getInitial(int i
, int j
);
145 char getFinal(int i
, int j
);
148 int operator==(const Pattern
& p
);
149 std::string
printPattern();
150 void to_snv(SnapshotVector
& snv
);
152 static int flipsX(int i
, int x
, int y
, int XX
, int YY
);
153 static int flipsY(int i
, int x
, int y
, int XX
, int YY
);
154 static int PatternInvFlip(int i
);
155 static int compose_flips(int i
, int j
); // returns index of flip "first j, then i"
160 int B
; // number of all black continuations
162 int tB
; // black tenuki
164 int wB
; // black wins (where cont. is B)
165 int lB
; // black loses (where cont. is B)
166 int wW
; // black wins (where cont. is W)
167 int lW
; // black loses (where cont. is W)
169 void from_snv(SnapshotVector
& snv
);
170 void to_snv(SnapshotVector
& snv
);
176 int fixedColor
; // allow switching colors
177 int nextMove
; // 1: next must be black, 2: next must be white
178 std::vector
<Pattern
> data
;
179 std::vector
<Symmetries
> symmetries
;
180 Continuation
* continuations
;
184 PatternList(Pattern
& p
, int fColor
, int nMove
) throw (PatternError
);
187 char invertColor(char co
);
191 char* updateContinuations(int orientation
, int x
, int y
, char co
, bool tenuki
, char winner
);
192 char* sortContinuations(); // and give them names to be used as labels
199 char orientation
; // == index in corresp patternList
201 Candidate(char X
, char Y
, char ORIENTATION
);
206 ExtendedMoveNumber
* pos
;
207 char* label
; // this does not really contain the label, but rather the position of the continuation move
208 Hit(ExtendedMoveNumber
* POS
, char* LABEL
);
209 Hit(SnapshotVector
& snv
); // takes a SnapshotVector and reads information produced by Hit::to_snv()
211 static bool cmp_pts(Hit
* a
, Hit
* b
);
212 void to_snv(SnapshotVector
& snv
);
220 Algorithm(int bsize
);
221 virtual ~Algorithm();
223 virtual void initialize_process(sqlite3
* DB
);
224 virtual void newgame_process(int game_id
);
225 virtual void AB_process(int x
, int y
);
226 virtual void AW_process(int x
, int y
);
227 virtual void AE_process(int x
, int y
, char removed
);
228 virtual void endOfNode_process();
229 virtual void move_process(Move m
);
230 virtual void pass_process();
231 virtual void branchpoint_process();
232 virtual void endOfVariation_process();
233 virtual void endgame_process(bool commit
=true);
234 virtual void finalize_process();
235 virtual int readDB(sqlite3
* DB
);
236 virtual int search(PatternList
& patternList
, GameList
& gl
, SearchOptions
& options
);
243 class Algo_signature
: public Algorithm
{
245 Algo_signature(int bsize
);
247 void initialize_process(sqlite3
* DB
) throw(DBError
);
248 void newgame_process(int game_id
);
249 void AB_process(int x
, int y
);
250 void AW_process(int x
, int y
);
251 void AE_process(int x
, int y
, char removed
);
252 void endOfNode_process();
253 void move_process(Move m
);
255 void branchpoint_process();
256 void endOfVariation_process();
257 void endgame_process(bool commit
=true) throw(DBError
);
258 void finalize_process();
262 char* get_current_signature();
263 std::vector
<int> search_signature(char* sig
);
268 class Algo_finalpos
: public Algorithm
{
270 Algo_finalpos(int bsize
);
272 void initialize_process(sqlite3
* DB
) throw(DBError
);
273 void newgame_process(int game_id
);
274 void AB_process(int x
, int y
);
275 void AW_process(int x
, int y
);
276 void AE_process(int x
, int y
, char removed
);
277 void endOfNode_process();
278 void move_process(Move m
);
280 void branchpoint_process();
281 void endOfVariation_process();
282 void endgame_process(bool commit
=true) throw(DBError
);
283 void finalize_process();
287 std::map
<int, char* > *data
;
288 int readDB(sqlite3
* DB
);
289 int search(PatternList
& patternList
, GameList
& gl
, SearchOptions
& options
);
291 bool equal(unsigned int id1
, unsigned int id2
); // id1, id2 refer to id's in the database!
292 bool equals_current(unsigned int id1
);
296 const int ENDOFNODE
= 128;
297 const int BRANCHPOINT
= 64;
298 const int ENDOFVARIATION
= 32;
301 const int REMOVE
= 128;
302 const int BLACK
= 64;
303 const int WHITE
= 32;
311 ExtendedMoveNumber dictsF
;
313 ExtendedMoveNumber dictsFI
;
314 bool dictsFoundInitial
;
317 std::vector
<MoveNC
> contList
;
324 MovelistCand(Pattern
* P
, int ORIENTATION
, char* DICTS
, int NO
, char X
, char Y
);
326 char dictsget(char x
, char y
);
327 void dictsset(char x
, char y
, char d
);
328 bool in_relevant_region(char x
, char y
);
331 class VecMC
: public std::vector
<MovelistCand
* > {
335 VecMC
* deepcopy(ExtendedMoveNumber
& COUNTER
, int CANDSSIZE
);
336 ExtendedMoveNumber counter
;
340 class Algo_movelist
: public Algorithm
{
342 Algo_movelist(int bsize
);
344 void initialize_process(sqlite3
* DB
) throw(DBError
);
345 void newgame_process(int game_id
);
346 void AB_process(int x
, int y
);
347 void AW_process(int x
, int y
);
348 void AE_process(int x
, int y
, char removed
);
349 void endOfNode_process();
350 void move_process(Move m
);
352 void branchpoint_process();
353 void endOfVariation_process();
354 void endgame_process(bool commit
=true) throw(DBError
);
355 void finalize_process();
356 int readDB(sqlite3
* DB
);
357 int search(PatternList
& patternList
, GameList
& gl
, SearchOptions
& options
);
359 std::vector
<char> movelist
;
361 std::map
<int, char* > *data1
;
362 std::map
<int, char* > *data2
;
363 std::map
<int, int> *data1l
;
372 HashFEntry(hashtype HASHCODE
, char* BUF
, int LENGTH
);
373 HashFEntry(const HashFEntry
& hfe
);
377 class HashhitF
{ // hashing hit for full board search
382 ExtendedMoveNumber
* emn
;
385 HashhitF(int GAMEID
, char ORIENTATION
, char* blob
);
389 class HashhitCS
{ // hasihing hit for corner/side pattern search
394 HashhitCS(int GAMEID
, int POSITION
, bool CS
);
400 std::vector
<std::pair
<hashtype
, ExtendedMoveNumber
>* > * lfc
;
401 ExtendedMoveNumber
* moveNumber
;
404 HashVarInfo(hashtype CHC
, std::vector
<std::pair
<hashtype
, ExtendedMoveNumber
>* > * LFC
, ExtendedMoveNumber
* MOVENUMBER
, int NUMSTONES
);
407 class Algo_hash_full
: public Algorithm
{
409 Algo_hash_full(int bsize
, int MAXNUMSTONES
= 50);
411 void initialize_process(sqlite3
* DB
) throw(DBError
);
412 void newgame_process(int game_id
);
413 void AB_process(int x
, int y
);
414 void AW_process(int x
, int y
);
415 void AE_process(int x
, int y
, char removed
);
416 void endOfNode_process();
417 void move_process(Move m
) throw(DBError
);
419 void branchpoint_process();
420 void endOfVariation_process() throw(DBError
);
421 void endgame_process(bool commit
=true) throw(DBError
);
422 void finalize_process();
423 int search(PatternList
& patternList
, GameList
& gl
, SearchOptions
& options
, sqlite3
* db
);
425 hashtype
compute_hashkey(Pattern
& pattern
);
430 hashtype currentHashCode
;
431 ExtendedMoveNumber
* moveNumber
;
432 std::vector
<std::pair
<hashtype
, ExtendedMoveNumber
>* > *lfc
; // hash code + move number, still looking for continuation
433 std::stack
<HashVarInfo
>* branchpoints
;
434 int insert_hash(hashtype hashCode
, ExtendedMoveNumber
& mn
, Move
* continuation
);
435 int insert_all_hashes();
436 std::vector
<HashFEntry
> hash_vector
;
440 // When processing sgf games, Algo_hash maintains a list of HashInstance's -
441 // those are regions on the board for which hash codes are put into the
445 HashInstance(char X
, char Y
, char SIZEX
, char SIZEY
, int BOARDSIZE
);
447 bool inRelevantRegion(char X
, char Y
);
449 char xx
; // position on the board
453 char sizeX
; // size of the pattern
459 void addB(char x
, char y
);
460 void removeB(char x
, char y
);
461 void addW(char x
, char y
);
462 void removeW(char x
, char y
);
465 std::pair
<hashtype
,int> cHC(); // returns min(currentHashCode) and corresp. index
466 hashtype
* currentHashCode
; // array of 8 hashtype values (to automatically symmetrize hash codes)
467 std::stack
<std::pair
<hashtype
*,int> >* branchpoints
;
472 class Algo_hash
: public Algorithm
{
473 // This class should not be used by the "end-user" (see Algo_hash_corner and
474 // Algo_hash_sides instead)
477 Algo_hash(int bsize
, const std::string
& DBNAMEEXT
, int MAXNUMSTONES
);
478 virtual ~Algo_hash();
479 virtual void initialize_process(sqlite3
* DB
) throw(DBError
);
480 virtual void newgame_process(int game_id
);
481 virtual void AB_process(int x
, int y
);
482 virtual void AW_process(int x
, int y
);
483 virtual void AE_process(int x
, int y
, char removed
);
484 virtual void endOfNode_process();
485 virtual void move_process(Move m
) throw(DBError
);
486 virtual void pass_process();
487 virtual void branchpoint_process();
488 virtual void endOfVariation_process();
489 virtual void endgame_process(bool commit
=true);
490 virtual void finalize_process();
491 virtual int search(PatternList
& patternList
, GameList
& gl
, SearchOptions
& options
, sqlite3
* db
);
493 virtual std::pair
<hashtype
,int> compute_hashkey(PatternList
& pl
, int CS
);
494 static const hashtype hashCodes
[];
495 std::string dbnameext
;
496 std::vector
<HashInstance
>* hi
;
498 std::vector
<std::pair
<hashtype
, int> > hash_vector
;
499 virtual int insert_hash(hashtype hashCod
, int pos
);
500 int insert_all_hashes();
503 class Algo_hash_corner
: public Algo_hash
{
505 Algo_hash_corner(int bsize
, int SIZE
=7, int MAXNUMSTONES
= 20);
506 std::pair
<hashtype
,int> compute_hashkey(PatternList
& pl
, int CS
);
510 // class Algo_hash_side : public Algo_hash {
512 // Algo_hash_side(int bsize, int SIZEX=6, int SIZEY=4);
517 // class UIntervals {
522 // void append(UIntervals interv);
523 // void inters(UIntervals uinterv);
526 // std::vector<pair<int,int>> data;
531 // class Algo_intervals : public Algorithm {
533 // Algo_intervals(int bsize);
534 // ~Algo_intervals();
536 // std::vector<long> movesArr;
537 // std::vector<long> moveIntsArr;
539 // std::vector<vector<int>*> moves;
545 // const int MAXNOMOVES = 16777215;
546 // const int FLAG_POINTER = 16777216;
547 // const int FLAG_BLACK = 33554432;
548 // const int FLAG_WHITE = 67108864;
550 const int ALGO_FINALPOS
= 1;
551 const int ALGO_MOVELIST
= 2;
552 const int ALGO_HASH_FULL
= 4;
553 const int ALGO_HASH_CORNER
= 8;
554 const int ALGO_INTERVALS
= 16;
555 const int ALGO_HASH_CENTER
= 32;
556 const int ALGO_HASH_SIDE
= 64;
558 const int algo_finalpos
= 1;
559 const int algo_movelist
= 2;
560 const int algo_hash_full
= 3;
561 const int algo_hash_corner
= 4;
562 const int algo_intervals
= 5;
563 const int algo_hash_center
= 6;
564 const int algo_hash_side
= 7;
566 typedef Algorithm
* algo_p
;
568 class ProcessOptions
{
570 bool processVariations
;
572 std::string rootNodeTags
; // a comma-separated list of those SGF tags which should be written to the database
573 int algos
; // algorithms to be used
574 int algo_hash_full_maxNumStones
;
575 int algo_hash_corner_maxNumStones
;
577 std::string
asString();
579 std::vector
<std::string
>* SGFTagsAsStrings();
581 ProcessOptions(); // sets default values which have to be overwritten
582 ProcessOptions(std::string s
);
585 class SearchOptions
{
588 int nextMove
; // 0 undetermined, 1 = next move must be black, 2 = next move must be white
591 bool searchInVariations
;
595 SearchOptions(int FIXEDCOLOR
, int NEXTMOVE
, int MOVELIMIT
=10000);
596 SearchOptions(SnapshotVector
& snv
);
597 void to_snv(SnapshotVector
& snv
);
600 class GameListEntry
{
602 int id
; // id within the concerning database
603 std::string gameInfoStr
;
605 std::vector
<Hit
* > * hits
; // used for hits
606 std::vector
<Candidate
* > * candidates
; // used for candidates
608 GameListEntry(int ID
, char WINNER
, std::string GAMEINFOSTR
);
611 void hits_from_snv(SnapshotVector
& snv
);
620 VarInfo(Node
* N
, abstractBoard
* B
, int I
);
621 VarInfo(const VarInfo
& v
);
625 // process flags (used to determine the behavior for individual games - in contrast to
626 // options which apply to the whole GameList and are given in ProcessOptions)
627 const int CHECK_FOR_DUPLICATES
= 1; // check for duplicates using the signature
628 const int CHECK_FOR_DUPLICATES_STRICT
= 2; // check for duplicates using the final position
629 // (if ALGO_FINAPOS is available)
630 const int OMIT_DUPLICATES
= 4;
631 const int OMIT_GAMES_WITH_SGF_ERRORS
= 8;
633 // process return values
634 // 0: SGF error occurred when parsing the "tree structure" (i.e. before parsing the individual nodes)
635 // database was not changed
636 // n>0: n games were processed, use process_results to access the individual results
638 // flags used in process_results
639 const int UNACCEPTABLE_BOARDSIZE
= 1; // (database not changed)
640 const int SGF_ERROR
= 2;
641 // SGF error occurred when playing through the game
642 // (and the rest of the concerning variation was not used).
643 // Depending on OMIT_GAMES_WITH_SGF_ERRORS, everything before this node (and other variations,
644 // if any) was inserted, or the database was not changed.
645 const int IS_DUPLICATE
= 4;
646 const int NOT_INSERTED_INTO_DB
= 8;
647 const int INDEX_OUT_OF_RANGE
= 16;
654 // constructor receives a FORMAT string; see trac wiki for the syntax to be used
655 std::string format1
; // extracted from FORMAT; the column list of the sql query to retrieve the games
656 std::string format2
; // extracted from FORMAT, used as template when inserting the query results into the game list
658 int processVariations
;
660 std::vector
<int> boardsizes
;
661 std::vector
<algo_p
> algo_ps
;
662 std::vector
<sqlite3
*> algo_dbs
;
663 std::vector
<GameListEntry
* > * all
;
664 std::vector
<std::pair
<int,int> > * currentList
; // pair of game id and position within all
665 // (usually sorted w.r.t. second component)
666 std::vector
<std::pair
<int,int> > * oldList
;
671 Continuation
* continuations
;
676 Pattern
* mrs_pattern
; // most recent search pattern
677 SearchOptions
* searchOptions
;
678 // ----------------------------------------------------------------------------
679 // the following methods provide the user interface
681 // ------- constructor --------------------------------------------------------
682 // p_options will be copied by GameList, so the caller has to free the pointer
683 GameList(char* DBNAME
, std::string ORDERBY
="", std::string FORMAT
="", ProcessOptions
* p_options
=0, int cache
=100) throw(DBError
);
685 // ------- processing SGF games (to populate the db) --------------------------
686 void start_processing(int PROCESSVARIATIONS
=-1) throw(DBError
);
687 int process(const char* sgf
, const char* path
, const char* fn
,
688 const char* DBTREE
= 0, int flags
=0) throw(SGFError
,DBError
);
689 int process_results(unsigned int i
=0); // result for i-th processed game in most recently processed SGF collection
690 void finalize_processing() throw(DBError
);
692 // int remove_game(int index); // TODO
693 // int remove_all_current_games();
695 // ------- pattern search -----------------------------------------------------
696 // options is copied in the search method (if != 0), so the caller has to free the pointer
697 void search(Pattern
& pattern
, SearchOptions
* options
= 0) throw(DBError
);
698 char lookupLabel(char x
, char y
);
699 Continuation
lookupContinuation(char x
, char y
);
701 // ------- signature search ---------------------------------------------------
702 // if boardsize != 0 in sigsearch, then the signature is "symmetrized" with respect
704 void sigsearch(char* sig
, int boardsize
) throw(DBError
);
705 std::string
getSignature(int i
) throw(DBError
);
707 // ------- game info search ---------------------------------------------------
708 void gisearch(char* sql
, int complete
=0) throw(DBError
);
710 // ------- tagging ------------------------------------------------------------
711 void tagsearch(int tag
) throw(DBError
);
712 void setTag(int tag
, int start
=0, int end
=0) throw(DBError
);
713 void deleteTag(int tag
, int i
= -1) throw(DBError
);
714 std::vector
<int> getTags(int i
, int tag
=0) throw(DBError
); // note the order of arguments!
716 // ------- duplicates ---------------------------------------------------------
717 int find_duplicates(int bs
, bool strict
=false) throw(DBError
); // return number of duplicate array
718 std::vector
<int> retrieve_duplicates_VI(unsigned int i
);
719 int* retrieve_duplicates_PI(unsigned int i
); // same as above, but returns Pointer to Int
720 // (an array terminated by -1)
721 // The caller must free the pointer himself
722 // (before calling find_duplicates again).
724 // ------- snapshot, restore --------------------------------------------------
726 int snapshot() throw(DBError
);
727 void restore(int handle
, bool del
) throw(DBError
);
728 void delete_snapshot(int handle
) throw(DBError
);
729 void delete_all_snapshots() throw(DBError
);
731 // ------- misc ---------------------------------------------------------------
732 void reset(); // reset currentList to all
733 void resetFormat(std::string ORDERBY
="", std::string FORMAT
="");
736 std::string
resultsStr(GameListEntry
* gle
);
737 std::string
currentEntryAsString(int i
);
738 std::vector
<std::string
> currentEntriesAsStrings(int start
=0, int end
=0);
739 std::string
getSGF(int i
) throw(DBError
);
740 std::string
getCurrentProperty(int i
, std::string tag
) throw (DBError
);
742 // ------- list of all players -------------------------------------------------
744 std::string
plEntry(int i
);
746 // -----------------------------------------------------------------------------
747 // internal methods (called from the algorithm classes)
753 char getCurrentWinner();
754 std::vector
<Candidate
* > *getCurrentCandidateList();
755 void makeCurrentCandidate(std::vector
<Candidate
* > *candidates
);
756 void makeCurrentHit(std::vector
<Hit
* > *hits
);
757 void makeIndexCandidate(int index
, std::vector
<Candidate
* > *candidates
);
758 void makeIndexHit(int index
, std::vector
<Hit
* > *hits
);
759 void setCurrentFromIndex(int index
);
760 int get_current_index(int id
, int* start
); // returns the index in oldList of the game with game id "id"
761 // (if available, otherwise returns -1),
762 // use this between start_sorted and end_sorted
763 int get_current_index_CL(int id
, int start
=0); // returns the index in currentList of the game with game id "id"
764 // (if available, otherwise returns -1), requires currentList to
765 // be sorted wrt first component (see duplicates())
768 void createGamesDB() throw(DBError
);
769 void readDB() throw(DBError
);
770 void addAlgos(int bs
);
771 int posDT
; // used when parsing the DT, SZ, BR, WR, HA fields during processing
779 ProcessOptions
* p_op
;
780 std::vector
<std::string
>* SGFtags
;
781 std::string sql_ins_rnp
; // sql string to insert root node properties
782 std::vector
<std::string
> pl
; // list of all players
783 void readPlayersList() throw(DBError
);
784 std::vector
<std::vector
<int> >* duplicates
;
785 void insert_duplicate(int i1
, int i2
, std::vector
<std::vector
<int> >* dupl
);
786 std::vector
<int> process_results_vector
;
789 const int HANDI_TAG
= 1;
790 const int PROFESSIONAL_TAG
= 2;