add logic for pure quadratics
This commit is contained in:
@@ -124,6 +124,13 @@ typedef enum
|
||||
DELTA_MINUS = -1,
|
||||
} e_delta_sign;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RADICAND_ZERO = 0,
|
||||
RADICAND_PLUS = 1,
|
||||
RADICAND_MINUS = -1,
|
||||
} e_radicand_sign;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double a; // a in "ax + b"
|
||||
@@ -136,8 +143,8 @@ typedef struct
|
||||
double a; // a in "ax² + bx + c"
|
||||
double b; // b in "ax² + bx + c"
|
||||
double c; // c in "ax² + bx + c"
|
||||
e_delta_sign delta_sign; // DELTA_PLUS or DELTA_MINUS or DELTA_ZERO
|
||||
double delta; // Δ == b² - 4ac
|
||||
e_delta_sign delta_sign; // DELTA_PLUS or DELTA_MINUS or DELTA_ZERO
|
||||
double delta_absolute; // |Δ|
|
||||
double delta_sqrt; // √|Δ|
|
||||
double left_term; // double (-b / 2a)
|
||||
@@ -149,13 +156,29 @@ typedef struct
|
||||
int right_term_denominator; // 2a / gcd
|
||||
} s_solution_degree_2;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double a; // a in "ax² + c"
|
||||
double c; // c in "ax² + c"
|
||||
double radicand; // x² = -c/a
|
||||
e_radicand_sign radicand_sign; // RADICAND_PLUS or RADICAND_MINUS or RADICAND_ZERO
|
||||
double radicand_absolute; // |radicand|
|
||||
double radicand_sqrt; // √|radicand|
|
||||
double numerator_sqrt; // √a
|
||||
bool numerator_sqrt_is_int; // false if √a is a double
|
||||
double denominator_sqrt; // √c
|
||||
bool denominator_sqrt_is_int; // false if √c is a double
|
||||
} s_solution_degree_2_pure;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int degree;
|
||||
bool is_quadratic_pure;
|
||||
union
|
||||
{
|
||||
s_solution_degree_1 solution_degree_1;
|
||||
s_solution_degree_2 solution_degree_2;
|
||||
s_solution_degree_2_pure solution_degree_2_pure;
|
||||
};
|
||||
} s_solution;
|
||||
|
||||
@@ -166,6 +189,10 @@ void solve(const s_polynom *polynom, s_solution *solution);
|
||||
*/
|
||||
|
||||
bool is_nearly_equal_zero(double num);
|
||||
bool has_decimal_part(double num);
|
||||
bool any_has_decimal_part(double *num, size_t len);
|
||||
int gcd_int(int a, int b);
|
||||
int reduce_fraction(double *numerator, double *denominator);
|
||||
|
||||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* UTILS/ERRORS.C
|
||||
@@ -183,6 +210,7 @@ const char *token_tag_to_str(e_token_tag tag);
|
||||
const char *term_position_to_str(e_term_position pos);
|
||||
const char *term_sign_to_str(e_term_sign sign);
|
||||
const char *delta_sign_to_str(e_delta_sign sign);
|
||||
const char *radicand_sign_to_str(e_radicand_sign enum_value);
|
||||
|
||||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* UTILS/PRINT_DEBUG.C
|
||||
|
||||
@@ -2,12 +2,98 @@
|
||||
|
||||
#include "computorv1.h"
|
||||
|
||||
/**
|
||||
* DEGREE 1
|
||||
*/
|
||||
|
||||
static void print_solution_degree_1(s_solution_degree_1 solution)
|
||||
{
|
||||
ft_printf("The solution is:\n");
|
||||
printf("%g\n", solution.solution);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEGREE 2 PURE
|
||||
*/
|
||||
|
||||
static void print_solution_pure_radicand_zero()
|
||||
{
|
||||
ft_printf("Radicant is equal to zero, the solution is:\n");
|
||||
printf("0\n");
|
||||
}
|
||||
|
||||
static void print_solution_pure_radicand_positiv(s_solution_degree_2_pure solution)
|
||||
{
|
||||
double numerator;
|
||||
double denominator;
|
||||
|
||||
ft_printf("Radicant is strictly positive, the two solutions are:\n");
|
||||
if (solution.numerator_sqrt_is_int && solution.denominator_sqrt_is_int)
|
||||
{
|
||||
numerator = solution.numerator_sqrt;
|
||||
denominator = solution.denominator_sqrt;
|
||||
reduce_fraction(&numerator, &denominator);
|
||||
printf("%g/%g\n", numerator, denominator);
|
||||
printf("-%g/%g\n", numerator, denominator);
|
||||
}
|
||||
else if (solution.numerator_sqrt_is_int)
|
||||
{
|
||||
printf("%g/√(%g)\n", solution.numerator_sqrt, solution.c);
|
||||
printf("-%g/√(%g)\n", solution.numerator_sqrt, solution.c);
|
||||
}
|
||||
else if (solution.denominator_sqrt_is_int)
|
||||
{
|
||||
printf("√(%g)/%g\n", solution.a, solution.denominator_sqrt);
|
||||
printf("-√(%g)/%g\n", solution.a, solution.denominator_sqrt);
|
||||
}
|
||||
else
|
||||
{
|
||||
numerator = solution.numerator_sqrt;
|
||||
denominator = solution.denominator_sqrt;
|
||||
reduce_fraction(&numerator, &denominator);
|
||||
printf("√(%g/%g)\n", numerator, denominator);
|
||||
printf("-√(%g/%g)\n", numerator, denominator);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_solution_pure_radicand_negativ(s_solution_degree_2_pure solution)
|
||||
{
|
||||
double numerator;
|
||||
double denominator;
|
||||
|
||||
ft_printf("Radicant is strictly negative, the two complex solutions are:\n");
|
||||
if (solution.numerator_sqrt_is_int && solution.denominator_sqrt_is_int)
|
||||
{
|
||||
numerator = solution.numerator_sqrt;
|
||||
denominator = solution.denominator_sqrt;
|
||||
reduce_fraction(&numerator, &denominator);
|
||||
printf("i%g/%g\n", numerator, denominator);
|
||||
printf("-i%g/%g\n", numerator, denominator);
|
||||
}
|
||||
else if (solution.numerator_sqrt_is_int)
|
||||
{
|
||||
printf("i%g/√(%g)\n", solution.numerator_sqrt, solution.c);
|
||||
printf("-i%g/√(%g)\n", solution.numerator_sqrt, solution.c);
|
||||
}
|
||||
else if (solution.denominator_sqrt_is_int)
|
||||
{
|
||||
printf("i√(%g)/%g\n", solution.a, solution.denominator_sqrt);
|
||||
printf("-i√(%g)/%g\n", solution.a, solution.denominator_sqrt);
|
||||
}
|
||||
else
|
||||
{
|
||||
numerator = solution.a;
|
||||
denominator = solution.c;
|
||||
reduce_fraction(&numerator, &denominator);
|
||||
printf("i√(%g/%g)\n", numerator, denominator);
|
||||
printf("-i√(%g/%g)\n", numerator, denominator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEGREE 2
|
||||
*/
|
||||
|
||||
static void print_solution_delta_zero(s_solution_degree_2 solution)
|
||||
{
|
||||
ft_printf("Discriminant is equal to zero, the solution is:\n");
|
||||
@@ -59,7 +145,9 @@ static void print_solution_delta_negativ(s_solution_degree_2 solution)
|
||||
void print_solution(s_solution *solution)
|
||||
{
|
||||
s_solution_degree_2 solution_d2;
|
||||
s_solution_degree_2_pure solution_d2_pure;
|
||||
e_delta_sign delta_sign;
|
||||
e_radicand_sign radicand_sign;
|
||||
|
||||
// degree 1
|
||||
|
||||
@@ -67,7 +155,20 @@ void print_solution(s_solution *solution)
|
||||
{
|
||||
return print_solution_degree_1(solution->solution_degree_1);
|
||||
}
|
||||
else if (solution->degree == 2)
|
||||
else if (solution->degree == 2 && solution->is_quadratic_pure)
|
||||
{
|
||||
solution_d2_pure = solution->solution_degree_2_pure;
|
||||
radicand_sign = solution_d2_pure.radicand_sign;
|
||||
if (radicand_sign == RADICAND_ZERO)
|
||||
print_solution_pure_radicand_zero();
|
||||
else if (radicand_sign == RADICAND_PLUS)
|
||||
print_solution_pure_radicand_positiv(solution_d2_pure);
|
||||
else if (radicand_sign == RADICAND_MINUS)
|
||||
print_solution_pure_radicand_negativ(solution_d2_pure);
|
||||
else
|
||||
stop_errors("radicand sign is wrong : '%i' , radicand : '%g'", solution_d2_pure.radicand_sign, solution_d2_pure.radicand);
|
||||
}
|
||||
else if (solution->degree == 2 && !solution->is_quadratic_pure)
|
||||
{
|
||||
solution_d2 = solution->solution_degree_2;
|
||||
delta_sign = solution_d2.delta_sign;
|
||||
|
||||
55
src/solver.c
55
src/solver.c
@@ -32,36 +32,6 @@ static double positiv_zero(double num)
|
||||
return num;
|
||||
}
|
||||
|
||||
static int has_decimal_part(double num)
|
||||
{
|
||||
return (num != (int)num);
|
||||
}
|
||||
|
||||
static bool any_has_decimal_part(double *num, size_t len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
if (has_decimal_part(num[len - 1]))
|
||||
return true;
|
||||
len--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// find GCD of two integers using euclidean algorithm
|
||||
static int gcd_int(int a, int b)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
while (b != 0)
|
||||
{
|
||||
tmp = b;
|
||||
b = a % b;
|
||||
a = tmp;
|
||||
}
|
||||
return abs(a);
|
||||
}
|
||||
|
||||
static void solve_degree_1(s_solution_degree_1 *solution, double a, double b)
|
||||
{
|
||||
solution->a = a;
|
||||
@@ -120,6 +90,28 @@ static void solve_degree_2(s_solution_degree_2 *solution, double a, double b, do
|
||||
solution->left_term_denominator = (a * 2) / gcd;
|
||||
}
|
||||
|
||||
static void solve_degree_2_pure(s_solution_degree_2_pure *solution, double a, double c)
|
||||
{
|
||||
double radicand;
|
||||
|
||||
solution->a = a;
|
||||
solution->c = c;
|
||||
|
||||
radicand = -c / a;
|
||||
solution->radicand_sign = ft_fsign(radicand); // RADICAND_PLUS or RADICAND_MINUS or RADICAND_ZERO
|
||||
solution->radicand_absolute = ft_fabs(radicand); // |radicand|
|
||||
solution->radicand_sqrt = ft_sqrt(solution->radicand_absolute, DOUBLE_PRECISION); // √|radicand|
|
||||
|
||||
/**
|
||||
* SQARE ROOTS
|
||||
*/
|
||||
|
||||
solution->numerator_sqrt = ft_sqrt(a, DOUBLE_PRECISION);
|
||||
solution->numerator_sqrt_is_int = !has_decimal_part(solution->numerator_sqrt);
|
||||
solution->denominator_sqrt = ft_sqrt(c, DOUBLE_PRECISION);
|
||||
solution->denominator_sqrt_is_int = !has_decimal_part(solution->denominator_sqrt);
|
||||
}
|
||||
|
||||
void solve(const s_polynom *polynom, s_solution *solution)
|
||||
{
|
||||
double power2;
|
||||
@@ -138,7 +130,8 @@ void solve(const s_polynom *polynom, s_solution *solution)
|
||||
{
|
||||
if (is_nearly_equal_zero(power1))
|
||||
{
|
||||
// solve_degree_2_pure(&solution->solution_degree_2, power2, power0);
|
||||
solution->is_quadratic_pure = true;
|
||||
solve_degree_2_pure(&solution->solution_degree_2_pure, power2, power0);
|
||||
}
|
||||
else
|
||||
solve_degree_2(&solution->solution_degree_2, power2, power1, power0);
|
||||
|
||||
@@ -73,6 +73,7 @@ static void print_context_solution()
|
||||
{
|
||||
s_solution_degree_1 solution_d1;
|
||||
s_solution_degree_2 solution_d2;
|
||||
s_solution_degree_2_pure solution_d2_pure;
|
||||
|
||||
if (solution_g_err->degree == 1)
|
||||
{
|
||||
@@ -85,28 +86,51 @@ static void print_context_solution()
|
||||
}
|
||||
else if (solution_g_err->degree == 2)
|
||||
{
|
||||
solution_d2 = solution_g_err->solution_degree_2;
|
||||
dprintf(STDERR_FILENO, "degree 2 : delta > 0 ( -b / 2a +- √Δ / 2a )\n");
|
||||
dprintf(STDERR_FILENO, " delta == 0 ( -b / 2a )\n");
|
||||
dprintf(STDERR_FILENO, " delta < 0 ( -b / 2a +- i√|Δ| / 2a )\n");
|
||||
if (solution_g_err->is_quadratic_pure)
|
||||
{
|
||||
solution_d2_pure = solution_g_err->solution_degree_2_pure;
|
||||
dprintf(STDERR_FILENO, "degree 2 pure : radicand > 0 ( +-( -c / 2a ) )\n");
|
||||
dprintf(STDERR_FILENO, " radicand == 0 ( 0 )\n");
|
||||
dprintf(STDERR_FILENO, " radicand < 0 ( +-i( -c / 2a ) )\n");
|
||||
|
||||
dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2.a);
|
||||
dprintf(STDERR_FILENO, "b : %15g ( b )\n", solution_d2.b);
|
||||
dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2.c);
|
||||
dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2_pure.a);
|
||||
dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2_pure.c);
|
||||
|
||||
dprintf(STDERR_FILENO, "delta : %15g ( Δ == b² - 4ac )\n", solution_d2.delta);
|
||||
dprintf(STDERR_FILENO, "delta_sign : %15s ( '-' || '+' || '0' )\n", delta_sign_to_str(solution_d2.delta_sign));
|
||||
dprintf(STDERR_FILENO, "delta_absolute : %15g ( |Δ| )\n", solution_d2.delta_absolute);
|
||||
dprintf(STDERR_FILENO, "delta_sqrt : %15g ( √|Δ| )\n", solution_d2.delta_sqrt);
|
||||
dprintf(STDERR_FILENO, "radicand : %15g ( r == x² == -c / a )\n", solution_d2_pure.radicand);
|
||||
dprintf(STDERR_FILENO, "radicand_sign : %15s ( '-' || '+' || '0' )\n", radicand_sign_to_str(solution_d2_pure.radicand_sign));
|
||||
dprintf(STDERR_FILENO, "radicand_absolute : %15g ( |r| )\n", solution_d2_pure.radicand_absolute);
|
||||
dprintf(STDERR_FILENO, "radicand_sqrt : %15g ( √|r| )\n", solution_d2_pure.radicand_sqrt);
|
||||
|
||||
dprintf(STDERR_FILENO, "left_term : %15g ( -b / 2a )\n", solution_d2.left_term);
|
||||
dprintf(STDERR_FILENO, "right_term : %15g ( √|Δ| / 2a )\n", solution_d2.right_term);
|
||||
dprintf(STDERR_FILENO, "numerator_sqrt : %15g ( √a )\n", solution_d2_pure.numerator_sqrt);
|
||||
dprintf(STDERR_FILENO, "numerator_sqrt_is_int : %15i ( false if √a is double )\n", solution_d2_pure.numerator_sqrt_is_int);
|
||||
dprintf(STDERR_FILENO, "denominator_sqrt : %15g ( √c )\n", solution_d2_pure.denominator_sqrt);
|
||||
dprintf(STDERR_FILENO, "denominator_sqrt_is_int: %15i ( false if √c is double )\n", solution_d2_pure.denominator_sqrt_is_int);
|
||||
}
|
||||
else
|
||||
{
|
||||
solution_d2 = solution_g_err->solution_degree_2;
|
||||
dprintf(STDERR_FILENO, "degree 2 : delta > 0 ( -b / 2a +- √Δ / 2a )\n");
|
||||
dprintf(STDERR_FILENO, " delta == 0 ( -b / 2a )\n");
|
||||
dprintf(STDERR_FILENO, " delta < 0 ( -b / 2a +- i√|Δ| / 2a )\n");
|
||||
|
||||
dprintf(STDERR_FILENO, "all_int : %15i ( are int : b, a, √|Δ| )\n", solution_d2.all_int);
|
||||
dprintf(STDERR_FILENO, "left_term_numerator : %15i ( -b / left_gcd )\n", solution_d2.left_term_numerator);
|
||||
dprintf(STDERR_FILENO, "left_term_denominator : %15i ( 2a / left_gcd )\n", solution_d2.left_term_denominator);
|
||||
dprintf(STDERR_FILENO, "right_term_numerator : %15i ( √|Δ| / right_gcd )\n", solution_d2.right_term_numerator);
|
||||
dprintf(STDERR_FILENO, "right_term_denominator : %15i ( 2a / right_gcd )\n", solution_d2.right_term_denominator);
|
||||
dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2.a);
|
||||
dprintf(STDERR_FILENO, "b : %15g ( b )\n", solution_d2.b);
|
||||
dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2.c);
|
||||
|
||||
dprintf(STDERR_FILENO, "delta : %15g ( Δ == b² - 4ac )\n", solution_d2.delta);
|
||||
dprintf(STDERR_FILENO, "delta_sign : %15s ( '-' || '+' || '0' )\n", delta_sign_to_str(solution_d2.delta_sign));
|
||||
dprintf(STDERR_FILENO, "delta_absolute : %15g ( |Δ| )\n", solution_d2.delta_absolute);
|
||||
dprintf(STDERR_FILENO, "delta_sqrt : %15g ( √|Δ| )\n", solution_d2.delta_sqrt);
|
||||
|
||||
dprintf(STDERR_FILENO, "left_term : %15g ( -b / 2a )\n", solution_d2.left_term);
|
||||
dprintf(STDERR_FILENO, "right_term : %15g ( √|Δ| / 2a )\n", solution_d2.right_term);
|
||||
|
||||
dprintf(STDERR_FILENO, "all_int : %15i ( are int : b, a, √|Δ| )\n", solution_d2.all_int);
|
||||
dprintf(STDERR_FILENO, "left_term_numerator : %15i ( -b / left_gcd )\n", solution_d2.left_term_numerator);
|
||||
dprintf(STDERR_FILENO, "left_term_denominator : %15i ( 2a / left_gcd )\n", solution_d2.left_term_denominator);
|
||||
dprintf(STDERR_FILENO, "right_term_numerator : %15i ( √|Δ| / right_gcd )\n", solution_d2.right_term_numerator);
|
||||
dprintf(STDERR_FILENO, "right_term_denominator : %15i ( 2a / right_gcd )\n", solution_d2.right_term_denominator);
|
||||
}
|
||||
}
|
||||
|
||||
ft_putchar_fd('\n', STDERR_FILENO);
|
||||
|
||||
@@ -10,3 +10,48 @@ bool is_nearly_equal_zero(double num)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_decimal_part(double num)
|
||||
{
|
||||
return (num != (int)num);
|
||||
}
|
||||
|
||||
bool any_has_decimal_part(double *num, size_t len)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
if (has_decimal_part(num[len - 1]))
|
||||
return true;
|
||||
len--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// find GCD of two integers using euclidean algorithm
|
||||
int gcd_int(int a, int b)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
while (b != 0)
|
||||
{
|
||||
tmp = b;
|
||||
b = a % b;
|
||||
a = tmp;
|
||||
}
|
||||
return abs(a);
|
||||
}
|
||||
|
||||
// returns the gcd, and modify arguments with new reduced values
|
||||
int reduce_fraction(double *numerator, double *denominator)
|
||||
{
|
||||
int gcd;
|
||||
|
||||
if (any_has_decimal_part((double[]){*numerator, *denominator}, 2))
|
||||
return 0;
|
||||
|
||||
gcd = gcd_int((int)*numerator, (int)*denominator);
|
||||
*numerator /= gcd;
|
||||
*denominator /= gcd;
|
||||
|
||||
return gcd;
|
||||
}
|
||||
@@ -78,4 +78,16 @@ const char *delta_sign_to_str(e_delta_sign enum_value)
|
||||
return "DELTA_ZERO";
|
||||
else
|
||||
return "invalid enum value";
|
||||
}
|
||||
|
||||
const char *radicand_sign_to_str(e_radicand_sign enum_value)
|
||||
{
|
||||
if (enum_value == RADICAND_PLUS)
|
||||
return "RADICAND_PLUS";
|
||||
else if (enum_value == RADICAND_MINUS)
|
||||
return "RADICAND_MINUS";
|
||||
else if (enum_value == RADICAND_ZERO)
|
||||
return "RADICAND_ZERO";
|
||||
else
|
||||
return "invalid enum value";
|
||||
}
|
||||
47
tester.sh
47
tester.sh
@@ -324,3 +324,50 @@ Polynomial degree: 2
|
||||
Discriminant is strictly negative, the two complex solutions are:
|
||||
4.89898i/6
|
||||
-4.89898i/6"
|
||||
|
||||
run_test \
|
||||
"26. degree 2 pure" \
|
||||
"3 * x^2 + 5 * x^1 - 2 * x^0 = 5 * x" "\
|
||||
Reduced form: -2 * x^0 + 0 * x^1 + 3 * x^2 = 0
|
||||
Polynomial degree: 2
|
||||
Discriminant is strictly positive, the two solutions are:
|
||||
√(2/3)
|
||||
-√(2/3)"
|
||||
|
||||
run_test \
|
||||
"27. degree 2 pure" \
|
||||
"9 * x^2 + 5 * x^1 - 2 * x^0 = 5 * x" "\
|
||||
Reduced form: -2 * x^0 + 0 * x^1 + 9 * x^2 = 0
|
||||
Polynomial degree: 2
|
||||
Discriminant is strictly positive, the two solutions are:
|
||||
(√2)/3
|
||||
-(√2)/3"
|
||||
|
||||
run_test \
|
||||
"28. degree 2 pure" \
|
||||
"3 * x^2 + 5 * x^1 - 4 * x^0 = 5 * x" "\
|
||||
Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0
|
||||
Polynomial degree: 2
|
||||
Discriminant is strictly positive, the two solutions are:
|
||||
2/√(3)
|
||||
-2/√(3)"
|
||||
|
||||
|
||||
run_test \
|
||||
"29. degree 2 pure" \
|
||||
"16 * x^2 + 5 * x^1 - 4 * x^0 = 5 * x" "\
|
||||
Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0
|
||||
Polynomial degree: 2
|
||||
Discriminant is strictly positive, the two solutions are:
|
||||
1/2
|
||||
-1/2"
|
||||
|
||||
|
||||
run_test \
|
||||
"30. degree 2 pure" \
|
||||
"4 * x^2 + 5 * x^1 - 16 * x^0 = 5 * x" "\
|
||||
Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0
|
||||
Polynomial degree: 2
|
||||
Discriminant is strictly positive, the two solutions are:
|
||||
2
|
||||
-2"
|
||||
|
||||
Reference in New Issue
Block a user