link.cpp 16.9 KB
Newer Older
1 2 3 4 5 6 7
/*! \file src/link.cpp
    \author Klaas Holwerda
 
    Copyright: 2001-2004 (C) Klaas Holwerda
 
    Licence: see kboollicense.txt 
 
charras's avatar
charras committed
8
    RCS-ID: $Id: link.cpp,v 1.4 2009/09/07 19:23:28 titato Exp $
9 10
*/

11
#include "kbool/booleng.h"
12

13 14
#include "kbool/link.h"
#include "kbool/line.h"
15 16 17
#include <math.h>
#include <assert.h>

18 19 20
#include "kbool/node.h"
#include "kbool/graph.h"
#include "kbool/graphlst.h"
21

charras's avatar
charras committed
22
int linkXYsorter( kbLink *, kbLink * );
23 24 25 26

//
// Default constructor
//
charras's avatar
charras committed
27
kbLink::kbLink( Bool_Engine* GC )
28
{
29 30
    _GC = GC;
    Reset();
31 32 33 34 35 36
}


//
// This constructor makes this link a valid part of a graph
//
charras's avatar
charras committed
37
kbLink::kbLink( int graphnr, kbNode *begin, kbNode *end, Bool_Engine* GC )
38
{
39 40
    _GC = GC;
    Reset();
41

42 43 44 45 46 47
    // Set the references of the node and of this link correct
    begin->AddLink( this );
    end->AddLink( this );
    m_beginnode = begin;
    m_endnode = end;
    m_graphnum = graphnr;
48 49 50 51 52
}

//
// This constructor makes this link a valid part of a graph
//
charras's avatar
charras committed
53
kbLink::kbLink( kbNode *begin, kbNode *end, Bool_Engine* GC )
54
{
55 56
    _GC = GC;
    Reset();
57

58 59 60 61 62 63
    // Set the references of the node and of this link correct
    begin->AddLink( this );
    end->AddLink( this );
    m_beginnode = begin;
    m_endnode = end;
    m_graphnum = 0;
64 65 66 67 68 69
}


//
// Destructor
//
charras's avatar
charras committed
70
kbLink::~kbLink()
71
{
72
    UnLink();
73 74 75
}

//
76
// Checks whether the current algorithm has been on this link
77
//
charras's avatar
charras committed
78
bool kbLink::BeenHere()
79
{
80 81
    if ( m_bin ) return true;
    return false;
82 83
}

charras's avatar
charras committed
84
void kbLink::TakeOverOperationFlags( kbLink* link )
85
{
86 87 88 89 90
    m_merge_L = link->m_merge_L;
    m_a_substract_b_L = link->m_a_substract_b_L;
    m_b_substract_a_L = link->m_b_substract_a_L;
    m_intersect_L = link->m_intersect_L;
    m_exor_L = link->m_exor_L;
91

92 93 94 95 96
    m_merge_R = link->m_merge_R;
    m_a_substract_b_R = link->m_a_substract_b_R;
    m_b_substract_a_R = link->m_b_substract_a_R;
    m_intersect_R = link->m_intersect_R;
    m_exor_R = link->m_exor_R;
97 98
}
//
99
// Returns the next link from the argument
100
//
charras's avatar
charras committed
101
kbLink* kbLink::Forth( kbNode *node )
102
{
103 104
    assert( node == m_beginnode || node == m_endnode );
    return node->GetOtherLink( this );
105 106 107 108 109
}

//
// Returns the Beginnode
//
charras's avatar
charras committed
110
kbNode *kbLink::GetBeginNode()
111
{
112
    return m_beginnode;
113 114 115 116 117
}

//
// Returns the endnode
//
charras's avatar
charras committed
118
kbNode* kbLink::GetEndNode()
119
{
120
    return m_endnode;
121 122
}

charras's avatar
charras committed
123
kbNode* kbLink::GetLowNode()
124
{
125
    return ( ( m_beginnode->GetY() < m_endnode->GetY() ) ? m_beginnode : m_endnode );
126 127
}

charras's avatar
charras committed
128
kbNode* kbLink::GetHighNode()
129
{
130
    return ( ( m_beginnode->GetY() > m_endnode->GetY() ) ? m_beginnode : m_endnode );
131 132 133 134 135
}

