From 4681f99048349ed47a24b3a392aa14c6453b0b2f Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 16 Dec 2025 14:56:30 +0100 Subject: [PATCH] Moved algorithms and fixed norm --- Makefile | 2 +- algorithm_complex.c | 18 ++ algorithm_quicksort.c | 68 ------ ...thm_selection_sort.c => algorithm_simple.c | 4 +- algorithm_splitsort.c | 222 ------------------ main_pushswap.c | 10 +- pushswap.h | 9 +- splitsort.c | 74 ++++++ splitsort.h | 16 +- splitsort_final.c | 37 +++ splitsort_iter.c | 59 ++++- splitsort_part.c | 82 +++++++ 12 files changed, 296 insertions(+), 305 deletions(-) create mode 100644 algorithm_complex.c delete mode 100644 algorithm_quicksort.c rename algorithm_selection_sort.c => algorithm_simple.c (89%) delete mode 100644 algorithm_splitsort.c create mode 100644 splitsort.c create mode 100644 splitsort_final.c create mode 100644 splitsort_part.c diff --git a/Makefile b/Makefile index 2d5cfc5..f90e9ea 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NAME=push_swap -SRCS=algorithm_leafsort.c algorithm_quicksort.c algorithm_selection_sort.c algorithm_splitsort.c cheatalloc.c clist.c closure.c leaf_sort.c leaf_sort_index.c leaf_sort_parse.c list.c main_pushswap.c op_output.c op_p.c op_parse.c op_r.c op_rr.c op_s.c ops.c ops_groups.c ops_optimizer.c output.c psval.c slice.c stack.c stacks.c stacks_get.c +SRCS=algorithm_complex.c algorithm_leafsort.c algorithm_simple.c cheatalloc.c clist.c closure.c leaf_sort.c leaf_sort_index.c leaf_sort_parse.c list.c main_pushswap.c op_output.c op_p.c op_parse.c op_r.c op_rr.c op_s.c ops.c ops_groups.c ops_optimizer.c output.c psval.c slice.c splitsort.c splitsort_final.c splitsort_iter.c splitsort_part.c stack.c stacks.c stacks_get.c RESSRCS=_res_leaf_sort_lookup.h diff --git a/algorithm_complex.c b/algorithm_complex.c new file mode 100644 index 0000000..f19f357 --- /dev/null +++ b/algorithm_complex.c @@ -0,0 +1,18 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* algorithm_complex.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/16 14:52:06 by agilliar #+# #+# */ +/* Updated: 2025/12/16 14:55:13 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "pushswap.h" + +void algorithm_complex(const t_stacks *stacks, t_closure cb) +{ + splitsort(stacks, cb, 5); +} diff --git a/algorithm_quicksort.c b/algorithm_quicksort.c deleted file mode 100644 index 377c0a3..0000000 --- a/algorithm_quicksort.c +++ /dev/null @@ -1,68 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* algorithm_quicksort.c :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agilliar +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/12/05 19:07:14 by agilliar #+# #+# */ -/* Updated: 2025/12/11 01:12:14 by agilliar ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "pushswap.h" - -static void quicksort_sift(const t_stacks *stacks, t_closure cb, t_list range) -{ - size_t a; - size_t b; - t_psval pivot; - - a = 0; - b = 0; - pivot = range.ptr[range.len / 2]; - while (a + b < range.len) - { - if (clist_get_at(stacks->a.list, 0) < pivot) - { - a++; - closure_call(cb, OP_RA); - } - else - { - b++; - closure_call(cb, OP_PB); - } - } -} - -static void quicksort_rec(const t_stacks *stacks, t_closure cb, t_list range) -{ - size_t i; - size_t pivot; - - if (range.len == 2 - && clist_get_at(stacks->a.list, 0) > clist_get_at(stacks->a.list, -1)) - closure_call(cb, OP_SA); - if (range.len <= 2) - return ; - pivot = range.len / 2; - quicksort_sift(stacks, cb, range); - i = 0; - while (i++ < range.len - pivot) - closure_call(cb, OP_PA); - quicksort_rec(stacks, cb, list_sub(range, pivot, range.len)); - i = 0; - while (i++ < pivot) - closure_call(cb, OP_RRA); - quicksort_rec(stacks, cb, list_sub(range, 0, pivot)); -} - -void algorithm_quicksort(const t_stacks *stacks, t_closure cb) -{ - t_list sorted; - - sorted = list_new(stacks->a); - list_sort(sorted); - quicksort_rec(stacks, cb, sorted); -} diff --git a/algorithm_selection_sort.c b/algorithm_simple.c similarity index 89% rename from algorithm_selection_sort.c rename to algorithm_simple.c index d164d54..887046f 100644 --- a/algorithm_selection_sort.c +++ b/algorithm_simple.c @@ -6,13 +6,13 @@ /* By: clefrere +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/09 15:43:09 by clefrere #+# #+# */ -/* Updated: 2025/12/11 01:12:26 by agilliar ### ########.fr */ +/* Updated: 2025/12/16 14:54:44 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "pushswap.h" -void algorithm_selection_sort(const t_stacks *stacks, t_closure cb) +void algorithm_simple(const t_stacks *stacks, t_closure cb) { t_list sorted; diff --git a/algorithm_splitsort.c b/algorithm_splitsort.c deleted file mode 100644 index 236e3cb..0000000 --- a/algorithm_splitsort.c +++ /dev/null @@ -1,222 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* algorithm_splitsort.c :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agilliar +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/12/11 12:45:16 by agilliar #+# #+# */ -/* Updated: 2025/12/16 12:55:57 by agilliar ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "splitsort.h" - -static t_iter iter_new(t_list range, size_t blocks, bool rev) -{ - t_iter res; - - if (blocks > range.len) - blocks = range.len; - res.range = range; - res.blocks = blocks; - res.rev = rev; - return (res); -} - -static t_list iter_next(t_iter *self) -{ - t_list res; - size_t count; - - if (!self->blocks) - return (list_split(&self->range, 0)); - count = self->range.len / self->blocks; - if (self->rev) - { - res = self->range; - self->range = list_split(&res, res.len - count); - } - else - { - if (self->range.len % self->blocks) - count++; - res = list_split(&self->range, count); - } - self->blocks--; - return (res); -} - -static bool iter_nexti(t_iter *self, t_list *dst) -{ - *dst = iter_next(self); - return (dst->len != 0); -} - -// Takes the self iterator, skip n elements, and return an iterator which only -// yields said elements -static t_iter iter_splitoff(t_iter *self, size_t n) -{ - t_iter res; - - res = *self; - while (n--) - iter_next(self); - res.range.len -= self->range.len; - res.blocks -= self->blocks; - if (res.rev) - res.range.ptr += self->range.len; - return (res); -} - -static void splitsort_part_once(t_part self, t_splitsort sort, size_t count) -{ - const t_stack *src; - t_psval val; - - src = stacks_geta(sort.stacks, self.mirror); - while (count--) - { - if (self.rev) - closure_callm(sort.cb, OP_RRA, self.mirror); - val = clist_get_at(src->list, 0); - if (list_get_sorted(self.top, val) != -1) - { - closure_callm(sort.cb, OP_PB, self.mirror); - closure_callm(sort.cb, OP_RB, self.mirror); - } - else if (list_get_sorted(self.bottom, val) != -1) - closure_callm(sort.cb, OP_PB, self.mirror); - else if (!self.rev) - closure_callm(sort.cb, OP_RA, self.mirror); - } -} - -static void splitsort_part(t_splitsort sort, t_list range, t_mode mode) -{ - t_iter topi; - t_iter bottomi; - t_part part; - size_t top_count; - - bottomi = iter_new(range, sort.blocks, mode == MODE_A); - topi = iter_splitoff(&bottomi, sort.blocks / 2); - bottomi.rev = mode == MODE_B; - topi.rev = !bottomi.rev; - top_count = 0; - part.mirror = mode == MODE_B; - part.rev = false; - while (iter_nexti(&topi, &part.top) | iter_nexti(&bottomi, &part.bottom)) - { - splitsort_part_once(part, sort, range.len - top_count); - part.rev = !part.rev; - top_count += part.top.len; - range.len -= part.bottom.len; - } - while (mode != MODE_A_WRAP && top_count--) - closure_callm(sort.cb, OP_RRB, mode == MODE_B); -} - -// Paritions b while also sorting the leaves of a -void splitsort_final_mixed(t_splitsort sort, t_list range) -{ - t_iter iter; - t_part part; - - iter = iter_new(range, sort.blocks, true); - part.mirror = true; - part.rev = false; - while (iter_nexti(&iter, &part.bottom) | iter_nexti(&iter, &part.top)) - { - splitsort_part_once(part, sort, range.len); - part.rev = !part.rev; - range.len -= part.bottom.len + part.top.len; - leaf_top_a_a(sort.stacks, sort.cb, part.bottom.len); - leaf_bot_a_a(sort.stacks, sort.cb, part.top.len); - } -} - -void splitsort_final_simple(t_splitsort sort, t_list range) -{ - leaf_top_b_a(sort.stacks, sort.cb, range.len); -} - -// Parts range elements from stack a (which is in ascending order) onto stack b -// in descending order -static void splitsort_part_a(t_splitsort sort, t_list range) -{ - splitsort_part(sort, range, MODE_A); -} - -// Parts range elements from stack a (which is in ascending order) onto stack b -// in descending order, assuming that stack b is initially empty -static void splitsort_part_a_wrap(t_splitsort sort, t_list range) -{ - splitsort_part(sort, range, MODE_A_WRAP); -} - -// Parts range elements from stack b (which is in descending order) onto stack -// a in ascending order -static void splitsort_part_b(t_splitsort sort, t_list range) -{ - splitsort_part(sort, range, MODE_B); -} - -static t_layer_func *layer_from_info(size_t depth, size_t blocks, size_t len) -{ - size_t i; - size_t state; - - i = 0; - state = 4; - while (i++ < depth) - state *= blocks; - if (depth == 0) - return (&splitsort_part_a_wrap); - if (depth % 2 == 0) - return (&splitsort_part_a); - if (len <= state) - return (&splitsort_final_simple); - if (len <= state * blocks) - return (&splitsort_final_mixed); - return (&splitsort_part_b); -} - -static void splitsort_visit_layer(t_visit self, t_splitsort sort) -{ - t_iter iter; - - if (self.depth == 0) - return (self.cb(sort, self.range)); - self.depth--; - iter = iter_new(self.range, sort.blocks, self.rev); - while (iter_nexti(&iter, &self.range)) - splitsort_visit_layer(self, sort); -} - -void splitsort_part_layer(t_splitsort sort, t_list range, size_t depth) -{ - t_visit visit; - - visit.range = range; - visit.depth = depth; - visit.cb = layer_from_info(depth, sort.blocks, range.len); - visit.rev = depth % 2 == 1; - splitsort_visit_layer(visit, sort); -} - -void test_splitsort(const t_stacks *stacks, t_closure cb) -{ - t_splitsort sort; - t_list range; - size_t i; - - sort.stacks = stacks; - sort.cb = cb; - sort.blocks = 5; - range = list_new(stacks->a); - list_sort(range); - i = 0; - while (!stacks_is_solved(stacks)) - splitsort_part_layer(sort, range, i++); -} diff --git a/main_pushswap.c b/main_pushswap.c index f8028e4..b9c59bd 100644 --- a/main_pushswap.c +++ b/main_pushswap.c @@ -6,7 +6,7 @@ /* By: clefrere +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/02 22:15:12 by agilliar #+# #+# */ -/* Updated: 2025/12/16 13:03:28 by agilliar ### ########.fr */ +/* Updated: 2025/12/16 14:55:34 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -22,22 +22,18 @@ static bool ft_streq(char *str1, char *str2) return (str1[i] == '\0' && str2[i] == '\0'); } -void test_splitsort(const t_stacks *stacks, t_closure cb); - static void arg_step(char *str, t_args *arg) { if (ft_streq(str, "--simple")) - arg->algo = &algorithm_selection_sort; + arg->algo = &algorithm_simple; else if (ft_streq(str, "--medium")) arg->algo = NULL; else if (ft_streq(str, "--complex")) - arg->algo = &algorithm_quicksort; + arg->algo = &algorithm_complex; else if (ft_streq(str, "--adaptive")) arg->algo = NULL; else if (ft_streq(str, "--leaf")) arg->algo = &algorithm_leafsort; - else if (ft_streq(str, "--split")) - arg->algo = &test_splitsort; else if (ft_streq(str, "--bench")) arg->bench = true; else diff --git a/pushswap.h b/pushswap.h index 8b42dbd..16b06f1 100644 --- a/pushswap.h +++ b/pushswap.h @@ -6,7 +6,7 @@ /* By: clefrere +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/02 11:02:44 by agilliar #+# #+# */ -/* Updated: 2025/12/15 20:28:36 by agilliar ### ########.fr */ +/* Updated: 2025/12/16 14:54:32 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -178,7 +178,10 @@ void leaf_bot_a_a(const t_stacks *stacks, t_closure cb, void leaf_top_b_a(const t_stacks *stacks, t_closure cb, size_t size); -void algorithm_selection_sort(const t_stacks *stacks, t_closure cb); -void algorithm_quicksort(const t_stacks *stacks, t_closure cb); +void splitsort(const t_stacks *stacks, t_closure cb, size_t blocks); + +void algorithm_simple(const t_stacks *stacks, t_closure cb); +void algorithm_complex(const t_stacks *stacks, t_closure cb); void algorithm_leafsort(const t_stacks *stacks, t_closure cb); + #endif diff --git a/splitsort.c b/splitsort.c new file mode 100644 index 0000000..a614a54 --- /dev/null +++ b/splitsort.c @@ -0,0 +1,74 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* splitsort.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/11 12:45:16 by agilliar #+# #+# */ +/* Updated: 2025/12/16 14:46:51 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "splitsort.h" + +static t_layer_func *layer_from_info(size_t depth, size_t blocks, size_t len) +{ + size_t i; + size_t state; + + i = 0; + state = 4; + while (i++ < depth) + state *= blocks; + if (depth == 0) + return (&splitsort_part_a_wrap); + if (depth % 2 == 0) + return (&splitsort_part_a); + if (len <= state) + return (&splitsort_final_simple); + if (len <= state * blocks) + return (&splitsort_final_mixed); + return (&splitsort_part_b); +} + +static void splitsort_visit_layer(t_visit self, t_splitsort sort) +{ + t_iter iter; + + if (self.depth == 0) + return (self.cb(sort, self.range)); + self.depth--; + iter = iter_new(self.range, sort.blocks, self.rev); + while (iter_nexti(&iter, &self.range)) + splitsort_visit_layer(self, sort); +} + +static void splitsort_part_layer(t_splitsort sort, t_list range, size_t depth) +{ + t_visit visit; + + visit.range = range; + visit.depth = depth; + visit.cb = layer_from_info(depth, sort.blocks, range.len); + visit.rev = depth % 2 == 1; + splitsort_visit_layer(visit, sort); +} + +void splitsort(const t_stacks *stacks, t_closure cb, size_t blocks) +{ + t_splitsort sort; + t_list range; + size_t i; + + sort.stacks = stacks; + sort.cb = cb; + sort.blocks = blocks; + if (blocks < 2) + cheatexit(1); + range = list_new(stacks->a); + list_sort(range); + i = 0; + while (!stacks_is_solved(stacks)) + splitsort_part_layer(sort, range, i++); +} diff --git a/splitsort.h b/splitsort.h index 29d67b2..44f0b46 100644 --- a/splitsort.h +++ b/splitsort.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/16 12:54:11 by agilliar #+# #+# */ -/* Updated: 2025/12/16 12:56:36 by agilliar ### ########.fr */ +/* Updated: 2025/12/16 14:49:37 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -54,4 +54,18 @@ typedef struct s_visit bool rev; } t_visit; +t_iter iter_new(t_list range, size_t blocks, bool rev); +t_list iter_next(t_iter *self); +bool iter_nexti(t_iter *self, t_list *dst); +t_iter iter_splitoff(t_iter *self, size_t n); + +void splitsort_part_once(t_part self, t_splitsort sort, size_t count); +void splitsort_part(t_splitsort sort, t_list range, t_mode mode); +void splitsort_part_a(t_splitsort sort, t_list range); +void splitsort_part_a_wrap(t_splitsort sort, t_list range); +void splitsort_part_b(t_splitsort sort, t_list range); + +void splitsort_final_mixed(t_splitsort sort, t_list range); +void splitsort_final_simple(t_splitsort sort, t_list range); + #endif diff --git a/splitsort_final.c b/splitsort_final.c new file mode 100644 index 0000000..5b3d047 --- /dev/null +++ b/splitsort_final.c @@ -0,0 +1,37 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* splitsort_final.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/16 14:43:59 by agilliar #+# #+# */ +/* Updated: 2025/12/16 14:51:41 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "splitsort.h" + +// Paritions b while also sorting the leaves of a +void splitsort_final_mixed(t_splitsort sort, t_list range) +{ + t_iter iter; + t_part part; + + iter = iter_new(range, sort.blocks, true); + part.mirror = true; + part.rev = false; + while (iter_nexti(&iter, &part.bottom) | iter_nexti(&iter, &part.top)) + { + splitsort_part_once(part, sort, range.len); + part.rev = !part.rev; + range.len -= part.bottom.len + part.top.len; + leaf_top_a_a(sort.stacks, sort.cb, part.bottom.len); + leaf_bot_a_a(sort.stacks, sort.cb, part.top.len); + } +} + +void splitsort_final_simple(t_splitsort sort, t_list range) +{ + leaf_top_b_a(sort.stacks, sort.cb, range.len); +} diff --git a/splitsort_iter.c b/splitsort_iter.c index 2b6e345..603fdf6 100644 --- a/splitsort_iter.c +++ b/splitsort_iter.c @@ -6,8 +6,65 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/16 12:57:22 by agilliar #+# #+# */ -/* Updated: 2025/12/16 12:57:29 by agilliar ### ########.fr */ +/* Updated: 2025/12/16 14:39:19 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "splitsort.h" + +t_iter iter_new(t_list range, size_t blocks, bool rev) +{ + t_iter res; + + if (blocks > range.len) + blocks = range.len; + res.range = range; + res.blocks = blocks; + res.rev = rev; + return (res); +} + +t_list iter_next(t_iter *self) +{ + t_list res; + size_t count; + + if (!self->blocks) + return (list_split(&self->range, 0)); + count = self->range.len / self->blocks; + if (self->rev) + { + res = self->range; + self->range = list_split(&res, res.len - count); + } + else + { + if (self->range.len % self->blocks) + count++; + res = list_split(&self->range, count); + } + self->blocks--; + return (res); +} + +bool iter_nexti(t_iter *self, t_list *dst) +{ + *dst = iter_next(self); + return (dst->len != 0); +} + +// Takes the self iterator, skip n elements, and return an iterator which only +// yields said elements +t_iter iter_splitoff(t_iter *self, size_t n) +{ + t_iter res; + + res = *self; + while (n--) + iter_next(self); + res.range.len -= self->range.len; + res.blocks -= self->blocks; + if (res.rev) + res.range.ptr += self->range.len; + return (res); +} diff --git a/splitsort_part.c b/splitsort_part.c new file mode 100644 index 0000000..98e7b55 --- /dev/null +++ b/splitsort_part.c @@ -0,0 +1,82 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* splitsort_part.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/12/16 14:42:51 by agilliar #+# #+# */ +/* Updated: 2025/12/16 14:44:49 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "splitsort.h" + +void splitsort_part_once(t_part self, t_splitsort sort, size_t count) +{ + const t_stack *src; + t_psval val; + + src = stacks_geta(sort.stacks, self.mirror); + while (count--) + { + if (self.rev) + closure_callm(sort.cb, OP_RRA, self.mirror); + val = clist_get_at(src->list, 0); + if (list_get_sorted(self.top, val) != -1) + { + closure_callm(sort.cb, OP_PB, self.mirror); + closure_callm(sort.cb, OP_RB, self.mirror); + } + else if (list_get_sorted(self.bottom, val) != -1) + closure_callm(sort.cb, OP_PB, self.mirror); + else if (!self.rev) + closure_callm(sort.cb, OP_RA, self.mirror); + } +} + +void splitsort_part(t_splitsort sort, t_list range, t_mode mode) +{ + t_iter topi; + t_iter bottomi; + t_part part; + size_t top_count; + + bottomi = iter_new(range, sort.blocks, mode == MODE_A); + topi = iter_splitoff(&bottomi, sort.blocks / 2); + bottomi.rev = mode == MODE_B; + topi.rev = !bottomi.rev; + top_count = 0; + part.mirror = mode == MODE_B; + part.rev = false; + while (iter_nexti(&topi, &part.top) | iter_nexti(&bottomi, &part.bottom)) + { + splitsort_part_once(part, sort, range.len - top_count); + part.rev = !part.rev; + top_count += part.top.len; + range.len -= part.bottom.len; + } + while (mode != MODE_A_WRAP && top_count--) + closure_callm(sort.cb, OP_RRB, mode == MODE_B); +} + +// Parts range elements from stack a (which is in ascending order) onto stack b +// in descending order +void splitsort_part_a(t_splitsort sort, t_list range) +{ + splitsort_part(sort, range, MODE_A); +} + +// Parts range elements from stack a (which is in ascending order) onto stack b +// in descending order, assuming that stack b is initially empty +void splitsort_part_a_wrap(t_splitsort sort, t_list range) +{ + splitsort_part(sort, range, MODE_A_WRAP); +} + +// Parts range elements from stack b (which is in descending order) onto stack +// a in ascending order +void splitsort_part_b(t_splitsort sort, t_list range) +{ + splitsort_part(sort, range, MODE_B); +} -- 2.51.0