mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
disjoint_subsets: add get_number_of_sets and get_size_of_set functions (#880)
* disjoint_subsets: make clear and sizde functions noexcept * disjoint_subsets: add get_number_of_sets function, documentations and tests * disjoint_subsets: add get_size_of_set function, documentation and tests * Split disjoint_subsets in a hierarchy. Modify disjoint_subsets to make it a valid base class. Add new class disjoint_subsets_sized, with information about number of subsets, and size of each subset. Add necessary unit test for the new class. * Replace tabs by spaces. * Remove virtuals from disjoint_subsets, and modify disjoint_subsets_sized. Now disjoint_subsets_sized is implemented in terms of disjoint_subsets, instead of inherit from it.
This commit is contained in:
parent
15c04ab224
commit
f3ec2abb1b
@ -17,7 +17,7 @@ namespace dlib
|
||||
public:
|
||||
|
||||
void clear (
|
||||
)
|
||||
) noexcept
|
||||
{
|
||||
items.clear();
|
||||
}
|
||||
@ -35,21 +35,21 @@ namespace dlib
|
||||
}
|
||||
|
||||
unsigned long size (
|
||||
) const
|
||||
) const noexcept
|
||||
{
|
||||
return items.size();
|
||||
}
|
||||
|
||||
unsigned long find_set (
|
||||
unsigned long item
|
||||
unsigned long item
|
||||
) const
|
||||
{
|
||||
// make sure requires clause is not broken
|
||||
DLIB_ASSERT(item < size(),
|
||||
DLIB_ASSERT(item < size(),
|
||||
"\t unsigned long disjoint_subsets::find_set()"
|
||||
<< "\n\t item must be less than size()"
|
||||
<< "\n\t item: " << item
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t item: " << item
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t this: " << this
|
||||
);
|
||||
|
||||
@ -88,16 +88,16 @@ namespace dlib
|
||||
// make sure requires clause is not broken
|
||||
DLIB_ASSERT(a != b &&
|
||||
a < size() &&
|
||||
b < size() &&
|
||||
b < size() &&
|
||||
find_set(a) == a &&
|
||||
find_set(b) == b,
|
||||
"\t unsigned long disjoint_subsets::merge_sets(a,b)"
|
||||
<< "\n\t invalid arguments were given to this function"
|
||||
<< "\n\t a: " << a
|
||||
<< "\n\t b: " << b
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t find_set(a): " << find_set(a)
|
||||
<< "\n\t find_set(b): " << find_set(b)
|
||||
<< "\n\t a: " << a
|
||||
<< "\n\t b: " << b
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t find_set(a): " << find_set(a)
|
||||
<< "\n\t find_set(b): " << find_set(b)
|
||||
<< "\n\t this: " << this
|
||||
);
|
||||
|
||||
@ -139,4 +139,3 @@ namespace dlib
|
||||
}
|
||||
|
||||
#endif // DLIB_DISJOINT_SUBsETS_Hh_
|
||||
|
||||
|
@ -20,13 +20,13 @@ namespace dlib
|
||||
WHAT THIS OBJECT REPRESENTS
|
||||
This object represents a set of integers which is partitioned into
|
||||
a number of disjoint subsets. It supports the two fundamental operations
|
||||
of finding which subset a particular integer belongs to as well as
|
||||
of finding which subset a particular integer belongs to as well as
|
||||
merging subsets.
|
||||
!*/
|
||||
public:
|
||||
|
||||
void clear (
|
||||
);
|
||||
) noexcept;
|
||||
/*!
|
||||
ensures
|
||||
- #size() == 0
|
||||
@ -45,28 +45,28 @@ namespace dlib
|
||||
!*/
|
||||
|
||||
unsigned long size (
|
||||
) const;
|
||||
) const noexcept;
|
||||
/*!
|
||||
ensures
|
||||
- returns the total number of integer elements represented
|
||||
by this object.
|
||||
by this object.
|
||||
!*/
|
||||
|
||||
unsigned long find_set (
|
||||
unsigned long item
|
||||
unsigned long item
|
||||
) const;
|
||||
/*!
|
||||
requires
|
||||
- item < size()
|
||||
ensures
|
||||
- Each disjoint subset can be represented by any of its elements (since
|
||||
the sets are all disjoint). In particular, for each subset we define
|
||||
a special "representative element" which is used to represent it.
|
||||
Therefore, this function returns the representative element for the
|
||||
- Each disjoint subset can be represented by any of its elements (since
|
||||
the sets are all disjoint). In particular, for each subset we define
|
||||
a special "representative element" which is used to represent it.
|
||||
Therefore, this function returns the representative element for the
|
||||
set which contains item.
|
||||
- find_set(find_set(item)) == find_set(item)
|
||||
- Note that if A and B are both elements of the same subset then we always
|
||||
have find_set(A) == find_set(B).
|
||||
have find_set(A) == find_set(B).
|
||||
!*/
|
||||
|
||||
unsigned long merge_sets (
|
||||
@ -87,7 +87,6 @@ namespace dlib
|
||||
(i.e. merges the set's containing a and b)
|
||||
- returns #find_set(a)
|
||||
!*/
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
@ -95,5 +94,3 @@ namespace dlib
|
||||
}
|
||||
|
||||
#endif // DLIB_DISJOINT_SUBsETS_ABSTRACT_Hh_
|
||||
|
||||
|
||||
|
130
dlib/disjoint_subsets/disjoint_subsets_sized.h
Normal file
130
dlib/disjoint_subsets/disjoint_subsets_sized.h
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_DISJOINT_SUBsETS_SIZED_Hh_
|
||||
#define DLIB_DISJOINT_SUBsETS_SIZED_Hh_
|
||||
|
||||
#include "disjoint_subsets_sized_abstract.h"
|
||||
#include "disjoint_subsets.h"
|
||||
#include <vector>
|
||||
#include "../algs.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
class disjoint_subsets_sized
|
||||
{
|
||||
public:
|
||||
|
||||
void clear (
|
||||
) noexcept
|
||||
{
|
||||
disjoint_subsets_.clear();
|
||||
sets_size.clear();
|
||||
number_of_sets = 0;
|
||||
}
|
||||
|
||||
void set_size (
|
||||
unsigned long new_size
|
||||
)
|
||||
{
|
||||
disjoint_subsets_.set_size(new_size);
|
||||
sets_size.assign(new_size, 1);
|
||||
number_of_sets = new_size;
|
||||
}
|
||||
|
||||
unsigned long size (
|
||||
) const noexcept
|
||||
{
|
||||
return disjoint_subsets_.size();
|
||||
}
|
||||
|
||||
unsigned long find_set (
|
||||
unsigned long item
|
||||
) const
|
||||
{
|
||||
// make sure requires clause is not broken
|
||||
DLIB_ASSERT(item < size(),
|
||||
"\t unsigned long disjoint_subsets::find_set()"
|
||||
<< "\n\t item must be less than size()"
|
||||
<< "\n\t item: " << item
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t this: " << this
|
||||
);
|
||||
|
||||
return disjoint_subsets_.find_set(item);
|
||||
}
|
||||
|
||||
unsigned long merge_sets (
|
||||
unsigned long a,
|
||||
unsigned long b
|
||||
)
|
||||
{
|
||||
// make sure requires clause is not broken
|
||||
DLIB_ASSERT(a != b &&
|
||||
a < size() &&
|
||||
b < size() &&
|
||||
find_set(a) == a &&
|
||||
find_set(b) == b,
|
||||
"\t unsigned long disjoint_subsets::merge_sets(a,b)"
|
||||
<< "\n\t invalid arguments were given to this function"
|
||||
<< "\n\t a: " << a
|
||||
<< "\n\t b: " << b
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t find_set(a): " << find_set(a)
|
||||
<< "\n\t find_set(b): " << find_set(b)
|
||||
<< "\n\t this: " << this
|
||||
);
|
||||
|
||||
disjoint_subsets_.merge_sets(a, b);
|
||||
|
||||
if (find_set(a) == a) sets_size[a] += sets_size[b];
|
||||
else sets_size[b] += sets_size[a];
|
||||
--number_of_sets;
|
||||
|
||||
return find_set(a);
|
||||
}
|
||||
|
||||
unsigned long get_number_of_sets (
|
||||
) const noexcept
|
||||
{
|
||||
return number_of_sets;
|
||||
}
|
||||
|
||||
unsigned long get_size_of_set(
|
||||
unsigned long item
|
||||
) const
|
||||
{
|
||||
// make sure requires clause is not broken
|
||||
DLIB_ASSERT(item < size() &&
|
||||
find_set(item) == item,
|
||||
"\t unsigned long disjoint_subsets::get_size_of_set()"
|
||||
<< "\n\t invalid arguments were given to this function"
|
||||
<< "\n\t item: " << item
|
||||
<< "\n\t size(): " << size()
|
||||
<< "\n\t find_set(item): " << find_set(item)
|
||||
<< "\n\t this: " << this
|
||||
);
|
||||
|
||||
return sets_size[item];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
See the book Introduction to Algorithms by Cormen, Leiserson, Rivest and Stein
|
||||
for a discussion of how this algorithm works.
|
||||
*/
|
||||
|
||||
mutable std::vector<unsigned long> sets_size;
|
||||
unsigned long number_of_sets{0};
|
||||
disjoint_subsets disjoint_subsets_;
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
#endif // DLIB_DISJOINT_SUBsETS_SIZED_Hh_
|
121
dlib/disjoint_subsets/disjoint_subsets_sized_abstract.h
Normal file
121
dlib/disjoint_subsets/disjoint_subsets_sized_abstract.h
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#undef DLIB_DISJOINT_SUBsETS_SIZED_ABSTRACT_Hh_
|
||||
#ifdef DLIB_DISJOINT_SUBsETS_SIZED_ABSTRACT_Hh_
|
||||
|
||||
#include <vector>
|
||||
#include "../algs.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
class disjoint_subsets_sized : public disjoint_subsets
|
||||
{
|
||||
/*!
|
||||
INITIAL VALUE
|
||||
- size() == 0
|
||||
- get_number_of_sets() == 0
|
||||
|
||||
WHAT THIS OBJECT REPRESENTS
|
||||
This object represents a set of integers which is partitioned into
|
||||
a number of disjoint subsets. It supports the two fundamental operations
|
||||
of finding which subset a particular integer belongs to as well as
|
||||
merging subsets.
|
||||
!*/
|
||||
public:
|
||||
|
||||
virtual void clear (
|
||||
) noexcept;
|
||||
/*!
|
||||
ensures
|
||||
- #size() == 0
|
||||
- #get_number_of_sets() == 0
|
||||
- returns this object to its initial value
|
||||
!*/
|
||||
|
||||
virtual void set_size (
|
||||
unsigned long new_size
|
||||
);
|
||||
/*!
|
||||
ensures
|
||||
- #size() == new_size
|
||||
- #get_number_of_sets() == new_size
|
||||
- for all valid i:
|
||||
- #find_set(i) == i
|
||||
(i.e. this object contains new_size subsets, each containing exactly one element)
|
||||
- #get_size_of_set(i) == 1
|
||||
!*/
|
||||
|
||||
virtual unsigned long size (
|
||||
) const noexcept;
|
||||
/*!
|
||||
ensures
|
||||
- returns the total number of integer elements represented
|
||||
by this object.
|
||||
!*/
|
||||
|
||||
virtual unsigned long find_set (
|
||||
unsigned long item
|
||||
) const;
|
||||
/*!
|
||||
requires
|
||||
- item < size()
|
||||
ensures
|
||||
- Each disjoint subset can be represented by any of its elements (since
|
||||
the sets are all disjoint). In particular, for each subset we define
|
||||
a special "representative element" which is used to represent it.
|
||||
Therefore, this function returns the representative element for the
|
||||
set which contains item.
|
||||
- find_set(find_set(item)) == find_set(item)
|
||||
- Note that if A and B are both elements of the same subset then we always
|
||||
have find_set(A) == find_set(B).
|
||||
!*/
|
||||
|
||||
virtual unsigned long merge_sets (
|
||||
unsigned long a,
|
||||
unsigned long b
|
||||
);
|
||||
/*!
|
||||
requires
|
||||
- a != b
|
||||
- a < size()
|
||||
- b < size()
|
||||
- find_set(a) == a
|
||||
(i.e. a is the representative element of some set)
|
||||
- find_set(b) == b
|
||||
(i.e. b is the representative element of some set)
|
||||
ensures
|
||||
- #find_set(a) == #find_set(b)
|
||||
(i.e. merges the set's containing a and b)
|
||||
- #get_size_of_set(#find_set(a)) == get_size_of_set(a) + get_size_of_set(b)
|
||||
- #get_number_of_sets() == get_number_of_sets() - 1
|
||||
- returns #find_set(a)
|
||||
!*/
|
||||
|
||||
unsigned long get_number_of_sets (
|
||||
) const noexcept;
|
||||
/*!
|
||||
ensures
|
||||
- returns the current number of different subsets.
|
||||
!*/
|
||||
|
||||
unsigned long get_size_of_set(
|
||||
unsigned long item
|
||||
) const;
|
||||
/*!
|
||||
requires
|
||||
- item < size()
|
||||
- find_set(item) == item
|
||||
(i.e. item is the representative element of some set)
|
||||
ensures
|
||||
- returns the number of elements which belongs to the set where item is the representative element.
|
||||
!*/
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
#endif // DLIB_DISJOINT_SUBsETS_ABSTRACT_Hh_
|
9
dlib/disjoint_subsets_sized.h
Normal file
9
dlib/disjoint_subsets_sized.h
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_DISJOINt_SUBSETS_SIZED_
|
||||
#define DLIB_DISJOINt_SUBSETS_SIZED_
|
||||
|
||||
|
||||
#include "disjoint_subsets/disjoint_subsets_sized.h"
|
||||
|
||||
#endif // DLIB_DISJOINt_SUBSETS_SIZED_
|
@ -49,6 +49,7 @@ set (tests
|
||||
directed_graph.cpp
|
||||
discriminant_pca.cpp
|
||||
disjoint_subsets.cpp
|
||||
disjoint_subsets_sized.cpp
|
||||
ekm_and_lisf.cpp
|
||||
empirical_kernel_map.cpp
|
||||
entropy_coder.cpp
|
||||
@ -176,5 +177,3 @@ if (NOT DLIB_NO_GUI_SUPPORT)
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "tester.h"
|
||||
|
||||
namespace
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace test;
|
||||
@ -35,7 +35,6 @@ namespace
|
||||
DLIB_TEST(s.find_set(3) == 3);
|
||||
DLIB_TEST(s.find_set(4) == 4);
|
||||
|
||||
|
||||
unsigned long id = s.merge_sets(1,3);
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.find_set(1) == id);
|
||||
@ -101,6 +100,3 @@ namespace
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
143
dlib/test/disjoint_subsets_sized.cpp
Normal file
143
dlib/test/disjoint_subsets_sized.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright (C) 2011 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <dlib/disjoint_subsets_sized.h>
|
||||
|
||||
#include "tester.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace test;
|
||||
using namespace dlib;
|
||||
using namespace std;
|
||||
|
||||
logger dlog("test.disjoint_subsets_sized");
|
||||
|
||||
void test_disjoint_subsets_sized()
|
||||
{
|
||||
print_spinner();
|
||||
disjoint_subsets_sized s;
|
||||
|
||||
DLIB_TEST(s.size() == 0);
|
||||
DLIB_TEST(s.get_number_of_sets() == 0);
|
||||
|
||||
s.set_size(5);
|
||||
DLIB_TEST(s.size() == 5);
|
||||
DLIB_TEST(s.get_number_of_sets() == 5);
|
||||
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.find_set(1) == 1);
|
||||
DLIB_TEST(s.find_set(2) == 2);
|
||||
DLIB_TEST(s.find_set(3) == 3);
|
||||
DLIB_TEST(s.find_set(4) == 4);
|
||||
|
||||
DLIB_TEST(s.get_size_of_set(0) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(1) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(2) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(3) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(4) == 1);
|
||||
|
||||
unsigned long id = s.merge_sets(1,3);
|
||||
DLIB_TEST(s.get_number_of_sets() == 4);
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.find_set(1) == id);
|
||||
DLIB_TEST(s.find_set(2) == 2);
|
||||
DLIB_TEST(s.find_set(3) == id);
|
||||
DLIB_TEST(s.find_set(4) == 4);
|
||||
DLIB_TEST(s.get_size_of_set(0) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 2);
|
||||
DLIB_TEST(s.get_size_of_set(2) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 2);
|
||||
DLIB_TEST(s.get_size_of_set(4) == 1);
|
||||
|
||||
id = s.merge_sets(s.find_set(1),4);
|
||||
DLIB_TEST(s.get_number_of_sets() == 3);
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.find_set(1) == id);
|
||||
DLIB_TEST(s.find_set(2) == 2);
|
||||
DLIB_TEST(s.find_set(3) == id);
|
||||
DLIB_TEST(s.find_set(4) == id);
|
||||
DLIB_TEST(s.get_size_of_set(0) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 3);
|
||||
DLIB_TEST(s.get_size_of_set(2) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 3);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 3);
|
||||
|
||||
unsigned long id2 = s.merge_sets(0,2);
|
||||
DLIB_TEST(s.get_number_of_sets() == 2);
|
||||
DLIB_TEST(s.find_set(0) == id2);
|
||||
DLIB_TEST(s.find_set(1) == id);
|
||||
DLIB_TEST(s.find_set(2) == id2);
|
||||
DLIB_TEST(s.find_set(3) == id);
|
||||
DLIB_TEST(s.find_set(4) == id);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 2);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 3);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(2)) == 2);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 3);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 3);
|
||||
|
||||
id = s.merge_sets(s.find_set(1),s.find_set(0));
|
||||
DLIB_TEST(s.get_number_of_sets() == 1);
|
||||
DLIB_TEST(s.find_set(0) == id);
|
||||
DLIB_TEST(s.find_set(1) == id);
|
||||
DLIB_TEST(s.find_set(2) == id);
|
||||
DLIB_TEST(s.find_set(3) == id);
|
||||
DLIB_TEST(s.find_set(4) == id);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 5);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 5);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(2)) == 5);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(3)) == 5);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(4)) == 5);
|
||||
|
||||
DLIB_TEST(s.size() == 5);
|
||||
s.set_size(1);
|
||||
DLIB_TEST(s.size() == 1);
|
||||
DLIB_TEST(s.get_number_of_sets() == 1);
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.get_size_of_set(0) == 1);
|
||||
s.set_size(2);
|
||||
DLIB_TEST(s.size() == 2);
|
||||
DLIB_TEST(s.get_number_of_sets() == 2);
|
||||
DLIB_TEST(s.find_set(0) == 0);
|
||||
DLIB_TEST(s.find_set(1) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(0) == 1);
|
||||
DLIB_TEST(s.get_size_of_set(1) == 1);
|
||||
id = s.merge_sets(0,1);
|
||||
DLIB_TEST(s.size() == 2);
|
||||
DLIB_TEST(s.get_number_of_sets() == 1);
|
||||
DLIB_TEST(id == s.find_set(0));
|
||||
DLIB_TEST(id == s.find_set(1));
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(0)) == 2);
|
||||
DLIB_TEST(s.get_size_of_set(s.find_set(1)) == 2);
|
||||
DLIB_TEST(s.size() == 2);
|
||||
s.clear();
|
||||
DLIB_TEST(s.size() == 0);
|
||||
DLIB_TEST(s.get_number_of_sets() == 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
class tester_disjoint_subsets_sized : public tester
|
||||
{
|
||||
public:
|
||||
tester_disjoint_subsets_sized (
|
||||
) :
|
||||
tester ("test_disjoint_subsets_sized",
|
||||
"Runs tests on the disjoint_subsets_sized component.")
|
||||
{}
|
||||
|
||||
void perform_test (
|
||||
)
|
||||
{
|
||||
test_disjoint_subsets_sized();
|
||||
}
|
||||
} a;
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user