//
// Returns the graphnumber
//
charras's avatar
charras committed
136
int kbLink::GetGraphNum()
137
{
138
    return m_graphnum;
139 140
}

charras's avatar
charras committed
141
bool kbLink::GetInc()
142
{
143
    return m_Inc;
144
//   if (Inc) return true;
145
// return false;
146 147
}

charras's avatar
charras committed
148
void kbLink::SetInc( bool inc )
149
{
150
    m_Inc = inc;
151 152 153 154
//   Inc=0;
//   if (inc) Inc=1;
}

charras's avatar
charras committed
155
bool kbLink::GetLeftA()
156
{
157
    return m_LeftA;
158 159
}

charras's avatar
charras committed
160
void kbLink::SetLeftA( bool la )
161
{
162
    m_LeftA = la;
163 164
}

charras's avatar
charras committed
165
bool kbLink::GetLeftB()
166
{
167
    return m_LeftB;
168 169
}

charras's avatar
charras committed
170
void kbLink::SetLeftB( bool lb )
171
{
172
    m_LeftB = lb;
173 174
}

charras's avatar
charras committed
175
bool kbLink::GetRightA()
176
{
177
    return m_RightA;
178 179
}

charras's avatar
charras committed
180
void kbLink::SetRightA( bool ra )
181
{
182
    m_RightA = ra;
183 184
}

charras's avatar
charras committed
185
bool kbLink::GetRightB()
186
{
187
    return m_RightB;
188 189
}

charras's avatar
charras committed
190
void kbLink::SetRightB( bool rb )
191
{
192
    m_RightB = rb;
193 194 195
}

//
196
// This function is very popular by GP-faults
197 198
// It returns the node different from a
//
charras's avatar
charras committed
199
kbNode* kbLink::GetOther( const kbNode *const a )
200
{
201
    return ( ( a != m_beginnode ) ? m_beginnode : m_endnode );
202 203 204 205
}


//
206
// Is this marked for given operation
207
//
charras's avatar
charras committed
208
bool kbLink::IsMarked( BOOL_OP operation )
209
{
210 211 212 213 214 215 216 217 218
    switch ( operation )
    {
        case( BOOL_OR ):     return m_merge_L || m_merge_R;
        case( BOOL_AND ):    return m_intersect_L || m_intersect_R;
        case( BOOL_A_SUB_B ):  return m_a_substract_b_L || m_a_substract_b_R;
        case( BOOL_B_SUB_A ):  return m_b_substract_a_L || m_b_substract_a_R;
        case( BOOL_EXOR ):    return m_exor_L || m_exor_R;
        default:             return false;
    }
219 220
}

charras's avatar
charras committed
221
bool kbLink::IsMarkedLeft( BOOL_OP operation )
222
{
223 224 225 226 227 228 229 230 231
    switch ( operation )
    {
        case( BOOL_OR ):      return m_merge_L;
        case( BOOL_AND ):     return m_intersect_L;
        case( BOOL_A_SUB_B ): return m_a_substract_b_L;
        case( BOOL_B_SUB_A ): return m_b_substract_a_L;
        case( BOOL_EXOR ):    return m_exor_L;
        default:            return false;
    }
232 233
}

charras's avatar
charras committed
234
bool kbLink::IsMarkedRight( BOOL_OP operation )
235
{
236 237 238 239 240 241 242 243 244
    switch ( operation )
    {
        case( BOOL_OR ):      return m_merge_R;
        case( BOOL_AND ):     return m_intersect_R;
        case( BOOL_A_SUB_B ): return m_a_substract_b_R;
        case( BOOL_B_SUB_A ): return m_b_substract_a_R;
        case( BOOL_EXOR ):    return m_exor_R;
        default:            return false;
    }
245 246 247
}

//
248
// Is this a hole for given operation
249
// beginnode must be to the left
charras's avatar
charras committed
250
bool kbLink::IsHole( BOOL_OP operation )
251 252
{

253
    bool topsideA, topsideB;
254

255 256 257 258
    if ( m_beginnode->GetX() < m_endnode->GetX() ) //going to the right?
    {  topsideA = m_RightA; topsideB = m_RightB;  }
    else
    {  topsideA = m_LeftA; topsideB = m_LeftB; }
259

260 261 262 263 264 265 266 267 268
    switch ( operation )
    {
        case( BOOL_OR ):      return ( !topsideB && !topsideA );
        case( BOOL_AND ):     return ( !topsideB || !topsideA );
        case( BOOL_A_SUB_B ): return ( topsideB || !topsideA );
        case( BOOL_B_SUB_A ): return ( topsideA || !topsideB );
        case( BOOL_EXOR ):    return !( ( topsideB && !topsideA ) || ( !topsideB && topsideA ) );
        default:            return false;
    }
269 270 271 272 273
}

//
// Is this a part of a hole
//
charras's avatar
charras committed
274
bool kbLink::GetHole()
275
{
276
    return ( m_hole );
277 278 279
}


charras's avatar
charras committed
280
void kbLink::SetHole( bool h )
281
{
282
    m_hole = h;
283 284 285 286
}


//
287
// Is this not marked at all
288
//
charras's avatar
charras committed
289
bool kbLink::IsUnused()
290
{
291 292 293 294 295 296
    return
        !( m_merge_L || m_merge_R ||
           m_a_substract_b_L || m_a_substract_b_R ||
           m_b_substract_a_L || m_b_substract_a_R ||
           m_intersect_L || m_intersect_R ||
           m_exor_L || m_exor_R );
297 298 299
}


charras's avatar
charras committed
300
bool kbLink::IsZero( B_INT marge )
301
{
302
    return ( m_beginnode->Equal( m_endnode, marge ) ) ;
303 304 305
}


charras's avatar
charras committed
306
bool kbLink::ShorterThan( B_INT marge )
307
{
308
    return ( m_beginnode->ShorterThan( m_endnode, marge ) ) ;
309 310 311 312
}


//
313
// Mark this link
314
//
charras's avatar
charras committed
315
void kbLink::Mark()
316
{
317
    m_mark = true;
318 319 320 321 322 323 324 325 326 327 328 329 330
}


#ifndef ABS
#define ABS(a) (((a)<0) ? -(a) : (a))
#endif


//
// This makes from the begin and endnode one node (argument begin_or_end_node)
// The references to this link in the node will also be deleted
// After doing that, link link can be deleted or be recycled.
//
charras's avatar
charras committed
331
void kbLink::MergeNodes( kbNode *const begin_or_end_node )
332
{
333 334
// assert(beginnode && endnode);
// assert ((begin_or_end_node == beginnode)||(begin_or_end_node == endnode));
335

336 337
    m_beginnode->RemoveLink( this );
    m_endnode->RemoveLink( this );
338

339 340 341 342 343 344
    if ( m_endnode != m_beginnode )
    { // only if beginnode and endnode are different nodes
        begin_or_end_node->Merge( GetOther( begin_or_end_node ) );
    }
    m_endnode = NULL;
    m_beginnode = NULL;
345 346 347
}

//
348
// Return the position of the second link compared to this link
349 350 351 352
// Result = IS_ON | IS_LEFT | IS_RIGHT
// Here Left and Right is defined as being left or right from
// the this link towards the center (common) node
//
charras's avatar
charras committed
353
LinkStatus kbLink::OutProduct( kbLink* const two, double accur )
354
{
charras's avatar
charras committed
355
    kbNode * center;
356 357 358 359 360
    double distance;
    if ( two->GetBeginNode()->Equal( two->GetEndNode(), 1 ) )
        assert( !two );
    if ( GetBeginNode()->Equal( GetEndNode(), 1 ) )
        assert( !this );
charras's avatar
charras committed
361
    kbLine* temp_line = new kbLine( this, _GC );
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

    //the this link should connect to the other two link at at least one node
    if ( m_endnode == two->m_endnode || m_endnode == two->m_beginnode )
        center = m_endnode;
    else
    {
        center = m_beginnode;
//  assert(center==two->endnode || center==two->beginnode);
    }

    //here something tricky
    // the factor 10000.0 is needed to asure that the pointonline
    // is more accurate in this case compared to the intersection for graphs
    int uitp = temp_line->PointOnLine( two->GetOther( center ), distance, accur );

    delete temp_line;

    /*double uitp= (_x - first._x) * (third._y - _y) -
        (_y - first._y) * (third._x - _x);
    if (uitp>0) return IS_LEFT;
    if (uitp<0) return IS_RIGHT;
    return IS_ON;*/

    //depending on direction of this link (going to or coming from centre)
    if ( center == m_endnode )
    {
        if ( uitp == LEFT_SIDE )
            return IS_LEFT;
        if ( uitp == RIGHT_SIDE )
            return IS_RIGHT;
    }
    else  //center=beginnode
    {
        if ( uitp == LEFT_SIDE )
            return IS_RIGHT;
        if ( uitp == RIGHT_SIDE )
            return IS_LEFT;
    }
    return IS_ON;
}

//
// Return the position of the third link compared to this link and
405 406 407
// the second link
// Result = IS_ON | IS_LEFT | IS_RIGHT
//
charras's avatar
charras committed
408
LinkStatus kbLink::PointOnCorner( kbLink* const two, kbLink* const third )
409
{
410 411 412 413 414
    LinkStatus
    TwoToOne,  // Position of two to this line
    ThirdToOne,    // Position of third to this line
    ThirdToTwo,  // Position of third to two
    Result;
415

charras's avatar
charras committed
416
//m  kbNode* center;
417 418

//the this link should connect to the other two link at at least one node
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
//m  if (endnode==two->endnode || endnode==two->beginnode)
//m   center=endnode;
//m  else
//m  { center=beginnode;
//  assert(center==two->endnode || center==two->beginnode);
//m }
// assert(center==third->endnode || center==third->beginnode);



    // Calculate the position of the links compared to eachother
    TwoToOne  = OutProduct( two, _GC->GetAccur() );
    ThirdToOne = OutProduct( third, _GC->GetAccur() );
    //center is used in outproduct to give de direction of two
    // this is why the result should be swapped
    ThirdToTwo = two->OutProduct( third, _GC->GetAccur() );
    if ( ThirdToTwo == IS_RIGHT )
        ThirdToTwo = IS_LEFT;
    else if ( ThirdToTwo == IS_LEFT )
        ThirdToTwo = IS_RIGHT;

    // Select the result
    switch( TwoToOne )
    {
            // Line 2 lies on  leftside of this line
        case IS_LEFT : if ( ( ThirdToOne == IS_RIGHT ) || ( ThirdToTwo == IS_RIGHT ) ) return IS_RIGHT;
            else if ( ( ThirdToOne == IS_LEFT ) && ( ThirdToTwo == IS_LEFT ) ) return IS_LEFT;
            else Result = IS_ON; break;
            // Line 2 lies on this line
        case IS_ON  : if ( ( ThirdToOne == IS_RIGHT ) && ( ThirdToTwo == IS_RIGHT ) )    return IS_RIGHT;
            else if ( ( ThirdToOne == IS_LEFT ) && ( ThirdToTwo == IS_LEFT ) )   return IS_LEFT;
            //  else if ((ThirdToOne==IS_RIGHT) && (ThirdToTwo==IS_LEFT))   return IS_RIGHT;
            //  else if ((ThirdToOne==IS_LEFT) && (ThirdToTwo==IS_RIGHT))   return IS_LEFT;
            else Result = IS_ON; break;
            // Line 2 lies on right side of this line
        case IS_RIGHT : if ( ( ThirdToOne == IS_RIGHT ) && ( ThirdToTwo == IS_RIGHT ) ) return IS_RIGHT;
            else if ( ( ThirdToOne == IS_LEFT ) || ( ThirdToTwo == IS_LEFT ) ) return IS_LEFT;
            else Result = IS_ON; break;
        default: Result = IS_ON; assert( false );
    }
    return Result;
460 461 462 463 464
}

//
// Remove the reference from this link to a_node
//
charras's avatar
charras committed
465
void kbLink::Remove( kbNode *a_node )
466
{
467
    ( m_beginnode == a_node ) ? m_beginnode = NULL : m_endnode = NULL;
468 469 470 471
}


//
472
// Replace oldnode by newnode and correct the references
473
//
charras's avatar
charras committed
474
void kbLink::Replace( kbNode *oldnode, kbNode *newnode )
475 476 477 478 479 480 481 482 483 484 485 486 487
{
    if ( m_beginnode == oldnode )
    {
        m_beginnode->RemoveLink( this ); // remove the reference to this
        newnode->AddLink( this );       // let newnode refer to this
        m_beginnode = newnode;    // let this refer to newnode
    }
    else
    { //assert(endnode==oldnode);
        m_endnode->RemoveLink( this );
        newnode->AddLink( this );
        m_endnode = newnode;
    }
488 489 490 491
}


//
492
// Reset all values
493
//
charras's avatar
charras committed
494
void kbLink::Reset()
495
{
496 497 498
    m_beginnode = 0;
    m_endnode = 0;
    Reset_flags();
499 500 501 502
}


//
503
// Reset all flags
504
//
charras's avatar
charras committed
505
void kbLink::Reset_flags()
506
{
507 508 509 510 511 512 513 514 515 516
    m_bin = false;    // Marker for walking over the graph
    m_hole  = false;   // Is this a part of hole ?
    m_hole_top = false;    // link that is toplink of hole?
    m_group = GROUP_A;  // Does this belong to group A or B ( o.a. for boolean operations between graphs)
    m_LeftA = false;      // Is left in polygongroup A
    m_RightA = false;      // Is right in polygon group A
    m_LeftB = false;      // Is left in polygon group B
    m_RightB = false;      // Is right in polygongroup B
    m_mark = false;      // General purose marker, internally unused
    m_holelink = false;
517

518 519 520 521 522
    m_merge_L = m_merge_R = false;   // Marker for Merge
    m_a_substract_b_L = m_a_substract_b_R = false; // Marker for substract
    m_b_substract_a_L = m_b_substract_a_R = false; // Marker for substract
    m_intersect_L = m_intersect_R = false;  // Marker for intersect
    m_exor_L = m_exor_R = false;          // Marker for Exor
523 524 525
}

//
526
// Refill this link by the arguments
527
//
charras's avatar
charras committed
528
void kbLink::Reset( kbNode *begin, kbNode *end, int graphnr )
529
{
530 531 532 533 534 535 536 537 538 539
    // Remove all the previous references
    UnLink();
    Reset();
    // Set the references of the node and of this link correct
    begin->AddLink( this );
    end->AddLink( this );
    m_beginnode = begin;
    m_endnode = end;
    if ( graphnr != 0 )
        m_graphnum = graphnr;
540 541 542
}


charras's avatar
charras committed
543
void kbLink::Set( kbNode *begin, kbNode *end )
544
{
545 546
    m_beginnode = begin;
    m_endnode = end;
547 548
}

charras's avatar
charras committed
549
void kbLink::SetBeenHere()
550
{
551
    m_bin = true;
552 553
}

charras's avatar
charras committed
554
void kbLink::SetNotBeenHere()
555
{
556
    m_bin = false;
557 558
}

charras's avatar
charras committed
559
void kbLink::SetBeginNode( kbNode* new_node )
560
{
561
    m_beginnode = new_node;
562 563 564
}


charras's avatar
charras committed
565
void kbLink::SetEndNode( kbNode* new_node )
566
{
567
    m_endnode = new_node;
568 569 570 571
}


//
572
// Sets the graphnumber to argument num
573
//
charras's avatar
charras committed
574
void kbLink::SetGraphNum( int num )
575
{
576
    m_graphnum = num;
577 578
}

charras's avatar
charras committed
579
GroupType kbLink::Group()
580
{
581
    return m_group;
582 583 584 585 586 587
}


//
// Reset the groupflag to argument groep
//
charras's avatar
charras committed
588
void kbLink::SetGroup( GroupType groep )
589
{
590
    m_group = groep;
591 592 593 594
}


//
595
// Remove all references to this link and from this link
596
//
charras's avatar
charras committed
597
void kbLink::UnLink()
598
{
599 600 601 602 603 604 605 606 607 608 609 610
    if ( m_beginnode )
    {
        m_beginnode->RemoveLink( this );
        if ( !m_beginnode->GetNumberOfLinks() ) delete m_beginnode;
    }
    m_beginnode = NULL;
    if ( m_endnode )
    {
        m_endnode->RemoveLink( this );
        if ( !m_endnode->GetNumberOfLinks() ) delete m_endnode;
    }
    m_endnode = NULL;
611 612 613
}


charras's avatar
charras committed
614
void kbLink::UnMark()
615
{
616 617
    m_mark = false;
    m_bin = false;
618 619
}

charras's avatar
charras committed
620
void kbLink::SetMark( bool value )
621
{
622
    m_mark = value;
623 624 625
}

//
626
// general purpose mark checker
627
//
charras's avatar
charras committed
628
bool kbLink::IsMarked() { return m_mark; }
629

charras's avatar
charras committed
630
void  kbLink::SetTopHole( bool value ) { m_hole_top = value; }
631

charras's avatar
charras committed
632
bool kbLink::IsTopHole() { return m_hole_top; }
633 634 635 636

//
// Calculates the merge/substact/exor/intersect flags
//
charras's avatar
charras committed
637
void kbLink::SetLineTypes()
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
{
    m_merge_R     =
        m_a_substract_b_R =
            m_b_substract_a_R =
                m_intersect_R =
                    m_exor_R      =
                        m_merge_L     =
                            m_a_substract_b_L =
                                m_b_substract_a_L =
                                    m_intersect_L =
                                        m_exor_L      = false;

    //if left side is in group A and B then it is for the merge
    m_merge_L   = m_LeftA || m_LeftB;
    m_merge_R   = m_RightA || m_RightB;
    //both in mean does not add to result.
    if ( m_merge_L && m_merge_R )
        m_merge_L = m_merge_R = false;

    m_a_substract_b_L = m_LeftA && !m_LeftB;
    m_a_substract_b_R = m_RightA && !m_RightB;
    //both in mean does not add to result.
    if ( m_a_substract_b_L && m_a_substract_b_R )
        m_a_substract_b_L = m_a_substract_b_R = false;

    m_b_substract_a_L = m_LeftB && !m_LeftA;
    m_b_substract_a_R = m_RightB && !m_RightA;
    //both in mean does not add to result.
    if ( m_b_substract_a_L && m_b_substract_a_R )
        m_b_substract_a_L = m_b_substract_a_R = false;

    m_intersect_L = m_LeftB && m_LeftA;
    m_intersect_R = m_RightB && m_RightA;
    //both in mean does not add to result.
    if ( m_intersect_L && m_intersect_R )
        m_intersect_L = m_intersect_R = false;

    m_exor_L = !( ( m_LeftB && m_LeftA ) || ( !m_LeftB && !m_LeftA ) );
    m_exor_R = !( ( m_RightB && m_RightA ) || ( !m_RightB && !m_RightA ) );
    //both in mean does not add to result.
    if ( m_exor_L && m_exor_R )
        m_exor_L = m_exor_R = false;
680 681 682 683
}


//put in direction with a_node as beginnode
charras's avatar
charras committed
684
void  kbLink::Redirect( kbNode* a_node )
685 686 687 688
{
    if ( a_node != m_beginnode )
    {
        // swap the begin- and endnode of the current link
charras's avatar
charras committed
689
        kbNode * dummy = m_beginnode;
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
        m_beginnode = m_endnode;
        m_endnode = dummy;

        bool swap = m_LeftA;
        m_LeftA = m_RightA;
        m_RightA = swap;

        swap = m_LeftB;
        m_LeftB = m_RightB;
        m_RightB = swap;

        swap = m_merge_L ;
        m_merge_L = m_merge_R;
        m_merge_R = swap;

        swap = m_a_substract_b_L;
        m_a_substract_b_L = m_a_substract_b_R;
        m_a_substract_b_R = swap;

        swap = m_b_substract_a_L;
        m_b_substract_a_L = m_b_substract_a_R;
        m_b_substract_a_R = swap;

        swap = m_intersect_L;
        m_intersect_L = m_intersect_R;
        m_intersect_R = swap;

        swap = m_exor_L;
        m_exor_L = m_exor_R;
        m_exor_R = swap;
    }
721
}