From b233ac8c6cb25e8477a3bc678c65d9d10ebbfc62 Mon Sep 17 00:00:00 2001
From: gmillot <gael.millot@pasteur.fr>
Date: Sat, 1 Apr 2023 00:30:22 +0200
Subject: [PATCH] test

---
 cute_little_R_functions.R    | 6903 +++++++++++++++++-----------------
 examples_fun_gg_donut.R      |   89 +-
 fun_gg_boxplot.docx          |  Bin 116136 -> 116122 bytes
 fun_gg_donut.R               |   81 +-
 ~$te_little_R_functions.docx |  Bin 0 -> 162 bytes
 5 files changed, 3555 insertions(+), 3518 deletions(-)
 create mode 100644 ~$te_little_R_functions.docx

diff --git a/cute_little_R_functions.R b/cute_little_R_functions.R
index e8a2c90..2683ccd 100755
--- a/cute_little_R_functions.R
+++ b/cute_little_R_functions.R
@@ -9138,31 +9138,66 @@ fun_get_message <- function(
 
 
 
+# Error: class order not good when a class is removed due to NA
+# Error: line 136 in check 20201126 with add argument
+# Solve this: sometimes error messages can be more than the max display (8170). Thus, check every paste0("ERROR IN ", function.name, and trunck the message if to big. In addition, add at the begining of the warning message that it is too long and see the $warn output for complete message. Add also this into fun_scatter
+# add dot.shape ? See with available aesthetic layers
+# rasterise: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html
+# add horizontal argument and deal any conflict with vertical argument. Start with horizontal = NULL as default. If ! is.null() -> convert vertical if required
+# time for excecution : microbenchmark package. See also in RStudio time per line of code. See also https://stackoverflow.com/questions/7561362/what-can-cause-a-program-to-run-much-faster-the-second-time
 
 
-fun_gg_donut <- function(
+fun_gg_boxplot <- function(
     data1, 
-    freq, 
+    y, 
     categ, 
-    fill.palette = NULL,
-    fill.color = NULL, 
-    hole.size = 0.5, 
-    hole.text = TRUE, 
-    hole.text.size = 14, 
-    border.color = "gray50", 
-    border.size = 0.2, 
+    categ.class.order = NULL, 
+    categ.color = NULL, 
+    box.legend.name = NULL, 
+    box.fill = FALSE, 
+    box.width = 0.5, 
+    box.space = 0.1, 
+    box.line.size = 0.75, 
+    box.notch = FALSE, 
+    box.alpha = 1, 
+    box.mean = TRUE, 
+    box.whisker.kind = "std", 
+    box.whisker.width = 0, 
+    dot.color = grey(0.25), 
+    dot.categ = NULL, 
+    dot.categ.class.order = NULL, 
+    dot.legend.name = NULL, 
+    dot.tidy = FALSE, 
+    dot.tidy.bin.nb = 50, 
+    dot.jitter = 0.5, 
+    dot.seed = 2, 
+    dot.size = 3, 
+    dot.alpha = 0.5, 
+    dot.border.size = 0.5, 
+    dot.border.color = NULL, 
+    x.lab = NULL, 
+    x.angle = 0, 
+    y.lab = NULL, 
+    y.lim = NULL, 
+    y.log = "no", 
+    y.tick.nb = NULL, 
+    y.second.tick.nb = 1, 
+    y.include.zero = FALSE, 
+    y.top.extra.margin = 0.05, 
+    y.bottom.extra.margin = 0.05, 
+    stat.pos = "top", 
+    stat.mean = FALSE, 
+    stat.size = 4, 
+    stat.dist = 5, 
+    stat.angle = 0, 
+    vertical = TRUE, 
+    text.size = 12, 
     title = "", 
-    title.text.size = 12, 
-    annotation = NULL,
-    annotation.distance = 0,
-    annotation.size = 3,
-    annotation.force = 1,
-    annotation.force.pull = 100,
+    title.text.size = 8, 
     legend.show = TRUE, 
-    legend.width = 0.25, 
-    legend.name = NULL, 
-    legend.limit = NULL, 
-    legend.add.prop = FALSE,
+    legend.width = 0.5, 
+    article = TRUE, 
+    grid = FALSE, 
     add = NULL, 
     return = FALSE, 
     return.ggplot = FALSE,
@@ -9172,86 +9207,157 @@ fun_gg_donut <- function(
     lib.path = NULL
 ){
     # AIM
-    # Plot a ggplot2 donut using contingency data, systematically in the decreasing order of frequencies, starting at the top and turning clockwise
+    # Plot ggplot2 boxplots + dots + means
     # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html
     # WARNINGS
-    # Rows containing NA in data1[, c(freq, categ)] will be removed before processing, with a warning (see below)
-    # Size arguments (hole.text.size, border.size, title.text.size and annotation.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
+    # Rows containing NA in data1[, c(y, categ)] will be removed before processing, with a warning (see below)
+    # Hinges are not computed like in the classical boxplot() function of R. See https://ggplot2.tidyverse.org/reference/geom_boxplot.html
+    # To have a single box, please create a factor column with a single class and specify the name of this column in the categ argument. For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1, knowing that categ2 must also be specified in this situation). See categ argument below
+    # The dot.alpha argument can alter the display of the color boxes when using pdf output
+    # Size arguments (box.line.size, dot.size, dot.border.size, stat.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
+    # Display seems to be done twice on Windows devices (like a blink). However, no double plots on pdf devices. Thus, the blink remains mysterious
+    # To remove boxes and have only dots, use box.alpha = 0
     # ARGUMENTS
-    # data1: a dataframe compatible with ggplot2
-    # freq: single character string of the data1 column name of the frequencies
-    # categ: single character string of the data1 column name of categories (qualitative variable)
-    # fill.palette: single character string of a palette name (see ?ggplot2::scale_fill_brewer() for the list).Ignored if fill.color is not NULL
-    # fill.color: either (1) NULL, or (2) a vector of character strings or integers of same length as the number of classes in categ. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette). The order of the elements will be used according to the frequency values, from highest to lowest. An easy way to use this argument is to sort data1 according to the frequencies values, add a color column with the corresponding desired colors and use the content of this column as values of fill.color. If color is NULL and fill.palette is NULL, default colors of ggplot2 are used. If color is not NULL, it overrides fill.palette
-    # hole.size: single positive proportion of donut central hole, 0 meaning no hole and 1 no donut
-    # hole.text: logical (either TRUE or FALSE). Display the sum of frequencies (column of data1 indicated in the freq argument) ?
-    # hole.text.size: single positive numeric value of the title font size in mm. Ignored if hole.text is FALSE
-    # border.color: a single character string or integer. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette)
-    # border.size: single numeric value of border tickness in mm. Write zero for no dot border
-    # title: single character string of the graph title
-    # title.text.size: single numeric value of the title font size in mm
-    # annotation: single character string of the data1 column name of annotations. Values inside this column will be displayed over the corresponding slices of the donut. Write NULL if not required
-    # annotation.distance: single positive numeric value of the distance from the center of the slice. 0 means center of the slice, 0.5 means at the edge. Above 0.5, the donut will be reduced to make place for the annotation. Ignored if annotation is NULL
-    # annotation.size: single positive numeric value of the annotation font size in mm. Ignored if annotation is NULL
-    # annotation.force: single positive numeric value of the force of repulsion between overlapping text labels. See ?ggrepel::geom_text_repel() in R. Ignored if annotation is NULL
-    # annotation.force.pull: single positive numeric value of the force of attraction between a text label and its corresponding data point. See ?ggrepel::geom_text_repel() in R. Ignored if annotation is NULL
-    # legend.show: logical (either TRUE or FALSE). Show legend? 
+    # data1: data frame containing one column of quantitative values (see the y argument below) and one or two columns of categories (see the categ argument below). Duplicated column names are not allowed
+    # y: character string of the data1 column name for y-axis (column containing numeric values). Numeric values will be split according to the classes of the column names indicated in the categ argument to generate the boxes and will also be used to plot the dots
+    # categ: vector of character strings of the data1 column name for categories (column of characters or factors). Must be either one or two column names. If a single column name (further referred to as categ1), then one box per class of categ1. If two column names (further referred to as categ1 and categ2), then one box per class of categ2, which form a group of boxes in each class of categ1. WARNING: no empty classes allowed. To have a single box, create a factor column with a single class and specify the name of this column in the categ argument (here, no categ2 in categ argument). For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1), in addition to the already used category (as categ2 in this situation)
+    # categ.class.order: list indicating the order of the classes of categ1 and categ2 represented on the boxplot (the first compartment for categ1 and and the second for categ2). If categ.class.order == NULL, classes are represented according to the alphabetical order. Some compartments can be NULL and others not. See the categ argument for categ1 and categ2 description
+    # categ.color: vector of color character string for box frames (see the categ argument for categ1 and categ2 description)
+    # If categ.color == NULL, default colors of ggplot2, whatever categ1 and categ2
+    # If categ.color is non-null and only categ1 in categ argument, categ.color can be either:
+    # (1) a single color string. All the boxes will have this color, whatever the number of classes of categ1
+    # (2) a vector of string colors, one for each class of categ1. Each color will be associated according to categ.class.order of categ1
+    # (3) a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ1 and a single class of categ1 per color must be respected
+    # Color functions, like grey(), hsv(), etc., are also accepted
+    # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the maximal integer value among all the integers in categ.color (see fun_gg_palette())
+    # If categ.color is non-null and categ1 and categ2 are specified, all the rules described above will apply to categ2 instead of categ1 (colors will be determined for boxes inside a group of boxes)
+    # box.legend.name: character string of the legend title. If box.legend.name is NULL, then box.legend.name <- categ1 if only categ1 is present, and box.legend.name <- categ2 if categ1 and categ2 are present in the categ argument. Write "" if no legend required. See the categ argument for categ1 and categ2 description
+    # box.fill: logical. Fill the box? If TRUE, the categ.color argument will be used to generate filled boxplots (the box frames being black) as well as filled outlier dots (the dot border being controlled by the dot.border.color argument). If all the dots are plotted (argument dot.color other than NULL), they will be over the boxes. If FALSE, the categ.color argument will be used to color the box frames and the outlier dot borders. If all the dots are plotted, they will be beneath the boxes
+    # box.width: single numeric value (from 0 to 1) of width of either boxes or group of boxes
+    # When categ argument has a single categ1 element (i.e., separate boxes. See the categ argument for categ1 and categ2 description), then each class of categ1 is represented by a single box. In that case, box.width argument defines each box width, from 0 (no box width) to 1 (max box width), but also the space between boxes (the code uses 1 - box.width for the box spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each box)
+    # When categ argument has a two categ1 and categ2 elements (i.e., grouped boxes), box.width argument defines the width allocated for each set of grouped boxes, from 0 (no group width) to 1 (max group width), but also the space between grouped boxes (the code uses 1 - box.width for the spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each set of grouped box)
+    # box.space: single numeric value (from 0 to 1) indicating the box separation inside grouped boxes, when categ argument has a two categ1 and categ2 elements. 0 means no space and 1 means boxes shrunk to a vertical line. Ignored if categ argument has a single categ1 element
+    # box.line.size: single numeric value of line width of boxes and whiskers in mm
+    # box.notch: logical. Notched boxplot? It TRUE, display notched boxplot, notches corresponding approximately to the 95% confidence interval of the median (the notch interval is exactly 1.58 x Inter Quartile Range (IQR) / sqrt(n), with n the number of values that made the box). If notch intervals between two boxes do not overlap, it can be interpreted as significant median differences
+    # box.alpha: single numeric value (from 0 to 1) of box transparency (full transparent to full opaque, respectively). To remove boxplots, use box.alpha = 0
+    # box.mean: logical. Add mean value? If TRUE, a diamond-shaped dot, with the horizontal diagonal corresponding to the mean value, is displayed over each boxplot
+    # box.whisker.kind: range of the whiskers. Either "no" (no whiskers), or "std" (length of each whisker equal to 1.5 x Inter Quartile Range (IQR)), or "max" (length of the whiskers up or down to the most distant dot)
+    # box.whisker.width: single numeric value (from 0 to 1) of the whisker width, with 0 meaning no whiskers and 1 meaning a width equal to the box width
+    # dot.color: vector of color character string ruling the dot colors and the dot display. See the example section below for easier understanding of the rules described here
+    # If NULL, no dots plotted
+    # If "same", the dots will have the same colors as the respective boxplots
+    # Otherwise, as in the rule (1), (2) or (3) described in the categ.color argument, except that in the possibility (3), the rule "a single color per class of categ and a single class of categ per color", does not have to be respected (for instance, each dot can have a different color). Colors will also depend on the dot.categ argument. If dot.categ is NULL, then colors will be applied to each class of the last column name specified in categ. If dot.categ is non-NULL, colors will be applied to each class of the column name specified in dot.categ. See examples
+    # dot.categ: optional single character string of a column name (further referred to as categ3) of the data1 argument. This column of data1 will be used to generate a legend for dots, in addition to the legend for boxes. See the dot.color argument for details about the way the legend is built using the two dot.categ and dot.color arguments. If NULL, no legend created and the colors of dots will depend on dot.color and categ arguments (as explained in the dot.color argument)
+    # dot.categ.class.order: optional vector of character strings indicating the order of the classes of categ3 (see the dot.categ argument). If dot.categ is non-NULL and dot.categ.class.order is NULL, classes are displayed in the legend according to the alphabetical order. Ignored if dot.categ is NULL
+    # dot.legend.name: optional character string of the legend title for categ3 (see the dot.categ argument). If dot.legend.name == NULL, dot.categ value is used (name of the column in data1). Write "" if no legend required. Ignored if dot.categ is NULL
+    # dot.tidy: logical. Nice dot spreading? If TRUE, use the geom_dotplot() function for a nice representation. WARNING: change the true quantitative coordinates of dots (i.e., y-axis values for vertical display) because of binning. Thus, the gain in aestheticism is associated with a loss in precision that can be very important. If FALSE, dots are randomly spread on the qualitative axis, using the dot.jitter argument (see below) keeping the true quantitative coordinates
+    # dot.tidy.bin.nb: positive integer indicating the number of bins (i.e., nb of separations) of the y.lim range. Each dot will then be put in one of the bin, with a diameter of the width of the bin. In other words, increase the number of bins to have smaller dots. Not considered if dot.tidy is FALSE
+    # dot.jitter: numeric value (from 0 to 1) of random dot horizontal dispersion (for vertical display), with 0 meaning no dispersion and 1 meaning dispersion in the corresponding box width interval. Not considered if dot.tidy is TRUE
+    # dot.seed: integer value that set the random seed. Using the same number will generate the same dot jittering. Write NULL to have different jittering each time the same instruction is run. Ignored if dot.tidy is TRUE
+    # dot.size: numeric value of dot diameter in mm. Not considered if dot.tidy is TRUE
+    # dot.alpha: numeric value (from 0 to 1) of dot transparency (full transparent to full opaque, respectively)
+    # dot.border.size: numeric value of border dot width in mm. Write zero for no dot border. If dot.tidy is TRUE, value 0 remove the border and other values leave the border without size control (geom_doplot() feature)
+    # dot.border.color: single character color string defining the color of the dot border (same color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette()
+    # x.lab: a character string or expression for x-axis legend. If NULL, character string of categ1 (see the categ argument for categ1 and categ2 description)
+    # x.angle: integer value of the text angle for the x-axis numbers, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
+    # y.lab: a character string or expression for y-axis legend. If NULL, character string of the y argument
+    # y.lim: 2 numeric values indicating the range of the y-axis. Order matters (for inverted axis). If NULL, the range of the x column name of data1 will be used. 
+    # y.log: either "no", "log2" (values in the y argument column of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y argument column of the data1 data frame will be log10 transformed and y-axis will be log10 scaled). WARNING: not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881)
+    # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
+    # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
+    # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10"
+    # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis
+    # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis
+    # stat.pos: add the median number above the corresponding box. Either NULL (no number shown), "top" (at the top of the plot region) or "above" (above each box)
+    # stat.mean: logical. Display mean numbers instead of median numbers? Ignored if stat.pos is NULL
+    # stat.size: numeric value of the stat font size in mm. Ignored if stat.pos is NULL
+    # stat.dist: numeric value of the stat distance in percentage of the y-axis range (stat.dist = 5 means move the number displayed at 5% of the y-axis range). Ignored if stat.pos is NULL or "top"
+    # stat.angle: integer value of the angle of stat, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
+    # vertical: logical. Vertical boxes? WARNING: will be automatically set to TRUE if y.log argument is other than "no". Indeed, not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881)
+    # text.size: numeric value of the font size of the (1) axis numbers, (2) axis labels and (3) texts in the graphic legend (in mm)
+    # title: character string of the graph title
+    # title.text.size: numeric value of the title font size in mm
+    # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not
     # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend
-    # legend.name: character string of the legend title. If legend.name is NULL then legend.name is the value of the categ argument. Write legend.name = "" to remove the legend
-    # legend.limit: single positive proportion of the classes displayed in the legend for which the corresponding proportion is over legend.limit. Write NULL to display all the classes
-    # legend.add.prop: logical (either TRUE or FALSE). add the proportion after the class names in the legend ?
+    # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme
+    # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present)
     # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL
     # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()"
-    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_donut() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
+    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_boxplot() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
     # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions
-    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_donut() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
-    # return: logical (either TRUE or FALSE). Return the graph parameters?
-    # return.ggplot: logical (either TRUE or FALSE). Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_donut() function (e.g., a <- fun_gg_donut()) into something if the return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
-    # return.gtable: logical (either TRUE or FALSE). Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
-    # plot: logical (either TRUE or FALSE). Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
-    # warn.print: logical (either TRUE or FALSE). Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
-    # lib.path: vector of character strings indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
+    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_boxplot() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
+    # return: logical. Return the graph parameters?
+    # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_boxplot() function (e.g., a <- fun_gg_boxplot()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
+    # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
+    # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
+    # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
+    # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
     # RETURN
-    # a donut plot if plot argument is TRUE
-    # a list of the graph info if return argument is TRUE:
-    # $data: the initial data with modifications and with graphic information added
-    # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed
-    # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed
+    # A boxplot if plot argument is TRUE
+    # A list of the graph info if return argument is TRUE:
+    # $data: the initial data with graphic information added
+    # $stat: the graphic statistics (mostly equivalent to ggplot_build()$data[[2]])
+    # $removed.row.nb: which rows have been removed due to NA/Inf detection in y and categ columns (NULL if no row removed)
+    # $removed.rows: removed rows (NULL if no row removed)
+    # $plot: the graphic box and dot coordinates
+    # $dots: dot coordinates
+    # $main.box: coordinates of boxes
+    # $median: median coordinates
+    # $sup.whisker: coordinates of top whiskers (y for base and y.end for extremities)
+    # $inf.whisker: coordinates of bottom whiskers (y for base and y.end for extremities)
+    # $sup.whisker.edge: coordinates of top whisker edges (x and xend)
+    # $inf.whisker.edge: coordinates of bottom whisker edges(x and xend)
+    # $mean: diamond mean coordinates (only if box.mean argument is TRUE)
+    # $stat.pos: coordinates of stat numbers (only if stat.pos argument is not NULL)
+    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no")
+    # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no")
     # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
     # $axes: the x-axis and y-axis info
+    
     # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
-    # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Warning: the legend is not in $ggplot as it is in a separated grob (use $gtable to get it). Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
+    # $ggplot: ggplot object that can be used for reprint (use print(...$ggplot) or update (use ...$ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-NULL $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
     # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
     # REQUIRED PACKAGES
     # ggplot2
     # gridExtra
-    # grid
     # lemon (in case of use in the add argument)
-    # ggrepel
+    # scales
     # REQUIRED FUNCTIONS FROM THE cute PACKAGE
+    # fun_check()
+    # fun_comp_1d()
+    # fun_comp_2d()
+    # fun_gg_just()
     # fun_gg_palette()
-    # fun_gg_get_legend()
+    # fun_inter_ticks()
+    # fun_name_change()
     # fun_pack()
-    # fun_check()
-    # EXAMPLES
-    # obs1 <- data.frame(Km = c(20, 10, 1, 5), Car = c("TUUT", "WIIM", "BIP", "WROUM"), Color1 = 1:4, color2 = c("red", "blue", "green", "black"), Country = c("FR", "UK", "US", NA), stringsAsFactors = TRUE) ; fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", annotation = "Country")
+    # fun_round()
+    # fun_scale()
+    # EXAMPLE
+    # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(20, 100, 10), rnorm(20, 200, 50), rnorm(20, 500, 60), rnorm(20, 100, 50)), Categ1 = rep(c("CAT", "DOG"), times = 40), Categ2 = rep(c("A", "B", "C", "D"), each = 20), Color1 = rep(c("coral", "lightblue"), times = 40), Color2 = rep(c("#9F2108", "#306100", "#007479", "#8500C0"), each = 20), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Categ1")
+    # see http
     # DEBUGGING
-    # obs1 <- data.frame(Km = c(20, 10, 1, 5), Car = c("TUUT", "WIIM", "BIP", "WROUM"), Color1 = 1:4, color2 = c("red", "blue", "green", "black"), Country = c("FR", "UK", "US", NA), stringsAsFactors = TRUE) ; data1 = obs1 ; freq = "Km" ; categ = "Car" ; fill.palette = NULL ; fill.color = NULL ; hole.size = 0.5 ; hole.text = TRUE ; hole.text.size = 12 ; border.color = "gray50" ; border.size = 0.1 ; title = "" ; title.text.size = 12 ; annotation = "Country" ; annotation.distance = 0.5 ; annotation.size = 3 ; annotation.force = 1 ; annotation.force.pull = 100 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; legend.limit = NULL ; legend.add.prop = FALSE ; add = NULL ; return = TRUE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
+    # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Categ1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$Time[1:10] <- NA ; data1 = obs1 ; y = "Time" ; categ = c("Categ1") ; categ.class.order = NULL ; categ.color = NULL ; box.legend.name = NULL ; box.fill = FALSE ; box.width = 0.5 ; box.space = 0.1 ; box.line.size = 0.75 ; box.notch = FALSE ; box.alpha = 1 ; box.mean = TRUE ; box.whisker.kind = "std" ; box.whisker.width = 0 ; dot.color = grey(0.25) ; dot.categ = NULL ; dot.categ.class.order = NULL ; dot.legend.name = NULL ; dot.tidy = FALSE ; dot.tidy.bin.nb = 50 ; dot.jitter = 0.5 ; dot.seed = 2 ; dot.size = 3 ; dot.alpha = 0.5 ; dot.border.size = 0.5 ; dot.border.color = NULL ; x.lab = NULL ; x.angle = 0 ; y.lab = NULL ; y.lim = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = 1 ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; stat.pos = "top" ; stat.mean = FALSE ; stat.size = 4 ; stat.dist = 5 ; stat.angle = 0 ; vertical = TRUE ; text.size = 12 ; title = "" ; title.text.size = 8 ; legend.show = TRUE ; legend.width = 0.5 ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
     # function name
-    function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()")
+    function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()")
     arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments
-    arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
+    arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
     # end function name
     # required function checking
     req.function <- c(
-        "fun_check", 
+        "fun_comp_2d", 
+        "fun_gg_just", 
         "fun_gg_palette", 
-        "fun_gg_get_legend", 
-        "fun_pack"
+        "fun_name_change", 
+        "fun_pack", 
+        "fun_check", 
+        "fun_round", 
+        "fun_scale",
+        "fun_inter_ticks"
     )
     tempo <- NULL
     for(i1 in req.function){
-        if(length(find(i1, mode = "function"))== 0L){
+        if(length(find(i1, mode = "function")) == 0L){
             tempo <- c(tempo, i1)
         }
     }
@@ -9260,16 +9366,17 @@ fun_gg_donut <- function(
         stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
     # end required function checking
-    # reserved words to avoid bugs (used in this function)
+    # reserved words to avoid bugs (names of dataframe columns used in this function)
+    reserved.words <- c("categ.check", "categ.color", "dot.color", "dot.categ", "dot.max", "dot.min", "group", "PANEL", "group.check", "MEAN", "tempo.categ1", "tempo.categ2", "text.max.pos", "text.min.pos", "x", "x.y", "y", "y.check", "y_from.dot.max", "ymax", "tidy_group", "binwidth")
     # end reserved words to avoid bugs (used in this function)
     # arg with no default values
     mandat.args <- c(
         "data1", 
-        "freq", 
+        "y", 
         "categ"
     )
     tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")")))
-    if(any(tempo)){
+    if(any(tempo)){ # normally no NA for missing() output
         tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n"))
         stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
@@ -9280,84 +9387,234 @@ fun_gg_donut <- function(
     checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools
     ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name))
     tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = freq, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = categ, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(fill.palette)){
-        tempo <- fun_check(data = fill.palette, options = c("BrBG", "PiYG", "PRGn", "PuOr", "RdBu", "RdGy", "RdYlBu", "RdYlGn", "Spectral", "Accent", "Dark2", "Paired", "Pastel1", "Pastel2", "Set1", "Set2", "Set3", "Blues", "BuGn", "BuPu", "GnBu", "Greens", "Greys", "Oranges", "OrRd", "PuBu", "PuBuGn", "PuRd", "Purples", "RdPu", "Reds", "YlGn", "YlGnBu", "YlOrBr", "YlOrRd"), length = 1, fun.name = function.name) ; eval(ee)
-    }else{
+    tempo <- fun_check(data = y, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = categ, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
+    if( ! is.null(categ.class.order)){
+        tempo <- fun_check(data = categ.class.order, class = "list", fun.name = function.name) ; eval(ee)
+    }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = fill.palette, class = "vector")
+        tempo <- fun_check(data = categ.class.order, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(fill.color)){
-        tempo1 <- fun_check(data = fill.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = fill.color, class = "factor", na.contain = TRUE, fun.name = function.name)
-        tempo3 <- fun_check(data = fill.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name) # not need to test inf with integers
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE A VECTOR OF (1) HEXADECIMAL COLOR STRINGS STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) POSITIVE INTEGER VALUES")
+    if( ! is.null(box.legend.name)){
+        tempo <- fun_check(data = box.legend.name, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = box.legend.name, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(categ.color)){
+        tempo1 <- fun_check(data = categ.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = categ.color, class = "factor", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem
+            if(tempo.check.color == TRUE){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }else if(any(categ.color == 0L, na.rm = TRUE)){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = categ.color, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = box.fill, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.space, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.notch, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.mean, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.whisker.kind, options = c("no", "std", "max"), length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = box.whisker.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(dot.color)){
+        tempo1 <- fun_check(data = dot.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = dot.color, class = "factor", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem
+            if(tempo.check.color == TRUE){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }else if(any(dot.color == 0L, na.rm = TRUE)){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.color, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(dot.categ)){
+        tempo <- fun_check(data = dot.categ, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.categ, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(dot.categ.class.order)){
+        tempo <- fun_check(data = dot.categ.class.order, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.categ.class.order, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(dot.legend.name)){
+        tempo <- fun_check(data = dot.legend.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.legend.name, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = dot.tidy, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = dot.tidy.bin.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    if(tempo$problem == FALSE){
+        if(dot.tidy.bin.nb == 0L){ # length and NA checked above
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.tidy.bin.nb ARGUMENT MUST BE A NON-NULL AND POSITVE INTEGER VALUE") # integer possible because dealt above
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
-            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
-        }else if(tempo3$problem == FALSE & any(is.infinite(fill.color))){ # is.infinite() deals with NA as FALSE
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT CANNOT CONTAIN Inf VALUES AMONG POSITIVE INTEGER VALUES")
+        }
+    }
+    tempo <- fun_check(data = dot.jitter, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(dot.seed)){
+        tempo <- fun_check(data = dot.seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.seed, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = dot.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    if( ! is.null(dot.border.color)){
+        tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
-            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
-        }else if(tempo3$problem == FALSE & any(fill.color == 0, na.rm = TRUE)){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT CANNOT CONTAIN 0 AMONG POSITIVE INTEGER VALUES")
+        }else if(tempo1$problem == FALSE & tempo2$problem == TRUE){
+            if( ! all(dot.border.color %in% colors() | grepl(pattern = "^#", dot.border.color), na.rm = TRUE)){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.border.color, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(x.lab)){
+        tempo1 <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = x.lab,  class = "vector", mode = "character", length = 1, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION")
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
-            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
         }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x.lab, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo <- fun_check(data = hole.size, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = hole.text, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = hole.text.size, class = "vector", mode = "numeric", neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo1 <- fun_check(data = border.color, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = border.color, class = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, na.contain = FALSE, length = 1, fun.name = function.name) # not need to test inf with integers
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nborder.color ARGUMENT MUST BE A SINGLE CHARACTER STRING OR POSITIVE INTEGER")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-        checked.arg.names <- c(checked.arg.names, tempo1$object.name)
+    tempo <- fun_check(data = x.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.lab)){
+        tempo1 <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = y.lab,  class = "vector", mode = "character", length = 1, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = y.lab, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo <- fun_check(data = border.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(annotation)){
-        tempo <- fun_check(data = annotation, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-        tempo <- fun_check(data = annotation.distance, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-        tempo <- fun_check(data = annotation.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-        tempo <- fun_check(data = annotation.force, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
-        tempo <- fun_check(data = annotation.force.pull, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.lim)){
+        tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE){
+            if(any(is.infinite(y.lim))){ # normally no NA for is.infinite() output
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = annotation, class = "vector")
+        tempo <- fun_check(data = y.lim, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(legend.width)){
-        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.tick.nb)){
+        tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE){
+            if(y.tick.nb < 0){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER")
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.width, class = "vector")
+        tempo <- fun_check(data = y.tick.nb, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(legend.name)){
-        tempo <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.second.tick.nb)){
+        tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE){
+            if(y.second.tick.nb <= 0){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.second.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER")
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.name, class = "vector")
+        tempo <- fun_check(data = y.second.tick.nb, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(legend.limit)){
-        tempo <- fun_check(data = legend.limit, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(stat.pos)){
+        tempo <- fun_check(data = stat.pos, options = c("top", "above"), length = 1, fun.name = function.name) ; eval(ee)
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.limit, class = "vector")
+        tempo <- fun_check(data = stat.pos, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo <- fun_check(data = legend.add.prop, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = stat.mean, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = stat.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = stat.dist, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = stat.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = vertical, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(legend.width)){
+        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = legend.width, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
     if( ! is.null(add)){
         tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
     }else{
@@ -9371,65 +9628,95 @@ fun_gg_donut <- function(
     tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
     tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
     if( ! is.null(lib.path)){
-        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) # several possible paths
+        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE){
+            if( ! all(dir.exists(lib.path), na.rm = TRUE)){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
+        }
     }else{
         # no fun_check test here, it is just for checked.arg.names
         tempo <- fun_check(data = lib.path, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if(any(arg.check) == TRUE){
-        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) #
+    if(any(arg.check) == TRUE){ # normally no NA
+        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == #
     }
     # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check()
     # end argument primary checking
     # second round of checking and data preparation
     # management of NA arguments
     tempo.arg <- names(arg.user.setting) # values provided by the user
-    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA
-    if(any(tempo.log) == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE JUST NA")
+    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA
+    if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here
+        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA")
         stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
     # end management of NA arguments
     # management of NULL arguments
     tempo.arg <-c(
         "data1", 
-        "freq", 
+        "y", 
         "categ", 
-        # "fill.palette", # inactivated because can be null
-        # "fill.color", # inactivated because can be null
-        "hole.size", 
-        "hole.text", 
-        "hole.text.size", 
-        "border.color", 
-        "border.size", 
+        "box.fill", 
+        "box.width", 
+        "box.space", 
+        "box.line.size", 
+        "box.notch", 
+        "box.alpha", 
+        "box.mean", 
+        "box.whisker.kind", 
+        "box.whisker.width", 
+        # "dot.color", # inactivated because can be null
+        "dot.tidy", 
+        "dot.tidy.bin.nb", 
+        "dot.jitter", 
+        # "dot.seed", # inactivated because can be null
+        "dot.size", 
+        "dot.alpha", 
+        "dot.border.size", 
+        "x.angle", 
+        "y.log", 
+        # "y.second.tick.nb", # inactivated because can be null
+        "y.include.zero", 
+        "y.top.extra.margin", 
+        "y.bottom.extra.margin", 
+        # "stat.pos", # inactivated because can be null
+        "stat.mean", 
+        "stat.size", 
+        "stat.dist", 
+        "stat.angle", 
+        "vertical", 
+        "text.size", 
         "title", 
         "title.text.size", 
-        # "annotation", # inactivated because can be null
-        "annotation.distance", 
-        "annotation.size", 
-        "annotation.force", 
-        "annotation.force.pull", 
         "legend.show", 
         # "legend.width", # inactivated because can be null
-        # "legend.name", # inactivated because can be null
-        # "legend.limit", # inactivated because can be null
-        "legend.add.prop", 
-        # "add", # inactivated because can be null
+        "article", 
+        "grid", 
         "return", 
         "return.ggplot", 
         "return.gtable", 
         "plot", 
         "warn.print"
-        # "lib.path" # inactivated because can be null
     )
     tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null)
-    if(any(tempo.log) == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL")
+    if(any(tempo.log) == TRUE){# normally no NA with is.null()
+        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL")
         stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
     # end management of NULL arguments
     # code that protects set.seed() in the global environment
+    # see also Protocol 100-rev0 Parallelization in R.docx
+    if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment
+        tempo.random.seed <- .Random.seed
+        on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv))
+    }else{
+        on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness
+    }
+    set.seed(dot.seed)
     # end code that protects set.seed() in the global environment
     # warning initiation
     ini.warning.length <- options()$warning.length
@@ -9438,139 +9725,101 @@ fun_gg_donut <- function(
     warn.count <- 0
     # end warning initiation
     # other checkings
-    removed.row.nb <- NULL
-    removed.rows <- data.frame(stringsAsFactors = FALSE)
-    data1.ini <- data1 # strictly identical to data1
-    if( ! freq %in% names(data1)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nfreq ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    if(any(duplicated(names(data1)), na.rm = TRUE)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " "))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    if( ! (y %in% names(data1))){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE A COLUMN NAME OF data1")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }else{
-        if(all(is.na(data1[ , freq]) | is.infinite(data1[ , freq]))){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE freq COLUMN OF data1 CANNOT BE JUST NA OR Inf")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }
-        tempo <- fun_check(data = data1[ , freq], mode = "numeric", neg.values = FALSE, fun.name = function.name)
+        tempo <- fun_check(data = data1[, y], data.name = "y COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name)
         if(tempo$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\n", tempo$text)
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }
-        # Inf and NA removal
-        if(any(is.infinite(data1[, freq]) | is.na(data1[, freq]))){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") PRESENCE OF Inf OR NA VALUES IN THE ", freq, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            tempo <- which(is.infinite(data1.ini[, freq]) | is.na(data1.ini[, freq])) # data.ini used for the output
-            removed.row.nb <- c(removed.row.nb, tempo)
-            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
-            data1 <- data1[ ! (is.infinite(data1[, freq]) | is.na(data1[, freq])), ] #
-        }
-        # end Inf and NA removal
-        # 0 removal
-        if(any(data1[, freq] == 0)){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") PRESENCE OF 0 VALUES IN THE ", freq, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            tempo <- which(data1[, freq] == 0) # data.ini used for the output
-            removed.row.nb <- c(removed.row.nb, tempo)
-            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
-            data1 <- data1[ data1[, freq] != 0, ] #
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE NUMERIC COLUMN IN data1")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
-        # end 0 removal
     }
-    
-    if( ! categ %in% names(data1)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-    }else{
-        if(all(is.na(data1[ , categ]))){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 CANNOT BE JUST NA")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }
-        tempo1 <- fun_check(data = categ, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = categ, class = "factor", na.contain = TRUE, fun.name = function.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 MUST BE CLASS \"factor\" OR \"character\"")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }
-        # NA removal
-        if(any(is.na(data1[, categ]))){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") PRESENCE OF NA VALUES IN THE ", categ, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            tempo <- which(is.na(data1.ini[, categ])) # data.ini used for the output
-            removed.row.nb <- c(removed.row.nb, tempo)
-            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
-            data1 <- data1[ ! is.na(data1[, categ]), ] #
-        }
-        # end Inf and NA removal
-        if(any(duplicated(data1[, categ]))){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 CANNOT CONTAIN DUPLICATED VALUES\n", paste(data1[, categ][duplicated(data1[, categ])], collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }
+    if(length(categ) > 2){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT CANNOT HAVE MORE THAN 2 COLUMN NAMES OF data1")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }else if( ! all(categ %in% names(data1))){ # all() without na.rm -> ok because categ cannot be NA (tested above)
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(categ, collapse = " "))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    
-    if( ! is.null(annotation)){
-        if( ! annotation %in% names(data1)){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nannotation ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }else{
-            if(all(is.na(data1[ , annotation]))){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nIF NON NULL, THE annotation COLUMN OF data1 CANNOT BE JUST NA")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    if(length(dot.categ) > 1){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT HAVE MORE THAN 1 COLUMN NAMES OF data1")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }else if( ! all(dot.categ %in% names(data1))){ # all() without na.rm -> ok because dot.categ cannot be NA (tested above)
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(dot.categ, collapse = " "))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # reserved word checking
+    if(any(names(data1) %in% reserved.words, na.rm = TRUE)){
+        if(any(duplicated(names(data1)), na.rm = TRUE)){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
+        if( ! is.null(dot.categ)){
+            if(dot.categ %in% categ){
+                reserved.words <- c(reserved.words, paste0(dot.categ, "_DOT")) # paste0(dot.categ, "_DOT") is added to the reserved words because in such situation, a new column will be added to data1 that is named paste0(dot.categ, "_DOT")
             }
-            tempo1 <- fun_check(data = annotation, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-            tempo2 <- fun_check(data = annotation, class = "factor", na.contain = TRUE, fun.name = function.name)
-            if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE annotation COLUMN OF data1 MUST BE CLASS \"factor\" OR \"character\"")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+        tempo.output <- fun_name_change(names(data1), reserved.words)
+        for(i2 in 1:length(tempo.output$ini)){ # a loop to be sure to take the good ones
+            names(data1)[names(data1) == tempo.output$ini[i2]] <- tempo.output$post[i2]
+            if(any(y == tempo.output$ini[i2])){ # any() without na.rm -> ok because y cannot be NA (tested above)
+                y[y == tempo.output$ini[i2]] <- tempo.output$post[i2]
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") IN y ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN y ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
             }
-            if(any(duplicated(data1[, annotation]))){
+            # WARNING: names of y argument potentially replaced
+            if(any(categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because categ cannot be NA (tested above)
+                categ[categ == tempo.output$ini[i2]] <- tempo.output$post[i2]
                 warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") PRESENCE OF DUPLICATED VALUES IN THE ", annotation, " COLUMN OF THE data1 ARGUMENT: ", paste0(data1[, annotation][duplicated(data1[, annotation])], collapse = " "))
+                tempo.warn <- paste0("(", warn.count,") IN categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
                 warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
             }
+            # WARNING: names of categ argument potentially replaced
+            if( ! is.null(dot.categ)){
+                if(any(dot.categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because dot.categ cannot be NA (tested above)
+                    dot.categ[dot.categ == tempo.output$ini[i2]] <- tempo.output$post[i2]
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN dot.categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }
+            # WARNING: names of dot.categ argument potentially replaced
         }
-    }
-    if(length(data1) == 0){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE data1 ARGUMENT IS EMPTY AFTER Inf, NA AND 0 REMOVAL IN THE ", freq, ifelse(is.null(annotation), " AND ", ", "), categ, ifelse(is.null(annotation), "", " AND "), " COLUMNS")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-    }
-    if( ! is.null(fill.color)){
-        if( ! is.numeric(fill.color)){
-            if( ! all(fill.color[ ! is.na(fill.color)] %in% colors() | grepl(pattern = "^#", fill.color[ ! is.na(fill.color)]), na.rm = TRUE)){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE A VECTOR OF (1) HEXADECIMAL COLOR STRINGS STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGER VALUES")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-            }else{
-                fill.color <- as.character(fill.color) # remove class factor is any
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") REGARDING COLUMN NAMES REPLACEMENT, THE NAMES\n", paste(tempo.output$ini, collapse = " "), "\nHAVE BEEN REPLACED BY\n", paste(tempo.output$post, collapse = " "))
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        if( ! (is.null(add) | is.null(tempo.output$ini))){
+            if(grepl(x = add, pattern = paste(tempo.output$ini, collapse = "|"))){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nCOLUMN NAMES HAVE TO BE CHANGED\nTHE PROBLEMATIC COLUMN NAMES ARE SOME OF THESE NAMES:\n", paste(tempo.output$ini, collapse = " "), "\nIN THE DATA FRAME OF data1 AND IN THE STRING OF add ARGUMENT, TRY TO REPLACE NAMES BY:\n", paste(tempo.output$post, collapse = " "), "\n\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n"))
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
             }
         }
     }
-    if( ! is.numeric(border.color)){
-        if( ! (border.color %in% colors() | grepl(pattern = "^#", border.color))){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }else{
-            border.color <- as.character(border.color) # remove class factor is any
+    if( ! (is.null(add))){
+        if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name)
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    # legend name filling
-    if(is.null(legend.name)){
-        legend.name <- categ
-    }
-    # legend.name not NULL anymore
-    # end legend name filling
+    # end reserved word checking
     # verif of add
     if( ! is.null(add)){
         if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by +
             tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-            
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ #
             tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by )
             tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
     }
     # end verif of add
@@ -9582,3108 +9831,2145 @@ fun_gg_donut <- function(
         tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap")
         tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid")
         tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep")
-        
-        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"))){
+        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){
             tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")])))
-            facet.categ <- list(names(tempo1$params$facets)) # list of length 1
+            facet.categ <- names(tempo1$params$facets)
             tempo.text <- "facet_wrap OR facet_rep_wrap"
             facet.check <- FALSE
         }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){
             tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")])))
-            facet.categ <- list(c(names(tempo1$params$rows), names(tempo1$params$cols))) # list of length 1
+            facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols))
             tempo.text <- "facet_grid OR facet_rep_grid"
             facet.check <- FALSE
         }
-        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL
+        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above)
             tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
     }
-    # if facet.categ is not NULL, it is a list of length 1 now
     # end management of add containing facet
-    if( ! is.null(lib.path)){
-        if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    # conversion of categ columns in data1 into factors
+    for(i1 in 1:length(categ)){
+        tempo1 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\n", paste0("categ NUMBER ", i1, " OF data1"), " MUST BE A FACTOR OR CHARACTER VECTOR")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else if(tempo1$problem == FALSE){ # character vector
+            if(box.alpha != 0){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") IN categ NUMBER ", i1, " IN data1, THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR, WITH LEVELS ACCORDING TO THE ALPHABETICAL ORDER")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
         }
+        data1[, categ[i1]] <- factor(data1[, categ[i1]]) # if already a factor, change nothing, if characters, levels according to alphabetical order
     }
-    # end other checkings
-    # reserved word checking
-    if( ! (is.null(add))){
-        if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){
+    # OK: all the categ columns of data1 are factors from here
+    # end conversion of categ columns in data1 into factors
+    
+    
+    
+    # management of log scale and Inf removal
+    if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf  # normally no NA with is.finite0() and is.na()
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    }
+    data1.ini <- data1 # strictly identical to data1 except that in data1 y is log converted if and only if y.log != "no"
+    if(y.log != "no"){
+        tempo1 <- ! is.finite(data1[, y]) # where are initial NA and Inf
+        data1[, y] <- suppressWarnings(get(y.log)(data1[, y]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope
+        if(any( ! (tempo1 | is.finite(data1[, y])))){ # normally no NA with is.finite
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name)
+            tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    # verif of add
-    if( ! is.null(add)){
-        if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by +
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ #
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " "))
+    # Inf removal
+    if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite
+        removed.row.nb <- which(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))
+        removed.rows <- data1.ini[removed.row.nb, ] # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation
+        data1 <- data1[-removed.row.nb, ] #
+        data1.ini <- data1.ini[-removed.row.nb, ] #
+    }else{
+        removed.row.nb <- NULL
+        removed.rows <- data.frame(stringsAsFactors = FALSE)
+    }
+    # From here, data1 and data.ini have no more Inf
+    # end Inf removal
+    if(y.log != "no" & ! is.null(y.lim)){
+        if(any(y.lim <= 0)){ # any() without na.rm -> ok because y.lim cannot be NA (tested above)
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "))
             stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by )
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " "))
+        }else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ # normally no NA with is.finite
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " "))
             stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
     }
-    # end verif of add
-    # management of add containing facet
-    facet.categ <- NULL
-    if( ! is.null(add)){
-        facet.check <- TRUE
-        tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) #
-        tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap")
-        tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid")
-        tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep")
-        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){
-            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")])))
-            facet.categ <- names(tempo1$params$facets)
-            tempo.text <- "facet_wrap OR facet_rep_wrap"
-            facet.check <- FALSE
-        }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){
-            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")])))
-            facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols))
-            tempo.text <- "facet_grid OR facet_rep_grid"
-            facet.check <- FALSE
+    if(y.log != "no" & y.include.zero == TRUE){
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        y.include.zero <- FALSE
+    }
+    if(y.log != "no" & vertical == FALSE){
+        vertical <- TRUE
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") BECAUSE OF A BUG IN ggplot2, CANNOT FLIP BOXES HORIZONTALLY WITH A Y.LOG SCALE -> vertical ARGUMENT RESET TO TRUE")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    }
+    # end management of log scale and Inf removal
+    # na detection and removal (done now to be sure of the correct length of categ)
+    column.check <- unique(c(y, categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){dot.categ}, if( ! is.null(facet.categ)){facet.categ})) # dot.categ because can be a 3rd column of data1, categ.color and dot.color will be tested later
+    if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        for(i2 in 1:length(column.check)){
+            if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na
+                tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn)))
+            }
         }
-        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above)
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which)))
+        removed.row.nb <- c(removed.row.nb, tempo) # removed.row.nb created to remove Inf
+        removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance)
+        column.check <- column.check[ ! column.check == y] # remove y to keep quali columns
+        if(length(tempo) != 0){
+            data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
+            data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former
+            for(i3 in 1:length(column.check)){
+                if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }
+        }
+        count.categ <- 0
+        for(i2 in 1:length(column.check)){
+            if(column.check[i2] %in% categ){
+                count.categ <- count.categ + 1
+            }
+            if(column.check[i2] == categ[count.categ]){
+                categ.class.order[count.categ] <- list(levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])]) # remove the absent color in the character vector
+                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.class.order[[count.categ]]))
+            }
+            if( ! is.null(dot.color) & ! is.null(dot.categ)){ # reminder : dot.categ cannot be a column name of categ anymore (because in that case dot.categ name is changed into "..._DOT"
+                if(column.check[i2] == dot.categ){
+                    dot.categ.class.order <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
+                    data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.categ.class.order))
+                }
+            }
+            if(column.check[i2] %in% facet.categ){ # works if facet.categ == NULL this method should keep the order of levels when removing some levels
+                tempo.levels <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(as.character(data1[, column.check[i2]]))]
+                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = tempo.levels)
+            }
         }
     }
-    # end management of add containing facet
-    # end reserved word checking
-    # end second round of checking and data preparation
+    # end na detection and removal (done now to be sure of the correct length of categ)
+    # From here, data1 and data.ini have no more NA or NaN in y, categ, dot.categ (if dot.color != NULL) and facet.categ
     
-    # package checking
-    fun_pack(req.package = c(
-        "gridExtra", 
-        "ggplot2", 
-        "lemon",
-        "grid",
-        "ggrepel"
-    ), lib.path = lib.path)
-    # end package checking
     
-    # main code
-    data1 <- data.frame(data1, prop = data1[ , freq] / sum(data1[ , freq]))
-    if(legend.add.prop == TRUE){
-        data1[ , categ] <- paste0(data1[ , categ], " (", round(data1$prop, 2), ")")
-    }
-    data1[ , categ] <- factor(data1[ , categ], levels = data1[ , categ][order(data1$prop, decreasing = TRUE)]) # reorder so that the donut is according to decreasing proportion starting at the top in a clockwise direction
-    data1 <- data1[order(as.numeric(data1[ , categ]), decreasing = FALSE), ] # data1[ , categ] with rows in decreasing order, according to prop
-    data1 <- data.frame(data1, x = 0) # staked bar at the origin of the donut set to x = 0
-    tempo.gg.name <- "gg.indiv.plot."
-    tempo.gg.count <- 0
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
-    bar_width = 1
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_col(
-        data = data1,
-        mapping = ggplot2::aes_string(x = "x", y = freq, fill = categ), 
-        color = border.color, 
-        size = border.size, 
-        width = bar_width
-    )) # size is size of the separation in the donut
-    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
-    #     ggplot2::aes(label = Freq), 
-    #     position = ggplot2::position_stack(vjust = 0.5)
-    # ))
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_x_continuous(
-        expand = c(0, 0), # prevent extra limits in x axis
-        limits = c(- bar_width / 2 - (bar_width * hole.size) / (1 - hole.size), max(bar_width / 2, annotation.distance))
-    )) # must be centered on x = 0
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylim(c(0, max(cumsum(data1[ , freq])))))
-    if(hole.text == TRUE){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-            geom = "text", 
-            x = - bar_width / 2 - (bar_width * hole.size) / (1 - hole.size), 
-            y = 0, 
-            label = sum(data1[ , freq]), 
-            size = hole.text.size
-        ))
-    }
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_polar(theta = "y", direction = -1, start = 0, clip = "on"))
-    if(is.null(fill.color) & ! is.null(fill.palette)){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_brewer(palette = fill.palette, name = legend.name))
-    }else if( ! is.null(fill.color)){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_manual(values = fill.color, name = legend.name, na.value = "white"))
-    }else if(! is.null(legend.name)){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::labs(fill = legend.name))
-    }
     
-    if( ! is.null(add)){ # if add is NULL, then = 0
-        if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme_void() HAS BEEN INACTIVATED, SO THAT THE USER THEME CAN BE EFFECTIVE")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            add.check <- FALSE
+    if( ! is.null(categ.class.order)){
+        if(length(categ.class.order) != length(categ)){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.class.order ARGUMENT MUST BE A LIST OF LENGTH EQUAL TO LENGTH OF categ\nHERE IT IS LENGTH: ", length(categ.class.order), " VERSUS ", length(categ))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }else{
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void())
+            for(i3 in 1:length(categ.class.order)){
+                if(is.null(categ.class.order[[i3]])){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i3, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED")
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                    data1[, categ[i3]] <- factor(as.character(data1[, categ[i3]])) # if already a factor, change nothing, if characters, levels according to alphabetical order
+                    categ.class.order[[i3]] <- levels(data1[, categ[i3]]) # character vector that will be used later
+                }else{
+                    tempo <- fun_check(data = categ.class.order[[i3]], data.name = paste0("COMPARTMENT ", i3 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[, categ[i3]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok
+                    if(tempo$problem == TRUE){
+                        stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+                    }
+                }
+                if(any(duplicated(categ.class.order[[i3]]), na.rm = TRUE)){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i3]], collapse = " "))
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }else if( ! (all(categ.class.order[[i3]] %in% unique(data1[, categ[i3]]), na.rm = TRUE) & all(unique(data1[, categ[i3]]) %in% categ.class.order[[i3]], na.rm = TRUE))){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT MUST BE CLASSES OF ELEMENT ", i3, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i3]], collapse = " "), "\nFOR COMPARTMENT ", i3, " OF categ.class.order AND IT IS:\n", paste(unique(data1[, categ[i3]]), collapse = " "), "\nFOR COLUMN ", categ[i3], " OF data1")
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }else{
+                    data1[, categ[i3]] <- factor(data1[, categ[i3]], levels = categ.class.order[[i3]]) # reorder the factor
+                    
+                }
+                names(categ.class.order)[i3] <- categ[i3]
+            }
         }
     }else{
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void())
-    }
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(
-        fill = ggplot2::guide_legend(override.aes = list(color = "white", size  = 2, stroke = 1))
-    )) # remove border of squares in legend
-    
-    # annotations on slices
-    if( ! is.null(annotation)){
-        tempo <- rev(cumsum(rev(data1[ , freq])))
-        data1 <- data.frame(data1, text_y = tempo - (tempo - c(tempo[-1], 0)) / 2)
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggrepel::geom_text_repel(
-            data = data1, 
-            mapping = ggplot2::aes_string(
-                x = "x", 
-                y = "text_y", 
-                label = annotation
-            ), 
-            size = annotation.size, 
-            force = annotation.force, 
-            force_pull = annotation.force.pull, 
-            nudge_x = annotation.distance, # knowing that the bar is centered on x = 0 and that the right edge is at bar_width / 2, 0 means center of the slice, 0.5 means at the edge if bar_width = 1
-            show.legend = FALSE
-        ))
-    }
-    # end annotations on slices
-    
-    # legend management
-    # removal of part of the legend 
-    if( ! is.null(legend.limit)){
-        if(sum(data1$prop >= legend.limit) == 0){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE legend.limit PARAMETER VALUE (", legend.limit, ") IS TOO HIGH FOR THE PROPORTIONS IN THE DONUT PLOT:\n", paste0(data1$prop, collapse = "\n"))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-        }else{
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_discrete(
-                breaks = as.character(data1[ , categ][data1$prop >= legend.limit])
-            ))
+        categ.class.order <- vector("list", length = length(categ))
+        tempo.categ.class.order <- NULL
+        for(i2 in 1:length(categ.class.order)){
+            categ.class.order[[i2]] <- levels(data1[, categ[i2]])
+            names(categ.class.order)[i2] <- categ[i2]
+            tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]])
         }
-    }
-    # end removal of part of the legend
-    if(legend.show == FALSE){ # must be here because must be before bef.final.plot
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none")) # inactivate the initial legend
-    }
-    bef.final.plot <- suppressWarnings(suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))))
-    if( ! is.null(legend.width)){
-        legend.final <- suppressWarnings(suppressMessages(fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path))) # get legend
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none")) # inactivate the initial legend
-        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
-            legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
+        if(box.alpha != 0){
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n")
+            tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR BOX ORDERING:\n", paste(tempo.categ.class.order, collapse = " "))
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    # end legend management
-    
-    # drawing
-    final.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
-    
-    # title
-    if(title != ""){
-        title.grob <- grid::textGrob(
-            label = title,
-            x = grid::unit(0, "lines"), 
-            y = grid::unit(0, "lines"),
-            hjust = 0,
-            vjust = 0,
-            gp = grid::gpar(fontsize = title.text.size)
-        )
-        pdf(NULL)
-        final.plot <- suppressMessages(suppressWarnings(gridExtra::arrangeGrob(final.plot, top = title.grob, left = " ", right = " "))) # , left = " ", right = " " : trick to add margins in the plot. padding =  unit(0.5, "inch") is for top margin above the title
-        dev.off()
+    # categ.class.order not NULL anymore (list)
+    if(is.null(box.legend.name) & box.alpha != 0){
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") THE box.legend.name SETTING IS NULL. NAMES OF categ WILL BE USED: ", paste(categ, collapse = " "))
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        box.legend.name <- categ[length(categ)] # if only categ1, then legend name of categ1, if length(categ) == 2L, then legend name of categ2
     }
-    # end title
-    
-    grob.save <- NULL
-    if(plot == TRUE){
-        if( ! is.null(legend.width)){
-            grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(final.plot, legend.final, ncol=2, widths=c(1, legend.width))))
+    # box.legend.name not NULL anymore (character string)
+    # management of categ.color
+    if( ! is.null(categ.color)){
+        # check the nature of color
+        # integer colors into gg_palette
+        tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem
+        if(tempo.check.color == FALSE){
+            # convert integers into colors
+            categ.color <- fun_gg_palette(max(categ.color, na.rm = TRUE))[categ.color]
+        }
+        # end integer colors into gg_palette
+        if( ! (all(categ.color %in% colors() | grepl(pattern = "^#", categ.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because categ.color cannot be NA (tested above)
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors() OR A COLUMN NAME OF THE data1 PARAMETER: ", paste(unique(categ.color), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
+        if(any(is.na(categ.color)) & box.alpha != 0){ # normally no NA with is.na
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT CONTAINS NA")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+        # end check the nature of color
+        # check the length of color
+        categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
+        if(length(data1[, categ[categ.len]]) == length(levels(data1[, categ[categ.len]])) & length(categ.color) == length(data1[, categ[categ.len]])){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", categ[categ.len], " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE categ.color ARGUMENT ARE ALL EQUAL. BOX COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", categ[categ.len], ", NOT ACCORDING TO THE ROWS OF ", categ[categ.len])
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+        if(length(categ.color) == length(levels(data1[, categ[categ.len]]))){ # here length(categ.color) is equal to the different number of categ
+            # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+            data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)  # no need stringsAsFactors here for stat.nolog as factors remain factors
+            data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]])
+            if(box.alpha != 0){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(categ.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "))
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+        }else if(length(categ.color) == length(data1[, categ[categ.len]])){# here length(categ.color) is equal to nrow(data1) -> Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]])))
+            data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE)
+            tempo.check <- unique(data1[ , c(categ[categ.len], "categ.color")])
+            if( ! (nrow(tempo.check) == length(unique(categ.color)) & nrow(tempo.check) == length(unique(data1[ , categ[categ.len]])))){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], ":\n", paste(unique(mapply(FUN = "paste", data1[ ,categ[categ.len]], data1[ ,"categ.color"])), collapse = "\n"))
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }else{
+                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+                categ.color <- unique(data1$categ.color[order(data1[, categ[categ.len]])]) # Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]])))
+                if(box.alpha != 0){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], " AS:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\n", paste(categ.color, collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }
+        }else if(length(categ.color) == 1L){
+            # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+            data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE)
+            categ.color <- rep(categ.color, length(levels(data1[, categ[categ.len]])))
+            if(box.alpha != 0){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(categ.color, collapse = " "))
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
         }else{
-            grob.save <- suppressMessages(suppressWarnings(print(final.plot)))
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(categ.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
     }else{
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-    }
-    # end drawing
-    
-    
-    
-    # output
-    if(warn.print == TRUE & ! is.null(warn)){
-        on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
-    }
-    on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
-    if(return == TRUE){
-        output <- suppressMessages(ggplot2::ggplot_build(final.plot))
-        if(is.null(unlist(removed.row.nb))){
-            removed.row.nb <- NULL
-            removed.rows <- NULL
+        categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
+        # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+        categ.color <- fun_gg_palette(length(levels(data1[, categ[categ.len]])))
+        data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)
+        data1$categ.color <- factor(data1$categ.color, labels = categ.color)  # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]])
+        if(box.alpha != 0){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") NULL categ.color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", categ[categ.len], " IN data1:\n", paste(categ.color, collapse = " "), "\n", paste(levels(data1[, categ[categ.len]]), collapse = " "))
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
-        tempo <- output$layout$panel_params[[1]]
-        output <- list(
-            data = data1, 
-            removed.row.nb = removed.row.nb, 
-            removed.rows = removed.rows, 
-            plot = output$data, 
-            panel = facet.categ, 
-            axes = list(
-                x.range = tempo$x.range, 
-                x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
-                x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
-                y.range = tempo$y.range, 
-                y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
-                y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
-            ), 
-            warn = paste0("\n", warn, "\n\n"), 
-            ggplot = if(return.ggplot == TRUE){final.plot}else{NULL}, # final.plot plots the graph if return == TRUE
-            gtable = if(return.gtable == TRUE){grob.save}else{NULL} #
-        )
-        return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
     }
-    # end output
-    # end main code
-}
-
-
-
-# Error: class order not good when a class is removed due to NA
-# Error: line 136 in check 20201126 with add argument
-# Solve this: sometimes error messages can be more than the max display (8170). Thus, check every paste0("ERROR IN ", function.name, and trunck the message if to big. In addition, add at the begining of the warning message that it is too long and see the $warn output for complete message. Add also this into fun_scatter
-# add dot.shape ? See with available aesthetic layers
-# rasterise: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html
-# add horizontal argument and deal any conflict with vertical argument. Start with horizontal = NULL as default. If ! is.null() -> convert vertical if required
-# time for excecution : microbenchmark package. See also in RStudio time per line of code. See also https://stackoverflow.com/questions/7561362/what-can-cause-a-program-to-run-much-faster-the-second-time
-
-
-fun_gg_boxplot <- function(
-    data1, 
-    y, 
-    categ, 
-    categ.class.order = NULL, 
-    categ.color = NULL, 
-    box.legend.name = NULL, 
-    box.fill = FALSE, 
-    box.width = 0.5, 
-    box.space = 0.1, 
-    box.line.size = 0.75, 
-    box.notch = FALSE, 
-    box.alpha = 1, 
-    box.mean = TRUE, 
-    box.whisker.kind = "std", 
-    box.whisker.width = 0, 
-    dot.color = grey(0.25), 
-    dot.categ = NULL, 
-    dot.categ.class.order = NULL, 
-    dot.legend.name = NULL, 
-    dot.tidy = FALSE, 
-    dot.tidy.bin.nb = 50, 
-    dot.jitter = 0.5, 
-    dot.seed = 2, 
-    dot.size = 3, 
-    dot.alpha = 0.5, 
-    dot.border.size = 0.5, 
-    dot.border.color = NULL, 
-    x.lab = NULL, 
-    x.angle = 0, 
-    y.lab = NULL, 
-    y.lim = NULL, 
-    y.log = "no", 
-    y.tick.nb = NULL, 
-    y.second.tick.nb = 1, 
-    y.include.zero = FALSE, 
-    y.top.extra.margin = 0.05, 
-    y.bottom.extra.margin = 0.05, 
-    stat.pos = "top", 
-    stat.mean = FALSE, 
-    stat.size = 4, 
-    stat.dist = 5, 
-    stat.angle = 0, 
-    vertical = TRUE, 
-    text.size = 12, 
-    title = "", 
-    title.text.size = 8, 
-    legend.show = TRUE, 
-    legend.width = 0.5, 
-    article = TRUE, 
-    grid = FALSE, 
-    add = NULL, 
-    return = FALSE, 
-    return.ggplot = FALSE,
-    return.gtable = TRUE,
-    plot = TRUE, 
-    warn.print = FALSE, 
-    lib.path = NULL
-){
-    # AIM
-    # Plot ggplot2 boxplots + dots + means
-    # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html
-    # WARNINGS
-    # Rows containing NA in data1[, c(y, categ)] will be removed before processing, with a warning (see below)
-    # Hinges are not computed like in the classical boxplot() function of R. See https://ggplot2.tidyverse.org/reference/geom_boxplot.html
-    # To have a single box, please create a factor column with a single class and specify the name of this column in the categ argument. For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1, knowing that categ2 must also be specified in this situation). See categ argument below
-    # The dot.alpha argument can alter the display of the color boxes when using pdf output
-    # Size arguments (box.line.size, dot.size, dot.border.size, stat.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
-    # Display seems to be done twice on Windows devices (like a blink). However, no double plots on pdf devices. Thus, the blink remains mysterious
-    # To remove boxes and have only dots, use box.alpha = 0
-    # ARGUMENTS
-    # data1: data frame containing one column of quantitative values (see the y argument below) and one or two columns of categories (see the categ argument below). Duplicated column names are not allowed
-    # y: character string of the data1 column name for y-axis (column containing numeric values). Numeric values will be split according to the classes of the column names indicated in the categ argument to generate the boxes and will also be used to plot the dots
-    # categ: vector of character strings of the data1 column name for categories (column of characters or factors). Must be either one or two column names. If a single column name (further referred to as categ1), then one box per class of categ1. If two column names (further referred to as categ1 and categ2), then one box per class of categ2, which form a group of boxes in each class of categ1. WARNING: no empty classes allowed. To have a single box, create a factor column with a single class and specify the name of this column in the categ argument (here, no categ2 in categ argument). For a single set of grouped boxes, create a factor column with a single class and specify this column in categ argument as first element (i.e., as categ1), in addition to the already used category (as categ2 in this situation)
-    # categ.class.order: list indicating the order of the classes of categ1 and categ2 represented on the boxplot (the first compartment for categ1 and and the second for categ2). If categ.class.order == NULL, classes are represented according to the alphabetical order. Some compartments can be NULL and others not. See the categ argument for categ1 and categ2 description
-    # categ.color: vector of color character string for box frames (see the categ argument for categ1 and categ2 description)
-    # If categ.color == NULL, default colors of ggplot2, whatever categ1 and categ2
-    # If categ.color is non-null and only categ1 in categ argument, categ.color can be either:
-    # (1) a single color string. All the boxes will have this color, whatever the number of classes of categ1
-    # (2) a vector of string colors, one for each class of categ1. Each color will be associated according to categ.class.order of categ1
-    # (3) a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ1 and a single class of categ1 per color must be respected
-    # Color functions, like grey(), hsv(), etc., are also accepted
-    # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the maximal integer value among all the integers in categ.color (see fun_gg_palette())
-    # If categ.color is non-null and categ1 and categ2 are specified, all the rules described above will apply to categ2 instead of categ1 (colors will be determined for boxes inside a group of boxes)
-    # box.legend.name: character string of the legend title. If box.legend.name is NULL, then box.legend.name <- categ1 if only categ1 is present, and box.legend.name <- categ2 if categ1 and categ2 are present in the categ argument. Write "" if no legend required. See the categ argument for categ1 and categ2 description
-    # box.fill: logical. Fill the box? If TRUE, the categ.color argument will be used to generate filled boxplots (the box frames being black) as well as filled outlier dots (the dot border being controlled by the dot.border.color argument). If all the dots are plotted (argument dot.color other than NULL), they will be over the boxes. If FALSE, the categ.color argument will be used to color the box frames and the outlier dot borders. If all the dots are plotted, they will be beneath the boxes
-    # box.width: single numeric value (from 0 to 1) of width of either boxes or group of boxes
-    # When categ argument has a single categ1 element (i.e., separate boxes. See the categ argument for categ1 and categ2 description), then each class of categ1 is represented by a single box. In that case, box.width argument defines each box width, from 0 (no box width) to 1 (max box width), but also the space between boxes (the code uses 1 - box.width for the box spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each box)
-    # When categ argument has a two categ1 and categ2 elements (i.e., grouped boxes), box.width argument defines the width allocated for each set of grouped boxes, from 0 (no group width) to 1 (max group width), but also the space between grouped boxes (the code uses 1 - box.width for the spaces). Of note, xmin and xmax of the fun_gg_boxplot() output report the box boundaries (around x-axis unit 1, 2, 3, etc., for each set of grouped box)
-    # box.space: single numeric value (from 0 to 1) indicating the box separation inside grouped boxes, when categ argument has a two categ1 and categ2 elements. 0 means no space and 1 means boxes shrunk to a vertical line. Ignored if categ argument has a single categ1 element
-    # box.line.size: single numeric value of line width of boxes and whiskers in mm
-    # box.notch: logical. Notched boxplot? It TRUE, display notched boxplot, notches corresponding approximately to the 95% confidence interval of the median (the notch interval is exactly 1.58 x Inter Quartile Range (IQR) / sqrt(n), with n the number of values that made the box). If notch intervals between two boxes do not overlap, it can be interpreted as significant median differences
-    # box.alpha: single numeric value (from 0 to 1) of box transparency (full transparent to full opaque, respectively). To remove boxplots, use box.alpha = 0
-    # box.mean: logical. Add mean value? If TRUE, a diamond-shaped dot, with the horizontal diagonal corresponding to the mean value, is displayed over each boxplot
-    # box.whisker.kind: range of the whiskers. Either "no" (no whiskers), or "std" (length of each whisker equal to 1.5 x Inter Quartile Range (IQR)), or "max" (length of the whiskers up or down to the most distant dot)
-    # box.whisker.width: single numeric value (from 0 to 1) of the whisker width, with 0 meaning no whiskers and 1 meaning a width equal to the box width
-    # dot.color: vector of color character string ruling the dot colors and the dot display. See the example section below for easier understanding of the rules described here
-    # If NULL, no dots plotted
-    # If "same", the dots will have the same colors as the respective boxplots
-    # Otherwise, as in the rule (1), (2) or (3) described in the categ.color argument, except that in the possibility (3), the rule "a single color per class of categ and a single class of categ per color", does not have to be respected (for instance, each dot can have a different color). Colors will also depend on the dot.categ argument. If dot.categ is NULL, then colors will be applied to each class of the last column name specified in categ. If dot.categ is non-NULL, colors will be applied to each class of the column name specified in dot.categ. See examples
-    # dot.categ: optional single character string of a column name (further referred to as categ3) of the data1 argument. This column of data1 will be used to generate a legend for dots, in addition to the legend for boxes. See the dot.color argument for details about the way the legend is built using the two dot.categ and dot.color arguments. If NULL, no legend created and the colors of dots will depend on dot.color and categ arguments (as explained in the dot.color argument)
-    # dot.categ.class.order: optional vector of character strings indicating the order of the classes of categ3 (see the dot.categ argument). If dot.categ is non-NULL and dot.categ.class.order is NULL, classes are displayed in the legend according to the alphabetical order. Ignored if dot.categ is NULL
-    # dot.legend.name: optional character string of the legend title for categ3 (see the dot.categ argument). If dot.legend.name == NULL, dot.categ value is used (name of the column in data1). Write "" if no legend required. Ignored if dot.categ is NULL
-    # dot.tidy: logical. Nice dot spreading? If TRUE, use the geom_dotplot() function for a nice representation. WARNING: change the true quantitative coordinates of dots (i.e., y-axis values for vertical display) because of binning. Thus, the gain in aestheticism is associated with a loss in precision that can be very important. If FALSE, dots are randomly spread on the qualitative axis, using the dot.jitter argument (see below) keeping the true quantitative coordinates
-    # dot.tidy.bin.nb: positive integer indicating the number of bins (i.e., nb of separations) of the y.lim range. Each dot will then be put in one of the bin, with a diameter of the width of the bin. In other words, increase the number of bins to have smaller dots. Not considered if dot.tidy is FALSE
-    # dot.jitter: numeric value (from 0 to 1) of random dot horizontal dispersion (for vertical display), with 0 meaning no dispersion and 1 meaning dispersion in the corresponding box width interval. Not considered if dot.tidy is TRUE
-    # dot.seed: integer value that set the random seed. Using the same number will generate the same dot jittering. Write NULL to have different jittering each time the same instruction is run. Ignored if dot.tidy is TRUE
-    # dot.size: numeric value of dot diameter in mm. Not considered if dot.tidy is TRUE
-    # dot.alpha: numeric value (from 0 to 1) of dot transparency (full transparent to full opaque, respectively)
-    # dot.border.size: numeric value of border dot width in mm. Write zero for no dot border. If dot.tidy is TRUE, value 0 remove the border and other values leave the border without size control (geom_doplot() feature)
-    # dot.border.color: single character color string defining the color of the dot border (same color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette()
-    # x.lab: a character string or expression for x-axis legend. If NULL, character string of categ1 (see the categ argument for categ1 and categ2 description)
-    # x.angle: integer value of the text angle for the x-axis numbers, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
-    # y.lab: a character string or expression for y-axis legend. If NULL, character string of the y argument
-    # y.lim: 2 numeric values indicating the range of the y-axis. Order matters (for inverted axis). If NULL, the range of the x column name of data1 will be used. 
-    # y.log: either "no", "log2" (values in the y argument column of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y argument column of the data1 data frame will be log10 transformed and y-axis will be log10 scaled). WARNING: not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881)
-    # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
-    # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
-    # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10"
-    # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis
-    # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis
-    # stat.pos: add the median number above the corresponding box. Either NULL (no number shown), "top" (at the top of the plot region) or "above" (above each box)
-    # stat.mean: logical. Display mean numbers instead of median numbers? Ignored if stat.pos is NULL
-    # stat.size: numeric value of the stat font size in mm. Ignored if stat.pos is NULL
-    # stat.dist: numeric value of the stat distance in percentage of the y-axis range (stat.dist = 5 means move the number displayed at 5% of the y-axis range). Ignored if stat.pos is NULL or "top"
-    # stat.angle: integer value of the angle of stat, using the same rules as in ggplot2. Positive values for counterclockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Negative values for clockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
-    # vertical: logical. Vertical boxes? WARNING: will be automatically set to TRUE if y.log argument is other than "no". Indeed, not possible to have horizontal boxes with a log axis, due to a bug in ggplot2 (see https://github.com/tidyverse/ggplot2/issues/881)
-    # text.size: numeric value of the font size of the (1) axis numbers, (2) axis labels and (3) texts in the graphic legend (in mm)
-    # title: character string of the graph title
-    # title.text.size: numeric value of the title font size in mm
-    # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not
-    # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend
-    # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme
-    # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present)
-    # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL
-    # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()"
-    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_boxplot() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
-    # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions
-    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_boxplot() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
-    # return: logical. Return the graph parameters?
-    # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_boxplot() function (e.g., a <- fun_gg_boxplot()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
-    # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
-    # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
-    # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
-    # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
-    # RETURN
-    # A boxplot if plot argument is TRUE
-    # A list of the graph info if return argument is TRUE:
-    # $data: the initial data with graphic information added
-    # $stat: the graphic statistics (mostly equivalent to ggplot_build()$data[[2]])
-    # $removed.row.nb: which rows have been removed due to NA/Inf detection in y and categ columns (NULL if no row removed)
-    # $removed.rows: removed rows (NULL if no row removed)
-    # $plot: the graphic box and dot coordinates
-    # $dots: dot coordinates
-    # $main.box: coordinates of boxes
-    # $median: median coordinates
-    # $sup.whisker: coordinates of top whiskers (y for base and y.end for extremities)
-    # $inf.whisker: coordinates of bottom whiskers (y for base and y.end for extremities)
-    # $sup.whisker.edge: coordinates of top whisker edges (x and xend)
-    # $inf.whisker.edge: coordinates of bottom whisker edges(x and xend)
-    # $mean: diamond mean coordinates (only if box.mean argument is TRUE)
-    # $stat.pos: coordinates of stat numbers (only if stat.pos argument is not NULL)
-    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no")
-    # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-NULL or if y.log argument is different from "no")
-    # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
-    # $axes: the x-axis and y-axis info
-    # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
-    # $ggplot: ggplot object that can be used for reprint (use print(...$ggplot) or update (use ...$ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-NULL $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
-    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
-    # REQUIRED PACKAGES
-    # ggplot2
-    # gridExtra
-    # lemon (in case of use in the add argument)
-    # scales
-    # REQUIRED FUNCTIONS FROM THE cute PACKAGE
-    # fun_check()
-    # fun_comp_1d()
-    # fun_comp_2d()
-    # fun_gg_just()
-    # fun_gg_palette()
-    # fun_inter_ticks()
-    # fun_name_change()
-    # fun_pack()
-    # fun_round()
-    # fun_scale()
-    # EXAMPLE
-    # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(20, 100, 10), rnorm(20, 200, 50), rnorm(20, 500, 60), rnorm(20, 100, 50)), Categ1 = rep(c("CAT", "DOG"), times = 40), Categ2 = rep(c("A", "B", "C", "D"), each = 20), Color1 = rep(c("coral", "lightblue"), times = 40), Color2 = rep(c("#9F2108", "#306100", "#007479", "#8500C0"), each = 20), stringsAsFactors = TRUE) ; set.seed(NULL) ; fun_gg_boxplot(data1 = obs1, y = "Time", categ = "Categ1")
-    # see http
-    # DEBUGGING
-    # set.seed(1) ; obs1 <- data.frame(Time = c(rnorm(10), rnorm(10) + 2), Categ1 = rep(c("G", "H"), each = 10), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$Time[1:10] <- NA ; data1 = obs1 ; y = "Time" ; categ = c("Categ1") ; categ.class.order = NULL ; categ.color = NULL ; box.legend.name = NULL ; box.fill = FALSE ; box.width = 0.5 ; box.space = 0.1 ; box.line.size = 0.75 ; box.notch = FALSE ; box.alpha = 1 ; box.mean = TRUE ; box.whisker.kind = "std" ; box.whisker.width = 0 ; dot.color = grey(0.25) ; dot.categ = NULL ; dot.categ.class.order = NULL ; dot.legend.name = NULL ; dot.tidy = FALSE ; dot.tidy.bin.nb = 50 ; dot.jitter = 0.5 ; dot.seed = 2 ; dot.size = 3 ; dot.alpha = 0.5 ; dot.border.size = 0.5 ; dot.border.color = NULL ; x.lab = NULL ; x.angle = 0 ; y.lab = NULL ; y.lim = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = 1 ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; stat.pos = "top" ; stat.mean = FALSE ; stat.size = 4 ; stat.dist = 5 ; stat.angle = 0 ; vertical = TRUE ; text.size = 12 ; title = "" ; title.text.size = 8 ; legend.show = TRUE ; legend.width = 0.5 ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
-    # function name
-    function.name <- paste0(as.list(match.call(expand.dots = FALSE))[[1]], "()")
-    arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments
-    arg.user.setting <- as.list(match.call(expand.dots = FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
-    # end function name
-    # required function checking
-    req.function <- c(
-        "fun_comp_2d", 
-        "fun_gg_just", 
-        "fun_gg_palette", 
-        "fun_name_change", 
-        "fun_pack", 
-        "fun_check", 
-        "fun_round", 
-        "fun_scale",
-        "fun_inter_ticks"
-    )
-    tempo <- NULL
-    for(i1 in req.function){
-        if(length(find(i1, mode = "function")) == 0L){
-            tempo <- c(tempo, i1)
-        }
-    }
-    if( ! is.null(tempo)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n"))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # end required function checking
-    # reserved words to avoid bugs (names of dataframe columns used in this function)
-    reserved.words <- c("categ.check", "categ.color", "dot.color", "dot.categ", "dot.max", "dot.min", "group", "PANEL", "group.check", "MEAN", "tempo.categ1", "tempo.categ2", "text.max.pos", "text.min.pos", "x", "x.y", "y", "y.check", "y_from.dot.max", "ymax", "tidy_group", "binwidth")
-    # end reserved words to avoid bugs (used in this function)
-    # arg with no default values
-    mandat.args <- c(
-        "data1", 
-        "y", 
-        "categ"
-    )
-    tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")")))
-    if(any(tempo)){ # normally no NA for missing() output
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n"))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # end arg with no default values
-    # argument primary checking
-    arg.check <- NULL #
-    text.check <- NULL #
-    checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools
-    ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name))
-    tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = categ, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
-    if( ! is.null(categ.class.order)){
-        tempo <- fun_check(data = categ.class.order, class = "list", fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = categ.class.order, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(box.legend.name)){
-        tempo <- fun_check(data = box.legend.name, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = box.legend.name, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(categ.color)){
-        tempo1 <- fun_check(data = categ.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = categ.color, class = "factor", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem
-            if(tempo.check.color == TRUE){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
-            }else if(any(categ.color == 0L, na.rm = TRUE)){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
-            }
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = categ.color, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = box.fill, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.space, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.notch, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.mean, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.whisker.kind, options = c("no", "std", "max"), length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = box.whisker.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    # categ.color not NULL anymore
+    categ.color <- as.character(categ.color)
+    # categ.color is a character string representing the diff classes
+    data1$categ.color <- factor(data1$categ.color, levels = unique(categ.color)) # ok because if categ.color is a character string, the order make class 1, class 2, etc. unique() because no duplicates allowed
+    # data1$categ.color is a factor with order of levels -> categ.color
+    # end management of categ.color
+    # management of dot.color
     if( ! is.null(dot.color)){
-        tempo1 <- fun_check(data = dot.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = dot.color, class = "factor", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name)$problem
-            if(tempo.check.color == TRUE){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
-            }else if(any(dot.color == 0L, na.rm = TRUE)){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE A FACTOR OR CHARACTER VECTOR OR POSITVE INTEGER VECTOR") # integer possible because dealt above
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
+        # optional legend of dot colors
+        if( ! is.null(dot.categ)){
+            ini.dot.categ <- dot.categ
+            if( ! dot.categ %in% names(data1)){ # no need to use all() because length(dot.categ) = 1
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE A COLUMN NAME OF data1. HERE IT IS:\n", dot.categ)
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }else if(dot.categ %in% categ){ # no need to use all() because length(dot.categ) = 1. Do not use dot.categ %in% categ[length(categ)] -> error
+                # management of dot legend if dot.categ %in% categ (because legends with the same name are joined in ggplot2) 
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") THE COLUMN NAME OF data1 INDICATED IN THE dot.categ ARGUMENT (", dot.categ, ") HAS BEEN REPLACED BY ", paste0(dot.categ, "_DOT"), " TO AVOID MERGED LEGEND BY GGPLOT2")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                data1 <- data.frame(data1, dot.categ = data1[, dot.categ], stringsAsFactors = TRUE) # dot.categ is not a column name of data1 (checked above with reserved words)
+                dot.categ <- paste0(dot.categ, "_DOT")
+                names(data1)[names(data1) == "dot.categ"] <- dot.categ # paste0(dot.categ, "_DOT") is not a column name of data1 (checked above with reserved words)
+                # tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT BE A COLUMN NAME OF data1 ALREADY SPECIFIED IN THE categ ARGUMENT:\n", dot.categ, "\nINDEED, dot.categ ARGUMENT IS MADE TO HAVE MULTIPLE DOT COLORS NOT RELATED TO THE BOXPLOT CATEGORIES")
+                # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
             }
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.color, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(dot.categ)){
-        tempo <- fun_check(data = dot.categ, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.categ, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(dot.categ.class.order)){
-        tempo <- fun_check(data = dot.categ.class.order, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.categ.class.order, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+            tempo1 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+            tempo2 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name)
+            if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ COLUMN MUST BE A FACTOR OR CHARACTER VECTOR") #
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }
+            data1[, dot.categ] <- factor(data1[, dot.categ]) # if already a factor, change nothing, if characters, levels according to alphabetical order
+            # dot.categ column of data1 is factor from here
+            if( ! is.null(dot.categ.class.order)){
+                if(any(duplicated(dot.categ.class.order), na.rm = TRUE)){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(dot.categ.class.order, collapse = " "))
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }else if( ! (all(dot.categ.class.order %in% levels(data1[, dot.categ])) & all(levels(data1[, dot.categ]) %in% dot.categ.class.order, na.rm = TRUE))){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT MUST BE CLASSES OF dot.categ ARGUMENT\nHERE IT IS:\n", paste(dot.categ.class.order, collapse = " "), "\nFOR dot.categ.class.order AND IT IS:\n", paste(levels(data1[, dot.categ]), collapse = " "), "\nFOR dot.categ COLUMN (", ini.dot.categ, ") OF data1")
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }else{
+                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
+                }
+            }else{
+                if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
+                    dot.categ.class.order <- unlist(categ.class.order[length(categ)])
+                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL AND dot.color IS \"same\". ORDER OF categ.class.order WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }else{
+                    dot.categ.class.order <- sort(levels(data1[, dot.categ]))
+                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }
+            # dot.categ.class.order not NULL anymore (character string) if dot.categ is not NULL
+            if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
+                if( ! identical(ini.dot.categ, categ[length(categ)])){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\", THE COLUMN NAME IN dot.categ ARGUMENT MUST BE IDENTICAL TO THE LAST COLUMN NAME IN categ ARGUMENT. HERE IT IS:\ndot.categ: ", paste(ini.dot.categ, collapse = " "), "\ncateg: ", paste(categ, collapse = " "))
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }else if( ! fun_comp_1d(unlist(categ.class.order[length(categ)]), dot.categ.class.order)$identical.content){
+                    tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\",\nLAST COMPARTMENT OF categ.class.order ARGUMENT AND dot.categ.class.order ARGUMENT CANNOT BE DIFFERENT:\nLAST COMPARTMENT OF categ.class.order: ", paste(unlist(categ.class.order[length(categ)]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "))
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }
+            }
+            for(i3 in 1:length(categ)){
+                if(identical(categ[i3], ini.dot.categ) & ! identical(unlist(categ.class.order[i3]), dot.categ.class.order) & identical(sort(unlist(categ.class.order[i3])), sort(dot.categ.class.order))){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") THE dot.categ ARGUMENT SETTING IS PRESENT IN THE categ ARGUMENT SETTING, BUT ORDER OF THE CLASSES IS NOT THE SAME:\ncateg.class.order: ", paste(unlist(categ.class.order[i3]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "), "\nNOTE THAT ORDER OF categ.class.order IS THE ONE USED FOR THE AXIS REPRESENTATION")
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }
+            if(is.null(dot.legend.name)){
+                dot.legend.name <- if(ini.dot.categ %in% categ[length(categ)]){dot.categ}else{ini.dot.categ} #
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") THE dot.legend.name SETTING IS NULL -> ", dot.legend.name, " WILL BE USED AS LEGEND TITLE OF DOTS")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+            # dot.legend.name not NULL anymore (character string)
+        }else{
+            if( ! is.null(dot.categ.class.order)){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order ARGUMENT IS NOT NULL, BUT IS THE dot.categ ARGUMENT\n-> dot.categ.class.order NOT CONSIDERED AS NO LEGEND WILL BE DRAWN")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+            # But dot.categ.class.order will be converted to NULL below (not now)
+        }
+        # end optional legend of dot colors
+        # check the nature of color
+        # integer colors into gg_palette
+        tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem
+        if(tempo.check.color == FALSE){
+            # convert integers into colors
+            dot.color <- fun_gg_palette(max(dot.color, na.rm = TRUE))[dot.color]
+        }
+        # end integer colors into gg_palette
+        if(all(dot.color == "same") & length(dot.color)== 1L){# all() without na.rm -> ok because dot.color cannot be NA (tested above)
+            dot.color <- categ.color # same color of the dots as the corresponding box color
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS BEEN SET TO \"same\"\nTHUS, DOTS WILL HAVE THE SAME COLORS AS THE CORRESPONDING BOXPLOT")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }else if( ! (all(dot.color %in% colors() | grepl(pattern = "^#", dot.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because dot.color cannot be NA (tested above)
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR VECTOR STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGERS, OR THE STRING \"same\"\nHERE IT IS: ", paste(unique(dot.color), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
+        if(any(is.na(dot.color))){ # normally no NA with is.finite
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT CONTAINS NA")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+        # end check the nature of color
+        # check the length of color
+        if( ! is.null(dot.categ)){
+            # optional legend of dot colors
+            if(length(data1[, dot.categ]) == length(levels(data1[, dot.categ])) & length(dot.color) == length(data1[, dot.categ])){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", dot.categ, " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE dot.color ARGUMENT ARE ALL EQUAL. DOT COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", dot.categ, ", NOT ACCORDING TO THE ROWS OF ", dot.categ)
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+            if(length(dot.color) > 1 & ! (length(dot.color) == length(unique(data1[, dot.categ])) | length(dot.color) == length(data1[, dot.categ]))){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN LENGTH OF THE dot.color ARGUMENT IS MORE THAN 1, IT MUST BE EQUAL TO THE NUMBER OF 1) ROWS OR 2) LEVELS OF dot.categ COLUMN (", dot.categ, "):\ndot.color: ", paste(dot.color, collapse = " "), "\ndot.categ LEVELS: ", paste(levels(data1[, dot.categ]), collapse = " "))
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }else if(length(dot.color) > 1 & length(dot.color) == length(unique(data1[, dot.categ]))){
+                data1 <- data.frame(data1, dot.color = data1[, dot.categ], stringsAsFactors = TRUE)
+                data1$dot.color <- factor(data1$dot.color, labels = dot.color) # do not use labels = unique(dot.color). Otherwise, we can have green1 green2 when dot.color is c("green", "green")
+            }else if(length(dot.color) > 1 & length(dot.color) == length(data1[, dot.categ])){
+                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
+            }else if(length(dot.color)== 1L){ # to deal with single color. Warning: & length(dot.categ.class.order) > 1 removed because otherwise, the data1 is not with dot.color column when length(dot.categ.class.order) == 1
+                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
+            }
+            dot.color <- as.character(unique(data1$dot.color[order(data1[, dot.categ])])) # reorder the dot.color character vector
+            if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color
+                dot.color <- rep(dot.color, length(dot.categ.class.order))
+            }
+            tempo.check <- unique(data1[ , c(dot.categ, "dot.color")])
+            if(length(unique(data1[ , "dot.color"])) > 1 & ( ! (nrow(tempo.check) == length(unique(data1[ , "dot.color"])) & nrow(tempo.check) == length(unique(data1[ , dot.categ]))))){ # length(unique(data1[ , "dot.color"])) > 1 because if only one color, can be attributed to each class of dot.categ
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF dot.categ (", dot.categ, ") COLUMN:\n", paste(unique(mapply(FUN = "paste", data1[ , dot.categ], data1[ ,"dot.color"])), collapse = "\n"))
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }else{
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (", ini.dot.categ, "), THE FOLLOWING COLORS OF DOTS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(data1[, dot.categ]), collapse = " "))
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+            # dot.color is a character string representing the diff classes of dot.categ
+            # data1$dot.color is a factor with order of levels -> dot.categ
+            # end optional legend of dot colors
+        }else{
+            categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
+            if(length(dot.color) == length(levels(data1[, categ[categ.len]]))){ # here length(dot.color) is equal to the different number of categ
+                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+                data1 <- data.frame(data1, dot.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)
+                data1$dot.color <- factor(data1$dot.color, labels = dot.color)
+                if(box.alpha != 0){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
+            }else if(length(dot.color) == length(data1[, categ[categ.len]])){# here length(dot.color) is equal to nrow(data1) -> Modif to have length(dot.color) equal to the different number of categ (length(dot.color) == length(levels(data1[, categ[categ.len]])))
+                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
+            }else if(length(dot.color)== 1L & ! all(dot.color == "same")){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
+                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
+                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
+                dot.color <- rep(dot.color, length(levels(data1[, categ[categ.len]])))
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(dot.color, collapse = " "))
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }else{
+                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(dot.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }
+            # end check the length of color
+            dot.color <- as.character(dot.color)
+            # dot.color is a character string representing the diff classes
+            data1$dot.color <- factor(data1$dot.color, levels = unique(dot.color)) # ok because if dot.color is a character string, the order make class 1, class 2, etc. If dot.color is a column of data1, then levels will be created, without incidence, except if dot.categ specified (see below). unique() because no duplicates allowed
+            # data1$dot.color is a factor with order of levels -> dot.color
+        }
+        # end optional legend of dot colors
+    }else if(is.null(dot.color) & ! (is.null(dot.categ) & is.null(dot.categ.class.order) & is.null(dot.legend.name))){
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") dot.categ OR dot.categ.class.order OR dot.legend.name ARGUMENT HAS BEEN SPECIFIED BUT dot.color ARGUMENT IS NULL (NO DOT PLOTTED)")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
     }
-    if( ! is.null(dot.legend.name)){
-        tempo <- fun_check(data = dot.legend.name, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.legend.name, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    # dot.color either NULL (no dot plotted) or character string (potentially representing the diff classes of dot.categ)
+    # data1$dot.color is either NA or a factor (with order of levels -> depending on dot.categ or categ[length(categ)], or other
+    if(is.null(dot.categ)){
+        dot.categ.class.order <- NULL # because not used anyway
     }
-    tempo <- fun_check(data = dot.tidy, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = dot.tidy.bin.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    if(tempo$problem == FALSE){
-        if(dot.tidy.bin.nb == 0L){ # length and NA checked above
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.tidy.bin.nb ARGUMENT MUST BE A NON-NULL AND POSITVE INTEGER VALUE") # integer possible because dealt above
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
+    # dot.categ.class.order either NULL if dot.categ is NULL (no legend displayed) or character string (potentially representing the diff classes of dot.categ)
+    # end management of dot.color
+    if(is.null(dot.color) & box.fill == FALSE & dot.alpha <= 0.025){
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") THE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.alpha = ", fun_round(dot.alpha, 4), "\n-> POTENTIAL OUTLIER DOTS MIGHT NOT BE VISIBLE BECAUSE ALMOST TRANSPARENT")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
     }
-    tempo <- fun_check(data = dot.jitter, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(dot.seed)){
-        tempo <- fun_check(data = dot.seed, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, neg.values = TRUE, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.seed, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    if(is.null(dot.color) & box.fill == FALSE & dot.border.size == 0){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.border.size = 0 (NO BORDER FOR POTENTIAL OUTLIER DOTS)\n-> THESE SETTINGS ARE NOT ALLOWED BECAUSE THE POTENTIAL OUTLIER DOTS WILL NOT BE VISIBLE")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    tempo <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = dot.alpha, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    # integer dot.border.color into gg_palette
     if( ! is.null(dot.border.color)){
-        tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }else if(tempo1$problem == FALSE & tempo2$problem == TRUE){
-            if( ! all(dot.border.color %in% colors() | grepl(pattern = "^#", dot.border.color), na.rm = TRUE)){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.border.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
-            }
+        tempo <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
+        if(tempo$problem == FALSE){ # convert integers into colors
+            dot.border.color <- fun_gg_palette(max(dot.border.color, na.rm = TRUE))[dot.border.color]
         }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.border.color, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(x.lab)){
-        tempo1 <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = x.lab,  class = "vector", mode = "character", length = 1, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nx.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
+    # end integer dot.border.color into gg_palette
+    # na detection and removal (done now to be sure of the correct length of categ)
+    column.check <- c("categ.color", if( ! is.null(dot.color)){"dot.color"}) # 
+    if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        for(i2 in 1:length(column.check)){
+            if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na
+                tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1")
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn)))
+            }
         }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x.lab, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = x.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
-    if( ! is.null(y.lab)){
-        tempo1 <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = y.lab,  class = "vector", mode = "character", length = 1, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lab ARGUMENT MUST BE A SINGLE CHARACTER STRING OR EXPRESSION")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.lab, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(y.lim)){
-        tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE){
-            if(any(is.infinite(y.lim))){ # normally no NA for is.infinite() output
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
+        tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which)))
+        removed.row.nb <- c(removed.row.nb, tempo)
+        removed.rows <- rbind(removed.rows, data1[tempo, ], stringsAsFactors = FALSE) # here data1 used because categorical columns tested
+        if(length(tempo) != 0){
+            data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
+            data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
+            for(i3 in 1:length(column.check)){
+                if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){
+                    warn.count <- warn.count + 1
+                    tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " "))
+                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+                }
             }
         }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.lim, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(y.tick.nb)){
-        tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE){
-            if(y.tick.nb < 0){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER")
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
+        for(i2 in 1:length(column.check)){
+            if(column.check[i2] == "categ.color"){
+                categ.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
+                if(length(categ.color)== 1L & length(unlist(categ.class.order[length(categ)])) > 1){ # to deal with single color
+                    categ.color <- rep(categ.color, length(unlist(categ.class.order[length(categ)])))
+                }
+                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.color))
             }
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(y.second.tick.nb)){
-        tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE){
-            if(y.second.tick.nb <= 0){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ny.second.tick.nb ARGUMENT MUST BE A NON NULL POSITIVE INTEGER")
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
+            if(column.check[i2] == "dot.color"){
+                dot.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
+                if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color. If dot.categ.class.order == NULL (which is systematically the case if dot.categ == NULL), no rep(dot.color, length(dot.categ.class.order)
+                    dot.color <- rep(dot.color, length(dot.categ.class.order))
+                }
+                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.color))
             }
         }
+    }
+    # end na detection and removal (done now to be sure of the correct length of categ)
+    # From here, data1 and data.ini have no more NA or NaN
+    # end other checkings
+    # reserved word checking
+    #already done above
+    # end reserved word checking
+    # end second round of checking and data preparation
+    
+    
+    # package checking
+    fun_pack(req.package = c(
+        "ggplot2", 
+        "gridExtra", 
+        "lemon", 
+        "scales"
+    ), lib.path = lib.path)
+    # end package checking
+    
+    
+    
+    
+    
+    # main code
+    # y coordinates recovery (create ini.box.coord, dot.coord and modify data1)
+    if(length(categ)== 1L){
+        # width commputations
+        box.width2 <- box.width
+        box.space <- 0 # to inactivate the shrink that add space between grouped boxes, because no grouped boxes here
+        # end width commputations
+        # data1 check categ order for dots coordinates recovery
+        data1 <- data.frame(data1, categ.check = data1[, categ[1]], stringsAsFactors = TRUE)
+        data1$categ.check <- as.integer(data1$categ.check) # to check that data1[, categ[1]] and dot.coord$group are similar, during merging
+        # end data1 check categ order for dots coordinates recovery
+        # per box dots coordinates recovery
+        tempo.gg.name <- "gg.indiv.plot."
+        tempo.gg.count <- 0
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[1]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21))
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[1]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[1]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color}))
+        # end per box dots coordinates recovery
+    }else if(length(categ) == 2L){
+        # width commputations
+        box.width2 <- box.width / length(unique(data1[, categ[length(categ)]])) # real width of each box in x-axis unit, among the set of grouped box. Not relevant if no grouped boxes length(categ)== 1L
+        # end width commputations
+        # data1 check categ order for dots coordinates recovery
+        tempo.factor <- paste0(data1[order(data1[, categ[2]], data1[, categ[1]]), categ[2]], "_", data1[order(data1[, categ[2]], data1[, categ[1]]), categ[1]])
+        data1 <- data.frame(data1[order(data1[, categ[2]], data1[, categ[1]]), ], categ.check = factor(tempo.factor, levels = unique(tempo.factor)), stringsAsFactors = TRUE)
+        data1$categ.check <- as.integer(data1$categ.check)
+        # end data1 check categ order for dots coordinates recovery
+        # per box dots coordinates recovery
+        tempo.gg.name <- "gg.indiv.plot."
+        tempo.gg.count <- 0
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[2]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21))
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[2]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[2]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color}))
+        # end per box dots coordinates recovery
     }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.second.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
     if( ! is.null(stat.pos)){
-        tempo <- fun_check(data = stat.pos, options = c("top", "above"), length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = stat.pos, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+        stat.just <- fun_gg_just(
+            angle = stat.angle, 
+            pos = ifelse(
+                vertical == TRUE, 
+                ifelse(stat.pos == "top", "bottom", "top"), # "bottom" because we want justification for text that are below the ref point which is the top of the graph. The opposite for "above"
+                ifelse(stat.pos == "top", "left", "right") # "left" because we want justification for text that are on the left of the ref point which is the right border of the graph. The opposite for "above"
+            ), 
+            kind = "text"
+        )
     }
-    tempo <- fun_check(data = stat.mean, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = stat.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = stat.dist, class = "vector", mode = "numeric", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = stat.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = vertical, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(legend.width)){
-        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.width, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    # has in fact no interest because ggplot2 does not create room for geom_text()
+    tempo.data.max <- data1[which.max(data1[, y]), ]
+    tempo.data.max <- data.frame(tempo.data.max, label = formatC(tempo.data.max[, y], digit = 2, drop0trailing = TRUE, format = "f"), stringsAsFactors = TRUE)
+    # end has in fact no interest because ggplot2 does not create room for geom_text()
+    tempo.graph.info.ini <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if( ! is.null(stat.pos)){' + ggplot2::geom_text(data = tempo.data.max, mapping = ggplot2::aes_string(x = 1, y = y, label = "label"), size = stat.size, color = "black", angle = stat.angle, hjust = stat.just$hjust, vjust = stat.just$vjust)'})))) # added here to have room for annotation
+    dot.coord <- tempo.graph.info.ini$data[[1]]
+    dot.coord$x <- as.numeric(dot.coord$x) # because weird class
+    dot.coord$PANEL <- as.numeric(dot.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
+    tempo.mean <- aggregate(x = dot.coord$y, by = list(dot.coord$group, dot.coord$PANEL), FUN = mean, na.rm = TRUE)
+    names(tempo.mean)[names(tempo.mean) == "x"] <- "MEAN"
+    names(tempo.mean)[names(tempo.mean) == "Group.1"] <- "BOX"
+    names(tempo.mean)[names(tempo.mean) == "Group.2"] <- "PANEL"
+    dot.coord <- data.frame(
+        dot.coord[order(dot.coord$group, dot.coord$y), ], # dot.coord$PANEL deals below
+        y.check = as.double(data1[order(data1$categ.check, data1[, y]), y]), 
+        categ.check = data1[order(data1$categ.check, data1[, y]), "categ.check"], 
+        dot.color = if(is.null(dot.color)){NA}else{data1[order(data1$categ.check, data1[, y]), "dot.color"]}, 
+        data1[order(data1$categ.check, data1[, y]), ][categ], # avoid the renaming below
+        stringsAsFactors = TRUE
+    ) # y.check to be sure that the order is the same between the y of data1 and the y of dot.coord
+    # names(dot.coord)[names(dot.coord) == "tempo.categ1"] <- categ[1]
+    if( ! is.null(dot.categ)){
+        dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][dot.categ], stringsAsFactors = TRUE) # avoid the renaming
     }
-    tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(add)){
-        tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = add, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    if( ! is.null(facet.categ)){
+        dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][facet.categ], stringsAsFactors = TRUE) # for facet panels
+        tempo.test <- NULL
+        for(i2 in 1:length(facet.categ)){
+            tempo.test <- paste0(tempo.test, ".", formatC(as.numeric(dot.coord[, facet.categ[i2]]), width = nchar(max(as.numeric(dot.coord[, facet.categ[i2]]), na.rm = TRUE)), flag = "0")) # convert factor into numeric with leading zero for proper ranking # merge the formatC() to create a new factor. The convertion to integer should recreate the correct group number. Here as.numeric is used and not as.integer in case of numeric in facet.categ (because comes from add and not checked by fun_check, contrary to categ)
+        }
+        tempo.test <- as.integer(factor(tempo.test))
+        if( ! identical(as.integer(dot.coord$PANEL), tempo.test)){
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nas.integer(dot.coord$PANEL) AND tempo.test MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
     }
-    tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(lib.path)){
-        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE){
-            if( ! all(dir.exists(lib.path), na.rm = TRUE)){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
+    if(dot.tidy == TRUE){
+        if( ! is.null(dot.categ)){
+            dot.coord <- data.frame(dot.coord, tidy_group = data1[order(data1$categ.check, data1[, y]), ][, dot.categ], stringsAsFactors = TRUE) # avoid the renaming
+            # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates
+            if(dot.categ %in% categ){
+                dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE)
+            }else{
+                dot.coord <- data.frame(dot.coord, tidy_group_coord = as.integer(factor(paste0(
+                    formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
+                    ".", 
+                    if(length(categ) == 2L){formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")}, # convert factor into numeric with leading zero for proper ranking
+                    if(length(categ) == 2L){"."}, 
+                    formatC(as.integer(dot.coord[, dot.categ]), width = nchar(max(as.integer(dot.coord[, dot.categ]), na.rm = TRUE)), flag = "0") # convert factor into numeric with leading zero for proper ranking
+                )), stringsAsFactors = TRUE) # merge the 2 or 3 formatC() to create a new factor. The convertion to integer should recreate the correct group number
+                ) # for tidy dot plots
             }
+        }else{
+            dot.coord <- data.frame(dot.coord, tidy_group = if(length(categ)== 1L){
+                dot.coord[, categ]}else{as.integer(factor(paste0(
+                    formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
+                    ".", 
+                    formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
+                )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
+                }) # for tidy dot plots
+            # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates
+            dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE)
         }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = lib.path, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if(any(arg.check) == TRUE){ # normally no NA
-        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between == #
     }
-    # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check()
-    # end argument primary checking
-    # second round of checking and data preparation
-    # management of NA arguments
-    tempo.arg <- names(arg.user.setting) # values provided by the user
-    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length) == 1L # no argument provided by the user can be just NA
-    if(any(tempo.log) == TRUE){ # normally no NA because is.na() used here
-        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # end management of NA arguments
-    # management of NULL arguments
-    tempo.arg <-c(
-        "data1", 
-        "y", 
-        "categ", 
-        "box.fill", 
-        "box.width", 
-        "box.space", 
-        "box.line.size", 
-        "box.notch", 
-        "box.alpha", 
-        "box.mean", 
-        "box.whisker.kind", 
-        "box.whisker.width", 
-        # "dot.color", # inactivated because can be null
-        "dot.tidy", 
-        "dot.tidy.bin.nb", 
-        "dot.jitter", 
-        # "dot.seed", # inactivated because can be null
-        "dot.size", 
-        "dot.alpha", 
-        "dot.border.size", 
-        "x.angle", 
-        "y.log", 
-        # "y.second.tick.nb", # inactivated because can be null
-        "y.include.zero", 
-        "y.top.extra.margin", 
-        "y.bottom.extra.margin", 
-        # "stat.pos", # inactivated because can be null
-        "stat.mean", 
-        "stat.size", 
-        "stat.dist", 
-        "stat.angle", 
-        "vertical", 
-        "text.size", 
-        "title", 
-        "title.text.size", 
-        "legend.show", 
-        # "legend.width", # inactivated because can be null
-        "article", 
-        "grid", 
-        "return", 
-        "return.ggplot", 
-        "return.gtable", 
-        "plot", 
-        "warn.print"
-    )
-    tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null)
-    if(any(tempo.log) == TRUE){# normally no NA with is.null()
-        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # end management of NULL arguments
-    # code that protects set.seed() in the global environment
-    # see also Protocol 100-rev0 Parallelization in R.docx
-    if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment
-        tempo.random.seed <- .Random.seed
-        on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv))
-    }else{
-        on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness
-    }
-    set.seed(dot.seed)
-    # end code that protects set.seed() in the global environment
-    # warning initiation
-    ini.warning.length <- options()$warning.length
-    options(warning.length = 8170)
-    warn <- NULL
-    warn.count <- 0
-    # end warning initiation
-    # other checkings
-    if(any(duplicated(names(data1)), na.rm = TRUE)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " "))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    if( ! (y %in% names(data1))){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE A COLUMN NAME OF data1")
+    if( ! (identical(dot.coord$y, dot.coord$y.check) & identical(dot.coord$group, dot.coord$categ.check))){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(dot.coord$y AND dot.coord$y.check) AS WELL AS (dot.coord$group AND dot.coord$categ.check) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
         stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }else{
-        tempo <- fun_check(data = data1[, y], data.name = "y COLUMN OF data1", class = "vector", mode = "numeric", na.contain = TRUE, fun.name = function.name)
-        if(tempo$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ny ARGUMENT MUST BE NUMERIC COLUMN IN data1")
+        if( ! identical(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ]$BOX, unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c("group", "PANEL")])$group)){
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(tempo.mean$BOX, tempo.mean$PANEL) AND (dot.coord$group, dot.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
             stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else{
+            tempo <- unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}), drop = FALSE])
+            # names(tempo) <- paste0(names(tempo), ".mean")
+            tempo.mean <- data.frame(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ], tempo, stringsAsFactors = TRUE)
         }
     }
-    if(length(categ) > 2){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT CANNOT HAVE MORE THAN 2 COLUMN NAMES OF data1")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }else if( ! all(categ %in% names(data1))){ # all() without na.rm -> ok because categ cannot be NA (tested above)
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(categ, collapse = " "))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    if(length(dot.categ) > 1){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT HAVE MORE THAN 1 COLUMN NAMES OF data1")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }else if( ! all(dot.categ %in% names(data1))){ # all() without na.rm -> ok because dot.categ cannot be NA (tested above)
-        tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE COLUMN NAMES OF data1. HERE IT IS:\n", paste(dot.categ, collapse = " "))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # reserved word checking
-    if(any(names(data1) %in% reserved.words, na.rm = TRUE)){
-        if(any(duplicated(names(data1)), na.rm = TRUE)){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nDUPLICATED COLUMN NAMES OF data1 ARGUMENT NOT ALLOWED:\n", paste(names(data1)[duplicated(names(data1))], collapse = " "))
+    # at that stage, categ color and dot color are correctly attributed in data1, box.coord and dot.coord
+    # end y dot coordinates recovery (create ini.box.coord, dot.coord and modify data1)
+    # ylim range
+    if(is.null(y.lim)){
+        y.lim <- tempo.graph.info.ini$layout$panel_params[[1]]$y.range # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only
+        if(any(( ! is.finite(y.lim)) | is.na(y.lim)) | length(y.lim) != 2){ # kept but normally no more Inf in data1 # normally no NA with is.finite, etc.
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntempo.graph.info.ini$layout$panel_params[[1]]$y.range[1] CONTAINS NA OR Inf OR HAS LENGTH 1")
             stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
-        if( ! is.null(dot.categ)){
-            if(dot.categ %in% categ){
-                reserved.words <- c(reserved.words, paste0(dot.categ, "_DOT")) # paste0(dot.categ, "_DOT") is added to the reserved words because in such situation, a new column will be added to data1 that is named paste0(dot.categ, "_DOT")
-            }
-        }
-        tempo.output <- fun_name_change(names(data1), reserved.words)
-        for(i2 in 1:length(tempo.output$ini)){ # a loop to be sure to take the good ones
-            names(data1)[names(data1) == tempo.output$ini[i2]] <- tempo.output$post[i2]
-            if(any(y == tempo.output$ini[i2])){ # any() without na.rm -> ok because y cannot be NA (tested above)
-                y[y == tempo.output$ini[i2]] <- tempo.output$post[i2]
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") IN y ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN y ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-            # WARNING: names of y argument potentially replaced
-            if(any(categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because categ cannot be NA (tested above)
-                categ[categ == tempo.output$ini[i2]] <- tempo.output$post[i2]
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") IN categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-            # WARNING: names of categ argument potentially replaced
-            if( ! is.null(dot.categ)){
-                if(any(dot.categ == tempo.output$ini[i2])){ # any() without na.rm -> ok because dot.categ cannot be NA (tested above)
-                    dot.categ[dot.categ == tempo.output$ini[i2]] <- tempo.output$post[i2]
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (COLUMN NAMES OF data1 ARGUMENT),\n", tempo.output$ini[i2], " HAS BEEN REPLACED BY ", tempo.output$post[i2], "\nBECAUSE RISK OF BUG AS SOME NAMES IN dot.categ ARGUMENT ARE RESERVED WORD USED BY THE ", function.name, " FUNCTION")
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
-            }
-            # WARNING: names of dot.categ argument potentially replaced
-        }
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") REGARDING COLUMN NAMES REPLACEMENT, THE NAMES\n", paste(tempo.output$ini, collapse = " "), "\nHAVE BEEN REPLACED BY\n", paste(tempo.output$post, collapse = " "))
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        if( ! (is.null(add) | is.null(tempo.output$ini))){
-            if(grepl(x = add, pattern = paste(tempo.output$ini, collapse = "|"))){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF COLUMN NAMES OF data1 IN THE add ARGUMENT STRING, THAT CORRESPOND TO RESERVED STRINGS FOR ", function.name, "\nCOLUMN NAMES HAVE TO BE CHANGED\nTHE PROBLEMATIC COLUMN NAMES ARE SOME OF THESE NAMES:\n", paste(tempo.output$ini, collapse = " "), "\nIN THE DATA FRAME OF data1 AND IN THE STRING OF add ARGUMENT, TRY TO REPLACE NAMES BY:\n", paste(tempo.output$post, collapse = " "), "\n\nFOR INFORMATION, THE RESERVED WORDS ARE:\n", paste(reserved.words, collapse = "\n"))
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-        }
-    }
-    if( ! (is.null(add))){
-        if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name)
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        }
+    }else if(y.log != "no"){
+        y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope
     }
-    # end reserved word checking
-    # verif of add
-    if( ! is.null(add)){
-        if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by +
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ #
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by )
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " "))
+    if(y.log != "no"){
+        # normally this control is not necessary anymore
+        if(any( ! is.finite(y.lim))){ # normally no NA with is.finite
+            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)")
             stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
     }
-    # end verif of add
-    # management of add containing facet
-    facet.categ <- NULL
-    if( ! is.null(add)){
-        facet.check <- TRUE
-        tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) #
-        tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap")
-        tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid")
-        tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep")
-        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){
-            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")])))
-            facet.categ <- names(tempo1$params$facets)
-            tempo.text <- "facet_wrap OR facet_rep_wrap"
-            facet.check <- FALSE
-        }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){
-            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")])))
-            facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols))
-            tempo.text <- "facet_grid OR facet_rep_grid"
-            facet.check <- FALSE
-        }
-        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above)
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
+    if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # all() without na.rm -> ok because y.lim cannot be NA (tested above)
+        # normally this control is not necessary anymore
+        tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS Inf VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    # end management of add containing facet
-    # conversion of categ columns in data1 into factors
-    for(i1 in 1:length(categ)){
-        tempo1 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = data1[, categ[i1]], data.name = paste0("categ NUMBER ", i1, " OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\n", paste0("categ NUMBER ", i1, " OF data1"), " MUST BE A FACTOR OR CHARACTER VECTOR")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if(tempo1$problem == FALSE){ # character vector
-            if(box.alpha != 0){
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") IN categ NUMBER ", i1, " IN data1, THE CHARACTER COLUMN HAS BEEN CONVERTED TO FACTOR, WITH LEVELS ACCORDING TO THE ALPHABETICAL ORDER")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-        }
-        data1[, categ[i1]] <- factor(data1[, categ[i1]]) # if already a factor, change nothing, if characters, levels according to alphabetical order
+    if(suppressWarnings(any(is.na(y.lim)))){ # normally no NA with is.na
+        # normally this control is not necessary anymore
+        tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS NA OR NaN VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    # OK: all the categ columns of data1 are factors from here
-    # end conversion of categ columns in data1 into factors
+    y.lim.order <- order(y.lim) # to deal with inverse axis
+    y.lim <- sort(y.lim)
+    y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis
+    y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis
+    if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before
+        y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only
+    }
+    y.lim <- y.lim[y.lim.order]
+    if(any(is.na(y.lim))){ # normally no NA with is.na
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # end ylim range
     
     
     
-    # management of log scale and Inf removal
-    if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf  # normally no NA with is.finite0() and is.na()
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") PRESENCE OF -Inf OR Inf VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-    }
-    data1.ini <- data1 # strictly identical to data1 except that in data1 y is log converted if and only if y.log != "no"
-    if(y.log != "no"){
-        tempo1 <- ! is.finite(data1[, y]) # where are initial NA and Inf
-        data1[, y] <- suppressWarnings(get(y.log)(data1[, y]))# no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope
-        if(any( ! (tempo1 | is.finite(data1[, y])))){ # normally no NA with is.finite
+    
+    
+    
+    # drawing
+    # constant part
+    tempo.gg.name <- "gg.indiv.plot."
+    tempo.gg.count <- 0
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add is directly put here to deal with additional variable of data, like when using facet_grid. No problem if add is a theme, will be dealt below
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){categ[1]}else{x.lab}))
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y}else{y.lab}))
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title))
+    # text angle management
+    axis.just <- fun_gg_just(angle = x.angle, pos = ifelse(vertical == TRUE, "bottom", "left"), kind = "axis")
+    # end text angle management
+    add.check <- TRUE
+    if( ! is.null(add)){ # if add is NULL, then = 0
+        if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") LOG CONVERSION INTRODUCED -Inf OR Inf OR NaN VALUES IN THE ", y, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+            tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED")
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            add.check <- FALSE
         }
     }
-    # Inf removal
-    if(any(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))){ # is.finite also detects NA: ( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])) detects only Inf # normally no NA with is.finite
-        removed.row.nb <- which(( ! is.finite(data1[, y])) & ( ! is.na(data1[, y])))
-        removed.rows <- data1.ini[removed.row.nb, ] # here data1.ini used to have the y = O rows that will be removed because of Inf creation after log transformation
-        data1 <- data1[-removed.row.nb, ] #
-        data1.ini <- data1.ini[-removed.row.nb, ] #
-    }else{
-        removed.row.nb <- NULL
-        removed.rows <- data.frame(stringsAsFactors = FALSE)
-    }
-    # From here, data1 and data.ini have no more Inf
-    # end Inf removal
-    if(y.log != "no" & ! is.null(y.lim)){
-        if(any(y.lim <= 0)){ # any() without na.rm -> ok because y.lim cannot be NA (tested above)
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else if(any( ! is.finite(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}))){ # normally no NA with is.finite
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT RETURNS INF/NA WITH THE y.log ARGUMENT SET TO ", y.log, "\nAS SCALE COMPUTATION IS ", ifelse(y.log == "log10", "log10", "log2"), ":\n", paste(if(y.log == "log10"){log10(y.lim)}else{log2(y.lim)}, collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
-    }
-    if(y.log != "no" & y.include.zero == TRUE){
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") y.log ARGUMENT SET TO ", y.log, " AND y.include.zero ARGUMENT SET TO TRUE -> y.include.zero ARGUMENT RESET TO FALSE BECAUSE 0 VALUE CANNOT BE REPRESENTED IN LOG SCALE")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        y.include.zero <- FALSE
-    }
-    if(y.log != "no" & vertical == FALSE){
-        vertical <- TRUE
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") BECAUSE OF A BUG IN ggplot2, CANNOT FLIP BOXES HORIZONTALLY WITH A Y.LOG SCALE -> vertical ARGUMENT RESET TO TRUE")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-    }
-    # end management of log scale and Inf removal
-    # na detection and removal (done now to be sure of the correct length of categ)
-    column.check <- unique(c(y, categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){dot.categ}, if( ! is.null(facet.categ)){facet.categ})) # dot.categ because can be a 3rd column of data1, categ.color and dot.color will be tested later
-    if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        for(i2 in 1:length(column.check)){
-            if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na
-                tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn)))
-            }
-        }
-        tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which)))
-        removed.row.nb <- c(removed.row.nb, tempo) # removed.row.nb created to remove Inf
-        removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # here data1.ini used to have the non NA rows that will be removed because of NAN creation after log transformation (neg values for instance)
-        column.check <- column.check[ ! column.check == y] # remove y to keep quali columns
-        if(length(tempo) != 0){
-            data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
-            data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers than the former
-            for(i3 in 1:length(column.check)){
-                if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
-            }
-        }
-        count.categ <- 0
-        for(i2 in 1:length(column.check)){
-            if(column.check[i2] %in% categ){
-                count.categ <- count.categ + 1
-            }
-            if(column.check[i2] == categ[count.categ]){
-                categ.class.order[count.categ] <- list(levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])]) # remove the absent color in the character vector
-                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.class.order[[count.categ]]))
-            }
-            if( ! is.null(dot.color) & ! is.null(dot.categ)){ # reminder : dot.categ cannot be a column name of categ anymore (because in that case dot.categ name is changed into "..._DOT"
-                if(column.check[i2] == dot.categ){
-                    dot.categ.class.order <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
-                    data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.categ.class.order))
-                }
-            }
-            if(column.check[i2] %in% facet.categ){ # works if facet.categ == NULL this method should keep the order of levels when removing some levels
-                tempo.levels <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(as.character(data1[, column.check[i2]]))]
-                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = tempo.levels)
-            }
+    if(add.check == TRUE & article == TRUE){
+        # WARNING: not possible to add theme()several times. NO message but the last one overwrites the others
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size))
+        if(grid == TRUE){
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
+                text = ggplot2::element_text(size = text.size), 
+                plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
+                line = ggplot2::element_line(size = 0.5), 
+                legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
+                axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis
+                axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis
+                panel.grid.major.x = if(vertical == TRUE){NULL}else{ggplot2::element_line(colour = "grey85", size = 0.75)},
+                panel.grid.major.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey85", size = 0.75)}else{NULL},
+                panel.grid.minor.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey90", size = 0.25)}else{NULL},
+                axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
+                axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)},
+                strip.background = ggplot2::element_rect(fill = NA, colour = NA) # for facet background
+            ))
+        }else{
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
+                text = ggplot2::element_text(size = text.size), 
+                plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
+                line = ggplot2::element_line(size = 0.5), 
+                legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
+                axis.line.y.left = ggplot2::element_line(colour = "black"), 
+                axis.line.x.bottom = ggplot2::element_line(colour = "black"),
+                axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
+                axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)},
+                strip.background = ggplot2::element_rect(fill = NA, colour = NA)
+            ))
         }
+    }else if(add.check == TRUE & article == FALSE){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
+            text = ggplot2::element_text(size = text.size), 
+            plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
+            line = ggplot2::element_line(size = 0.5), 
+            legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
+            panel.background = ggplot2::element_rect(fill = "grey95"), 
+            axis.line.y.left = ggplot2::element_line(colour = "black"), 
+            axis.line.x.bottom = ggplot2::element_line(colour = "black"), 
+            panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), 
+            panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), 
+            panel.grid.minor.x = ggplot2::element_blank(), 
+            panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), 
+            strip.background = ggplot2::element_rect(fill = NA, colour = NA),
+            axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
+            axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}
+        ))
     }
-    # end na detection and removal (done now to be sure of the correct length of categ)
-    # From here, data1 and data.ini have no more NA or NaN in y, categ, dot.categ (if dot.color != NULL) and facet.categ
+    # Contrary to fun_gg_bar(), cannot plot the boxplot right now, because I need the dots plotted first
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, group = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), color = NA, width = box.width, fill = NA)) # this is to set the graph (i.e., a blanck boxplot to be able to use x coordinates to plot dots before boxes)
+    # end constant part
     
     
     
-    if( ! is.null(categ.class.order)){
-        if(length(categ.class.order) != length(categ)){
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.class.order ARGUMENT MUST BE A LIST OF LENGTH EQUAL TO LENGTH OF categ\nHERE IT IS LENGTH: ", length(categ.class.order), " VERSUS ", length(categ))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else{
-            for(i3 in 1:length(categ.class.order)){
-                if(is.null(categ.class.order[[i3]])){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") THE categ.class.order COMPARTMENT ", i3, " IS NULL. ALPHABETICAL ORDER WILL BE APPLIED")
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                    data1[, categ[i3]] <- factor(as.character(data1[, categ[i3]])) # if already a factor, change nothing, if characters, levels according to alphabetical order
-                    categ.class.order[[i3]] <- levels(data1[, categ[i3]]) # character vector that will be used later
-                }else{
-                    tempo <- fun_check(data = categ.class.order[[i3]], data.name = paste0("COMPARTMENT ", i3 , " OF categ.class.order ARGUMENT"), class = "vector", mode = "character", length = length(levels(data1[, categ[i3]])), fun.name = function.name) # length(data1[, categ[i1]) -> if data1[, categ[i1] was initially character vector, then conversion as factor after the NA removal, thus class number ok. If data1[, categ[i1] was initially factor, no modification after the NA removal, thus class number ok
-                    if(tempo$problem == TRUE){
-                        stop(paste0("\n\n================\n\n", tempo$text, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-                    }
-                }
-                if(any(duplicated(categ.class.order[[i3]]), na.rm = TRUE)){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(categ.class.order[[i3]], collapse = " "))
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }else if( ! (all(categ.class.order[[i3]] %in% unique(data1[, categ[i3]]), na.rm = TRUE) & all(unique(data1[, categ[i3]]) %in% categ.class.order[[i3]], na.rm = TRUE))){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\nCOMPARTMENT ", i3, " OF categ.class.order ARGUMENT MUST BE CLASSES OF ELEMENT ", i3, " OF categ ARGUMENT\nHERE IT IS:\n", paste(categ.class.order[[i3]], collapse = " "), "\nFOR COMPARTMENT ", i3, " OF categ.class.order AND IT IS:\n", paste(unique(data1[, categ[i3]]), collapse = " "), "\nFOR COLUMN ", categ[i3], " OF data1")
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }else{
-                    data1[, categ[i3]] <- factor(data1[, categ[i3]], levels = categ.class.order[[i3]]) # reorder the factor
-                    
-                }
-                names(categ.class.order)[i3] <- categ[i3]
-            }
-        }
+    
+    # graphic info recovery (including means)
+    tempo.graph.info <- ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}) + ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color})')))) # will be recovered later again, when ylim will be considered
+    tempo.yx.ratio <- (tempo.graph.info$layout$panel_params[[1]]$y.range[2] - tempo.graph.info$layout$panel_params[[1]]$y.range[1]) / (tempo.graph.info$layout$panel_params[[1]]$x.range[2] - tempo.graph.info$layout$panel_params[[1]]$x.range[1])
+    box.coord <- tempo.graph.info$data[[2]] # to have the summary statistics of the plot. Contrary to ini.box.plot, now integrates ylim Here because can be required for stat.pos when just box are plotted
+    box.coord$x <- as.numeric(box.coord$x) # because x is of special class that block comparison of values using identical
+    box.coord$PANEL <- as.numeric(box.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
+    box.coord <- box.coord[order(box.coord$group, box.coord$PANEL), ]
+    if( ! (identical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL))){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL) DO NOT HAVE THE SAME VALUE ORDER")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }else{
-        categ.class.order <- vector("list", length = length(categ))
-        tempo.categ.class.order <- NULL
-        for(i2 in 1:length(categ.class.order)){
-            categ.class.order[[i2]] <- levels(data1[, categ[i2]])
-            names(categ.class.order)[i2] <- categ[i2]
-            tempo.categ.class.order <- c(tempo.categ.class.order, ifelse(i2 != 1, "\n", ""), categ.class.order[[i2]])
-        }
-        if(box.alpha != 0){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") THE categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR BOX ORDERING:\n", paste(tempo.categ.class.order, collapse = " "))
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        # tempo <- c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ})
+        if(any(names(tempo.mean) %in% names(box.coord), na.rm = TRUE)){
+            names(tempo.mean)[names(tempo.mean) %in% names(box.coord)] <- paste0(names(tempo.mean)[names(tempo.mean) %in% names(box.coord)], ".mean")
         }
+        box.coord <- data.frame(box.coord, tempo.mean, stringsAsFactors = TRUE)
     }
-    # categ.class.order not NULL anymore (list)
-    if(is.null(box.legend.name) & box.alpha != 0){
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") THE box.legend.name SETTING IS NULL. NAMES OF categ WILL BE USED: ", paste(categ, collapse = " "))
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        box.legend.name <- categ[length(categ)] # if only categ1, then legend name of categ1, if length(categ) == 2L, then legend name of categ2
+    # end graphic info recovery (including means)
+    
+    
+    
+    # stat output (will also serve for boxplot and mean display)
+    # x not added now (to do not have them in stat.nolog)
+    stat <- data.frame(
+        MIN = box.coord$ymin_final, 
+        QUART1 = box.coord$lower, 
+        MEDIAN = box.coord$middle, 
+        MEAN = box.coord$MEAN, 
+        QUART3 = box.coord$upper, 
+        MAX = box.coord$ymax_final, 
+        WHISK_INF = box.coord$ymin, 
+        BOX_INF = box.coord$lower, 
+        NOTCH_INF = box.coord$notchlower, 
+        NOTCH_SUP = box.coord$notchupper, 
+        BOX_SUP = box.coord$upper, 
+        WHISK_SUP = box.coord$ymax, 
+        OUTLIERS = box.coord["outliers"], 
+        tempo.mean[colnames(tempo.mean) != "MEAN"], 
+        COLOR = box.coord$fill, 
+        stringsAsFactors = TRUE
+    ) # box.coord["outliers"] written like this because it is a list. X coordinates not put now because several features to set
+    names(stat)[names(stat) == "outliers"] <- "OUTLIERS"
+    stat.nolog <- stat # stat.nolog ini will serve for outputs
+    if(y.log != "no"){
+        stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")] <- ifelse(y.log == "log2", 2, 10)^(stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")])
+        stat.nolog$OUTLIERS <- lapply(stat.nolog$OUTLIERS, FUN = function(X){ifelse(y.log == "log2", 2, 10)^X})
     }
-    # box.legend.name not NULL anymore (character string)
-    # management of categ.color
-    if( ! is.null(categ.color)){
-        # check the nature of color
-        # integer colors into gg_palette
-        tempo.check.color <- fun_check(data = categ.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem
-        if(tempo.check.color == FALSE){
-            # convert integers into colors
-            categ.color <- fun_gg_palette(max(categ.color, na.rm = TRUE))[categ.color]
-        }
-        # end integer colors into gg_palette
-        if( ! (all(categ.color %in% colors() | grepl(pattern = "^#", categ.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because categ.color cannot be NA (tested above)
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE A HEXADECIMAL COLOR VECTOR STARTING BY # AND/OR COLOR NAMES GIVEN BY colors() OR A COLUMN NAME OF THE data1 PARAMETER: ", paste(unique(categ.color), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
-        if(any(is.na(categ.color)) & box.alpha != 0){ # normally no NA with is.na
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT CONTAINS NA")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        }
-        # end check the nature of color
-        # check the length of color
-        categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
-        if(length(data1[, categ[categ.len]]) == length(levels(data1[, categ[categ.len]])) & length(categ.color) == length(data1[, categ[categ.len]])){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", categ[categ.len], " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE categ.color ARGUMENT ARE ALL EQUAL. BOX COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", categ[categ.len], ", NOT ACCORDING TO THE ROWS OF ", categ[categ.len])
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        }
-        if(length(categ.color) == length(levels(data1[, categ[categ.len]]))){ # here length(categ.color) is equal to the different number of categ
-            # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-            data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)  # no need stringsAsFactors here for stat.nolog as factors remain factors
-            data1$categ.color <- factor(data1$categ.color, labels = categ.color) # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]])
-            if(box.alpha != 0){
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(categ.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "))
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-        }else if(length(categ.color) == length(data1[, categ[categ.len]])){# here length(categ.color) is equal to nrow(data1) -> Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]])))
-            data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE)
-            tempo.check <- unique(data1[ , c(categ[categ.len], "categ.color")])
-            if( ! (nrow(tempo.check) == length(unique(categ.color)) & nrow(tempo.check) == length(unique(data1[ , categ[categ.len]])))){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nBUT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], ":\n", paste(unique(mapply(FUN = "paste", data1[ ,categ[categ.len]], data1[ ,"categ.color"])), collapse = "\n"))
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }else{
-                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-                categ.color <- unique(data1$categ.color[order(data1[, categ[categ.len]])]) # Modif to have length(categ.color) equal to the different number of categ (length(categ.color) == length(levels(data1[, categ[categ.len]])))
-                if(box.alpha != 0){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS THE LENGTH OF data1 ROW NUMBER\nCOLORS HAVE BEEN RESPECTIVELY ASSOCIATED TO EACH CLASS OF categ ", categ[categ.len], " AS:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\n", paste(categ.color, collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
-            }
-        }else if(length(categ.color) == 1L){
-            # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-            data1 <- data.frame(data1, categ.color = categ.color, stringsAsFactors = TRUE)
-            categ.color <- rep(categ.color, length(levels(data1[, categ[categ.len]])))
-            if(box.alpha != 0){
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") categ.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(categ.color, collapse = " "))
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-        }else{
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(categ.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
+    # end stat output (will also serve for boxplot and mean display)
+    
+    
+    
+    
+    
+    
+    # x coordinates management (for random plotting and for stat display)
+    # width commputations
+    width.ini <- c(box.coord$xmax - box.coord$xmin)[1] # all the box widths are equal here. Only the first one taken
+    width.correct <- width.ini * box.space / 2
+    if( ! (identical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL))){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
     }else{
-        categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
-        # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-        categ.color <- fun_gg_palette(length(levels(data1[, categ[categ.len]])))
-        data1 <- data.frame(data1, categ.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)
-        data1$categ.color <- factor(data1$categ.color, labels = categ.color)  # replace the characters of data1[, categ[categ.len]] put in the categ.color column by the categ.color (can be write like this because categ.color is length of levels of data1[, categ[categ.len]])
-        if(box.alpha != 0){
+        stat <- data.frame(
+            stat, 
+            X = box.coord$x, 
+            X_BOX_INF = box.coord$xmin + width.correct, 
+            X_BOX_SUP = box.coord$xmax - width.correct, 
+            X_NOTCH_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) / 2, 
+            X_NOTCH_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) / 2, 
+            X_WHISK_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, 
+            X_WHISK_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, 
+            # tempo.mean[colnames(tempo.mean) != "MEAN"], # already added above
+            stringsAsFactors = TRUE
+        )
+        stat$COLOR <- factor(stat$COLOR, levels = unique(categ.color))
+        if( ! all(stat$NOTCH_SUP < stat$BOX_SUP & stat$NOTCH_INF > stat$BOX_INF, na.rm = TRUE) & box.notch == TRUE){
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") NULL categ.color ARGUMENT -> COLORS RESPECTIVELY ATTRIBUTED TO EACH CLASS OF ", categ[categ.len], " IN data1:\n", paste(categ.color, collapse = " "), "\n", paste(levels(data1[, categ[categ.len]]), collapse = " "))
+            tempo.warn <- paste0("(", warn.count,") SOME NOTCHES ARE BEYOND BOX HINGES. TRY ARGUMENT box.notch = FALSE")
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    # categ.color not NULL anymore
-    categ.color <- as.character(categ.color)
-    # categ.color is a character string representing the diff classes
-    data1$categ.color <- factor(data1$categ.color, levels = unique(categ.color)) # ok because if categ.color is a character string, the order make class 1, class 2, etc. unique() because no duplicates allowed
-    # data1$categ.color is a factor with order of levels -> categ.color
-    # end management of categ.color
-    # management of dot.color
+    dot.jitter <- c((box.coord$xmax - width.correct) - (box.coord$xmin + width.correct))[1] * dot.jitter # real dot.jitter. (box.coord$xmin + width.correct) - (box.coord$xmax - width.correct))[1] is the width of the box. Is equivalent to (box.coord$x - (box.coord$xmin + width.correct))[1] * 2
+    # end width commputations
     if( ! is.null(dot.color)){
-        # optional legend of dot colors
-        if( ! is.null(dot.categ)){
-            ini.dot.categ <- dot.categ
-            if( ! dot.categ %in% names(data1)){ # no need to use all() because length(dot.categ) = 1
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT MUST BE A COLUMN NAME OF data1. HERE IT IS:\n", dot.categ)
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }else if(dot.categ %in% categ){ # no need to use all() because length(dot.categ) = 1. Do not use dot.categ %in% categ[length(categ)] -> error
-                # management of dot legend if dot.categ %in% categ (because legends with the same name are joined in ggplot2) 
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") THE COLUMN NAME OF data1 INDICATED IN THE dot.categ ARGUMENT (", dot.categ, ") HAS BEEN REPLACED BY ", paste0(dot.categ, "_DOT"), " TO AVOID MERGED LEGEND BY GGPLOT2")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                data1 <- data.frame(data1, dot.categ = data1[, dot.categ], stringsAsFactors = TRUE) # dot.categ is not a column name of data1 (checked above with reserved words)
-                dot.categ <- paste0(dot.categ, "_DOT")
-                names(data1)[names(data1) == "dot.categ"] <- dot.categ # paste0(dot.categ, "_DOT") is not a column name of data1 (checked above with reserved words)
-                # tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ ARGUMENT CANNOT BE A COLUMN NAME OF data1 ALREADY SPECIFIED IN THE categ ARGUMENT:\n", dot.categ, "\nINDEED, dot.categ ARGUMENT IS MADE TO HAVE MULTIPLE DOT COLORS NOT RELATED TO THE BOXPLOT CATEGORIES")
-                # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            tempo1 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-            tempo2 <- fun_check(data = data1[, dot.categ], data.name = paste0(dot.categ, " COLUMN OF data1"), class = "factor", na.contain = TRUE, fun.name = function.name)
-            if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ COLUMN MUST BE A FACTOR OR CHARACTER VECTOR") #
+        # random dots
+        if(dot.tidy == FALSE){
+            dot.coord.rd1 <- merge(dot.coord, box.coord[c("fill", "PANEL", "group", "x")], by = c("PANEL", "group"), sort = FALSE) # rd for random. Send the coord of the boxes into the coord data.frame of the dots (in the column x.y). WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column
+            if(nrow(dot.coord.rd1) != nrow(dot.coord)){
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd1 DATA FRAME. CODE HAS TO BE MODIFIED")
                 stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
             }
-            data1[, dot.categ] <- factor(data1[, dot.categ]) # if already a factor, change nothing, if characters, levels according to alphabetical order
-            # dot.categ column of data1 is factor from here
-            if( ! is.null(dot.categ.class.order)){
-                if(any(duplicated(dot.categ.class.order), na.rm = TRUE)){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT CANNOT HAVE DUPLICATED CLASSES: ", paste(dot.categ.class.order, collapse = " "))
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }else if( ! (all(dot.categ.class.order %in% levels(data1[, dot.categ])) & all(levels(data1[, dot.categ]) %in% dot.categ.class.order, na.rm = TRUE))){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.categ.class.order ARGUMENT MUST BE CLASSES OF dot.categ ARGUMENT\nHERE IT IS:\n", paste(dot.categ.class.order, collapse = " "), "\nFOR dot.categ.class.order AND IT IS:\n", paste(levels(data1[, dot.categ]), collapse = " "), "\nFOR dot.categ COLUMN (", ini.dot.categ, ") OF data1")
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }else{
-                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
-                }
+            sampled.dot.jitter <- if(nrow(dot.coord.rd1)== 1L){runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2)}else{sample(x = runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2), size = nrow(dot.coord.rd1), replace = FALSE)}
+            dot.coord.rd2 <- data.frame(dot.coord.rd1, dot.x = dot.coord.rd1$x.y + sampled.dot.jitter, stringsAsFactors = TRUE) # set the dot.jitter thanks to runif and dot.jitter range. Then, send the coord of the boxes into the coord data.frame of the dots (in the column x.y)
+            if(length(categ)== 1L){
+                tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor
+                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
+                verif <- paste0(categ[1], ".check")
+            }else if(length(categ) == 2L){
+                tempo.data1 <- unique(
+                    data.frame(
+                        data1[c(categ[1], categ[2])], 
+                        group = as.integer(factor(paste0(
+                            formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
+                            ".", 
+                            formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
+                        )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
+                    )
+                ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis
+                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
+                names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check")
+                verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check"))
             }else{
-                if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
-                    dot.categ.class.order <- unlist(categ.class.order[length(categ)])
-                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL AND dot.color IS \"same\". ORDER OF categ.class.order WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }else{
-                    dot.categ.class.order <- sort(levels(data1[, dot.categ]))
-                    data1[, dot.categ] <- factor(data1[, dot.categ], levels = dot.categ.class.order) # reorder the factor
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order SETTING IS NULL. ALPHABETICAL ORDER WILL BE APPLIED FOR LEGEND DISPLAY: ", paste(dot.categ.class.order, collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
             }
-            # dot.categ.class.order not NULL anymore (character string) if dot.categ is not NULL
-            if(all(dot.color == "same") & length(dot.color)== 1L){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
-                if( ! identical(ini.dot.categ, categ[length(categ)])){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\", THE COLUMN NAME IN dot.categ ARGUMENT MUST BE IDENTICAL TO THE LAST COLUMN NAME IN categ ARGUMENT. HERE IT IS:\ndot.categ: ", paste(ini.dot.categ, collapse = " "), "\ncateg: ", paste(categ, collapse = " "))
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }else if( ! fun_comp_1d(unlist(categ.class.order[length(categ)]), dot.categ.class.order)$identical.content){
-                    tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN dot.color ARGUMENT IS \"same\",\nLAST COMPARTMENT OF categ.class.order ARGUMENT AND dot.categ.class.order ARGUMENT CANNOT BE DIFFERENT:\nLAST COMPARTMENT OF categ.class.order: ", paste(unlist(categ.class.order[length(categ)]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "))
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }
+            dot.coord.rd3 <- merge(dot.coord.rd2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have replaced by = "group" by intersect("group", "group") because of an error due to wrong group group merging in dot.coord.rd3
+            if(nrow(dot.coord.rd3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.rd3[categ], dot.coord.rd3[verif])$identical.content)){
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd3 DATA FRAME. CODE HAS TO BE MODIFIED")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
             }
-            for(i3 in 1:length(categ)){
-                if(identical(categ[i3], ini.dot.categ) & ! identical(unlist(categ.class.order[i3]), dot.categ.class.order) & identical(sort(unlist(categ.class.order[i3])), sort(dot.categ.class.order))){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") THE dot.categ ARGUMENT SETTING IS PRESENT IN THE categ ARGUMENT SETTING, BUT ORDER OF THE CLASSES IS NOT THE SAME:\ncateg.class.order: ", paste(unlist(categ.class.order[i3]), collapse = " "), "\ndot.categ.class.order: ", paste(dot.categ.class.order, collapse = " "), "\nNOTE THAT ORDER OF categ.class.order IS THE ONE USED FOR THE AXIS REPRESENTATION")
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
+            # end random dots
+        }
+        # tidy dots
+        # coordinates are recovered during plotting (see dot.coord.tidy1 below)
+        # end tidy dots
+    }
+    # end x coordinates management (for random plotting and for stat display)
+    
+    
+    
+    
+    
+    # boxplot display before dot display if box.fill = TRUE
+    coord.names <- NULL
+    # creation of the data frame for (main box + legend) and data frame for means
+    if(box.notch == FALSE){
+        for(i3 in 1:length(categ)){
+            if(i3== 1L){
+                tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE)
+            }else{
+                tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE)
             }
-            if(is.null(dot.legend.name)){
-                dot.legend.name <- if(ini.dot.categ %in% categ[length(categ)]){dot.categ}else{ini.dot.categ} #
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") THE dot.legend.name SETTING IS NULL -> ", dot.legend.name, " WILL BE USED AS LEGEND TITLE OF DOTS")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+        names(tempo.polygon) <- categ
+        tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "BOX_SUP", "BOX_SUP", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE)
+        if( ! is.null(facet.categ)){
+            for(i4 in 1:length(facet.categ)){
+                tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE)
+                names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4]
             }
-            # dot.legend.name not NULL anymore (character string)
-        }else{
-            if( ! is.null(dot.categ.class.order)){
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") THE dot.categ.class.order ARGUMENT IS NOT NULL, BUT IS THE dot.categ ARGUMENT\n-> dot.categ.class.order NOT CONSIDERED AS NO LEGEND WILL BE DRAWN")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+    }else{
+        for(i3 in 1:length(categ)){
+            if(i3== 1L){
+                tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE)
+            }else{
+                tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE)
             }
-            # But dot.categ.class.order will be converted to NULL below (not now)
         }
-        # end optional legend of dot colors
-        # check the nature of color
-        # integer colors into gg_palette
-        tempo.check.color <- fun_check(data = dot.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)$problem
-        if(tempo.check.color == FALSE){
-            # convert integers into colors
-            dot.color <- fun_gg_palette(max(dot.color, na.rm = TRUE))[dot.color]
+        names(tempo.polygon) <- categ
+        tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_NOTCH_SUP", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF", "X_NOTCH_INF", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "NOTCH_INF", "MEDIAN", "NOTCH_SUP", "BOX_SUP", "BOX_SUP", "NOTCH_SUP", "MEDIAN", "NOTCH_INF", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE)
+        if( ! is.null(facet.categ)){
+            for(i4 in 1:length(facet.categ)){
+                tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE)
+                names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4]
+            }
         }
-        # end integer colors into gg_palette
-        if(all(dot.color == "same") & length(dot.color)== 1L){# all() without na.rm -> ok because dot.color cannot be NA (tested above)
-            dot.color <- categ.color # same color of the dots as the corresponding box color
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS BEEN SET TO \"same\"\nTHUS, DOTS WILL HAVE THE SAME COLORS AS THE CORRESPONDING BOXPLOT")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        }else if( ! (all(dot.color %in% colors() | grepl(pattern = "^#", dot.color)))){ # check that all strings of low.color start by #, # all() without na.rm -> ok because dot.color cannot be NA (tested above)
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR VECTOR STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGERS, OR THE STRING \"same\"\nHERE IT IS: ", paste(unique(dot.color), collapse = " "))
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
-        if(any(is.na(dot.color))){ # normally no NA with is.finite
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT CONTAINS NA")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        }
-        # end check the nature of color
-        # check the length of color
-        if( ! is.null(dot.categ)){
-            # optional legend of dot colors
-            if(length(data1[, dot.categ]) == length(levels(data1[, dot.categ])) & length(dot.color) == length(data1[, dot.categ])){
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") THE NUMBER OF CLASSES OF THE COLUMN ", dot.categ, " THE NUMBER OF ROWS OF THIS COLUMN AND THE NUMBER OF COLORS OF THE dot.color ARGUMENT ARE ALL EQUAL. DOT COLORS WILL BE ATTRIBUTED ACCORDING THE LEVELS OF ", dot.categ, ", NOT ACCORDING TO THE ROWS OF ", dot.categ)
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-            if(length(dot.color) > 1 & ! (length(dot.color) == length(unique(data1[, dot.categ])) | length(dot.color) == length(data1[, dot.categ]))){
-                tempo.cat <- paste0("ERROR IN ", function.name, "\nWHEN LENGTH OF THE dot.color ARGUMENT IS MORE THAN 1, IT MUST BE EQUAL TO THE NUMBER OF 1) ROWS OR 2) LEVELS OF dot.categ COLUMN (", dot.categ, "):\ndot.color: ", paste(dot.color, collapse = " "), "\ndot.categ LEVELS: ", paste(levels(data1[, dot.categ]), collapse = " "))
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }else if(length(dot.color) > 1 & length(dot.color) == length(unique(data1[, dot.categ]))){
-                data1 <- data.frame(data1, dot.color = data1[, dot.categ], stringsAsFactors = TRUE)
-                data1$dot.color <- factor(data1$dot.color, labels = dot.color) # do not use labels = unique(dot.color). Otherwise, we can have green1 green2 when dot.color is c("green", "green")
-            }else if(length(dot.color) > 1 & length(dot.color) == length(data1[, dot.categ])){
-                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
-            }else if(length(dot.color)== 1L){ # to deal with single color. Warning: & length(dot.categ.class.order) > 1 removed because otherwise, the data1 is not with dot.color column when length(dot.categ.class.order) == 1
-                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
-            }
-            dot.color <- as.character(unique(data1$dot.color[order(data1[, dot.categ])])) # reorder the dot.color character vector
-            if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color
-                dot.color <- rep(dot.color, length(dot.categ.class.order))
-            }
-            tempo.check <- unique(data1[ , c(dot.categ, "dot.color")])
-            if(length(unique(data1[ , "dot.color"])) > 1 & ( ! (nrow(tempo.check) == length(unique(data1[ , "dot.color"])) & nrow(tempo.check) == length(unique(data1[ , dot.categ]))))){ # length(unique(data1[ , "dot.color"])) > 1 because if only one color, can be attributed to each class of dot.categ
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT IS INCORRECTLY ASSOCIATED TO EACH CLASS OF dot.categ (", dot.categ, ") COLUMN:\n", paste(unique(mapply(FUN = "paste", data1[ , dot.categ], data1[ ,"dot.color"])), collapse = "\n"))
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }else{
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") IN dot.categ ARGUMENT (", ini.dot.categ, "), THE FOLLOWING COLORS OF DOTS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(data1[, dot.categ]), collapse = " "))
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }
-            # dot.color is a character string representing the diff classes of dot.categ
-            # data1$dot.color is a factor with order of levels -> dot.categ
-            # end optional legend of dot colors
-        }else{
-            categ.len <- length(categ) # if only categ1, then colors for classes of categ1, if length(categ) == 2L, then colors for classes of categ2
-            if(length(dot.color) == length(levels(data1[, categ[categ.len]]))){ # here length(dot.color) is equal to the different number of categ
-                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-                data1 <- data.frame(data1, dot.color = data1[, categ[categ.len]], stringsAsFactors = TRUE)
-                data1$dot.color <- factor(data1$dot.color, labels = dot.color)
-                if(box.alpha != 0){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") IN ", categ[categ.len], " OF categ ARGUMENT, THE FOLLOWING COLORS:\n", paste(dot.color, collapse = " "), "\nHAVE BEEN ATTRIBUTED TO THESE CLASSES:\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
-            }else if(length(dot.color) == length(data1[, categ[categ.len]])){# here length(dot.color) is equal to nrow(data1) -> Modif to have length(dot.color) equal to the different number of categ (length(dot.color) == length(levels(data1[, categ[categ.len]])))
-                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
-            }else if(length(dot.color)== 1L & ! all(dot.color == "same")){ # all() without na.rm -> ok because dot.color cannot be NA (tested above)
-                # data1[, categ[categ.len]] <- factor(data1[, categ[categ.len]]) # not required because sure that is is a factor
-                data1 <- data.frame(data1, dot.color = dot.color, stringsAsFactors = TRUE)
-                dot.color <- rep(dot.color, length(levels(data1[, categ[categ.len]])))
-                warn.count <- warn.count + 1
-                tempo.warn <- paste0("(", warn.count,") dot.color ARGUMENT HAS LENGTH 1, MEANING THAT ALL THE DIFFERENT CLASSES OF ", categ[categ.len], "\n", paste(levels(factor(data1[, categ[categ.len]])), collapse = " "), "\nWILL HAVE THE SAME COLOR\n", paste(dot.color, collapse = " "))
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            }else{
-                tempo.cat <- paste0("ERROR IN ", function.name, "\ndot.color ARGUMENT MUST BE (1) LENGTH 1, OR (2) THE LENGTH OF data1 NROWS AFTER NA/Inf REMOVAL, OR (3) THE LENGTH OF THE CLASSES IN THE categ ", categ[categ.len], " COLUMN. HERE IT IS COLOR LENGTH ", length(dot.color), " VERSUS CATEG LENGTH ", length(data1[, categ[categ.len]]), " AND CATEG CLASS LENGTH ", length(unique(data1[, categ[categ.len]])), "\nPRESENCE OF NA/Inf COULD BE THE PROBLEM")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            # end check the length of color
-            dot.color <- as.character(dot.color)
-            # dot.color is a character string representing the diff classes
-            data1$dot.color <- factor(data1$dot.color, levels = unique(dot.color)) # ok because if dot.color is a character string, the order make class 1, class 2, etc. If dot.color is a column of data1, then levels will be created, without incidence, except if dot.categ specified (see below). unique() because no duplicates allowed
-            # data1$dot.color is a factor with order of levels -> dot.color
-        }
-        # end optional legend of dot colors
-    }else if(is.null(dot.color) & ! (is.null(dot.categ) & is.null(dot.categ.class.order) & is.null(dot.legend.name))){
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") dot.categ OR dot.categ.class.order OR dot.legend.name ARGUMENT HAS BEEN SPECIFIED BUT dot.color ARGUMENT IS NULL (NO DOT PLOTTED)")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-    }
-    # dot.color either NULL (no dot plotted) or character string (potentially representing the diff classes of dot.categ)
-    # data1$dot.color is either NA or a factor (with order of levels -> depending on dot.categ or categ[length(categ)], or other
-    if(is.null(dot.categ)){
-        dot.categ.class.order <- NULL # because not used anyway
-    }
-    # dot.categ.class.order either NULL if dot.categ is NULL (no legend displayed) or character string (potentially representing the diff classes of dot.categ)
-    # end management of dot.color
-    if(is.null(dot.color) & box.fill == FALSE & dot.alpha <= 0.025){
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") THE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.alpha = ", fun_round(dot.alpha, 4), "\n-> POTENTIAL OUTLIER DOTS MIGHT NOT BE VISIBLE BECAUSE ALMOST TRANSPARENT")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
     }
-    if(is.null(dot.color) & box.fill == FALSE & dot.border.size == 0){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE FOLLOWING ARGUMENTS WERE SET AS:\ndot.color = NULL (NOT ALL DOTS BUT ONLY POTENTIAL OUTLIER DOTS DISPLAYED)\nbox.fill = FALSE (NO FILLING COLOR FOR BOTH BOXES AND POTENTIAL OUTLIER DOTS)\ndot.border.size = 0 (NO BORDER FOR POTENTIAL OUTLIER DOTS)\n-> THESE SETTINGS ARE NOT ALLOWED BECAUSE THE POTENTIAL OUTLIER DOTS WILL NOT BE VISIBLE")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    tempo.polygon$COLOR <- factor(tempo.polygon$COLOR, levels = unique(categ.color))
+    if( ! is.null(categ.class.order)){
+        for(i3 in 1:length(categ)){
+            tempo.polygon[, categ[i3]] <- factor(tempo.polygon[, categ[i3]], levels = categ.class.order[[i3]])
+        }
     }
-    # integer dot.border.color into gg_palette
-    if( ! is.null(dot.border.color)){
-        tempo <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
-        if(tempo$problem == FALSE){ # convert integers into colors
-            dot.border.color <- fun_gg_palette(max(dot.border.color, na.rm = TRUE))[dot.border.color]
+    # modified name of dot.categ column (e.g., "Categ1_DOT") must be included for boxplot using ridy dots
+    if( ! is.null(dot.color) & ! is.null(dot.categ)){
+        if(dot.categ != ini.dot.categ){
+            tempo.polygon <- data.frame(tempo.polygon, GROUPX = tempo.polygon[, ini.dot.categ], stringsAsFactors = TRUE)
+            names(tempo.polygon)[names(tempo.polygon) == "GROUPX"] <- dot.categ
+            
         }
     }
-    # end integer dot.border.color into gg_palette
-    # na detection and removal (done now to be sure of the correct length of categ)
-    column.check <- c("categ.color", if( ! is.null(dot.color)){"dot.color"}) # 
-    if(any(is.na(data1[, column.check]))){ # data1 used here instead of data1.ini in case of new NaN created by log conversion (neg values) # normally no NA with is.na
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") NA DETECTED IN COLUMNS ", paste(column.check, collapse = " "), " OF data1 AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        for(i2 in 1:length(column.check)){
-            if(any(is.na(data1[, column.check[i2]]))){ # normally no NA with is.na
-                tempo.warn <- paste0("NA REMOVAL DUE TO COLUMN ", column.check[i2], " OF data1")
-                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n", tempo.warn)))
-            }
+    tempo.diamon.mean <- data.frame(X = c(t(stat[, c("X", "X_NOTCH_INF", "X", "X_NOTCH_SUP", "X")])), Y = c(t(cbind(stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] + (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stringsAsFactors = TRUE))), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), GROUP = c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")])), stringsAsFactors = TRUE) # stringsAsFactors = TRUE for cbind() because stat["MEAN"] is a data frame. Otherwise, stringsAsFactors is not an argument for cbind() on vectors
+    if( ! is.null(facet.categ)){
+        for(i3 in 1:length(facet.categ)){
+            tempo.diamon.mean <- data.frame(tempo.diamon.mean, c(t(stat[, c(facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3])])), stringsAsFactors = TRUE)
+            names(tempo.diamon.mean)[length(names(tempo.diamon.mean))] <- facet.categ[i3]
         }
-        tempo <- unique(unlist(lapply(lapply(c(data1[column.check]), FUN = is.na), FUN = which)))
-        removed.row.nb <- c(removed.row.nb, tempo)
-        removed.rows <- rbind(removed.rows, data1[tempo, ], stringsAsFactors = FALSE) # here data1 used because categorical columns tested
-        if(length(tempo) != 0){
-            data1 <- data1[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
-            data1.ini <- data1.ini[-tempo, ] # WARNING tempo here and not removed.row.nb because the latter contain more numbers thant the former
-            for(i3 in 1:length(column.check)){
-                if(any( ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]]), na.rm = TRUE)){
-                    warn.count <- warn.count + 1
-                    tempo.warn <- paste0("(", warn.count,") IN COLUMN ", column.check[i3], " OF data1, THE FOLLOWING CLASSES HAVE DISAPPEARED AFTER NA/Inf REMOVAL (IF COLUMN USED IN THE PLOT, THIS CLASS WILL NOT BE DISPLAYED):\n", paste(unique(removed.rows[, column.check[i3]])[ ! unique(removed.rows[, column.check[i3]]) %in% unique(data1[, column.check[i3]])], collapse = " "))
-                    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-                }
-            }
+    }
+    tempo.diamon.mean$COLOR <- factor(tempo.diamon.mean$COLOR, levels = unique(categ.color))
+    # end creation of the data frame for (main box + legend) and data frame for means
+    if(box.fill == TRUE){
+        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, alpha = box.alpha, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{dot.border.color}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted. Finally, boxplot redrawn (see below)
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon(
+            data = tempo.polygon, 
+            mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", fill = categ[length(categ)], color = categ[length(categ)]), 
+            size = box.line.size, 
+            alpha = box.alpha # works only for fill, not for color
+        ))
+        coord.names <- c(coord.names, "main.box")
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "sup.whisker")
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "inf.whisker")
+        if(box.whisker.width > 0){
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # 
+            coord.names <- c(coord.names, "sup.whisker.edge")
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # 
+            coord.names <- c(coord.names, "inf.whisker.edge")
         }
-        for(i2 in 1:length(column.check)){
-            if(column.check[i2] == "categ.color"){
-                categ.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
-                if(length(categ.color)== 1L & length(unlist(categ.class.order[length(categ)])) > 1){ # to deal with single color
-                    categ.color <- rep(categ.color, length(unlist(categ.class.order[length(categ)])))
-                }
-                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(categ.color))
-            }
-            if(column.check[i2] == "dot.color"){
-                dot.color <- levels(data1[, column.check[i2]])[levels(data1[, column.check[i2]]) %in% unique(data1[, column.check[i2]])] # remove the absent color in the character vector
-                if(length(dot.color)== 1L & length(dot.categ.class.order) > 1){ # to deal with single color. If dot.categ.class.order == NULL (which is systematically the case if dot.categ == NULL), no rep(dot.color, length(dot.categ.class.order)
-                    dot.color <- rep(dot.color, length(dot.categ.class.order))
-                }
-                data1[, column.check[i2]] <- factor(as.character(data1[, column.check[i2]]), levels = unique(dot.color))
-            }
+        if(box.mean == TRUE){
+            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, fill = stat$COLOR, size = box.mean.size, color = "black", alpha = box.alpha)) # group used in aesthetic to do not have it in the legend
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon(
+                data = tempo.diamon.mean, 
+                mapping = ggplot2::aes(x = X, y = Y, group = GROUP), 
+                fill = tempo.diamon.mean[, "COLOR"], 
+                color = hsv(0, 0, 0, alpha = box.alpha), # outline of the polygon in black but with alpha
+                size = box.line.size, 
+                alpha = box.alpha
+            ))
+            coord.names <- c(coord.names, "mean")
         }
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = "black", size = box.line.size * 2, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "median")
     }
-    # end na detection and removal (done now to be sure of the correct length of categ)
-    # From here, data1 and data.ini have no more NA or NaN
-    # end other checkings
-    # reserved word checking
-    #already done above
-    # end reserved word checking
-    # end second round of checking and data preparation
-    
+    # end boxplot display before dot display if box.fill = TRUE
     
-    # package checking
-    fun_pack(req.package = c(
-        "ggplot2", 
-        "gridExtra", 
-        "lemon", 
-        "scales"
-    ), lib.path = lib.path)
-    # end package checking
     
     
     
     
     
-    # main code
-    # y coordinates recovery (create ini.box.coord, dot.coord and modify data1)
-    if(length(categ)== 1L){
-        # width commputations
-        box.width2 <- box.width
-        box.space <- 0 # to inactivate the shrink that add space between grouped boxes, because no grouped boxes here
-        # end width commputations
-        # data1 check categ order for dots coordinates recovery
-        data1 <- data.frame(data1, categ.check = data1[, categ[1]], stringsAsFactors = TRUE)
-        data1$categ.check <- as.integer(data1$categ.check) # to check that data1[, categ[1]] and dot.coord$group are similar, during merging
-        # end data1 check categ order for dots coordinates recovery
-        # per box dots coordinates recovery
-        tempo.gg.name <- "gg.indiv.plot."
-        tempo.gg.count <- 0
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[1]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21))
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[1]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[1]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[1]])))}else{categ.color}))
-        # end per box dots coordinates recovery
-    }else if(length(categ) == 2L){
-        # width commputations
-        box.width2 <- box.width / length(unique(data1[, categ[length(categ)]])) # real width of each box in x-axis unit, among the set of grouped box. Not relevant if no grouped boxes length(categ)== 1L
-        # end width commputations
-        # data1 check categ order for dots coordinates recovery
-        tempo.factor <- paste0(data1[order(data1[, categ[2]], data1[, categ[1]]), categ[2]], "_", data1[order(data1[, categ[2]], data1[, categ[1]]), categ[1]])
-        data1 <- data.frame(data1[order(data1[, categ[2]], data1[, categ[1]]), ], categ.check = factor(tempo.factor, levels = unique(tempo.factor)), stringsAsFactors = TRUE)
-        data1$categ.check <- as.integer(data1$categ.check)
-        # end data1 check categ order for dots coordinates recovery
-        # per box dots coordinates recovery
-        tempo.gg.name <- "gg.indiv.plot."
-        tempo.gg.count <- 0
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[2]), stroke = dot.border.size, size = dot.size, alpha = dot.alpha, shape = 21))
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(is.null(categ.color)){rep(NA, length(unique(data1[, categ[2]])))}else if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color})) # categ.color used for dot colors because at that stage, we do not care about colors
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[2]), coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf})) # fill because this is what is used with geom_box # to easily have the equivalent of the grouped boxes
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[2]])))}else{categ.color}))
-        # end per box dots coordinates recovery
-    }else{
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 1")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    if( ! is.null(stat.pos)){
-        stat.just <- fun_gg_just(
-            angle = stat.angle, 
-            pos = ifelse(
-                vertical == TRUE, 
-                ifelse(stat.pos == "top", "bottom", "top"), # "bottom" because we want justification for text that are below the ref point which is the top of the graph. The opposite for "above"
-                ifelse(stat.pos == "top", "left", "right") # "left" because we want justification for text that are on the left of the ref point which is the right border of the graph. The opposite for "above"
-            ), 
-            kind = "text"
-        )
-    }
-    # has in fact no interest because ggplot2 does not create room for geom_text()
-    tempo.data.max <- data1[which.max(data1[, y]), ]
-    tempo.data.max <- data.frame(tempo.data.max, label = formatC(tempo.data.max[, y], digit = 2, drop0trailing = TRUE, format = "f"), stringsAsFactors = TRUE)
-    # end has in fact no interest because ggplot2 does not create room for geom_text()
-    tempo.graph.info.ini <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if( ! is.null(stat.pos)){' + ggplot2::geom_text(data = tempo.data.max, mapping = ggplot2::aes_string(x = 1, y = y, label = "label"), size = stat.size, color = "black", angle = stat.angle, hjust = stat.just$hjust, vjust = stat.just$vjust)'})))) # added here to have room for annotation
-    dot.coord <- tempo.graph.info.ini$data[[1]]
-    dot.coord$x <- as.numeric(dot.coord$x) # because weird class
-    dot.coord$PANEL <- as.numeric(dot.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
-    tempo.mean <- aggregate(x = dot.coord$y, by = list(dot.coord$group, dot.coord$PANEL), FUN = mean, na.rm = TRUE)
-    names(tempo.mean)[names(tempo.mean) == "x"] <- "MEAN"
-    names(tempo.mean)[names(tempo.mean) == "Group.1"] <- "BOX"
-    names(tempo.mean)[names(tempo.mean) == "Group.2"] <- "PANEL"
-    dot.coord <- data.frame(
-        dot.coord[order(dot.coord$group, dot.coord$y), ], # dot.coord$PANEL deals below
-        y.check = as.double(data1[order(data1$categ.check, data1[, y]), y]), 
-        categ.check = data1[order(data1$categ.check, data1[, y]), "categ.check"], 
-        dot.color = if(is.null(dot.color)){NA}else{data1[order(data1$categ.check, data1[, y]), "dot.color"]}, 
-        data1[order(data1$categ.check, data1[, y]), ][categ], # avoid the renaming below
-        stringsAsFactors = TRUE
-    ) # y.check to be sure that the order is the same between the y of data1 and the y of dot.coord
-    # names(dot.coord)[names(dot.coord) == "tempo.categ1"] <- categ[1]
-    if( ! is.null(dot.categ)){
-        dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][dot.categ], stringsAsFactors = TRUE) # avoid the renaming
-    }
-    if( ! is.null(facet.categ)){
-        dot.coord <- data.frame(dot.coord, data1[order(data1$categ.check, data1[, y]), ][facet.categ], stringsAsFactors = TRUE) # for facet panels
-        tempo.test <- NULL
-        for(i2 in 1:length(facet.categ)){
-            tempo.test <- paste0(tempo.test, ".", formatC(as.numeric(dot.coord[, facet.categ[i2]]), width = nchar(max(as.numeric(dot.coord[, facet.categ[i2]]), na.rm = TRUE)), flag = "0")) # convert factor into numeric with leading zero for proper ranking # merge the formatC() to create a new factor. The convertion to integer should recreate the correct group number. Here as.numeric is used and not as.integer in case of numeric in facet.categ (because comes from add and not checked by fun_check, contrary to categ)
-        }
-        tempo.test <- as.integer(factor(tempo.test))
-        if( ! identical(as.integer(dot.coord$PANEL), tempo.test)){
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nas.integer(dot.coord$PANEL) AND tempo.test MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
-    }
-    if(dot.tidy == TRUE){
-        if( ! is.null(dot.categ)){
-            dot.coord <- data.frame(dot.coord, tidy_group = data1[order(data1$categ.check, data1[, y]), ][, dot.categ], stringsAsFactors = TRUE) # avoid the renaming
-            # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates
-            if(dot.categ %in% categ){
-                dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE)
+    # dot display
+    if( ! is.null(dot.color)){
+        if(dot.tidy == FALSE){
+            if(is.null(dot.categ)){
+                if(dot.border.size == 0){
+                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
+                        data = dot.coord.rd3, 
+                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), 
+                        size = dot.size, 
+                        shape = 19, 
+                        color = dot.coord.rd3$dot.color, 
+                        alpha = dot.alpha
+                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
+                }else{
+                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
+                        data = dot.coord.rd3, 
+                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), 
+                        shape = 21, 
+                        stroke = dot.border.size, 
+                        color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, 
+                        size = dot.size, 
+                        fill = dot.coord.rd3$dot.color, 
+                        alpha = dot.alpha
+                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
+                }
             }else{
-                dot.coord <- data.frame(dot.coord, tidy_group_coord = as.integer(factor(paste0(
-                    formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
-                    ".", 
-                    if(length(categ) == 2L){formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")}, # convert factor into numeric with leading zero for proper ranking
-                    if(length(categ) == 2L){"."}, 
-                    formatC(as.integer(dot.coord[, dot.categ]), width = nchar(max(as.integer(dot.coord[, dot.categ]), na.rm = TRUE)), flag = "0") # convert factor into numeric with leading zero for proper ranking
-                )), stringsAsFactors = TRUE) # merge the 2 or 3 formatC() to create a new factor. The convertion to integer should recreate the correct group number
-                ) # for tidy dot plots
+                if(dot.border.size == 0){
+                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
+                        data = dot.coord.rd3, 
+                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), 
+                        size = dot.size, 
+                        shape = 19, 
+                        color = dot.coord.rd3$dot.color
+                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
+                }else{
+                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
+                        data = dot.coord.rd3, 
+                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), 
+                        size = dot.size, 
+                        shape = 21, 
+                        stroke = dot.border.size, 
+                        color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, 
+                        fill = dot.coord.rd3$dot.color
+                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
+                }
+                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), guide = ggplot2::guide_legend(override.aes = list(fill = dot.color, color = if(is.null(dot.border.color)){dot.color}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
             }
-        }else{
-            dot.coord <- data.frame(dot.coord, tidy_group = if(length(categ)== 1L){
-                dot.coord[, categ]}else{as.integer(factor(paste0(
-                    formatC(as.integer(dot.coord[, categ[1]]), width = nchar(max(as.integer(dot.coord[, categ[1]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
-                    ".", 
-                    formatC(as.integer(dot.coord[, categ[2]]), width = nchar(max(as.integer(dot.coord[, categ[2]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
-                )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
-                }) # for tidy dot plots
-            # tidy_group_coord is to be able to fuse table when creating the table for dot coordinates
-            dot.coord <- data.frame(dot.coord, tidy_group_coord = dot.coord$group, stringsAsFactors = TRUE)
+            coord.names <- c(coord.names, "dots")
+        }else if(dot.tidy == TRUE){
+            # here plot using group -> no scale
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot(
+                data = dot.coord, 
+                mapping = ggplot2::aes_string(x = categ[1], y = "y", group = "group"), # not dot.categ here because the classes of dot.categ create new separations
+                position = ggplot2::position_dodge(width = box.width), 
+                binpositions = "all", 
+                binaxis = "y", 
+                stackdir = "center", 
+                alpha = dot.alpha, 
+                fill = dot.coord$dot.color, 
+                stroke = dot.border.size, 
+                color = if(is.null(dot.border.color)){dot.coord$dot.color}else{rep(dot.border.color, nrow(dot.coord))}, 
+                show.legend = FALSE, # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual()
+                binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb
+            )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.)
+            coord.names <- c(coord.names, "dots")
+            if( ! is.null(dot.categ)){
+                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot(
+                    data = dot.coord, 
+                    mapping = ggplot2::aes_string(x = categ[1], y = "y", alpha = dot.categ), # not dot.categ here because the classes of dot.categ create new separations
+                    position = ggplot2::position_dodge(width = box.width), 
+                    binpositions = "all", 
+                    binaxis = "y", 
+                    stackdir = "center", 
+                    fill = NA, 
+                    stroke = NA, 
+                    color = NA, 
+                    # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual()
+                    binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb
+                )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.)
+                # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = dot.legend.name, values = rep(1, length(categ.color)))) # values = rep("black", length(categ.color)) are the values of color (which is the border color of dots), and this modify the border color on the plot. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
+                coord.names <- c(coord.names, "bad_remove")
+                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), labels = dot.categ.class.order, guide = ggplot2::guide_legend(title = if(ini.dot.categ == categ[length(categ)]){dot.categ}else{ini.dot.categ}, override.aes = list(fill = levels(dot.coord$dot.color), color = if(is.null(dot.border.color)){levels(dot.coord$dot.color)}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
+            }
+            # coordinates of tidy dots
+            tempo.coord <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))$data # to have the tidy dot coordinates
+            if(length(which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))) != 1){ # detect the compartment of tempo.coord which is the binned data frame
+                # if(length(which(sapply(tempo.coord, FUN = nrow) == nrow(data1))) > if(is.null(dot.categ)){1}else{2}){ # this does not work if only one dot per class, thus replaced by above # if(is.null(dot.categ)){1}else{2} because 1 dotplot if dot.categ is NULL and 2 dotplots if not, with the second being a blank dotplot with wrong coordinates. Thus take the first in that situation
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nEITHER MORE THAN 1 OR NO COMPARTMENT HAVING A DATA FRAME WITH binwidth AS COLUMN NAME IN THE tempo.coord LIST (FOR TIDY DOT COORDINATES). CODE HAS TO BE MODIFIED")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }else{
+                # dot.coord.tidy1 <- tempo.coord[[which(sapply(tempo.coord, FUN = nrow) == nrow(data1))[1]]] # this does not work if only one dot per class, thus replaced by above # the second being a blank dotplot with wrong coordinates. Thus take the first whatever situation
+                dot.coord.tidy1 <- tempo.coord[[which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))]] # detect the compartment of tempo.coord which is the binned data frame
+                dot.coord.tidy1$x <- as.numeric(dot.coord.tidy1$x) # because weird class
+                dot.coord.tidy1$PANEL <- as.numeric(dot.coord.tidy1$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
+            }
+            # tempo.box.coord <- merge(box.coord, unique(dot.coord[, c("PANEL", "group", categ)]), by = c("PANEL", "group"), sort = FALSE) # not required anymore because box.coord already contains categ do not add dot.categ and tidy_group_coord here because the coordinates are for stats. Add the categ in box.coord. WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column
+            # below inactivated because not true when dealing with dot.categ different from categ
+            # if(nrow(tempo.box.coord) != nrow(box.coord)){
+            # tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT tempo.box.coord DATA FRAME. CODE HAS TO BE MODIFIED")
+            # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+            # }
+            dot.coord.tidy2 <- merge(dot.coord.tidy1, box.coord[c("fill", "PANEL", "group", "x", categ)], by = c("PANEL", "group"), sort = FALSE) # send the coord of the boxes into the coord data.frame of the dots (in the column x.y).WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in tempo.box.coord. Thus, no need to consider fill colum # DANGER: from here the fill.y and x.y (from tempo.box.coord) are not good in dot.coord.tidy2. It is ok because Categ1 Categ2 from tempo.box.coord are ok with the group column from dot.coord.tidy1. This is due to the fact that dot.coord.tidy resulting from geom_dotplot does not make the same groups as the other functions
+            if(nrow(dot.coord.tidy2) != nrow(dot.coord)){
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy2 DATA FRAME. CODE HAS TO BE MODIFIED")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }
+            # From here, check for dot.coord.tidy3 which wil be important for stat over the plot. WARNING: dot.categ has nothing to do here for stat coordinates. Thus, not in tempo.data1
+            if(length(categ)== 1L){
+                tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor
+                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
+                verif <- paste0(categ[1], ".check")
+            }else if(length(categ) == 2L){
+                tempo.data1 <- unique(
+                    data.frame(
+                        data1[c(categ[1], categ[2])], 
+                        group = as.integer(factor(paste0(
+                            formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
+                            ".", 
+                            formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
+                        )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
+                    )
+                ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis
+                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
+                names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check")
+                verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check"))
+            }else{
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }
+            dot.coord.tidy3 <- merge(dot.coord.tidy2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have tested intersect("group", "group") instead of by = "group". May be come back to by = "group" in case of error. But I did this because of an error in dot.coord.rd3 above
+            if(nrow(dot.coord.tidy3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.tidy3[categ], dot.coord.tidy3[verif])$identical.content)){
+                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy3 DATA FRAME. CODE HAS TO BE MODIFIED")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+            }
+            # end coordinates of tidy dots
         }
     }
-    if( ! (identical(dot.coord$y, dot.coord$y.check) & identical(dot.coord$group, dot.coord$categ.check))){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(dot.coord$y AND dot.coord$y.check) AS WELL AS (dot.coord$group AND dot.coord$categ.check) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    # end dot display
+    
+    
+    
+    # boxplot display (if box.fill = FALSE, otherwise, already plotted above)
+    if(box.fill == TRUE){
+        # overcome "work only for the filling of boxes, not for the frame. See https://github.com/tidyverse/ggplot2/issues/252"
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(order = 1))) #, guide = ggplot2::guide_legend(override.aes = list(fill = levels(tempo.polygon$COLOR), color = "black")))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = rep(hsv(0, 0, 0, alpha = box.alpha), length(unique(data1[, categ[length(categ)]]))), guide = ggplot2::guide_legend(order = 1))) # , guide = ggplot2::guide_legend(override.aes = list(color = "black", alpha = box.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor # outline of the polygon in black but with alpha
     }else{
-        if( ! identical(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ]$BOX, unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c("group", "PANEL")])$group)){
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\n(tempo.mean$BOX, tempo.mean$PANEL) AND (dot.coord$group, dot.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else{
-            tempo <- unique(dot.coord[order(dot.coord$group, dot.coord$PANEL), c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ}), drop = FALSE])
-            # names(tempo) <- paste0(names(tempo), ".mean")
-            tempo.mean <- data.frame(tempo.mean[order(tempo.mean$BOX, tempo.mean$PANEL), ], tempo, stringsAsFactors = TRUE)
-        }
-    }
-    # at that stage, categ color and dot color are correctly attributed in data1, box.coord and dot.coord
-    # end y dot coordinates recovery (create ini.box.coord, dot.coord and modify data1)
-    # ylim range
-    if(is.null(y.lim)){
-        y.lim <- tempo.graph.info.ini$layout$panel_params[[1]]$y.range # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only
-        if(any(( ! is.finite(y.lim)) | is.na(y.lim)) | length(y.lim) != 2){ # kept but normally no more Inf in data1 # normally no NA with is.finite, etc.
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntempo.graph.info.ini$layout$panel_params[[1]]$y.range[1] CONTAINS NA OR Inf OR HAS LENGTH 1")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, alpha = box.alpha, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{if(dot.border.size == 0){NA}else{dot.border.color}}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path(
+            data = tempo.polygon, 
+            mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", color = categ[length(categ)]), 
+            size = box.line.size, 
+            alpha = box.alpha, 
+            lineend = "round", 
+            linejoin = "round"
+        ))
+        coord.names <- c(coord.names, "main.box")
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size * 2, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "median")
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "sup.whisker")
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # 
+        coord.names <- c(coord.names, "inf.whisker")
+        if(box.whisker.width > 0){
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # 
+            coord.names <- c(coord.names, "sup.whisker.edge")
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # 
+            coord.names <- c(coord.names, "inf.whisker.edge")
         }
-    }else if(y.log != "no"){
-        y.lim <- get(y.log)(y.lim) # no env = sys.nframe(), inherit = FALSE in get() because look for function in the classical scope
-    }
-    if(y.log != "no"){
-        # normally this control is not necessary anymore
-        if(any( ! is.finite(y.lim))){ # normally no NA with is.finite
-            tempo.cat <- paste0("ERROR IN ", function.name, "\ny.lim ARGUMENT CANNOT HAVE ZERO OR NEGATIVE VALUES WITH THE y.log ARGUMENT SET TO ", y.log, ":\n", paste(y.lim, collapse = " "), "\nPLEASE, CHECK DATA VALUES (PRESENCE OF ZERO OR INF VALUES)")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        if(box.mean == TRUE){
+            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, color = stat$COLOR, size = box.mean.size, fill = NA, alpha = box.alpha)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path(
+                data = tempo.diamon.mean, 
+                mapping = ggplot2::aes(x = X, y = Y, group = GROUP), 
+                color = tempo.diamon.mean[, "COLOR"], 
+                size = box.line.size, 
+                alpha = box.alpha, 
+                lineend = "round", 
+                linejoin = "round"
+            ))
+            coord.names <- c(coord.names, "mean")
+        }
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = rep(NA, length(unique(data1[, categ[length(categ)]]))))) #, guide = ggplot2::guide_legend(override.aes = list(color = categ.color)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(override.aes = list(alpha = if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){1}else{box.alpha})))) # , guide = ggplot2::guide_legend(override.aes = list(color = as.character(categ.color))))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
+        if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1
+            # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    if(suppressWarnings(all(y.lim %in% c(Inf, -Inf)))){ # all() without na.rm -> ok because y.lim cannot be NA (tested above)
-        # normally this control is not necessary anymore
-        tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS Inf VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    if(suppressWarnings(any(is.na(y.lim)))){ # normally no NA with is.na
-        # normally this control is not necessary anymore
-        tempo.cat <- paste0("ERROR IN ", function.name, " y.lim CONTAINS NA OR NaN VALUES, MAYBE BECAUSE VALUES FROM data1 ARGUMENTS ARE NA OR Inf ONLY OR BECAUSE OF LOG SCALE REQUIREMENT")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    y.lim.order <- order(y.lim) # to deal with inverse axis
-    y.lim <- sort(y.lim)
-    y.lim[1] <- y.lim[1] - abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.bottom.extra.margin, y.top.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis
-    y.lim[2] <- y.lim[2] + abs(y.lim[2] - y.lim[1]) * ifelse(diff(y.lim.order) > 0, y.top.extra.margin, y.bottom.extra.margin) # diff(y.lim.order) > 0 medians not inversed axis
-    if(y.include.zero == TRUE){ # no need to check y.log != "no" because done before
-        y.lim <- range(c(y.lim, 0), na.rm = TRUE, finite = TRUE) # finite = TRUE removes all the -Inf and Inf except if only this. In that case, whatever the -Inf and/or Inf present, output -Inf;Inf range. Idem with NA only
-    }
-    y.lim <- y.lim[y.lim.order]
-    if(any(is.na(y.lim))){ # normally no NA with is.na
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 2")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    if(box.alpha == 0){ # remove box legend because no boxes drawn
+        # add this after the scale_xxx_manual() for boxplots
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none")) # inactivate the legend
     }
-    # end ylim range
-    
-    
+    # end boxplot display (if box.fill = FALSE, otherwise, already plotted above)
     
     
     
     
-    # drawing
-    # constant part
-    tempo.gg.name <- "gg.indiv.plot."
-    tempo.gg.count <- 0
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add is directly put here to deal with additional variable of data, like when using facet_grid. No problem if add is a theme, will be dealt below
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::xlab(if(is.null(x.lab)){categ[1]}else{x.lab}))
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylab(if(is.null(y.lab)){y}else{y.lab}))
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ggtitle(title))
-    # text angle management
-    axis.just <- fun_gg_just(angle = x.angle, pos = ifelse(vertical == TRUE, "bottom", "left"), kind = "axis")
-    # end text angle management
-    add.check <- TRUE
-    if( ! is.null(add)){ # if add is NULL, then = 0
-        if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme() AND theme_classic() HAVE BEEN INACTIVATED, TO BE USED BY THE USER\n-> article ARGUMENT WILL BE IGNORED")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-            add.check <- FALSE
-        }
-    }
-    if(add.check == TRUE & article == TRUE){
-        # WARNING: not possible to add theme()several times. NO message but the last one overwrites the others
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_classic(base_size = text.size))
-        if(grid == TRUE){
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
-                text = ggplot2::element_text(size = text.size), 
-                plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
-                line = ggplot2::element_line(size = 0.5), 
-                legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
-                axis.line.y.left = ggplot2::element_line(colour = "black"), # draw lines for the y axis
-                axis.line.x.bottom = ggplot2::element_line(colour = "black"), # draw lines for the x axis
-                panel.grid.major.x = if(vertical == TRUE){NULL}else{ggplot2::element_line(colour = "grey85", size = 0.75)},
-                panel.grid.major.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey85", size = 0.75)}else{NULL},
-                panel.grid.minor.y = if(vertical == TRUE){ggplot2::element_line(colour = "grey90", size = 0.25)}else{NULL},
-                axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
-                axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)},
-                strip.background = ggplot2::element_rect(fill = NA, colour = NA) # for facet background
-            ))
+    # stat display
+    # layer after dots but ok, behind dots on the plot
+    if( ! is.null(stat.pos)){
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") NUMBERS DISPLAYED ARE ", ifelse(stat.mean == FALSE, "MEDIANS", "MEANS"))
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        if(stat.pos == "top"){
+            tempo.stat <- data.frame(stat, Y = y.lim[2], stringsAsFactors = TRUE) # I had to create a data frame for geom_tex() so that facet is taken into account, (ggplot2::annotate() does not deal with facet because no data and mapping arguments). Of note, facet.categ is in tempo.stat, via tempo.mean, via dot.coord
+            if(stat.mean == FALSE){tempo.stat$MEDIAN <- formatC(stat.nolog$MEDIAN, digit = 2, drop0trailing = TRUE, format = "f")}else{tempo.stat$MEAN <- formatC(stat.nolog$MEAN, digit = 2, drop0trailing = TRUE, format = "f")}
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
+                data = tempo.stat, 
+                mapping = ggplot2::aes_string(x = "X", y = "Y", label = ifelse(stat.mean == FALSE, "MEDIAN", "MEAN")),
+                size = stat.size, 
+                color = "black", 
+                angle = stat.angle, 
+                hjust = stat.just$hjust, 
+                vjust = stat.just$vjust
+            )) # stat$X used here because identical to stat.nolog but has the X. WARNING: no need of order() for labels because box.coord$x set the order. For justification, see https://stackoverflow.com/questions/7263849/what-do-hjust-and-vjust-do-when-making-a-plot-using-ggplot
+            coord.names <- c(coord.names, "stat.pos")
+        }else if(stat.pos == "above"){
+            # stat coordinates
+            if( ! is.null(dot.color)){ # for text just above max dot
+                if(dot.tidy == FALSE){
+                    tempo.stat.ini <- dot.coord.rd3
+                }else if(dot.tidy == TRUE){
+                    tempo.stat.ini <- dot.coord.tidy3
+                    tempo.stat.ini$x.y <- tempo.stat.ini$x.x # this is just to be able to use tempo.stat.ini$x.y for untidy or tidy dots (remember that dot.coord.tidy3$x.y is not good, see above)
+                }
+                stat.coord1 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = min, na.rm = TRUE)
+                names(stat.coord1)[names(stat.coord1) == "y"] <- "dot.min"
+                stat.coord2 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = max, na.rm = TRUE)
+                names(stat.coord2) <- paste0(names(stat.coord2), "_from.dot.max")
+                names(stat.coord2)[names(stat.coord2) == "y_from.dot.max"] <- "dot.max"
+                stat.coord3 <- cbind(box.coord[order(box.coord$group, box.coord$PANEL), ], stat.coord1[order(stat.coord1$group, stat.coord1$x.y), ], stat.coord2[order(stat.coord2$group, stat.coord2$x.y), ], stringsAsFactors = TRUE) # 
+                if( ! all(identical(round(stat.coord3$x, 9), round(as.numeric(stat.coord3$x.y), 9)), na.rm = TRUE)){ # as.numeric() because stat.coord3$x is class "mapped_discrete" "numeric"
+                    tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nFUSION OF box.coord, stat.coord1 AND stat.coord2 ACCORDING TO box.coord$x, stat.coord1$x.y AND stat.coord2$x.y IS NOT CORRECT. CODE HAS TO BE MODIFIED")
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }
+                # text.coord <- stat.coord3[, c("x", "group", "dot.min", "dot.max")]
+                # names(text.coord)[names(text.coord) == "dot.min"] <- "text.min.pos"
+                #names(text.coord)[names(text.coord) == "dot.max"] <- "text.max.pos"
+                box.coord <- box.coord[order(box.coord$x, box.coord$group, box.coord$PANEL), ]
+                # text.coord <- text.coord[order(text.coord$x), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same
+                stat.coord3 <- stat.coord3[order(stat.coord3$x, stat.coord3$group, stat.coord3$PANEL), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same
+                if( ! (identical(box.coord$x, stat.coord3$x) & identical(box.coord$group, stat.coord3$group) & identical(box.coord$PANEL, stat.coord3$PANEL))){
+                    tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntext.coord AND box.coord DO NOT HAVE THE SAME x, group AND PANEL COLUMN CONTENT")
+                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+                }
+            }else{
+                stat.coord3 <- box.coord
+            }
+            stat.coord3 <- data.frame(
+                stat.coord3, 
+                Y = stat.coord3[, ifelse(
+                    is.null(dot.color), 
+                    ifelse(diff(y.lim) > 0, "ymax", "ymin"), 
+                    ifelse(diff(y.lim) > 0, "ymax_final", "ymin_final")
+                )], 
+                stringsAsFactors = TRUE
+            ) # ymax is top whisker, ymax_final is top dot
+            # stat.coord3 <- data.frame(stat.coord3, Y = vector("numeric", length = nrow(stat.coord3)), stringsAsFactors = TRUE)
+            # check.Y <- as.logical(stat.coord3$Y) # convert everything in Y into FALSE (because Y is full of zero)
+            # end stat coordinates
+            # stat display
+            # performed twice: first for y values >=0, then y values < 0, because only a single value allowed for hjust anf vjust
+            if(stat.mean == FALSE){
+                tempo.center.ref <- "middle"
+            }else{
+                tempo.center.ref <- "MEAN"
+            }
+            # if(is.null(dot.color)){
+            # tempo.low.ref <- "ymin"
+            # tempo.high.ref <- "ymax"
+            # }else{
+            # tempo.low.ref <- "ymin_final"
+            # tempo.high.ref <- "ymax_final"
+            # }
+            # tempo.log.high <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] >= 0}else{stat.coord3[, tempo.center.ref] < 0}
+            # tempo.log.low <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] < 0}else{stat.coord3[, tempo.center.ref] >= 0}
+            # stat.coord3$Y[tempo.log.high] <- stat.coord3[tempo.log.high, tempo.high.ref]
+            # stat.coord3$Y[tempo.log.low] <- stat.coord3[tempo.log.low, tempo.low.ref]
+            # add distance
+            stat.coord3$Y <- stat.coord3$Y + diff(y.lim) * stat.dist / 100
+            # end add distance
+            # correct median or mean text format
+            if(y.log != "no"){
+                stat.coord3[, tempo.center.ref] <- ifelse(y.log == "log2", 2, 10)^(stat.coord3[, tempo.center.ref])
+            }
+            stat.coord3[, tempo.center.ref] <- formatC(stat.coord3[, tempo.center.ref], digit = 2, drop0trailing = TRUE, format = "f")
+            # end correct median or mean text format
+            # if(any(tempo.log.high) == TRUE){
+            # tempo.stat <- stat.coord3[tempo.log.high,]
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
+                data = stat.coord3, 
+                mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref),
+                size = stat.size, 
+                color = "black", 
+                angle = stat.angle, 
+                hjust = stat.just$hjust, 
+                vjust = stat.just$vjust
+            )) # WARNING: no need of order() for labels because box.coord$x set the order
+            coord.names <- c(coord.names, "stat.pos")
+            # }
+            # if(any(tempo.log.low) == TRUE){
+            # tempo.stat <- stat.coord3[tempo.log.low,]
+            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
+            # data = tempo.stat, 
+            # mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref),
+            # size = stat.size, 
+            # color = "black", 
+            # hjust = ifelse(vertical == TRUE, 0.5, 0.5 + stat.dist), 
+            # vjust = ifelse(vertical == TRUE, 0.5 + stat.dist, 0.5)
+            # )) # WARNING: no need of order() for labels because box.coord$x set the order
+            # coord.names <- c(coord.names, "stat.pos.negative")
+            # }
+            # end stat display
         }else{
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
-                text = ggplot2::element_text(size = text.size), 
-                plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
-                line = ggplot2::element_line(size = 0.5), 
-                legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
-                axis.line.y.left = ggplot2::element_line(colour = "black"), 
-                axis.line.x.bottom = ggplot2::element_line(colour = "black"),
-                axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
-                axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)},
-                strip.background = ggplot2::element_rect(fill = NA, colour = NA)
-            ))
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
-    }else if(add.check == TRUE & article == FALSE){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), m.gg <- ggplot2::theme(
-            text = ggplot2::element_text(size = text.size), 
-            plot.title = ggplot2::element_text(size = title.text.size), # stronger than text
-            line = ggplot2::element_line(size = 0.5), 
-            legend.key = ggplot2::element_rect(color = "white", size = 1.5), # size of the frame of the legend
-            panel.background = ggplot2::element_rect(fill = "grey95"), 
-            axis.line.y.left = ggplot2::element_line(colour = "black"), 
-            axis.line.x.bottom = ggplot2::element_line(colour = "black"), 
-            panel.grid.major.x = ggplot2::element_line(colour = "grey85", size = 0.75), 
-            panel.grid.major.y = ggplot2::element_line(colour = "grey85", size = 0.75), 
-            panel.grid.minor.x = ggplot2::element_blank(), 
-            panel.grid.minor.y = ggplot2::element_line(colour = "grey90", size = 0.25), 
-            strip.background = ggplot2::element_rect(fill = NA, colour = NA),
-            axis.text.x = if(vertical == TRUE){ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}else{NULL},
-            axis.text.y = if(vertical == TRUE){NULL}else{ggplot2::element_text(angle = axis.just$angle, hjust = axis.just$hjust, vjust = axis.just$vjust)}
-        ))
     }
-    # Contrary to fun_gg_bar(), cannot plot the boxplot right now, because I need the dots plotted first
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, group = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), color = NA, width = box.width, fill = NA)) # this is to set the graph (i.e., a blanck boxplot to be able to use x coordinates to plot dots before boxes)
-    # end constant part
-    
+    # end stat display
+    # legend management
+    if(legend.show == FALSE){ # must be here because must be before bef.final.plot <- 
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend
+    }
+    # end legend management
     
     
     
-    # graphic info recovery (including means)
-    tempo.graph.info <- ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}) + ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color})')))) # will be recovered later again, when ylim will be considered
-    tempo.yx.ratio <- (tempo.graph.info$layout$panel_params[[1]]$y.range[2] - tempo.graph.info$layout$panel_params[[1]]$y.range[1]) / (tempo.graph.info$layout$panel_params[[1]]$x.range[2] - tempo.graph.info$layout$panel_params[[1]]$x.range[1])
-    box.coord <- tempo.graph.info$data[[2]] # to have the summary statistics of the plot. Contrary to ini.box.plot, now integrates ylim Here because can be required for stat.pos when just box are plotted
-    box.coord$x <- as.numeric(box.coord$x) # because x is of special class that block comparison of values using identical
-    box.coord$PANEL <- as.numeric(box.coord$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
-    box.coord <- box.coord[order(box.coord$group, box.coord$PANEL), ]
-    if( ! (identical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL))){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(tempo.mean$BOX, box.coord$group) & identical(tempo.mean$PANEL, box.coord$PANEL) DO NOT HAVE THE SAME VALUE ORDER")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    # y scale management (cannot be before dot plot management)
+    # the rescaling aspect is complicated and not intuitive. See:
+    # explaination: https://github.com/tidyverse/ggplot2/issues/3948
+    # the oob argument of scale_y_continuous() https://ggplot2.tidyverse.org/reference/scale_continuous.html
+    # see also https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf
+    # secondary ticks
+    bef.final.plot <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + if(vertical == TRUE){ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)}else{ggplot2::coord_flip(ylim = y.lim)}')))) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks and the legend. I DI NOT UNDERSTAND THE COMMENT HERE BECAUSE WE NEED COORD_FLiP
+    tempo.coord <- bef.final.plot$layout$panel_params[[1]]
+    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non NULL or if y.log argument is different from "no")
+    if(y.log != "no"){ # integer main ticks for log2 and log10
+        tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1)
     }else{
-        # tempo <- c(categ, if( ! is.null(dot.color) & ! is.null(dot.categ)){if(dot.categ != ini.dot.categ){dot.categ}}, if( ! is.null(facet.categ)){facet.categ})
-        if(any(names(tempo.mean) %in% names(box.coord), na.rm = TRUE)){
-            names(tempo.mean)[names(tempo.mean) %in% names(box.coord)] <- paste0(names(tempo.mean)[names(tempo.mean) %in% names(box.coord)], ".mean")
+        tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))}
+        if(all(is.na(tempo))){# all() without na.rm -> ok because is.na() cannot be NA
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
         }
-        box.coord <- data.frame(box.coord, tempo.mean, stringsAsFactors = TRUE)
+        tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb))
     }
-    # end graphic info recovery (including means)
-    
-    
-    
-    # stat output (will also serve for boxplot and mean display)
-    # x not added now (to do not have them in stat.nolog)
-    stat <- data.frame(
-        MIN = box.coord$ymin_final, 
-        QUART1 = box.coord$lower, 
-        MEDIAN = box.coord$middle, 
-        MEAN = box.coord$MEAN, 
-        QUART3 = box.coord$upper, 
-        MAX = box.coord$ymax_final, 
-        WHISK_INF = box.coord$ymin, 
-        BOX_INF = box.coord$lower, 
-        NOTCH_INF = box.coord$notchlower, 
-        NOTCH_SUP = box.coord$notchupper, 
-        BOX_SUP = box.coord$upper, 
-        WHISK_SUP = box.coord$ymax, 
-        OUTLIERS = box.coord["outliers"], 
-        tempo.mean[colnames(tempo.mean) != "MEAN"], 
-        COLOR = box.coord$fill, 
-        stringsAsFactors = TRUE
-    ) # box.coord["outliers"] written like this because it is a list. X coordinates not put now because several features to set
-    names(stat)[names(stat) == "outliers"] <- "OUTLIERS"
-    stat.nolog <- stat # stat.nolog ini will serve for outputs
+    y.second.tick.values <- NULL
+    y.second.tick.pos <- NULL
     if(y.log != "no"){
-        stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")] <- ifelse(y.log == "log2", 2, 10)^(stat.nolog[c("MIN", "QUART1", "MEDIAN", "MEAN", "QUART3", "MAX", "WHISK_INF", "BOX_INF", "NOTCH_INF", "NOTCH_SUP", "BOX_SUP", "WHISK_SUP")])
-        stat.nolog$OUTLIERS <- lapply(stat.nolog$OUTLIERS, FUN = function(X){ifelse(y.log == "log2", 2, 10)^X})
+        tempo <- fun_inter_ticks(lim = y.lim, log = y.log)
+        y.second.tick.values <- tempo$values
+        y.second.tick.pos <- tempo$coordinates
+        # if(vertical == TRUE){ # do not remove in case the bug is fixed
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = y.second.tick.pos, yend = y.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80))
+        # }else{ # not working because of the ggplot2 bug
+        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80))
+        # }
+        coord.names <- c(coord.names, "y.second.tick.positions")
+    }else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){
+        # if(y.second.tick.nb > 0){ #inactivated because already checked before
+        if(length(tempo.scale) < 2){
+            tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb")
+            tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ")
+            tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else{
+            tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb)
+        }
+        y.second.tick.values <- tempo$values
+        y.second.tick.pos <- tempo$coordinates
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+            geom = "segment", 
+            y = y.second.tick.pos, 
+            yend = y.second.tick.pos, 
+            x = if(vertical == TRUE){tempo.coord$x.range[1]}else{tempo.coord$y.range[1]}, 
+            xend = if(vertical == TRUE){tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80}else{tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80}
+        ))
+        coord.names <- c(coord.names, "y.second.tick.positions")
     }
-    # end stat output (will also serve for boxplot and mean display)
-    
-    
-    
-    
+    # end y.second.tick.positions
+    # for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous")))
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous(
+        breaks = tempo.scale, 
+        minor_breaks = y.second.tick.pos, 
+        labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 6") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, # == in stop() to be able to add several messages between ==
+        expand = c(0, 0), # remove space after after axis limits
+        limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used
+        oob = scales::rescale_none, 
+        trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this
+    ))
+    if(vertical == TRUE){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(ylim = y.lim)) # problem of ggplot2::ylim() is that it redraws new breaks # coord_cartesian(ylim = y.lim)) not used because bug -> y-axis label disappearance with y.lim decreasing I DO NOT UNDERSTAND THIS MESSAGE WHILE I USE COORD_CARTESIAN # clip = "off" to have secondary ticks outside plot region does not work
+    }else{
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_flip(ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region does not work # create the problem of y-axis label disappearance with y.lim decreasing. IDEM ABOVE
+        
+    }
+    # end y scale management (cannot be before dot plot management)
     
     
-    # x coordinates management (for random plotting and for stat display)
-    # width commputations
-    width.ini <- c(box.coord$xmax - box.coord$xmin)[1] # all the box widths are equal here. Only the first one taken
-    width.correct <- width.ini * box.space / 2
-    if( ! (identical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL))){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nidentical(stat$BOX, box.coord$group) & identical(stat$PANEL, box.coord$PANEL) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }else{
-        stat <- data.frame(
-            stat, 
-            X = box.coord$x, 
-            X_BOX_INF = box.coord$xmin + width.correct, 
-            X_BOX_SUP = box.coord$xmax - width.correct, 
-            X_NOTCH_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) / 2, 
-            X_NOTCH_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) / 2, 
-            X_WHISK_INF = box.coord$x - (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, 
-            X_WHISK_SUP = box.coord$x + (box.coord$x - (box.coord$xmin + width.correct)) * box.whisker.width, 
-            # tempo.mean[colnames(tempo.mean) != "MEAN"], # already added above
-            stringsAsFactors = TRUE
-        )
-        stat$COLOR <- factor(stat$COLOR, levels = unique(categ.color))
-        if( ! all(stat$NOTCH_SUP < stat$BOX_SUP & stat$NOTCH_INF > stat$BOX_INF, na.rm = TRUE) & box.notch == TRUE){
+    # legend management
+    if( ! is.null(legend.width)){
+        legend.final <- fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path) # get legend
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend
+        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
+            legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") SOME NOTCHES ARE BEYOND BOX HINGES. TRY ARGUMENT box.notch = FALSE")
+            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n")
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
     }
-    dot.jitter <- c((box.coord$xmax - width.correct) - (box.coord$xmin + width.correct))[1] * dot.jitter # real dot.jitter. (box.coord$xmin + width.correct) - (box.coord$xmax - width.correct))[1] is the width of the box. Is equivalent to (box.coord$x - (box.coord$xmin + width.correct))[1] * 2
-    # end width commputations
-    if( ! is.null(dot.color)){
-        # random dots
-        if(dot.tidy == FALSE){
-            dot.coord.rd1 <- merge(dot.coord, box.coord[c("fill", "PANEL", "group", "x")], by = c("PANEL", "group"), sort = FALSE) # rd for random. Send the coord of the boxes into the coord data.frame of the dots (in the column x.y). WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column
-            if(nrow(dot.coord.rd1) != nrow(dot.coord)){
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd1 DATA FRAME. CODE HAS TO BE MODIFIED")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            sampled.dot.jitter <- if(nrow(dot.coord.rd1)== 1L){runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2)}else{sample(x = runif(n = nrow(dot.coord.rd1), min = - dot.jitter / 2, max = dot.jitter / 2), size = nrow(dot.coord.rd1), replace = FALSE)}
-            dot.coord.rd2 <- data.frame(dot.coord.rd1, dot.x = dot.coord.rd1$x.y + sampled.dot.jitter, stringsAsFactors = TRUE) # set the dot.jitter thanks to runif and dot.jitter range. Then, send the coord of the boxes into the coord data.frame of the dots (in the column x.y)
-            if(length(categ)== 1L){
-                tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor
-                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
-                verif <- paste0(categ[1], ".check")
-            }else if(length(categ) == 2L){
-                tempo.data1 <- unique(
-                    data.frame(
-                        data1[c(categ[1], categ[2])], 
-                        group = as.integer(factor(paste0(
-                            formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
-                            ".", 
-                            formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
-                        )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
-                    )
-                ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis
-                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
-                names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check")
-                verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check"))
-            }else{
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 3")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            dot.coord.rd3 <- merge(dot.coord.rd2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have replaced by = "group" by intersect("group", "group") because of an error due to wrong group group merging in dot.coord.rd3
-            if(nrow(dot.coord.rd3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.rd3[categ], dot.coord.rd3[verif])$identical.content)){
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.rd3 DATA FRAME. CODE HAS TO BE MODIFIED")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            # end random dots
-        }
-        # tidy dots
-        # coordinates are recovered during plotting (see dot.coord.tidy1 below)
-        # end tidy dots
-    }
-    # end x coordinates management (for random plotting and for stat display)
+    # end legend management
     
     
+    # drawing
+    fin.plot <- suppressMessages(suppressWarnings(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))))
+    grob.save <- NULL
+    if(plot == TRUE){
+        # following lines inactivated because of problem in warn.recov and message.recov
+        # assign("env_fun_get_message", new.env())
+        # assign("tempo.gg.name", tempo.gg.name, envir = env_fun_get_message)
+        # assign("tempo.gg.count", tempo.gg.count, envir = env_fun_get_message)
+        # assign("add", add, envir = env_fun_get_message)
+        # two next line: for the moment, I cannot prevent the warning printing
+        # warn.recov <- fun_get_message(paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}), kind = "warning", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering warnings printed by ggplot() functions
+        # message.recov <- fun_get_message('print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))', kind = "message", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering messages printed by ggplot() functions
+        # if( ! (return == TRUE & return.ggplot == TRUE)){ # because return() plots when return.ggplot is TRUE # finally not used -> see return.ggplot description
+        if(is.null(legend.width)){
+            grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot)))
+        }else{
+            grob.save <-suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width))))
+        }
+        # }
+        # suppressMessages(suppressWarnings(print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))))
+    }else{
+        # following lines inactivated because of problem in warn.recov and message.recov
+        # message.recov <- NULL
+        # warn.recov <- NULL
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    }
+    # end drawing
     
     
     
-    # boxplot display before dot display if box.fill = TRUE
-    coord.names <- NULL
-    # creation of the data frame for (main box + legend) and data frame for means
-    if(box.notch == FALSE){
-        for(i3 in 1:length(categ)){
-            if(i3== 1L){
-                tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE)
-            }else{
-                tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 5)])), stringsAsFactors = TRUE)
-            }
-        }
-        names(tempo.polygon) <- categ
-        tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "BOX_SUP", "BOX_SUP", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE)
-        if( ! is.null(facet.categ)){
-            for(i4 in 1:length(facet.categ)){
-                tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE)
-                names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4]
-            }
-        }
-    }else{
-        for(i3 in 1:length(categ)){
-            if(i3== 1L){
-                tempo.polygon <- data.frame(GROUPX = c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE)
-            }else{
-                tempo.polygon <- cbind(tempo.polygon, c(t(stat[, rep(categ[i3], 11)])), stringsAsFactors = TRUE)
-            }
-        }
-        names(tempo.polygon) <- categ
-        tempo.polygon <- data.frame(X = c(t(stat[, c("X_BOX_INF", "X_BOX_SUP", "X_BOX_SUP", "X_NOTCH_SUP", "X_BOX_SUP", "X_BOX_SUP", "X_BOX_INF", "X_BOX_INF", "X_NOTCH_INF", "X_BOX_INF", "X_BOX_INF")])), Y = c(t(stat[, c("BOX_INF", "BOX_INF", "NOTCH_INF", "MEDIAN", "NOTCH_SUP", "BOX_SUP", "BOX_SUP", "NOTCH_SUP", "MEDIAN", "NOTCH_INF", "BOX_INF")])), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), BOX = as.character(c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX", "BOX")]))), tempo.polygon, stringsAsFactors = TRUE)
-        if( ! is.null(facet.categ)){
-            for(i4 in 1:length(facet.categ)){
-                tempo.polygon <- data.frame(tempo.polygon, c(t(stat[, c(facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4], facet.categ[i4])])), stringsAsFactors = TRUE)
-                names(tempo.polygon)[length(names(tempo.polygon))] <- facet.categ[i4]
-            }
-        }
+    # output
+    # following lines inactivated because of problem in warn.recov and message.recov
+    # if( ! (is.null(warn) & is.null(warn.recov) & is.null(message.recov))){
+    # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0 | length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", ifelse( ! is.null(warn.recov), unique(message.recov), ""), ifelse( ! is.null(message.recov), unique(message.recov), ""), collapse = "\n\n"), "\n\n")})
+    # }else if( ! (is.null(warn) & is.null(warn.recov)) & is.null(message.recov)){
+    # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(warn.recov), collapse = "\n\n"), "\n\n")})
+    # }else if( ! (is.null(warn) & is.null(message.recov)) & is.null(warn.recov)){
+    # warn <- paste0(warn, "\n\n", if(length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(message.recov), collapse = "\n\n"), "\n\n")})
+    # }
+    if(warn.print == TRUE & ! is.null(warn)){
+        on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
     }
-    tempo.polygon$COLOR <- factor(tempo.polygon$COLOR, levels = unique(categ.color))
-    if( ! is.null(categ.class.order)){
-        for(i3 in 1:length(categ)){
-            tempo.polygon[, categ[i3]] <- factor(tempo.polygon[, categ[i3]], levels = categ.class.order[[i3]])
-        }
-    }
-    # modified name of dot.categ column (e.g., "Categ1_DOT") must be included for boxplot using ridy dots
-    if( ! is.null(dot.color) & ! is.null(dot.categ)){
-        if(dot.categ != ini.dot.categ){
-            tempo.polygon <- data.frame(tempo.polygon, GROUPX = tempo.polygon[, ini.dot.categ], stringsAsFactors = TRUE)
-            names(tempo.polygon)[names(tempo.polygon) == "GROUPX"] <- dot.categ
-            
-        }
-    }
-    tempo.diamon.mean <- data.frame(X = c(t(stat[, c("X", "X_NOTCH_INF", "X", "X_NOTCH_SUP", "X")])), Y = c(t(cbind(stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] + (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stat["MEAN"], stat["MEAN"] - (stat[, "X"] - stat[, "X_NOTCH_INF"]) * tempo.yx.ratio, stringsAsFactors = TRUE))), COLOR = c(t(stat[, c("COLOR", "COLOR", "COLOR", "COLOR", "COLOR")])), GROUP = c(t(stat[, c("BOX", "BOX", "BOX", "BOX", "BOX")])), stringsAsFactors = TRUE) # stringsAsFactors = TRUE for cbind() because stat["MEAN"] is a data frame. Otherwise, stringsAsFactors is not an argument for cbind() on vectors
-    if( ! is.null(facet.categ)){
-        for(i3 in 1:length(facet.categ)){
-            tempo.diamon.mean <- data.frame(tempo.diamon.mean, c(t(stat[, c(facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3], facet.categ[i3])])), stringsAsFactors = TRUE)
-            names(tempo.diamon.mean)[length(names(tempo.diamon.mean))] <- facet.categ[i3]
-        }
-    }
-    tempo.diamon.mean$COLOR <- factor(tempo.diamon.mean$COLOR, levels = unique(categ.color))
-    # end creation of the data frame for (main box + legend) and data frame for means
-    if(box.fill == TRUE){
-        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, alpha = box.alpha, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{dot.border.color}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted. Finally, boxplot redrawn (see below)
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon(
-            data = tempo.polygon, 
-            mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", fill = categ[length(categ)], color = categ[length(categ)]), 
-            size = box.line.size, 
-            alpha = box.alpha # works only for fill, not for color
-        ))
-        coord.names <- c(coord.names, "main.box")
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "sup.whisker")
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "inf.whisker")
-        if(box.whisker.width > 0){
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # 
-            coord.names <- c(coord.names, "sup.whisker.edge")
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = "black", size = box.line.size, alpha = box.alpha, lineend = "round")) # 
-            coord.names <- c(coord.names, "inf.whisker.edge")
-        }
-        if(box.mean == TRUE){
-            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, fill = stat$COLOR, size = box.mean.size, color = "black", alpha = box.alpha)) # group used in aesthetic to do not have it in the legend
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_polygon(
-                data = tempo.diamon.mean, 
-                mapping = ggplot2::aes(x = X, y = Y, group = GROUP), 
-                fill = tempo.diamon.mean[, "COLOR"], 
-                color = hsv(0, 0, 0, alpha = box.alpha), # outline of the polygon in black but with alpha
-                size = box.line.size, 
-                alpha = box.alpha
-            ))
-            coord.names <- c(coord.names, "mean")
-        }
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = "black", size = box.line.size * 2, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "median")
-    }
-    # end boxplot display before dot display if box.fill = TRUE
-    
-    
-    
-    
-    
-    
-    # dot display
-    if( ! is.null(dot.color)){
-        if(dot.tidy == FALSE){
-            if(is.null(dot.categ)){
-                if(dot.border.size == 0){
-                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
-                        data = dot.coord.rd3, 
-                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), 
-                        size = dot.size, 
-                        shape = 19, 
-                        color = dot.coord.rd3$dot.color, 
-                        alpha = dot.alpha
-                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
-                }else{
-                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
-                        data = dot.coord.rd3, 
-                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", group = categ[length(categ)]), 
-                        shape = 21, 
-                        stroke = dot.border.size, 
-                        color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, 
-                        size = dot.size, 
-                        fill = dot.coord.rd3$dot.color, 
-                        alpha = dot.alpha
-                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
-                }
-            }else{
-                if(dot.border.size == 0){
-                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
-                        data = dot.coord.rd3, 
-                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), 
-                        size = dot.size, 
-                        shape = 19, 
-                        color = dot.coord.rd3$dot.color
-                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
-                }else{
-                    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(
-                        data = dot.coord.rd3, 
-                        mapping = ggplot2::aes_string(x = "dot.x", y = "y", alpha = dot.categ), 
-                        size = dot.size, 
-                        shape = 21, 
-                        stroke = dot.border.size, 
-                        color = if(is.null(dot.border.color)){dot.coord.rd3$dot.color}else{rep(dot.border.color, nrow(dot.coord.rd3))}, 
-                        fill = dot.coord.rd3$dot.color
-                    )) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
-                }
-                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), guide = ggplot2::guide_legend(override.aes = list(fill = dot.color, color = if(is.null(dot.border.color)){dot.color}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-            }
-            coord.names <- c(coord.names, "dots")
-        }else if(dot.tidy == TRUE){
-            # here plot using group -> no scale
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot(
-                data = dot.coord, 
-                mapping = ggplot2::aes_string(x = categ[1], y = "y", group = "group"), # not dot.categ here because the classes of dot.categ create new separations
-                position = ggplot2::position_dodge(width = box.width), 
-                binpositions = "all", 
-                binaxis = "y", 
-                stackdir = "center", 
-                alpha = dot.alpha, 
-                fill = dot.coord$dot.color, 
-                stroke = dot.border.size, 
-                color = if(is.null(dot.border.color)){dot.coord$dot.color}else{rep(dot.border.color, nrow(dot.coord))}, 
-                show.legend = FALSE, # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual()
-                binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb
-            )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.)
-            coord.names <- c(coord.names, "dots")
-            if( ! is.null(dot.categ)){
-                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_dotplot(
-                    data = dot.coord, 
-                    mapping = ggplot2::aes_string(x = categ[1], y = "y", alpha = dot.categ), # not dot.categ here because the classes of dot.categ create new separations
-                    position = ggplot2::position_dodge(width = box.width), 
-                    binpositions = "all", 
-                    binaxis = "y", 
-                    stackdir = "center", 
-                    fill = NA, 
-                    stroke = NA, 
-                    color = NA, 
-                    # WARNING: do not use show.legend = TRUE because it uses the arguments outside aes() as aesthetics (here color and fill). Thus I must find a way using ggplot2::scale_discrete_manual()
-                    binwidth = (y.lim[2] - y.lim[1]) / dot.tidy.bin.nb
-                )) # geom_dotplot ggplot2 v3.3.0: I had to remove rev() in fill and color # very weird behavior of geom_dotplot ggplot2 v3.2.1, (1) because with aes group = (to avoid legend), the dot plotting is not good in term of coordinates, and (2) because data1 seems reorderer according to x = categ[1] before plotting. Thus, I have to use fill = dot.coord[rev(order(dot.coord[, categ[1]], decreasing = TRUE)), "dot.color"] to have the good corresponding colors # show.legend option do not remove the legend, only the aesthetic of the legend (dot, line, etc.)
-                # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "linetype", name = dot.legend.name, values = rep(1, length(categ.color)))) # values = rep("black", length(categ.color)) are the values of color (which is the border color of dots), and this modify the border color on the plot. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-                coord.names <- c(coord.names, "bad_remove")
-                assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "alpha", name = dot.legend.name, values = rep(dot.alpha, length(dot.categ.class.order)), labels = dot.categ.class.order, guide = ggplot2::guide_legend(title = if(ini.dot.categ == categ[length(categ)]){dot.categ}else{ini.dot.categ}, override.aes = list(fill = levels(dot.coord$dot.color), color = if(is.null(dot.border.color)){levels(dot.coord$dot.color)}else{dot.border.color}, stroke = dot.border.size, alpha = dot.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-            }
-            # coordinates of tidy dots
-            tempo.coord <- ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))$data # to have the tidy dot coordinates
-            if(length(which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))) != 1){ # detect the compartment of tempo.coord which is the binned data frame
-                # if(length(which(sapply(tempo.coord, FUN = nrow) == nrow(data1))) > if(is.null(dot.categ)){1}else{2}){ # this does not work if only one dot per class, thus replaced by above # if(is.null(dot.categ)){1}else{2} because 1 dotplot if dot.categ is NULL and 2 dotplots if not, with the second being a blank dotplot with wrong coordinates. Thus take the first in that situation
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nEITHER MORE THAN 1 OR NO COMPARTMENT HAVING A DATA FRAME WITH binwidth AS COLUMN NAME IN THE tempo.coord LIST (FOR TIDY DOT COORDINATES). CODE HAS TO BE MODIFIED")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }else{
-                # dot.coord.tidy1 <- tempo.coord[[which(sapply(tempo.coord, FUN = nrow) == nrow(data1))[1]]] # this does not work if only one dot per class, thus replaced by above # the second being a blank dotplot with wrong coordinates. Thus take the first whatever situation
-                dot.coord.tidy1 <- tempo.coord[[which(sapply(X = tempo.coord, FUN = function(X){any(names(X) == "binwidth", na.rm = TRUE)}))]] # detect the compartment of tempo.coord which is the binned data frame
-                dot.coord.tidy1$x <- as.numeric(dot.coord.tidy1$x) # because weird class
-                dot.coord.tidy1$PANEL <- as.numeric(dot.coord.tidy1$PANEL) # because numbers as levels. But may be a problem is facet are reordered ?
-            }
-            # tempo.box.coord <- merge(box.coord, unique(dot.coord[, c("PANEL", "group", categ)]), by = c("PANEL", "group"), sort = FALSE) # not required anymore because box.coord already contains categ do not add dot.categ and tidy_group_coord here because the coordinates are for stats. Add the categ in box.coord. WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in box.coord. Thus, no need to consider fill column
-            # below inactivated because not true when dealing with dot.categ different from categ
-            # if(nrow(tempo.box.coord) != nrow(box.coord)){
-            # tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT tempo.box.coord DATA FRAME. CODE HAS TO BE MODIFIED")
-            # stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-            # }
-            dot.coord.tidy2 <- merge(dot.coord.tidy1, box.coord[c("fill", "PANEL", "group", "x", categ)], by = c("PANEL", "group"), sort = FALSE) # send the coord of the boxes into the coord data.frame of the dots (in the column x.y).WARNING: by = c("PANEL", "group") without fill column because PANEL & group columns are enough as only one value of x column per group number in tempo.box.coord. Thus, no need to consider fill colum # DANGER: from here the fill.y and x.y (from tempo.box.coord) are not good in dot.coord.tidy2. It is ok because Categ1 Categ2 from tempo.box.coord are ok with the group column from dot.coord.tidy1. This is due to the fact that dot.coord.tidy resulting from geom_dotplot does not make the same groups as the other functions
-            if(nrow(dot.coord.tidy2) != nrow(dot.coord)){
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy2 DATA FRAME. CODE HAS TO BE MODIFIED")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            # From here, check for dot.coord.tidy3 which wil be important for stat over the plot. WARNING: dot.categ has nothing to do here for stat coordinates. Thus, not in tempo.data1
-            if(length(categ)== 1L){
-                tempo.data1 <- unique(data.frame(data1[categ[1]], group = as.integer(data1[, categ[1]]), stringsAsFactors = TRUE)) # categ[1] is factor
-                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
-                verif <- paste0(categ[1], ".check")
-            }else if(length(categ) == 2L){
-                tempo.data1 <- unique(
-                    data.frame(
-                        data1[c(categ[1], categ[2])], 
-                        group = as.integer(factor(paste0(
-                            formatC(as.integer(data1[, categ[2]]), width = nchar(max(as.integer(data1[, categ[2]]), na.rm = TRUE)), flag = "0"), # convert factor into numeric with leading zero for proper ranking
-                            ".", 
-                            formatC(as.integer(data1[, categ[1]]), width = nchar(max(as.integer(data1[, categ[1]]), na.rm = TRUE)), flag = "0")# convert factor into numeric with leading zero for proper ranking
-                        )), stringsAsFactors = TRUE) # merge the 2 formatC() to create a new factor. The convertion to integer should recreate the correct group number
-                    )
-                ) # categ[2] first if categ[2] is used to make the categories in ggplot and categ[1] is used to make the x-axis
-                names(tempo.data1)[names(tempo.data1) == categ[1]] <- paste0(categ[1], ".check")
-                names(tempo.data1)[names(tempo.data1) == categ[2]] <- paste0(categ[2], ".check")
-                verif <- c(paste0(categ[1], ".check"), paste0(categ[2], ".check"))
-            }else{
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 4")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            dot.coord.tidy3 <- merge(dot.coord.tidy2, tempo.data1, by = intersect("group", "group"), sort = FALSE) # send the factors of data1 into coord. WARNING: I have tested intersect("group", "group") instead of by = "group". May be come back to by = "group" in case of error. But I did this because of an error in dot.coord.rd3 above
-            if(nrow(dot.coord.tidy3) != nrow(dot.coord) | ( ! fun_comp_2d(dot.coord.tidy3[categ], dot.coord.tidy3[verif])$identical.content)){
-                tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nTHE merge() FUNCTION DID NOT RETURN A CORRECT dot.coord.tidy3 DATA FRAME. CODE HAS TO BE MODIFIED")
-                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-            }
-            # end coordinates of tidy dots
+    on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
+    if(return == TRUE){
+        tempo.output <- ggplot2::ggplot_build(fin.plot)
+        tempo.output$data <- tempo.output$data[-1] # remove the first data because corresponds to the initial empty boxplot
+        if(length(tempo.output$data) != length(coord.names)){
+            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nlength(tempo.output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else{
+            names(tempo.output$data) <- coord.names
+            tempo.output$data <- tempo.output$data[coord.names != "bad_remove"]
         }
+        tempo <- tempo.output$layout$panel_params[[1]]
+        output <- list(
+            data = data1.ini, 
+            stat = stat.nolog, 
+            removed.row.nb = removed.row.nb, 
+            removed.rows = removed.rows, 
+            plot = c(tempo.output$data, y.second.tick.values = list(y.second.tick.values)), 
+            panel = facet.categ, 
+            axes = list(
+                x.range = tempo$x.range, 
+                x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
+                x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
+                y.range = tempo$y.range, 
+                y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
+                y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
+            ), 
+            warn = paste0("\n", warn, "\n\n"), 
+            ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE
+            gtable = if(return.gtable == TRUE){grob.save}else{NULL} 
+        )
+        return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
     }
-    # end dot display
-    
-    
-    
-    # boxplot display (if box.fill = FALSE, otherwise, already plotted above)
-    if(box.fill == TRUE){
-        # overcome "work only for the filling of boxes, not for the frame. See https://github.com/tidyverse/ggplot2/issues/252"
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(order = 1))) #, guide = ggplot2::guide_legend(override.aes = list(fill = levels(tempo.polygon$COLOR), color = "black")))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = rep(hsv(0, 0, 0, alpha = box.alpha), length(unique(data1[, categ[length(categ)]]))), guide = ggplot2::guide_legend(order = 1))) # , guide = ggplot2::guide_legend(override.aes = list(color = "black", alpha = box.alpha)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor # outline of the polygon in black but with alpha
-    }else{
-        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_boxplot(data = data1, mapping = ggplot2::aes_string(x = categ[1], y = y, color = categ[length(categ)], fill = categ[length(categ)]), position = ggplot2::position_dodge(width = NULL), width = box.width, size = box.line.size, notch = box.notch, alpha = box.alpha, coef = if(box.whisker.kind == "no"){0}else if(box.whisker.kind == "std"){1.5}else if(box.whisker.kind == "max"){Inf}, outlier.shape = if( ! is.null(dot.color)){NA}else{21}, outlier.color = if( ! is.null(dot.color)){NA}else{if(dot.border.size == 0){NA}else{dot.border.color}}, outlier.fill = if( ! is.null(dot.color)){NA}else{NULL}, outlier.size = if( ! is.null(dot.color)){NA}else{dot.size}, outlier.stroke = if( ! is.null(dot.color)){NA}else{dot.border.size}, outlier.alpha = if( ! is.null(dot.color)){NA}else{dot.alpha})) # the color, size, etc. of the outliers are dealt here. outlier.color = NA to do not plot outliers when dots are already plotted
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path(
-            data = tempo.polygon, 
-            mapping = ggplot2::aes_string(x = "X", y = "Y", group = "BOX", color = categ[length(categ)]), 
-            size = box.line.size, 
-            alpha = box.alpha, 
-            lineend = "round", 
-            linejoin = "round"
-        ))
-        coord.names <- c(coord.names, "main.box")
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = if(box.notch == FALSE){X_BOX_INF}else{X_NOTCH_INF}, xend = if(box.notch == FALSE){X_BOX_SUP}else{X_NOTCH_SUP}, y = MEDIAN, yend = MEDIAN, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size * 2, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "median")
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "sup.whisker")
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X, xend = X, y = BOX_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha)) # 
-        coord.names <- c(coord.names, "inf.whisker")
-        if(box.whisker.width > 0){
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_SUP, yend = WHISK_SUP, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # 
-            coord.names <- c(coord.names, "sup.whisker.edge")
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_segment(data = stat, mapping = ggplot2::aes(x = X_WHISK_INF, xend = X_WHISK_SUP, y = WHISK_INF, yend = WHISK_INF, group = categ[length(categ)]), color = stat$COLOR, size = box.line.size, alpha = box.alpha, lineend = "round")) # 
-            coord.names <- c(coord.names, "inf.whisker.edge")
-        }
-        if(box.mean == TRUE){
-            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_point(data = stat, mapping = ggplot2::aes_string(x = "X", y = "MEAN", group = categ[length(categ)]), shape = 23, stroke = box.line.size * 2, color = stat$COLOR, size = box.mean.size, fill = NA, alpha = box.alpha)) # group used in aesthetic to do not have it in the legend. Here ggplot2::scale_discrete_manual() cannot be used because of the group easthetic
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_path(
-                data = tempo.diamon.mean, 
-                mapping = ggplot2::aes(x = X, y = Y, group = GROUP), 
-                color = tempo.diamon.mean[, "COLOR"], 
-                size = box.line.size, 
-                alpha = box.alpha, 
-                lineend = "round", 
-                linejoin = "round"
-            ))
-            coord.names <- c(coord.names, "mean")
-        }
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "fill", name = box.legend.name, values = rep(NA, length(unique(data1[, categ[length(categ)]]))))) #, guide = ggplot2::guide_legend(override.aes = list(color = categ.color)))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_discrete_manual(aesthetics = "color", name = box.legend.name, values = if(length(categ.color)== 1L){rep(categ.color, length(unique(data1[, categ[length(categ)]])))}else{categ.color}, guide = ggplot2::guide_legend(override.aes = list(alpha = if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){1}else{box.alpha})))) # , guide = ggplot2::guide_legend(override.aes = list(color = as.character(categ.color))))) # values are the values of color (which is the border color in geom_box. WARNING: values = categ.color takes the numbers to make the colors if categ.color is a factor
-        if(plot == TRUE & ((length(dev.list()) > 0 & names(dev.cur()) == "windows") | (length(dev.list()) == 0L & Sys.info()["sysname"] == "Windows"))){ # if any Graph device already open and this device is "windows", or if no Graph device opened yet and we are on windows system -> prevention of alpha legend bug on windows using value 1
-            # to avoid a bug on windows: if alpha argument is different from 1 for lines (transparency), then lines are not correctly displayed in the legend when using the R GUI (bug https://github.com/tidyverse/ggplot2/issues/2452). No bug when using a pdf
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") GRAPHIC DEVICE USED ON A WINDOWS SYSTEM ->\nTRANSPARENCY OF THE LINES IS INACTIVATED IN THE LEGEND TO PREVENT A WINDOWS DEPENDENT BUG (SEE https://github.com/tidyverse/ggplot2/issues/2452)\nTO OVERCOME THIS ON WINDOWS, USE ANOTHER DEVICE (pdf() FOR INSTANCE)")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    # end output
+    # end main code
+}
+
+
+
+
+
+# add density
+# rasterise all kind: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html
+
+
+fun_gg_scatter <- function(
+    data1, 
+    x, 
+    y, 
+    categ = NULL, 
+    categ.class.order = NULL, 
+    color = NULL, 
+    geom = "geom_point", 
+    geom.step.dir = "hv", 
+    geom.stick.base = NULL, 
+    alpha = 0.5, 
+    dot.size = 2, 
+    dot.shape = 21, 
+    dot.border.size = 0.5, 
+    dot.border.color = NULL, 
+    line.size = 0.5, 
+    line.type = "solid", 
+    x.lim = NULL, 
+    x.lab = NULL, 
+    x.log = "no", 
+    x.tick.nb = NULL, 
+    x.second.tick.nb = NULL, 
+    x.include.zero = FALSE, 
+    x.left.extra.margin = 0.05, 
+    x.right.extra.margin = 0.05, 
+    x.text.angle = 0, 
+    y.lim = NULL, 
+    y.lab = NULL, 
+    y.log = "no", 
+    y.tick.nb = NULL, 
+    y.second.tick.nb = NULL, 
+    y.include.zero = FALSE, 
+    y.top.extra.margin = 0.05, 
+    y.bottom.extra.margin = 0.05, 
+    y.text.angle = 0, 
+    raster = FALSE, 
+    raster.ratio = 1, 
+    raster.threshold = NULL, 
+    text.size = 12, 
+    title = "", 
+    title.text.size = 12, 
+    legend.show = TRUE, 
+    legend.width = 0.5, 
+    legend.name = NULL, 
+    article = TRUE, 
+    grid = FALSE, 
+    add = NULL, 
+    return = FALSE, 
+    return.ggplot = FALSE,
+    return.gtable = TRUE,
+    plot = TRUE, 
+    warn.print = FALSE, 
+    lib.path = NULL
+){
+    # AIM
+    # Plot ggplot2 scatterplot with the possibility to overlay dots from up to 3 different data frames (-> three different legends) and lines from up to 3 different data frames (-> three different legends) -> up to 6 overlays totally
+    # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html
+    # WARNINGS
+    # Rows containing NA in data1[, c(x, y, categ)] will be removed before processing, with a warning (see below)
+    # Size arguments (dot.size, dot.border.size, line.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
+    # ARGUMENTS
+    # data1: a dataframe compatible with ggplot2, or a list of data frames. Order matters for the order of the legend and for the layer staking (starting from below to top)
+    # x: single character string of the data1 column name for x-axis coordinates. If data1 is a list, then x must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_hline" in geom argument
+    # y: single character string of the data1 column name for y-axis coordinates. If data1 is a list, then y must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_vline" in geom argument
+    # categ: either NULL or a single character string or a list of single character strings, indicating the data1 column names to use for categories which creates legend display
+    # If categ == NULL, no categories -> no legend displayed
+    # If data1 is a data frame, categ must be a single character string of the data1 column name for categories
+    # If data1 is a list, then categ must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (no legend display for these compartments), and other not
+    # categ.class.order: either (1) NULL or (2) a vector of character strings or (3) a list of these vectors, setting the order of the classes of categ in the legend display
+    # If categ.class.order is NULL, classes are represented according to the alphabetical order
+    # If data1 is a data frame, categ.class.order must be a vector of character strings specifying the different classes in the categ column name of data1
+    # If data1 is a list, then categ.class.order must be a list of vector of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (alphabetical order for these compartments), and other not
+    # color: either (1) NULL, or (2) a vector of character strings or integers, or (3) a list of vectors of character strings or integers
+    # If color is NULL, default colors of ggplot2
+    # If data1 is a data frame, color argument can be either:
+    # (1) a single color string. All the dots of the corresponding data1 will have this color, whatever the categ value (NULL or not)
+    # (2) if categ is non-null, a vector of string colors, one for each class of categ. Each color will be associated according to the categ.class.order argument if specified, or to the alphabetical order of categ classes otherwise
+    # (3) if categ is non-null, a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ and a single class of categ per color must be respected
+    # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the max integer value among all the integers in color (see fun_gg_palette())
+    # If data1 is a list, then color argument must be either: 
+    # (1) a list of character strings or integers, of same size as data1, with compartment 1 related to compartment 1 of data1, etc.
+    # (2) a single character string or a single integer
+    # With a list (first possibility), the rules described for when data1 is a data frame apply to each compartment of the list. Some of the compartments can be NULL. In that case, a different grey color will be used for each NULL compartment. With a single value (second possibility), the same color will be used for all the dots and lines, whatever the data1 list
+    # geom: single character string of the kind of plot, or a list of single character strings
+    # Either:
+    # "geom_point" (scatterplot)
+    # "geom_line" (coordinates plotted then line connection, from the lowest to highest x coordinates first and from the lowest to highest y coordinates thenafter)
+    # "geom_path" (coordinates plotted then line connection respecting the row order in data1)
+    # "geom_step" coordinates plotted then line connection respecting the row order in data1 but drawn in steps). See the geom.step.dir argument
+    # "geom_hline" (horizontal line, no x value provided)
+    # "geom_vline" (vertical line, no y value provided)
+    # "geom_stick" (dots as vertical bars)
+    # If data1 is a list, then geom must be either:
+    # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc.
+    # (2) a single character string. In that case the same kind of plot will apply for the different compartments of the data1 list
+    # WARNING concerning "geom_hline" or "geom_vline":
+    # (1) x or y argument must be NULL, respectively
+    # (2) x.lim or y.lim argument must NOT be NULL, respectively, if only these kind of lines are drawn (if other geom present, then x.lim = NULL and y.lim = NULL will generate x.lim and y.lim defined by these other geom, which is not possible with "geom_hline" or "geom_vline" alone)
+    # (3) the function will draw n lines for n values in the x argument column name of the data1 data frame. If several colors required, the categ argument must be specified and the corresponding categ column name must exist in the data1 data frame with a different class name for each row
+    # geom.step.dir: single character string indicating the direction when using "geom_step" of the geom argument, or a list of single character strings
+    # Either:
+    # "vh" (vertical then horizontal)
+    # "hv" (horizontal then vertical)
+    # "mid" (step half-way between adjacent x-values)
+    # See https://ggplot2.tidyverse.org/reference/geom_path.html
+    # If data1 is a list, then geom.step.dir must be either:
+    # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. The value in compartments related to other geom values than "geom_step" will be ignored
+    # (2) a single character string, which will be used for all the "geom_step" values of the geom argument, whatever the data1 list
+    # geom.stick.base: either (1) NULL or (2) a single numeric value or (3) a list of single numeric values, setting the base of the sticks when using "geom_stick" of the geom argument
+    # If geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base
+    # If data1 is a list, then geom.stick.base must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the values in compartments related to other geom values than "geom_stick" will be ignored. With a single value (latter possibility), the same base will be used for all the sticks, whatever the data1 list
+    # Warning: the y-axis limits are not modified by the value of geom.stick.base, meaning that this value can be outside of the range of y.lim. Add the value of geom.stick.base also in the y.lim argument if required
+    # Warning: if geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base. Thus, be careful with inverted y-axis
+    # alpha: single numeric value (from 0 to 1) of transparency. If data1 is a list, then alpha must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. In that case the same transparency will apply for the different compartments of the data1 list
+    # dot.size: single numeric value of dot shape radius? in mm. If data1 is a list, then dot.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.size will be used for all the dots, whatever the data1 list
+    # dot.shape: value indicating the shape of the dots (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then dot.shape must be either (1) a list of single shape values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single shape value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.shape will be used for all the dots, whatever the data1 list
+    # dot.border.size: single numeric value of border dot width in mm. Write zero for no dot border. If data1 is a list, then dot.border.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.border.size will be used for all the dots, whatever the data1 list
+    # dot.border.color: single character color string defining the color of the dot border (same border color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette()
+    # line.size: single numeric value of line width in mm. If data1 is a list, then line.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.size will be used for all the lines, whatever the data1 list
+    # line.type: value indicating the kind of lines (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then line.type must be either (1) a list of single line kind values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single line kind value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.type will be used for all the lines, whatever the data1 list
+    # x.lim: 2 numeric values setting the x-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the x column name of data1 will be used
+    # x.lab: a character string or expression for x-axis label. If NULL, will use the first value of x (x column name of the first data frame in data1). Warning message if the elements in x are different between data frames in data1
+    # x.log: either "no", "log2" (values in the x column name of the data1 data frame will be log2 transformed and x-axis will be log2 scaled) or "log10" (values in the x column name of the data1 data frame will be log10 transformed and x-axis will be log10 scaled)
+    # x.tick.nb: approximate number of desired values labeling the x-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if x.log is "no", then the number of labeling values is set by ggplot2. If NULL and if x.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the x.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for x.lim = c(9, 1200)). WARNING: if non-NULL and if x.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
+    # x.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if x.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$x.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
+    # x.include.zero: logical. Does x.lim range include 0? Ignored if x.log is "log2" or "log10"
+    # x.left.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to x.lim. If different from 0, add the range of the axis multiplied by x.left.extra.margin (e.g., abs(x.lim[2] - x.lim[1]) * x.left.extra.margin) to the left of x-axis
+    # x.right.extra.margin: idem as x.left.extra.margin but to the right of x-axis
+    # x.text.angle: integer value of the text angle for the x-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
+    # y.lim: 2 numeric values setting the y-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the y column name of data1 will be used
+    # y.lab: a character string or expression for y-axis label. If NULL, will use the first value of y (y column name of the first data frame in data1). Warning message if the elements in y are different between data frames in data1
+    # y.log: either "no", "log2" (values in the y column name of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y column name of the data1 data frame will be log10 transformed and y-axis will be log10 scaled)
+    # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
+    # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
+    # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10"
+    # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis
+    # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis
+    # y.text.angle: integer value of the text angle for the y-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
+    # raster: logical. Dots in raster mode? If FALSE, dots from each "geom_point" from geom argument are plotted in vectorial mode (bigger pdf and long to display if lots of dots). If TRUE, dots from each "geom_point" from geom argument are plotted in matricial mode (smaller pdf and easy display if lots of dots, but it takes time to generate the layer). If TRUE, the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If TRUE, solve the transparency problem with some GUI. Overriden by the non-NULL raster.threshold argument
+    # raster.ratio: single numeric value indicating the height / width ratio of the graphic device used (for instance provided by the $dim compartment in the output of the fun_open() function). The default value is 1 because by default R opens a square graphic device. But this argument has to be set when using other device dimensions. Ignored if raster == FALSE
+    # raster.threshold: positive integer value indicating the limit of the dot number above which "geom_point" layers from the geom argument switch from vectorial mode to matricial mode (see the raster argument). If any layer is matricial, then the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If non-NULL, it overrides the raster argument
+    # text.size: numeric value of the font size of the (1) axis numbers and axis legends and (2) texts in the graphic legend (in mm)
+    # title: character string of the graph title
+    # title.text.size: numeric value of the title font size in mm
+    # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not
+    # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend
+    # legend.name: character string of the legend title. If legend.name is NULL and categ argument is not NULL, then legend.name <- categ. If data1 is a list, then legend.name must be a list of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL, and other not
+    # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme
+    # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present)
+    # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL
+    # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()"
+    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_scatter() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
+    # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions
+    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_scatter() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
+    # return: logical. Return the graph parameters?
+    # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_scatter() function (e.g., a <- fun_gg_scatter()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
+    # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
+    # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
+    # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
+    # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
+    # RETURN
+    # a scatter plot if plot argument is TRUE
+    # a list of the graph info if return argument is TRUE:
+    # $data: the initial data with graphic information added. WARNING: if the x.log or y.log argument is not "no", x or y argument column of the data1 data frame are log2 or log10 converted in $data, respectively. Use 2^values or 10^$values to recover the initial values
+    # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed
+    # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed
+    # $plot: the graphic box and dot coordinates
+    # $dots: dot coordinates
+    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
+    # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
+    # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
+    # $axes: the x-axis and y-axis info
+    # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
+    # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
+    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
+    # REQUIRED PACKAGES
+    # ggplot2
+    # gridExtra
+    # lemon (in case of use in the add argument)
+    # scales
+    # if raster plots are drawn (see the raster and raster.threshold arguments):
+    # Cairo
+    # grid
+    # REQUIRED FUNCTIONS FROM THE cute PACKAGE
+    # fun_gg_empty_graph()
+    # fun_gg_palette()
+    # fun_gg_point_rast()
+    # fun_pack()
+    # fun_check()
+    # fun_round()
+    # fun_scale()
+    # fun_inter_ticks()
+    # EXAMPLES
+    # set.seed(1) ; obs1 <- data.frame(Km = c(2, 1, 6, 5, 4, 7), Time = c(2, 1, 6, 5, 4, 7)^2, Car = c("TUUT", "TUUT", "TUUT", "WIIM", "WIIM", "WIIM"), Color1 = rep(c("coral", "lightblue"), each = 3), stringsAsFactors = TRUE) ; fun_gg_scatter(data1 = obs1, x = "Km", y = "Time")
+    # DEBUGGING
+    # set.seed(1) ; obs1 <- data.frame(km = rnorm(1000, 10, 3), time = rnorm(1000, 10, 3), group1 = rep(c("A1", "A2"), 500), stringsAsFactors = TRUE) ; obs2 <-data.frame(km = rnorm(1000, 15, 3), time = rnorm(1000, 15, 3), group2 = rep(c("G1", "G2"), 500), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$km[2:3] <- NA ; data1 = list(L1 = obs1, L2 = obs2) ; x = list(L1 = "km", L2 = "km") ; y = list(L1 = "time", L2 = "time") ; categ = list(L1 = "group1", L2 = "group2") ; categ = NULL ; categ.class.order = NULL ; color = NULL ; geom = "geom_point" ; geom.step.dir = "hv" ; geom.stick.base = NULL ; alpha = 0.5 ; dot.size = 2 ; dot.shape = 21 ; dot.border.size = 0.5 ; dot.border.color = NULL ; line.size = 0.5 ; line.type = "solid" ; x.lim = NULL ; x.lab = NULL ; x.log = "no" ; x.tick.nb = NULL ; x.second.tick.nb = NULL ; x.include.zero = FALSE ; x.left.extra.margin = 0.05 ; x.right.extra.margin = 0.05 ; x.text.angle = 0 ; y.lim = NULL ; y.lab = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = NULL ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; y.text.angle = 0 ; raster = FALSE ; raster.ratio = 1 ; raster.threshold = NULL ; text.size = 12 ; title = "" ; title.text.size = 12 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
+    # function name
+    function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()")
+    arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments
+    arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
+    # end function name
+    # required function checking
+    req.function <- c(
+        "fun_check", 
+        "fun_gg_just", 
+        "fun_gg_empty_graph", 
+        "fun_gg_palette", 
+        "fun_gg_point_rast", 
+        "fun_round", 
+        "fun_pack", 
+        "fun_scale", 
+        "fun_inter_ticks"
+    )
+    tempo <- NULL
+    for(i1 in req.function){
+        if(length(find(i1, mode = "function"))== 0L){
+            tempo <- c(tempo, i1)
         }
     }
-    if(box.alpha == 0){ # remove box legend because no boxes drawn
-        # add this after the scale_xxx_manual() for boxplots
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none")) # inactivate the legend
+    if( ! is.null(tempo)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n"))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    # end boxplot display (if box.fill = FALSE, otherwise, already plotted above)
-    
-    
-    
-    
-    # stat display
-    # layer after dots but ok, behind dots on the plot
-    if( ! is.null(stat.pos)){
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") NUMBERS DISPLAYED ARE ", ifelse(stat.mean == FALSE, "MEDIANS", "MEANS"))
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-        if(stat.pos == "top"){
-            tempo.stat <- data.frame(stat, Y = y.lim[2], stringsAsFactors = TRUE) # I had to create a data frame for geom_tex() so that facet is taken into account, (ggplot2::annotate() does not deal with facet because no data and mapping arguments). Of note, facet.categ is in tempo.stat, via tempo.mean, via dot.coord
-            if(stat.mean == FALSE){tempo.stat$MEDIAN <- formatC(stat.nolog$MEDIAN, digit = 2, drop0trailing = TRUE, format = "f")}else{tempo.stat$MEAN <- formatC(stat.nolog$MEAN, digit = 2, drop0trailing = TRUE, format = "f")}
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
-                data = tempo.stat, 
-                mapping = ggplot2::aes_string(x = "X", y = "Y", label = ifelse(stat.mean == FALSE, "MEDIAN", "MEAN")),
-                size = stat.size, 
-                color = "black", 
-                angle = stat.angle, 
-                hjust = stat.just$hjust, 
-                vjust = stat.just$vjust
-            )) # stat$X used here because identical to stat.nolog but has the X. WARNING: no need of order() for labels because box.coord$x set the order. For justification, see https://stackoverflow.com/questions/7263849/what-do-hjust-and-vjust-do-when-making-a-plot-using-ggplot
-            coord.names <- c(coord.names, "stat.pos")
-        }else if(stat.pos == "above"){
-            # stat coordinates
-            if( ! is.null(dot.color)){ # for text just above max dot
-                if(dot.tidy == FALSE){
-                    tempo.stat.ini <- dot.coord.rd3
-                }else if(dot.tidy == TRUE){
-                    tempo.stat.ini <- dot.coord.tidy3
-                    tempo.stat.ini$x.y <- tempo.stat.ini$x.x # this is just to be able to use tempo.stat.ini$x.y for untidy or tidy dots (remember that dot.coord.tidy3$x.y is not good, see above)
-                }
-                stat.coord1 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = min, na.rm = TRUE)
-                names(stat.coord1)[names(stat.coord1) == "y"] <- "dot.min"
-                stat.coord2 <- aggregate(x = tempo.stat.ini["y"], by = {x.env <- if(length(categ)== 1L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]])}else if(length(categ) == 2L){list(tempo.stat.ini$group, tempo.stat.ini$PANEL, tempo.stat.ini$x.y, tempo.stat.ini[, categ[1]], tempo.stat.ini[, categ[2]])} ; names(x.env) <- if(length(categ)== 1L){c("group", "PANEL", "x.y", categ[1])}else if(length(categ) == 2L){c("group", "PANEL", "x.y", categ[1], categ[2])} ; x.env}, FUN = max, na.rm = TRUE)
-                names(stat.coord2) <- paste0(names(stat.coord2), "_from.dot.max")
-                names(stat.coord2)[names(stat.coord2) == "y_from.dot.max"] <- "dot.max"
-                stat.coord3 <- cbind(box.coord[order(box.coord$group, box.coord$PANEL), ], stat.coord1[order(stat.coord1$group, stat.coord1$x.y), ], stat.coord2[order(stat.coord2$group, stat.coord2$x.y), ], stringsAsFactors = TRUE) # 
-                if( ! all(identical(round(stat.coord3$x, 9), round(as.numeric(stat.coord3$x.y), 9)), na.rm = TRUE)){ # as.numeric() because stat.coord3$x is class "mapped_discrete" "numeric"
-                    tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nFUSION OF box.coord, stat.coord1 AND stat.coord2 ACCORDING TO box.coord$x, stat.coord1$x.y AND stat.coord2$x.y IS NOT CORRECT. CODE HAS TO BE MODIFIED")
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }
-                # text.coord <- stat.coord3[, c("x", "group", "dot.min", "dot.max")]
-                # names(text.coord)[names(text.coord) == "dot.min"] <- "text.min.pos"
-                #names(text.coord)[names(text.coord) == "dot.max"] <- "text.max.pos"
-                box.coord <- box.coord[order(box.coord$x, box.coord$group, box.coord$PANEL), ]
-                # text.coord <- text.coord[order(text.coord$x), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same
-                stat.coord3 <- stat.coord3[order(stat.coord3$x, stat.coord3$group, stat.coord3$PANEL), ] # to be sure to have the two objects in the same order for x. WARNING: cannot add identical(as.integer(text.coord$group), as.integer(box.coord$group)) because with error, the correspondence between x and group is not the same
-                if( ! (identical(box.coord$x, stat.coord3$x) & identical(box.coord$group, stat.coord3$group) & identical(box.coord$PANEL, stat.coord3$PANEL))){
-                    tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\ntext.coord AND box.coord DO NOT HAVE THE SAME x, group AND PANEL COLUMN CONTENT")
-                    stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-                }
-            }else{
-                stat.coord3 <- box.coord
-            }
-            stat.coord3 <- data.frame(
-                stat.coord3, 
-                Y = stat.coord3[, ifelse(
-                    is.null(dot.color), 
-                    ifelse(diff(y.lim) > 0, "ymax", "ymin"), 
-                    ifelse(diff(y.lim) > 0, "ymax_final", "ymin_final")
-                )], 
-                stringsAsFactors = TRUE
-            ) # ymax is top whisker, ymax_final is top dot
-            # stat.coord3 <- data.frame(stat.coord3, Y = vector("numeric", length = nrow(stat.coord3)), stringsAsFactors = TRUE)
-            # check.Y <- as.logical(stat.coord3$Y) # convert everything in Y into FALSE (because Y is full of zero)
-            # end stat coordinates
-            # stat display
-            # performed twice: first for y values >=0, then y values < 0, because only a single value allowed for hjust anf vjust
-            if(stat.mean == FALSE){
-                tempo.center.ref <- "middle"
-            }else{
-                tempo.center.ref <- "MEAN"
-            }
-            # if(is.null(dot.color)){
-            # tempo.low.ref <- "ymin"
-            # tempo.high.ref <- "ymax"
-            # }else{
-            # tempo.low.ref <- "ymin_final"
-            # tempo.high.ref <- "ymax_final"
-            # }
-            # tempo.log.high <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] >= 0}else{stat.coord3[, tempo.center.ref] < 0}
-            # tempo.log.low <- if(diff(y.lim) > 0){stat.coord3[, tempo.center.ref] < 0}else{stat.coord3[, tempo.center.ref] >= 0}
-            # stat.coord3$Y[tempo.log.high] <- stat.coord3[tempo.log.high, tempo.high.ref]
-            # stat.coord3$Y[tempo.log.low] <- stat.coord3[tempo.log.low, tempo.low.ref]
-            # add distance
-            stat.coord3$Y <- stat.coord3$Y + diff(y.lim) * stat.dist / 100
-            # end add distance
-            # correct median or mean text format
-            if(y.log != "no"){
-                stat.coord3[, tempo.center.ref] <- ifelse(y.log == "log2", 2, 10)^(stat.coord3[, tempo.center.ref])
-            }
-            stat.coord3[, tempo.center.ref] <- formatC(stat.coord3[, tempo.center.ref], digit = 2, drop0trailing = TRUE, format = "f")
-            # end correct median or mean text format
-            # if(any(tempo.log.high) == TRUE){
-            # tempo.stat <- stat.coord3[tempo.log.high,]
-            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
-                data = stat.coord3, 
-                mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref),
-                size = stat.size, 
-                color = "black", 
-                angle = stat.angle, 
-                hjust = stat.just$hjust, 
-                vjust = stat.just$vjust
-            )) # WARNING: no need of order() for labels because box.coord$x set the order
-            coord.names <- c(coord.names, "stat.pos")
-            # }
-            # if(any(tempo.log.low) == TRUE){
-            # tempo.stat <- stat.coord3[tempo.log.low,]
-            # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
-            # data = tempo.stat, 
-            # mapping = ggplot2::aes_string(x = "x", y = "Y", label = tempo.center.ref),
-            # size = stat.size, 
-            # color = "black", 
-            # hjust = ifelse(vertical == TRUE, 0.5, 0.5 + stat.dist), 
-            # vjust = ifelse(vertical == TRUE, 0.5 + stat.dist, 0.5)
-            # )) # WARNING: no need of order() for labels because box.coord$x set the order
-            # coord.names <- c(coord.names, "stat.pos.negative")
-            # }
-            # end stat display
-        }else{
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 5")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }
+    # end required function checking
+    # reserved words to avoid bugs (used in this function)
+    reserved.words <- c("fake_x", "fake_y", "fake_categ")
+    # end reserved words to avoid bugs (used in this function)
+    # arg with no default values
+    mandat.args <- c(
+        "data1", 
+        "x", 
+        "y"
+    )
+    tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")")))
+    if(any(tempo)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n"))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
-    # end stat display
-    # legend management
-    if(legend.show == FALSE){ # must be here because must be before bef.final.plot <- 
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend
+    # end arg with no default values
+    # argument primary checking
+    arg.check <- NULL #
+    text.check <- NULL #
+    checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools
+    ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name))
+    tempo1 <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name)
+    tempo2 <- fun_check(data = data1, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
     }
-    # end legend management
-    
-    
-    
-    # y scale management (cannot be before dot plot management)
-    # the rescaling aspect is complicated and not intuitive. See:
-    # explaination: https://github.com/tidyverse/ggplot2/issues/3948
-    # the oob argument of scale_y_continuous() https://ggplot2.tidyverse.org/reference/scale_continuous.html
-    # see also https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf
-    # secondary ticks
-    bef.final.plot <- ggplot2::ggplot_build(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + if(vertical == TRUE){ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)}else{ggplot2::coord_flip(ylim = y.lim)}')))) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks and the legend. I DI NOT UNDERSTAND THE COMMENT HERE BECAUSE WE NEED COORD_FLiP
-    tempo.coord <- bef.final.plot$layout$panel_params[[1]]
-    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non NULL or if y.log argument is different from "no")
-    if(y.log != "no"){ # integer main ticks for log2 and log10
-        tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1)
-    }else{
-        tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))}
-        if(all(is.na(tempo))){# all() without na.rm -> ok because is.na() cannot be NA
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    if( ! is.null(x)){
+        tempo1 <- fun_check(data = x, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = x, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
         }
-        tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb))
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    y.second.tick.values <- NULL
-    y.second.tick.pos <- NULL
-    if(y.log != "no"){
-        tempo <- fun_inter_ticks(lim = y.lim, log = y.log)
-        y.second.tick.values <- tempo$values
-        y.second.tick.pos <- tempo$coordinates
-        # if(vertical == TRUE){ # do not remove in case the bug is fixed
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = y.second.tick.pos, yend = y.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80))
-        # }else{ # not working because of the ggplot2 bug
-        # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80))
-        # }
-        coord.names <- c(coord.names, "y.second.tick.positions")
-    }else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){
-        # if(y.second.tick.nb > 0){ #inactivated because already checked before
-        if(length(tempo.scale) < 2){
-            tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb")
-            tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ")
-            tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
-            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-        }else{
-            tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb)
+    if( ! is.null(y)){
+        tempo1 <- fun_check(data = y, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = y, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
         }
-        y.second.tick.values <- tempo$values
-        y.second.tick.pos <- tempo$coordinates
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-            geom = "segment", 
-            y = y.second.tick.pos, 
-            yend = y.second.tick.pos, 
-            x = if(vertical == TRUE){tempo.coord$x.range[1]}else{tempo.coord$y.range[1]}, 
-            xend = if(vertical == TRUE){tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80}else{tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80}
-        ))
-        coord.names <- c(coord.names, "y.second.tick.positions")
-    }
-    # end y.second.tick.positions
-    # for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous")))
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous(
-        breaks = tempo.scale, 
-        minor_breaks = y.second.tick.pos, 
-        labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 6") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, # == in stop() to be able to add several messages between ==
-        expand = c(0, 0), # remove space after after axis limits
-        limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used
-        oob = scales::rescale_none, 
-        trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this
-    ))
-    if(vertical == TRUE){
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(ylim = y.lim)) # problem of ggplot2::ylim() is that it redraws new breaks # coord_cartesian(ylim = y.lim)) not used because bug -> y-axis label disappearance with y.lim decreasing I DO NOT UNDERSTAND THIS MESSAGE WHILE I USE COORD_CARTESIAN # clip = "off" to have secondary ticks outside plot region does not work
     }else{
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_flip(ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region does not work # create the problem of y-axis label disappearance with y.lim decreasing. IDEM ABOVE
-        
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = y, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    # end y scale management (cannot be before dot plot management)
-    
-    
-    # legend management
-    if( ! is.null(legend.width)){
-        legend.final <- fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path) # get legend
-        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none", color = "none", alpha = "none")) # inactivate the initial legend
-        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
-            legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
-            warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n")
-            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    if( ! is.null(categ)){
+        tempo1 <- fun_check(data = categ, class = "vector", mode = "character", length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = categ, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
         }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = categ, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    # end legend management
-    
-    
-    # drawing
-    fin.plot <- suppressMessages(suppressWarnings(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))))
-    grob.save <- NULL
-    if(plot == TRUE){
-        # following lines inactivated because of problem in warn.recov and message.recov
-        # assign("env_fun_get_message", new.env())
-        # assign("tempo.gg.name", tempo.gg.name, envir = env_fun_get_message)
-        # assign("tempo.gg.count", tempo.gg.count, envir = env_fun_get_message)
-        # assign("add", add, envir = env_fun_get_message)
-        # two next line: for the moment, I cannot prevent the warning printing
-        # warn.recov <- fun_get_message(paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}), kind = "warning", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering warnings printed by ggplot() functions
-        # message.recov <- fun_get_message('print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))', kind = "message", header = FALSE, print.no = FALSE, env = env_fun_get_message) # for recovering messages printed by ggplot() functions
-        # if( ! (return == TRUE & return.ggplot == TRUE)){ # because return() plots when return.ggplot is TRUE # finally not used -> see return.ggplot description
-        if(is.null(legend.width)){
-            grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot)))
-        }else{
-            grob.save <-suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width))))
+    if( ! is.null(categ.class.order)){
+        if(is.null(categ)){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT IS NOT NULL, BUT categ IS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+        tempo1 <- fun_check(data = categ.class.order, class = "vector", mode = "character", fun.name = function.name)
+        tempo2 <- fun_check(data = categ.class.order, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A VECTOR OF CHARACTER STRINGS OR A LIST OF VECTOR OF CHARACTER STRINGS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
         }
-        # }
-        # suppressMessages(suppressWarnings(print(eval(parse(text = paste(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), if(is.null(add)){NULL}else{add}))))))
     }else{
-        # following lines inactivated because of problem in warn.recov and message.recov
-        # message.recov <- NULL
-        # warn.recov <- NULL
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = categ.class.order, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    # end drawing
-    
-    
-    
-    # output
-    # following lines inactivated because of problem in warn.recov and message.recov
-    # if( ! (is.null(warn) & is.null(warn.recov) & is.null(message.recov))){
-    # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0 | length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", ifelse( ! is.null(warn.recov), unique(message.recov), ""), ifelse( ! is.null(message.recov), unique(message.recov), ""), collapse = "\n\n"), "\n\n")})
-    # }else if( ! (is.null(warn) & is.null(warn.recov)) & is.null(message.recov)){
-    # warn <- paste0(warn, "\n\n", if(length(warn.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(warn.recov), collapse = "\n\n"), "\n\n")})
-    # }else if( ! (is.null(warn) & is.null(message.recov)) & is.null(warn.recov)){
-    # warn <- paste0(warn, "\n\n", if(length(message.recov) > 0){paste0(paste0("MESSAGES FROM ggplot2 FUNCTIONS: ", unique(message.recov), collapse = "\n\n"), "\n\n")})
-    # }
-    if(warn.print == TRUE & ! is.null(warn)){
-        on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
+    if( ! is.null(legend.name)){
+        tempo1 <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = legend.name, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = legend.name, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
-    if(return == TRUE){
-        tempo.output <- ggplot2::ggplot_build(fin.plot)
-        tempo.output$data <- tempo.output$data[-1] # remove the first data because corresponds to the initial empty boxplot
-        if(length(tempo.output$data) != length(coord.names)){
-            tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nlength(tempo.output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    if( ! is.null(color)){
+        tempo1 <- fun_check(data = color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = color, class = "factor", na.contain = TRUE, fun.name = function.name)
+        tempo3 <- fun_check(data = color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)
+        tempo4 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo4$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE & tempo4$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A VECTOR (OF CHARACTER STRINGS OR INTEGERS) OR A FACTOR OR A LIST OF THESE POSSIBILITIES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = color, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo1 <- fun_check(data = geom, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = geom, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    tempo1 <- fun_check(data = geom.step.dir, options = c("vh", "hv", "mid"), na.contain = FALSE, length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = geom.step.dir, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A SINGLE CHARACTER STRING (\"vh\" OR \"hv\" OR \"mid\") OR A LIST OF THESE CHARACTER STRINGS")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    if( ! is.null(geom.stick.base)){
+        tempo1 <- fun_check(data = geom.stick.base, class = "vector", mode = "numeric", na.contain = FALSE, length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = geom.stick.base, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo1 <- fun_check(data = alpha, prop = TRUE, length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = alpha, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A SINGLE NUMERIC VALUE BETWEEN 0 AND 1 OR A LIST OF SUCH VALUES")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    tempo1 <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
+    tempo2 <- fun_check(data = dot.size, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    tempo1 <- fun_check(data = dot.shape, class = "vector", length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = dot.shape, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A SINGLE SHAPE VALUE OR A LIST OF SINGLE SHAPE VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    tempo1 <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
+    tempo2 <- fun_check(data = dot.border.size, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    if( ! is.null(dot.border.color)){
+        tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name)
+        tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
+        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            # integer colors -> gg_palette
+            tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color MUST BE A SINGLE CHARACTER STRING OF COLOR OR A SINGLE INTEGER VALUE")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = dot.border.color, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo1 <- fun_check(data = line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
+    tempo2 <- fun_check(data = line.size, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    tempo1 <- fun_check(data = line.type, class = "vector", typeof = "integer", double.as.integer.allowed = FALSE, length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = line.type, class = "vector", mode = "character", length = 1, fun.name = function.name)
+    tempo3 <- fun_check(data = line.type, class = "list", na.contain = TRUE, fun.name = function.name)
+    checked.arg.names <- c(checked.arg.names, tempo3$object.name)
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A SINGLE LINE KIND VALUE OR A LIST OF SINGLE LINE KIND VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+    }
+    if( ! is.null(x.lim)){
+        tempo <- fun_check(data = x.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & any(x.lim %in% c(Inf, -Inf))){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": x.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x.lim, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(x.lab)){
+        if(all(class(x.lab) %in% "expression")){ # to deal with math symbols
+            tempo <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee)
         }else{
-            names(tempo.output$data) <- coord.names
-            tempo.output$data <- tempo.output$data[coord.names != "bad_remove"]
+            tempo <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
         }
-        tempo <- tempo.output$layout$panel_params[[1]]
-        output <- list(
-            data = data1.ini, 
-            stat = stat.nolog, 
-            removed.row.nb = removed.row.nb, 
-            removed.rows = removed.rows, 
-            plot = c(tempo.output$data, y.second.tick.values = list(y.second.tick.values)), 
-            panel = facet.categ, 
-            axes = list(
-                x.range = tempo$x.range, 
-                x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
-                x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
-                y.range = tempo$y.range, 
-                y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
-                y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
-            ), 
-            warn = paste0("\n", warn, "\n\n"), 
-            ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE
-            gtable = if(return.gtable == TRUE){grob.save}else{NULL} 
-        )
-        return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x.lab, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    # end output
-    # end main code
-}
-
-
-
-
-
-
-# add density
-# rasterise all kind: https://cran.r-project.org/web/packages/ggrastr/vignettes/Raster_geoms.html
-
-
-fun_gg_scatter <- function(
-    data1, 
-    x, 
-    y, 
-    categ = NULL, 
-    categ.class.order = NULL, 
-    color = NULL, 
-    geom = "geom_point", 
-    geom.step.dir = "hv", 
-    geom.stick.base = NULL, 
-    alpha = 0.5, 
-    dot.size = 2, 
-    dot.shape = 21, 
-    dot.border.size = 0.5, 
-    dot.border.color = NULL, 
-    line.size = 0.5, 
-    line.type = "solid", 
-    x.lim = NULL, 
-    x.lab = NULL, 
-    x.log = "no", 
-    x.tick.nb = NULL, 
-    x.second.tick.nb = NULL, 
-    x.include.zero = FALSE, 
-    x.left.extra.margin = 0.05, 
-    x.right.extra.margin = 0.05, 
-    x.text.angle = 0, 
-    y.lim = NULL, 
-    y.lab = NULL, 
-    y.log = "no", 
-    y.tick.nb = NULL, 
-    y.second.tick.nb = NULL, 
-    y.include.zero = FALSE, 
-    y.top.extra.margin = 0.05, 
-    y.bottom.extra.margin = 0.05, 
-    y.text.angle = 0, 
-    raster = FALSE, 
-    raster.ratio = 1, 
-    raster.threshold = NULL, 
-    text.size = 12, 
-    title = "", 
-    title.text.size = 12, 
-    legend.show = TRUE, 
-    legend.width = 0.5, 
-    legend.name = NULL, 
-    article = TRUE, 
-    grid = FALSE, 
-    add = NULL, 
-    return = FALSE, 
-    return.ggplot = FALSE,
-    return.gtable = TRUE,
-    plot = TRUE, 
-    warn.print = FALSE, 
-    lib.path = NULL
-){
-    # AIM
-    # Plot ggplot2 scatterplot with the possibility to overlay dots from up to 3 different data frames (-> three different legends) and lines from up to 3 different data frames (-> three different legends) -> up to 6 overlays totally
-    # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html
-    # WARNINGS
-    # Rows containing NA in data1[, c(x, y, categ)] will be removed before processing, with a warning (see below)
-    # Size arguments (dot.size, dot.border.size, line.size, text.size and title.text.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
-    # ARGUMENTS
-    # data1: a dataframe compatible with ggplot2, or a list of data frames. Order matters for the order of the legend and for the layer staking (starting from below to top)
-    # x: single character string of the data1 column name for x-axis coordinates. If data1 is a list, then x must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_hline" in geom argument
-    # y: single character string of the data1 column name for y-axis coordinates. If data1 is a list, then y must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Write NULL for each "geom_vline" in geom argument
-    # categ: either NULL or a single character string or a list of single character strings, indicating the data1 column names to use for categories which creates legend display
-    # If categ == NULL, no categories -> no legend displayed
-    # If data1 is a data frame, categ must be a single character string of the data1 column name for categories
-    # If data1 is a list, then categ must be a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (no legend display for these compartments), and other not
-    # categ.class.order: either (1) NULL or (2) a vector of character strings or (3) a list of these vectors, setting the order of the classes of categ in the legend display
-    # If categ.class.order is NULL, classes are represented according to the alphabetical order
-    # If data1 is a data frame, categ.class.order must be a vector of character strings specifying the different classes in the categ column name of data1
-    # If data1 is a list, then categ.class.order must be a list of vector of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL (alphabetical order for these compartments), and other not
-    # color: either (1) NULL, or (2) a vector of character strings or integers, or (3) a list of vectors of character strings or integers
-    # If color is NULL, default colors of ggplot2
-    # If data1 is a data frame, color argument can be either:
-    # (1) a single color string. All the dots of the corresponding data1 will have this color, whatever the categ value (NULL or not)
-    # (2) if categ is non-null, a vector of string colors, one for each class of categ. Each color will be associated according to the categ.class.order argument if specified, or to the alphabetical order of categ classes otherwise
-    # (3) if categ is non-null, a vector or factor of string colors, like if it was one of the column of data1 data frame. WARNING: a single color per class of categ and a single class of categ per color must be respected
-    # Positive integers are also accepted instead of character strings, as long as above rules about length are respected. Integers will be processed by fun_gg_palette() using the max integer value among all the integers in color (see fun_gg_palette())
-    # If data1 is a list, then color argument must be either: 
-    # (1) a list of character strings or integers, of same size as data1, with compartment 1 related to compartment 1 of data1, etc.
-    # (2) a single character string or a single integer
-    # With a list (first possibility), the rules described for when data1 is a data frame apply to each compartment of the list. Some of the compartments can be NULL. In that case, a different grey color will be used for each NULL compartment. With a single value (second possibility), the same color will be used for all the dots and lines, whatever the data1 list
-    # geom: single character string of the kind of plot, or a list of single character strings
-    # Either:
-    # "geom_point" (scatterplot)
-    # "geom_line" (coordinates plotted then line connection, from the lowest to highest x coordinates first and from the lowest to highest y coordinates thenafter)
-    # "geom_path" (coordinates plotted then line connection respecting the row order in data1)
-    # "geom_step" coordinates plotted then line connection respecting the row order in data1 but drawn in steps). See the geom.step.dir argument
-    # "geom_hline" (horizontal line, no x value provided)
-    # "geom_vline" (vertical line, no y value provided)
-    # "geom_stick" (dots as vertical bars)
-    # If data1 is a list, then geom must be either:
-    # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc.
-    # (2) a single character string. In that case the same kind of plot will apply for the different compartments of the data1 list
-    # WARNING concerning "geom_hline" or "geom_vline":
-    # (1) x or y argument must be NULL, respectively
-    # (2) x.lim or y.lim argument must NOT be NULL, respectively, if only these kind of lines are drawn (if other geom present, then x.lim = NULL and y.lim = NULL will generate x.lim and y.lim defined by these other geom, which is not possible with "geom_hline" or "geom_vline" alone)
-    # (3) the function will draw n lines for n values in the x argument column name of the data1 data frame. If several colors required, the categ argument must be specified and the corresponding categ column name must exist in the data1 data frame with a different class name for each row
-    # geom.step.dir: single character string indicating the direction when using "geom_step" of the geom argument, or a list of single character strings
-    # Either:
-    # "vh" (vertical then horizontal)
-    # "hv" (horizontal then vertical)
-    # "mid" (step half-way between adjacent x-values)
-    # See https://ggplot2.tidyverse.org/reference/geom_path.html
-    # If data1 is a list, then geom.step.dir must be either:
-    # (1) a list of single character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. The value in compartments related to other geom values than "geom_step" will be ignored
-    # (2) a single character string, which will be used for all the "geom_step" values of the geom argument, whatever the data1 list
-    # geom.stick.base: either (1) NULL or (2) a single numeric value or (3) a list of single numeric values, setting the base of the sticks when using "geom_stick" of the geom argument
-    # If geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base
-    # If data1 is a list, then geom.stick.base must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the values in compartments related to other geom values than "geom_stick" will be ignored. With a single value (latter possibility), the same base will be used for all the sticks, whatever the data1 list
-    # Warning: the y-axis limits are not modified by the value of geom.stick.base, meaning that this value can be outside of the range of y.lim. Add the value of geom.stick.base also in the y.lim argument if required
-    # Warning: if geom.stick.base is NULL, the bottom limit of the y-axis is taken as the base. Thus, be careful with inverted y-axis
-    # alpha: single numeric value (from 0 to 1) of transparency. If data1 is a list, then alpha must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. In that case the same transparency will apply for the different compartments of the data1 list
-    # dot.size: single numeric value of dot shape radius? in mm. If data1 is a list, then dot.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.size will be used for all the dots, whatever the data1 list
-    # dot.shape: value indicating the shape of the dots (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then dot.shape must be either (1) a list of single shape values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single shape value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.shape will be used for all the dots, whatever the data1 list
-    # dot.border.size: single numeric value of border dot width in mm. Write zero for no dot border. If data1 is a list, then dot.border.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to lines will be ignored. With a single value (latter possibility), the same dot.border.size will be used for all the dots, whatever the data1 list
-    # dot.border.color: single character color string defining the color of the dot border (same border color for all the dots, whatever their categories). If dot.border.color == NULL, the border color will be the same as the dot color. A single integer is also accepted instead of a character string, that will be processed by fun_gg_palette()
-    # line.size: single numeric value of line width in mm. If data1 is a list, then line.size must be either (1) a list of single numeric values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single numeric value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.size will be used for all the lines, whatever the data1 list
-    # line.type: value indicating the kind of lines (see https://ggplot2.tidyverse.org/articles/ggplot2-specs.html) If data1 is a list, then line.type must be either (1) a list of single line kind values, of same size as data1, with compartment 1 related to compartment 1 of data1, etc., or (2) a single line kind value. With a list (former possibility), the value in compartments related to dots will be ignored. With a single value (latter possibility), the same line.type will be used for all the lines, whatever the data1 list
-    # x.lim: 2 numeric values setting the x-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the x column name of data1 will be used
-    # x.lab: a character string or expression for x-axis label. If NULL, will use the first value of x (x column name of the first data frame in data1). Warning message if the elements in x are different between data frames in data1
-    # x.log: either "no", "log2" (values in the x column name of the data1 data frame will be log2 transformed and x-axis will be log2 scaled) or "log10" (values in the x column name of the data1 data frame will be log10 transformed and x-axis will be log10 scaled)
-    # x.tick.nb: approximate number of desired values labeling the x-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if x.log is "no", then the number of labeling values is set by ggplot2. If NULL and if x.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the x.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for x.lim = c(9, 1200)). WARNING: if non-NULL and if x.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
-    # x.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if x.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$x.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
-    # x.include.zero: logical. Does x.lim range include 0? Ignored if x.log is "log2" or "log10"
-    # x.left.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to x.lim. If different from 0, add the range of the axis multiplied by x.left.extra.margin (e.g., abs(x.lim[2] - x.lim[1]) * x.left.extra.margin) to the left of x-axis
-    # x.right.extra.margin: idem as x.left.extra.margin but to the right of x-axis
-    # x.text.angle: integer value of the text angle for the x-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
-    # y.lim: 2 numeric values setting the y-axis range. Order of the 2 values matters (for inverted axis). If NULL, the range of the y column name of data1 will be used
-    # y.lab: a character string or expression for y-axis label. If NULL, will use the first value of y (y column name of the first data frame in data1). Warning message if the elements in y are different between data frames in data1
-    # y.log: either "no", "log2" (values in the y column name of the data1 data frame will be log2 transformed and y-axis will be log2 scaled) or "log10" (values in the y column name of the data1 data frame will be log10 transformed and y-axis will be log10 scaled)
-    # y.tick.nb: approximate number of desired values labeling the y-axis (i.e., main ticks, see the n argument of the the cute::fun_scale() function). If NULL and if y.log is "no", then the number of labeling values is set by ggplot2. If NULL and if y.log is "log2" or "log10", then the number of labeling values corresponds to all the exposant integers in the y.lim range (e.g., 10^1, 10^2 and 10^3, meaning 3 main ticks for y.lim = c(9, 1200)). WARNING: if non-NULL and if y.log is "log2" or "log10", labeling can be difficult to read (e.g., ..., 10^2, 10^2.5, 10^3, ...)
-    # y.second.tick.nb: number of desired secondary ticks between main ticks. Ignored if y.log is other than "no" (log scale plotted). Use argument return = TRUE and see $plot$y.second.tick.values to have the values associated to secondary ticks. IF NULL, no secondary ticks
-    # y.include.zero: logical. Does y.lim range include 0? Ignored if y.log is "log2" or "log10"
-    # y.top.extra.margin: single proportion (between 0 and 1) indicating if extra margins must be added to y.lim. If different from 0, add the range of the axis multiplied by y.top.extra.margin (e.g., abs(y.lim[2] - y.lim[1]) * y.top.extra.margin) to the top of y-axis
-    # y.bottom.extra.margin: idem as y.top.extra.margin but to the bottom of y-axis
-    # y.text.angle: integer value of the text angle for the y-axis labeling values, using the same rules as in ggplot2. Use positive value for clockwise rotation: 0 for horizontal, 90 for vertical, 180 for upside down etc. Use negative values for counterclockwise rotation: 0 for horizontal, -90 for vertical, -180 for upside down etc.
-    # raster: logical. Dots in raster mode? If FALSE, dots from each "geom_point" from geom argument are plotted in vectorial mode (bigger pdf and long to display if lots of dots). If TRUE, dots from each "geom_point" from geom argument are plotted in matricial mode (smaller pdf and easy display if lots of dots, but it takes time to generate the layer). If TRUE, the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If TRUE, solve the transparency problem with some GUI. Overriden by the non-NULL raster.threshold argument
-    # raster.ratio: single numeric value indicating the height / width ratio of the graphic device used (for instance provided by the $dim compartment in the output of the fun_open() function). The default value is 1 because by default R opens a square graphic device. But this argument has to be set when using other device dimensions. Ignored if raster == FALSE
-    # raster.threshold: positive integer value indicating the limit of the dot number above which "geom_point" layers from the geom argument switch from vectorial mode to matricial mode (see the raster argument). If any layer is matricial, then the raster.ratio argument is used to avoid an ellipsoid representation of the dots. If non-NULL, it overrides the raster argument
-    # text.size: numeric value of the font size of the (1) axis numbers and axis legends and (2) texts in the graphic legend (in mm)
-    # title: character string of the graph title
-    # title.text.size: numeric value of the title font size in mm
-    # legend.show: logical. Show legend? Not considered if categ argument is NULL, because this already generate no legend, excepted if legend.width argument is non-NULL. In that specific case (categ is NULL, legend.show is TRUE and legend.width is non-NULL), an empty legend space is created. This can be useful when desiring graphs of exactly the same width, whatever they have legends or not
-    # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend
-    # legend.name: character string of the legend title. If legend.name is NULL and categ argument is not NULL, then legend.name <- categ. If data1 is a list, then legend.name must be a list of character strings, of same size as data1, with compartment 1 related to compartment 1 of data1, etc. Some of the list compartments can be NULL, and other not
-    # article: logical. If TRUE, use an article theme (article like). If FALSE, use a classic related ggplot theme. Use the add argument (e.g., add = "+ggplot2::theme_classic()" for the exact classic ggplot theme
-    # grid: logical. Draw lines in the background to better read the box values? Not considered if article == FALSE (grid systematically present)
-    # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL
-    # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()"
-    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_scatter() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
-    # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions
-    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_scatter() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
-    # return: logical. Return the graph parameters?
-    # return.ggplot: logical. Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_scatter() function (e.g., a <- fun_gg_scatter()) if return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
-    # return.gtable: logical. Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
-    # plot: logical. Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
-    # warn.print: logical. Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
-    # lib.path: character string indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
-    # RETURN
-    # a scatter plot if plot argument is TRUE
-    # a list of the graph info if return argument is TRUE:
-    # $data: the initial data with graphic information added. WARNING: if the x.log or y.log argument is not "no", x or y argument column of the data1 data frame are log2 or log10 converted in $data, respectively. Use 2^values or 10^$values to recover the initial values
-    # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed
-    # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed
-    # $plot: the graphic box and dot coordinates
-    # $dots: dot coordinates
-    # y.second.tick.positions: coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
-    # y.second.tick.values: values of secondary ticks. NULL except if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
-    # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
-    # $axes: the x-axis and y-axis info
-    # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
-    # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
-    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
-    # REQUIRED PACKAGES
-    # ggplot2
-    # gridExtra
-    # lemon (in case of use in the add argument)
-    # scales
-    # if raster plots are drawn (see the raster and raster.threshold arguments):
-    # Cairo
-    # grid
-    # REQUIRED FUNCTIONS FROM THE cute PACKAGE
-    # fun_gg_empty_graph()
-    # fun_gg_palette()
-    # fun_gg_point_rast()
-    # fun_pack()
-    # fun_check()
-    # fun_round()
-    # fun_scale()
-    # fun_inter_ticks()
-    # EXAMPLES
-    # set.seed(1) ; obs1 <- data.frame(Km = c(2, 1, 6, 5, 4, 7), Time = c(2, 1, 6, 5, 4, 7)^2, Car = c("TUUT", "TUUT", "TUUT", "WIIM", "WIIM", "WIIM"), Color1 = rep(c("coral", "lightblue"), each = 3), stringsAsFactors = TRUE) ; fun_gg_scatter(data1 = obs1, x = "Km", y = "Time")
-    # DEBUGGING
-    # set.seed(1) ; obs1 <- data.frame(km = rnorm(1000, 10, 3), time = rnorm(1000, 10, 3), group1 = rep(c("A1", "A2"), 500), stringsAsFactors = TRUE) ; obs2 <-data.frame(km = rnorm(1000, 15, 3), time = rnorm(1000, 15, 3), group2 = rep(c("G1", "G2"), 500), stringsAsFactors = TRUE) ; set.seed(NULL) ; obs1$km[2:3] <- NA ; data1 = list(L1 = obs1, L2 = obs2) ; x = list(L1 = "km", L2 = "km") ; y = list(L1 = "time", L2 = "time") ; categ = list(L1 = "group1", L2 = "group2") ; categ = NULL ; categ.class.order = NULL ; color = NULL ; geom = "geom_point" ; geom.step.dir = "hv" ; geom.stick.base = NULL ; alpha = 0.5 ; dot.size = 2 ; dot.shape = 21 ; dot.border.size = 0.5 ; dot.border.color = NULL ; line.size = 0.5 ; line.type = "solid" ; x.lim = NULL ; x.lab = NULL ; x.log = "no" ; x.tick.nb = NULL ; x.second.tick.nb = NULL ; x.include.zero = FALSE ; x.left.extra.margin = 0.05 ; x.right.extra.margin = 0.05 ; x.text.angle = 0 ; y.lim = NULL ; y.lab = NULL ; y.log = "no" ; y.tick.nb = NULL ; y.second.tick.nb = NULL ; y.include.zero = FALSE ; y.top.extra.margin = 0.05 ; y.bottom.extra.margin = 0.05 ; y.text.angle = 0 ; raster = FALSE ; raster.ratio = 1 ; raster.threshold = NULL ; text.size = 12 ; title = "" ; title.text.size = 12 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; article = TRUE ; grid = FALSE ; add = NULL ; return = FALSE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
-    # function name
-    function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()")
-    arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments
-    arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
-    # end function name
-    # required function checking
-    req.function <- c(
-        "fun_check", 
-        "fun_gg_just", 
-        "fun_gg_empty_graph", 
-        "fun_gg_palette", 
-        "fun_gg_point_rast", 
-        "fun_round", 
-        "fun_pack", 
-        "fun_scale", 
-        "fun_inter_ticks"
-    )
-    tempo <- NULL
-    for(i1 in req.function){
-        if(length(find(i1, mode = "function"))== 0L){
-            tempo <- c(tempo, i1)
+    tempo <- fun_check(data = x.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(x.tick.nb)){
+        tempo <- fun_check(data = x.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & x.tick.nb < 0){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": x.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
         }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x.tick.nb, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(tempo)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n"))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    if( ! is.null(x.second.tick.nb)){
+        tempo <- fun_check(data = x.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & x.second.tick.nb <= 0){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": x.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+        }
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = x.second.tick.nb, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    # end required function checking
-    # reserved words to avoid bugs (used in this function)
-    reserved.words <- c("fake_x", "fake_y", "fake_categ")
-    # end reserved words to avoid bugs (used in this function)
-    # arg with no default values
-    mandat.args <- c(
-        "data1", 
-        "x", 
-        "y"
-    )
-    tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")")))
-    if(any(tempo)){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n"))
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }
-    # end arg with no default values
-    # argument primary checking
-    arg.check <- NULL #
-    text.check <- NULL #
-    checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools
-    ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name))
-    tempo1 <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name)
-    tempo2 <- fun_check(data = data1, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": data1 ARGUMENT MUST BE A DATA FRAME OR A LIST OF DATA FRAMES")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    if( ! is.null(x)){
-        tempo1 <- fun_check(data = x, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = x, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": x ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+    tempo <- fun_check(data = x.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = x.left.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = x.right.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = x.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.lim)){
+        tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & any(y.lim %in% c(Inf, -Inf))){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": y.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
         }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x, class = "vector")
+        tempo <- fun_check(data = y.lim, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(y)){
-        tempo1 <- fun_check(data = y, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = y, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": y ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
+    if( ! is.null(y.lab)){
+        if(all(class(y.lab) %in% "expression")){ # to deal with math symbols
+            tempo <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee)
+        }else{
+            tempo <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
         }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y, class = "vector")
+        tempo <- fun_check(data = y.lab, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(categ)){
-        tempo1 <- fun_check(data = categ, class = "vector", mode = "character", length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = categ, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": categ ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
+    tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(y.tick.nb)){
+        tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & y.tick.nb < 0){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": y.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
         }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = categ, class = "vector")
+        tempo <- fun_check(data = y.tick.nb, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(categ.class.order)){
-        if(is.null(categ)){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT IS NOT NULL, BUT categ IS")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-        tempo1 <- fun_check(data = categ.class.order, class = "vector", mode = "character", fun.name = function.name)
-        tempo2 <- fun_check(data = categ.class.order, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": categ.class.order ARGUMENT MUST BE A VECTOR OF CHARACTER STRINGS OR A LIST OF VECTOR OF CHARACTER STRINGS")
+    if( ! is.null(y.second.tick.nb)){
+        tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE & y.second.tick.nb <= 0){
+            tempo.cat <- paste0("ERROR IN ", function.name, ": y.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
             text.check <- c(text.check, tempo.cat)
             arg.check <- c(arg.check, TRUE)
         }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = categ.class.order, class = "vector")
+        tempo <- fun_check(data = y.second.tick.nb, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(legend.name)){
-        tempo1 <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = TRUE, length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = legend.name, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": legend.name ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
+    tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = y.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = raster, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = raster.ratio, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    if( ! is.null(raster.threshold)){
+        tempo <- fun_check(data = raster.threshold, class = "vector", typeof = "integer", neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.name, class = "vector")
+        tempo <- fun_check(data = raster.threshold, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    if( ! is.null(color)){
-        tempo1 <- fun_check(data = color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
-        tempo2 <- fun_check(data = color, class = "factor", na.contain = TRUE, fun.name = function.name)
-        tempo3 <- fun_check(data = color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, fun.name = function.name)
-        tempo4 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo4$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE & tempo4$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": color ARGUMENT MUST BE A VECTOR (OF CHARACTER STRINGS OR INTEGERS) OR A FACTOR OR A LIST OF THESE POSSIBILITIES")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
+    tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(legend.width)){
+        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = color, class = "vector")
+        tempo <- fun_check(data = legend.width, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo1 <- fun_check(data = geom, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = geom, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": geom ARGUMENT MUST BE A SINGLE CHARACTER STRING OR A LIST OF CHARACTER STRINGS")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    tempo1 <- fun_check(data = geom.step.dir, options = c("vh", "hv", "mid"), na.contain = FALSE, length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = geom.step.dir, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": geom.step.dir ARGUMENT MUST BE A SINGLE CHARACTER STRING (\"vh\" OR \"hv\" OR \"mid\") OR A LIST OF THESE CHARACTER STRINGS")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    if( ! is.null(geom.stick.base)){
-        tempo1 <- fun_check(data = geom.stick.base, class = "vector", mode = "numeric", na.contain = FALSE, length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = color, class = "list", na.contain = TRUE, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": geom.stick.base ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
+    tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(add)){
+        tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = geom.stick.base, class = "vector")
+        tempo <- fun_check(data = add, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo1 <- fun_check(data = alpha, prop = TRUE, length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = alpha, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": alpha ARGUMENT MUST BE A SINGLE NUMERIC VALUE BETWEEN 0 AND 1 OR A LIST OF SUCH VALUES")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    tempo1 <- fun_check(data = dot.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
-    tempo2 <- fun_check(data = dot.size, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    tempo1 <- fun_check(data = dot.shape, class = "vector", length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = dot.shape, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.shape ARGUMENT MUST BE A SINGLE SHAPE VALUE OR A LIST OF SINGLE SHAPE VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    tempo1 <- fun_check(data = dot.border.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
-    tempo2 <- fun_check(data = dot.border.size, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    if( ! is.null(dot.border.color)){
-        tempo1 <- fun_check(data = dot.border.color, class = "vector", mode = "character", length = 1, fun.name = function.name)
-        tempo2 <- fun_check(data = dot.border.color, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, fun.name = function.name)
-        checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-            # integer colors -> gg_palette
-            tempo.cat <- paste0("ERROR IN ", function.name, ": dot.border.color MUST BE A SINGLE CHARACTER STRING OF COLOR OR A SINGLE INTEGER VALUE")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
+    tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(lib.path)){
+        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
+        if(tempo$problem == FALSE){
+            if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
+                tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
+                text.check <- c(text.check, tempo.cat)
+                arg.check <- c(arg.check, TRUE)
+            }
         }
     }else{
         # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = dot.border.color, class = "vector")
+        tempo <- fun_check(data = lib.path, class = "vector")
         checked.arg.names <- c(checked.arg.names, tempo$object.name)
     }
-    tempo1 <- fun_check(data = line.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name)
-    tempo2 <- fun_check(data = line.size, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo2$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": line.size ARGUMENT MUST BE A SINGLE NUMERIC VALUE OR A LIST OF SINGLE NUMERIC VALUES")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
-    }
-    tempo1 <- fun_check(data = line.type, class = "vector", typeof = "integer", double.as.integer.allowed = FALSE, length = 1, fun.name = function.name)
-    tempo2 <- fun_check(data = line.type, class = "vector", mode = "character", length = 1, fun.name = function.name)
-    tempo3 <- fun_check(data = line.type, class = "list", na.contain = TRUE, fun.name = function.name)
-    checked.arg.names <- c(checked.arg.names, tempo3$object.name)
-    if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ": line.type ARGUMENT MUST BE A SINGLE LINE KIND VALUE OR A LIST OF SINGLE LINE KIND VALUES (SEE https://ggplot2.tidyverse.org/articles/ggplot2-specs.html)")
-        text.check <- c(text.check, tempo.cat)
-        arg.check <- c(arg.check, TRUE)
+    if(any(arg.check) == TRUE){
+        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) #
     }
-    if( ! is.null(x.lim)){
-        tempo <- fun_check(data = x.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & any(x.lim %in% c(Inf, -Inf))){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": x.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x.lim, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(x.lab)){
-        if(all(class(x.lab) %in% "expression")){ # to deal with math symbols
-            tempo <- fun_check(data = x.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee)
-        }else{
-            tempo <- fun_check(data = x.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x.lab, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = x.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(x.tick.nb)){
-        tempo <- fun_check(data = x.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & x.tick.nb < 0){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": x.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(x.second.tick.nb)){
-        tempo <- fun_check(data = x.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & x.second.tick.nb <= 0){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": x.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = x.second.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = x.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = x.left.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = x.right.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = x.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
-    if( ! is.null(y.lim)){
-        tempo <- fun_check(data = y.lim, class = "vector", mode = "numeric", length = 2, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & any(y.lim %in% c(Inf, -Inf))){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": y.lim ARGUMENT CANNOT CONTAIN -Inf OR Inf VALUES")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.lim, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(y.lab)){
-        if(all(class(y.lab) %in% "expression")){ # to deal with math symbols
-            tempo <- fun_check(data = y.lab, class = "expression", length = 1, fun.name = function.name) ; eval(ee)
-        }else{
-            tempo <- fun_check(data = y.lab, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.lab, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = y.log, options = c("no", "log2", "log10"), length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(y.tick.nb)){
-        tempo <- fun_check(data = y.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & y.tick.nb < 0){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": y.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if( ! is.null(y.second.tick.nb)){
-        tempo <- fun_check(data = y.second.tick.nb, class = "vector", typeof = "integer", length = 1, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE & y.second.tick.nb <= 0){
-            tempo.cat <- paste0("ERROR IN ", function.name, ": y.second.tick.nb ARGUMENT MUST BE A NON-NULL POSITIVE INTEGER")
-            text.check <- c(text.check, tempo.cat)
-            arg.check <- c(arg.check, TRUE)
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = y.second.tick.nb, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = y.include.zero, class = "vector", mode = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y.top.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y.bottom.extra.margin, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = y.text.angle, class = "vector", typeof = "integer", double.as.integer.allowed = TRUE, length = 1, neg.values = TRUE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = raster, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = raster.ratio, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    if( ! is.null(raster.threshold)){
-        tempo <- fun_check(data = raster.threshold, class = "vector", typeof = "integer", neg.values = FALSE, double.as.integer.allowed = TRUE, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = raster.threshold, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", length = 1, neg.values = FALSE, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(legend.width)){
-        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = legend.width, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = article, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = grid, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(add)){
-        tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = add, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
-    if( ! is.null(lib.path)){
-        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee)
-        if(tempo$problem == FALSE){
-            if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
-                tempo.cat <- paste0("ERROR IN ", function.name, ": DIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
-                text.check <- c(text.check, tempo.cat)
-                arg.check <- c(arg.check, TRUE)
-            }
-        }
-    }else{
-        # no fun_check test here, it is just for checked.arg.names
-        tempo <- fun_check(data = lib.path, class = "vector")
-        checked.arg.names <- c(checked.arg.names, tempo$object.name)
-    }
-    if(any(arg.check) == TRUE){
-        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) #
-    }
-    # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check()
-    # end argument primary checking
-    
-    
-    # second round of checking and data preparation
-    # management of NA arguments
-    tempo.arg <- names(arg.user.setting) # values provided by the user
-    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA
-    if(any(tempo.log) == TRUE){
-        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check()
+    # end argument primary checking
+    
+    
+    # second round of checking and data preparation
+    # management of NA arguments
+    tempo.arg <- names(arg.user.setting) # values provided by the user
+    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA
+    if(any(tempo.log) == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, ":\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT JUST BE NA")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
     }
     # end management of NA arguments
     # management of NULL arguments
@@ -14103,340 +13389,1065 @@ fun_gg_scatter <- function(
             }
         }
     }
-    # end loop part
-    
+    # end loop part
+    
+    
+    
+    
+    # legend display
+    tempo.legend.final <- 'ggplot2::guides(
+fill = if(fin.lg.disp[[1]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[1]], 
+override.aes = list(
+fill = lg.color[[1]], 
+colour = if(lg.dot.shape[[1]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[1]]}else{lg.color[[1]]}, # lg.dot.shape[[1]] %in% 21:24 are the only one that can be filled
+shape = lg.dot.shape[[1]], 
+size = lg.dot.size[[1]], 
+stroke = lg.dot.border.size[[1]], 
+alpha = lg.alpha[[1]], 
+linetype = 0
+)
+)
+}else{
+"none"
+}, 
+shape = if(fin.lg.disp[[2]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[2]], 
+override.aes = list(
+fill = lg.color[[2]], 
+colour = if(lg.dot.shape[[2]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[2]]}else{lg.color[[2]]}, # lg.dot.shape[[2]] %in% 21:24 are the only one that can be filled
+shape = lg.dot.shape[[2]], 
+size = lg.dot.size[[2]], 
+stroke = lg.dot.border.size[[2]], 
+alpha = lg.alpha[[2]], 
+linetype = 0
+)
+)
+}else{
+"none"
+}, 
+stroke = if(fin.lg.disp[[3]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[3]], 
+override.aes = list(
+fill = lg.color[[3]], 
+colour = if(lg.dot.shape[[3]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[3]]}else{lg.color[[3]]}, # lg.dot.shape[[3]] %in% 21:24 are the only one that can be filled
+shape = lg.dot.shape[[3]], 
+size = lg.dot.size[[3]], 
+stroke = lg.dot.border.size[[3]], 
+alpha = lg.alpha[[3]], 
+linetype = 0
+)
+)
+}else{
+"none"
+}, 
+linetype = if(fin.lg.disp[[4]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[4]], 
+override.aes = list(
+color = lg.color[[4]], 
+size = lg.line.size[[4]], 
+linetype = lg.line.type[[4]], 
+alpha = lg.alpha[[4]], 
+shape = NA
+)
+)
+}else{
+"none"
+}, 
+alpha = if(fin.lg.disp[[5]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[5]], 
+override.aes = list(
+color = lg.color[[5]], 
+size = lg.line.size[[5]], 
+linetype = lg.line.type[[5]], 
+alpha = lg.alpha[[5]], 
+shape = NA
+)
+)
+}else{
+"none"
+}, 
+size = if(fin.lg.disp[[6]] == TRUE){
+ggplot2::guide_legend(
+order = lg.order[[6]], 
+override.aes = list(
+color = lg.color[[6]], 
+size = lg.line.size[[6]], 
+linetype = lg.line.type[[6]], 
+alpha = lg.alpha[[6]], 
+shape = NA
+)
+)
+}else{
+"none"
+}
+)' # clip = "off" to have secondary ticks outside plot region does not work
+if( ! is.null(legend.width)){
+    if(any(unlist(legend.disp))){ # means some TRUE
+        tempo.graph.info <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ', tempo.legend.final))))) # will be recovered later again, when ylim will be considered
+        legend.final <- fun_gg_get_legend(ggplot_built = tempo.graph.info, fun.name = function.name) # get legend
+        fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend
+        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
+            legend.final <- fun_gg_empty_graph() # empty graph instead of legend
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
+    }else if(plot == TRUE){ # means all FALSE
+        legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    }
+}
+if( ! any(unlist(legend.disp))){
+    fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend
+}
+assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = tempo.legend.final)))
+# end legend display
+
+
+
+
+
+# scale management
+tempo.coord <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ", ' + ggplot2::scale_x_continuous(expand = c(0, 0), limits = sort(x.lim), oob = scales::rescale_none) + ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)'))))$layout$panel_params[[1]]) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks
+# x.second.tick.positions # coordinates of secondary ticks (only if x.second.tick.nb argument is non-null or if x.log argument is different from "no")
+if(x.log != "no"){ # integer main ticks for log2 and log10
+    tempo.scale <- (as.integer(min(x.lim, na.rm = TRUE)) - 1):(as.integer(max(x.lim, na.rm = TRUE)) + 1)
+}else{
+    tempo <- if(is.null(attributes(tempo.coord$x$breaks))){tempo.coord$x$breaks}else{unlist(attributes(tempo.coord$x$breaks))}
+    if(all(is.na(tempo))){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$x$breaks")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }
+    if(length(unique(x.lim)) <= 1){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT X-AXIS VALUES HAVE A NULL RANGE: ", paste(x.lim, collapse = " "), "\nPLEASE, USE THE x.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }else{
+        tempo.scale <- fun_scale(lim = x.lim, n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) # in ggplot 3.3.0, tempo.coord$x.major_source replaced by tempo.coord$x$breaks. If fact: n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) replaced by n = ifelse(is.null(x.tick.nb), 4, x.tick.nb))
+    }
+}
+x.second.tick.values <- NULL
+x.second.tick.pos <- NULL
+if(x.log != "no"){
+    tempo <- fun_inter_ticks(lim = x.lim, log = x.log)
+    x.second.tick.values <- tempo$values
+    x.second.tick.pos <- tempo$coordinates
+    # if(vertical == TRUE){ # do not remove in case the bug is fixed
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+        geom = "segment", 
+        x = x.second.tick.pos, 
+        xend = x.second.tick.pos, 
+        y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, 
+        yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80}
+    ))
+    # }else{ # not working because of the ggplot2 bug
+    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = x.second.tick.pos, yend = x.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80))
+    # }
+    coord.names <- c(coord.names, "x.second.tick.positions")
+}else if(( ! is.null(x.second.tick.nb)) & x.log == "no"){
+    # if(x.second.tick.nb > 0){ #inactivated because already checked before
+    if(length(tempo.scale) < 2){
+        tempo.cat1 <- c("x.tick.nb", "x.second.tick.nb")
+        tempo.cat2 <- sapply(list(x.tick.nb, x.second.tick.nb), FUN = paste0, collapse = " ")
+        tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE X-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }else{
+        tempo <- fun_inter_ticks(lim = x.lim, log = x.log, breaks = tempo.scale, n = x.second.tick.nb)
+    }
+    x.second.tick.values <- tempo$values
+    x.second.tick.pos <- tempo$coordinates
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+        geom = "segment", 
+        x = x.second.tick.pos, 
+        xend = x.second.tick.pos, 
+        y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, 
+        yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80}
+    ))
+    coord.names <- c(coord.names, "x.second.tick.positions")
+}
+# for the ggplot2 bug with x.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & x.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_x_continuous")))
+assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_x_continuous(
+    breaks = tempo.scale, 
+    minor_breaks = x.second.tick.pos, 
+    labels = if(x.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(x.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(x.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, 
+    expand = c(0, 0), # remove space after after axis limits
+    limits = sort(x.lim), # NA indicate that limits must correspond to data limits but xlim() already used
+    oob = scales::rescale_none, 
+    trans = ifelse(diff(x.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_x_reverse() but create the problem of x-axis label disappearance with x.lim decreasing. Thus, do not use. Use xlim() below and after this
+))
+# end x.second.tick.positions
+# y.second.tick.positions # coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
+if(y.log != "no"){ # integer main ticks for log2 and log10
+    tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1)
+}else{
+    tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))}
+    if(all(is.na(tempo))){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }
+    if(length(unique(y.lim)) <= 1){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT Y-AXIS VALUES HAVE A NULL RANGE: ", paste(y.lim, collapse = " "), "\nPLEASE, USE THE y.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }else{
+        tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb))
+    }
+}
+y.second.tick.values <- NULL
+y.second.tick.pos <- NULL
+if(y.log != "no"){
+    tempo <- fun_inter_ticks(lim = y.lim, log = y.log)
+    y.second.tick.values <- tempo$values
+    y.second.tick.pos <- tempo$coordinates
+    # if(vertical == TRUE){ # do not remove in case the bug is fixed
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+        geom = "segment", 
+        y = y.second.tick.pos, 
+        yend = y.second.tick.pos, 
+        x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, 
+        xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80}
+    ))
+    # }else{ # not working because of the ggplot2 bug
+    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80))
+    # }
+    coord.names <- c(coord.names, "y.second.tick.positions")
+}else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){
+    # if(y.second.tick.nb > 0){ #inactivated because already checked before
+    if(length(tempo.scale) < 2){
+        tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb")
+        tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ")
+        tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }else{
+        tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb)
+    }
+    y.second.tick.values <- tempo$values
+    y.second.tick.pos <- tempo$coordinates
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+        geom = "segment", 
+        y = y.second.tick.pos, 
+        yend = y.second.tick.pos, 
+        x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, 
+        xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80}
+    ))
+    coord.names <- c(coord.names, "y.second.tick.positions")
+}
+# for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous")))
+assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous(
+    breaks = tempo.scale, 
+    minor_breaks = y.second.tick.pos, 
+    labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, 
+    expand = c(0, 0), # remove space after axis limits
+    limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used
+    oob = scales::rescale_none, 
+    trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this
+))
+# end y.second.tick.positions
+assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(xlim = x.lim, ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region. The problem is that points out of bounds are also drawn outside the plot region. Thus, I cannot use it # at that stage, x.lim and y.lim not NULL anymore
+# end scale management
+
+
+
+
+# drawing
+fin.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
+grob.save <- NULL
+if(plot == TRUE){
+    if( ! is.null(legend.width)){ # any(unlist(legend.disp)) == TRUE removed to have empty legend space # not & any(unlist(fin.lg.disp)) == TRUE here because converted to FALSE
+        grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width))))
+    }else{
+        grob.save <- suppressMessages(suppressWarnings(print(fin.plot)))
+    }
+}else{
+    warn.count <- warn.count + 1
+    tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
+    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+}
+# end drawing
+
+
+
+# output
+if(warn.print == TRUE & ! is.null(warn)){
+    on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
+}
+on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
+if(return == TRUE){
+    output <- suppressMessages(ggplot2::ggplot_build(fin.plot))
+    # output$data <- output$data[-1] # yes for boxplot but not for scatter # remove the first data because corresponds to the initial empty boxplot
+    if(length(output$data) != length(coord.names)){
+        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": length(output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }else{
+        names(output$data) <- coord.names
+    }
+    if(is.null(unlist(removed.row.nb))){
+        removed.row.nb <- NULL
+        removed.rows <- NULL
+    }else{
+        for(i3 in 1:length(data1)){
+            if( ! is.null(removed.row.nb[[i3]])){
+                removed.row.nb[[i3]] <- sort(removed.row.nb[[i3]])
+                removed.rows[[i3]] <- data1.ini[[i3]][removed.row.nb[[i3]], ]
+            }
+        }
+    }
+    tempo <- output$layout$panel_params[[1]]
+    output <- list(
+        data = data1, 
+        removed.row.nb = removed.row.nb, 
+        removed.rows = removed.rows, 
+        plot = c(output$data, x.second.tick.values = list(x.second.tick.values), y.second.tick.values = list(y.second.tick.values)), 
+        panel = facet.categ, 
+        axes = list(
+            x.range = tempo$x.range, 
+            x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
+            x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
+            y.range = tempo$y.range, 
+            y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
+            y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
+        ), 
+        warn = paste0("\n", warn, "\n\n"), 
+        ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE
+        gtable = if(return.gtable == TRUE){grob.save}else{NULL} #
+    )
+    return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
+}
+# end output
+# end main code
+}
+
+
+
+
+
+fun_gg_donut <- function(
+    data1, 
+    freq, 
+    categ, 
+    fill.palette = NULL,
+    fill.color = NULL, 
+    hole.size = 0.5, 
+    hole.text = TRUE, 
+    hole.text.size = 14, 
+    border.color = "gray50", 
+    border.size = 0.2, 
+    title = "", 
+    title.text.size = 12, 
+    annotation = NULL,
+    annotation.distance = 0,
+    annotation.size = 3,
+    annotation.force = 1,
+    annotation.force.pull = 100,
+    legend.show = TRUE, 
+    legend.width = 0.25, 
+    legend.name = NULL, 
+    legend.limit = NULL, 
+    legend.add.prop = FALSE,
+    add = NULL, 
+    return = FALSE, 
+    return.ggplot = FALSE,
+    return.gtable = TRUE,
+    plot = TRUE, 
+    warn.print = TRUE, 
+    lib.path = NULL
+){
+    # AIM
+    # Plot a ggplot2 donut using contingency data, systematically in the decreasing order of frequencies, starting at the top and turning clockwise
+    # For ggplot2 specifications, see: https://ggplot2.tidyverse.org/articles/ggplot2-specs.html
+    # WARNINGS
+    # Rows containing NA in data1[, c(freq, categ)] will be removed before processing, with a warning (see below)
+    # Size arguments (hole.text.size, border.size, title.text.size and annotation.size) are in mm. See Hadley comment in https://stackoverflow.com/questions/17311917/ggplot2-the-unit-of-size. See also http://sape.inf.usi.ch/quick-reference/ggplot2/size). Unit object are not accepted, but conversion can be used (e.g., grid::convertUnit(grid::unit(0.2, "inches"), "mm", valueOnly = TRUE))
+    # ARGUMENTS
+    # data1: a dataframe compatible with ggplot2
+    # freq: single character string of the data1 column name of the frequencies
+    # categ: single character string of the data1 column name of categories (qualitative variable)
+    # fill.palette: single character string of a palette name (see ?ggplot2::scale_fill_brewer() for the list).Ignored if fill.color is not NULL
+    # fill.color: either (1) NULL, or (2) a vector of character strings or integers of same length as the number of classes in categ. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette). The order of the elements will be used according to the frequency values, from highest to lowest. An easy way to use this argument is to sort data1 according to the frequencies values, add a color column with the corresponding desired colors and use the content of this column as values of fill.color. If color is NULL and fill.palette is NULL, default colors of ggplot2 are used. If color is not NULL, it overrides fill.palette
+    # hole.size: single positive proportion of donut central hole, 0 meaning no hole (pie chart) and 1 no plot (donut with a null thickness)
+    # hole.text: logical (either TRUE or FALSE). Display the sum of frequencies (column of data1 indicated in the freq argument) ?
+    # hole.text.size: single positive numeric value of the title font size in mm. Ignored if hole.text is FALSE
+    # border.color: a single character string or integer. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette)
+    # border.size: single numeric value of border tickness in mm. Write zero for no dot border
+    # title: single character string of the graph title
+    # title.text.size: single numeric value of the title font size in mm
+    # annotation: single character string of the data1 column name of annotations. Values inside this column will be displayed over the corresponding slices of the donut. Write NULL if not required
+    # annotation.distance: single positive numeric value of the distance from the center of the slice. 0 means center of the slice, 0.5 means at the edge. Above 0.5, the donut will be reduced to make place for the annotation. Ignored if annotation is NULL
+    # annotation.size: single positive numeric value of the annotation font size in mm. Ignored if annotation is NULL
+    # annotation.force: single positive numeric value of the force of repulsion between overlapping text labels. See ?ggrepel::geom_text_repel() in R. Ignored if annotation is NULL
+    # annotation.force.pull: single positive numeric value of the force of attraction between a text label and its corresponding data point. See ?ggrepel::geom_text_repel() in R. Ignored if annotation is NULL
+    # legend.show: logical (either TRUE or FALSE). Show legend? 
+    # legend.width: single proportion (between 0 and 1) indicating the relative width of the legend sector (on the right of the plot) relative to the width of the plot. Value 1 means that the window device width is split in 2, half for the plot and half for the legend. Value 0 means no room for the legend, which will overlay the plot region. Write NULL to inactivate the legend sector. In such case, ggplot2 will manage the room required for the legend display, meaning that the width of the plotting region can vary between graphs, depending on the text in the legend
+    # legend.name: character string of the legend title. If legend.name is NULL then legend.name is the value of the categ argument. Write legend.name = "" to remove the legend
+    # legend.limit: single positive proportion of the classes displayed in the legend for which the corresponding proportion is over legend.limit. Write NULL to display all the classes
+    # legend.add.prop: logical (either TRUE or FALSE). add the proportion after the class names in the legend ?
+    # add: character string allowing to add more ggplot2 features (dots, lines, themes, facet, etc.). Ignored if NULL
+    # WARNING: (1) the string must start with "+", (2) the string must finish with ")" and (3) each function must be preceded by "ggplot2::". Example: "+ ggplot2::coord_flip() + ggplot2::theme_bw()"
+    # If the character string contains the "ggplot2::theme" string, then the article argument of fun_gg_donut() (see above) is ignored with a warning. In addition, some arguments can be overwritten, like x.angle (check all the arguments)
+    # Handle the add argument with caution since added functions can create conflicts with the preexisting internal ggplot2 functions
+    # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_donut() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
+    # return: logical (either TRUE or FALSE). Return the graph parameters?
+    # return.ggplot: logical (either TRUE or FALSE). Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_donut() function (e.g., a <- fun_gg_donut()) into something if the return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
+    # return.gtable: logical (either TRUE or FALSE). Return the full graph (main, title and legend) as a gtable of grobs in the output list? See $gtable in the RETURN section below for more details
+    # plot: logical (either TRUE or FALSE). Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
+    # warn.print: logical (either TRUE or FALSE). Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
+    # lib.path: vector of character strings indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
+    # RETURN
+    # a donut plot if plot argument is TRUE
+    # a list of the graph info if return argument is TRUE:
+    # $data: the initial data with modifications and with graphic information added
+    # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed
+    # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed
+    # $plot.data
+    # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
+    # $axes: the x-axis and y-axis info
+    # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
+    # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Warning: the legend is not in $ggplot as it is in a separated grob (use $gtable to get it). Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
+    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
+    # REQUIRED PACKAGES
+    # ggplot2
+    # gridExtra
+    # grid
+    # lemon (in case of use in the add argument)
+    # ggrepel
+    # REQUIRED FUNCTIONS FROM THE cute PACKAGE
+    # fun_gg_palette()
+    # fun_gg_get_legend()
+    # fun_pack()
+    # fun_check()
+    # EXAMPLES
+    # obs1 <- data.frame(Km = c(20, 10, 1, 5), Car = c("TUUT", "WIIM", "BIP", "WROUM"), Color1 = 1:4, color2 = c("red", "blue", "green", "black"), Country = c("FR", "UK", "US", NA), stringsAsFactors = TRUE) ; fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", annotation = "Country")
+    # DEBUGGING
+    # obs1 <- data.frame(Km = c(20, 10, 1, 5), Car = c("TUUT", "WIIM", "BIP", "WROUM"), Color1 = 1:4, color2 = c("red", "blue", "green", "black"), Country = c("FR", "UK", "US", NA), stringsAsFactors = TRUE) ; data1 = obs1 ; freq = "Km" ; categ = "Car" ; fill.palette = NULL ; fill.color = NULL ; hole.size = 0.5 ; hole.text = TRUE ; hole.text.size = 12 ; border.color = "gray50" ; border.size = 0.1 ; title = "" ; title.text.size = 12 ; annotation = "Country" ; annotation.distance = 0.5 ; annotation.size = 3 ; annotation.force = 1 ; annotation.force.pull = 100 ; legend.show = TRUE ; legend.width = 0.5 ; legend.name = NULL ; legend.limit = NULL ; legend.add.prop = FALSE ; add = NULL ; return = TRUE ; return.ggplot = FALSE ; return.gtable = TRUE ; plot = TRUE ; warn.print = FALSE ; lib.path = NULL
+    # function name
+    function.name <- paste0(as.list(match.call(expand.dots=FALSE))[[1]], "()")
+    arg.names <- names(formals(fun = sys.function(sys.parent(n = 2)))) # names of all the arguments
+    arg.user.setting <- as.list(match.call(expand.dots=FALSE))[-1] # list of the argument settings (excluding default values not provided by the user)
+    # end function name
+    # required function checking
+    req.function <- c(
+        "fun_check", 
+        "fun_gg_palette", 
+        "fun_gg_get_legend", 
+        "fun_pack"
+    )
+    tempo <- NULL
+    for(i1 in req.function){
+        if(length(find(i1, mode = "function"))== 0L){
+            tempo <- c(tempo, i1)
+        }
+    }
+    if( ! is.null(tempo)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nREQUIRED cute FUNCTION", ifelse(length(tempo) > 1, "S ARE", " IS"), " MISSING IN THE R ENVIRONMENT:\n", paste0(tempo, collapse = "()\n"))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # end required function checking
+    # reserved words to avoid bugs (used in this function)
+    # end reserved words to avoid bugs (used in this function)
+    # arg with no default values
+    mandat.args <- c(
+        "data1", 
+        "freq", 
+        "categ"
+    )
+    tempo <- eval(parse(text = paste0("missing(", paste0(mandat.args, collapse = ") | missing("), ")")))
+    if(any(tempo)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nFOLLOWING ARGUMENT", ifelse(length(mandat.args) > 1, "S HAVE", "HAS"), " NO DEFAULT VALUE AND REQUIRE ONE:\n", paste0(mandat.args, collapse = "\n"))
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # end arg with no default values
+    # argument primary checking
+    arg.check <- NULL #
+    text.check <- NULL #
+    checked.arg.names <- NULL # for function debbuging: used by r_debugging_tools
+    ee <- expression(arg.check <- c(arg.check, tempo$problem) , text.check <- c(text.check, tempo$text) , checked.arg.names <- c(checked.arg.names, tempo$object.name))
+    tempo <- fun_check(data = data1, class = "data.frame", na.contain = TRUE, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = freq, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = categ, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(fill.palette)){
+        tempo <- fun_check(data = fill.palette, options = c("BrBG", "PiYG", "PRGn", "PuOr", "RdBu", "RdGy", "RdYlBu", "RdYlGn", "Spectral", "Accent", "Dark2", "Paired", "Pastel1", "Pastel2", "Set1", "Set2", "Set3", "Blues", "BuGn", "BuPu", "GnBu", "Greens", "Greys", "Oranges", "OrRd", "PuBu", "PuBuGn", "PuRd", "Purples", "RdPu", "Reds", "YlGn", "YlGnBu", "YlOrBr", "YlOrRd"), length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = fill.palette, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(fill.color)){
+        tempo1 <- fun_check(data = fill.color, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = fill.color, class = "factor", na.contain = TRUE, fun.name = function.name)
+        tempo3 <- fun_check(data = fill.color, class = "integer", double.as.integer.allowed = TRUE, na.contain = TRUE, neg.values = FALSE, fun.name = function.name) # not need to test inf with integers
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE & tempo3$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE A VECTOR OF (1) HEXADECIMAL COLOR STRINGS STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) POSITIVE INTEGER VALUES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
+        }else if(tempo3$problem == FALSE & any(is.infinite(fill.color))){ # is.infinite() deals with NA as FALSE
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT CANNOT CONTAIN Inf VALUES AMONG POSITIVE INTEGER VALUES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
+        }else if(tempo3$problem == FALSE & any(fill.color == 0, na.rm = TRUE)){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT CANNOT CONTAIN 0 AMONG POSITIVE INTEGER VALUES")
+            text.check <- c(text.check, tempo.cat)
+            arg.check <- c(arg.check, TRUE)
+            checked.arg.names <- c(checked.arg.names, tempo1$object.name)
+        }
+    }
+    tempo <- fun_check(data = hole.size, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = hole.text, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = hole.text.size, class = "vector", mode = "numeric", neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo1 <- fun_check(data = border.color, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name)
+    tempo2 <- fun_check(data = border.color, class = "integer", double.as.integer.allowed = TRUE, neg.values = FALSE, na.contain = FALSE, length = 1, fun.name = function.name) # not need to test inf with integers
+    if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nborder.color ARGUMENT MUST BE A SINGLE CHARACTER STRING OR POSITIVE INTEGER")
+        text.check <- c(text.check, tempo.cat)
+        arg.check <- c(arg.check, TRUE)
+        checked.arg.names <- c(checked.arg.names, tempo1$object.name)
+    }
+    tempo <- fun_check(data = border.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = title.text.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(annotation)){
+        tempo <- fun_check(data = annotation, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+        tempo <- fun_check(data = annotation.distance, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+        tempo <- fun_check(data = annotation.size, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+        tempo <- fun_check(data = annotation.force, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+        tempo <- fun_check(data = annotation.force.pull, class = "vector", mode = "numeric", na.contain = FALSE, neg.values = FALSE, inf.values = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = annotation, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = legend.show, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(legend.width)){
+        tempo <- fun_check(data = legend.width, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = legend.width, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(legend.name)){
+        tempo <- fun_check(data = legend.name, class = "vector", mode = "character", na.contain = FALSE, length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = legend.name, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if( ! is.null(legend.limit)){
+        tempo <- fun_check(data = legend.limit, prop = TRUE, length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = legend.limit, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = legend.add.prop, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(add)){
+        tempo <- fun_check(data = add, class = "vector", mode = "character", length = 1, fun.name = function.name) ; eval(ee)
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = add, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    tempo <- fun_check(data = return, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = return.ggplot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = return.gtable, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = plot, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    tempo <- fun_check(data = warn.print, class = "logical", length = 1, fun.name = function.name) ; eval(ee)
+    if( ! is.null(lib.path)){
+        tempo <- fun_check(data = lib.path, class = "vector", mode = "character", fun.name = function.name) ; eval(ee) # several possible paths
+    }else{
+        # no fun_check test here, it is just for checked.arg.names
+        tempo <- fun_check(data = lib.path, class = "vector")
+        checked.arg.names <- c(checked.arg.names, tempo$object.name)
+    }
+    if(any(arg.check) == TRUE){
+        stop(paste0("\n\n================\n\n", paste(text.check[arg.check], collapse = "\n"), "\n\n================\n\n"), call. = FALSE) #
+    }
+    # source("C:/Users/Gael/Documents/Git_versions_to_use/debugging_tools_for_r_dev-v1.7/r_debugging_tools-v1.7.R") ; eval(parse(text = str_basic_arg_check_dev)) ; eval(parse(text = str_arg_check_with_fun_check_dev)) # activate this line and use the function (with no arguments left as NULL) to check arguments status and if they have been checked using fun_check()
+    # end argument primary checking
+    # second round of checking and data preparation
+    # management of NA arguments
+    tempo.arg <- names(arg.user.setting) # values provided by the user
+    tempo.log <- suppressWarnings(sapply(lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.na), FUN = any)) & lapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = length)== 1L # no argument provided by the user can be just NA
+    if(any(tempo.log) == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE JUST NA")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # end management of NA arguments
+    # management of NULL arguments
+    tempo.arg <-c(
+        "data1", 
+        "freq", 
+        "categ", 
+        # "fill.palette", # inactivated because can be null
+        # "fill.color", # inactivated because can be null
+        "hole.size", 
+        "hole.text", 
+        "hole.text.size", 
+        "border.color", 
+        "border.size", 
+        "title", 
+        "title.text.size", 
+        # "annotation", # inactivated because can be null
+        "annotation.distance", 
+        "annotation.size", 
+        "annotation.force", 
+        "annotation.force.pull", 
+        "legend.show", 
+        # "legend.width", # inactivated because can be null
+        # "legend.name", # inactivated because can be null
+        # "legend.limit", # inactivated because can be null
+        "legend.add.prop", 
+        # "add", # inactivated because can be null
+        "return", 
+        "return.ggplot", 
+        "return.gtable", 
+        "plot", 
+        "warn.print"
+        # "lib.path" # inactivated because can be null
+    )
+    tempo.log <- sapply(lapply(tempo.arg, FUN = get, env = sys.nframe(), inherit = FALSE), FUN = is.null)
+    if(any(tempo.log) == TRUE){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\n", ifelse(sum(tempo.log, na.rm = TRUE) > 1, "THESE ARGUMENTS\n", "THIS ARGUMENT\n"), paste0(tempo.arg[tempo.log], collapse = "\n"),"\nCANNOT BE NULL")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n"), call. = FALSE) # == in stop() to be able to add several messages between ==
+    }
+    # end management of NULL arguments
+    # code that protects set.seed() in the global environment
+    if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment
+        tempo.random.seed <- .Random.seed
+        on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv))
+    }else{
+        on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness
+    }
+    set.seed(1)
+    # end code that protects set.seed() in the global environment
+    # warning initiation
+    ini.warning.length <- options()$warning.length
+    options(warning.length = 8170)
+    warn <- NULL
+    warn.count <- 0
+    # end warning initiation
+    # other checkings
+    removed.row.nb <- NULL
+    removed.rows <- data.frame(stringsAsFactors = FALSE)
+    data1.ini <- data1 # strictly identical to data1
+    if( ! freq %in% names(data1)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nfreq ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }else{
+        if(all(is.na(data1[ , freq]) | is.infinite(data1[ , freq]))){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE freq COLUMN OF data1 CANNOT BE JUST NA OR Inf")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+        tempo <- fun_check(data = data1[ , freq], mode = "numeric", neg.values = FALSE, fun.name = function.name)
+        if(tempo$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\n", tempo$text)
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+        # Inf and NA removal
+        if(any(is.infinite(data1[, freq]) | is.na(data1[, freq]))){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") PRESENCE OF Inf OR NA VALUES IN THE ", freq, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            tempo <- which(is.infinite(data1.ini[, freq]) | is.na(data1.ini[, freq])) # data.ini used for the output
+            removed.row.nb <- c(removed.row.nb, tempo)
+            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
+            data1 <- data1[ ! (is.infinite(data1[, freq]) | is.na(data1[, freq])), ] #
+        }
+        # end Inf and NA removal
+        # 0 removal
+        if(any(data1[, freq] == 0)){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") PRESENCE OF 0 VALUES IN THE ", freq, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            tempo <- which(data1[, freq] == 0) # data.ini used for the output
+            removed.row.nb <- c(removed.row.nb, tempo)
+            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
+            data1 <- data1[ data1[, freq] != 0, ] #
+        }
+        # end 0 removal
+    }
     
-    
-    
-    # legend display
-    tempo.legend.final <- 'ggplot2::guides(
-fill = if(fin.lg.disp[[1]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[1]], 
-override.aes = list(
-fill = lg.color[[1]], 
-colour = if(lg.dot.shape[[1]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[1]]}else{lg.color[[1]]}, # lg.dot.shape[[1]] %in% 21:24 are the only one that can be filled
-shape = lg.dot.shape[[1]], 
-size = lg.dot.size[[1]], 
-stroke = lg.dot.border.size[[1]], 
-alpha = lg.alpha[[1]], 
-linetype = 0
-)
-)
-}else{
-"none"
-}, 
-shape = if(fin.lg.disp[[2]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[2]], 
-override.aes = list(
-fill = lg.color[[2]], 
-colour = if(lg.dot.shape[[2]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[2]]}else{lg.color[[2]]}, # lg.dot.shape[[2]] %in% 21:24 are the only one that can be filled
-shape = lg.dot.shape[[2]], 
-size = lg.dot.size[[2]], 
-stroke = lg.dot.border.size[[2]], 
-alpha = lg.alpha[[2]], 
-linetype = 0
-)
-)
-}else{
-"none"
-}, 
-stroke = if(fin.lg.disp[[3]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[3]], 
-override.aes = list(
-fill = lg.color[[3]], 
-colour = if(lg.dot.shape[[3]] %in% 21:24 & ! is.null(dot.border.color)){lg.dot.border.color[[3]]}else{lg.color[[3]]}, # lg.dot.shape[[3]] %in% 21:24 are the only one that can be filled
-shape = lg.dot.shape[[3]], 
-size = lg.dot.size[[3]], 
-stroke = lg.dot.border.size[[3]], 
-alpha = lg.alpha[[3]], 
-linetype = 0
-)
-)
-}else{
-"none"
-}, 
-linetype = if(fin.lg.disp[[4]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[4]], 
-override.aes = list(
-color = lg.color[[4]], 
-size = lg.line.size[[4]], 
-linetype = lg.line.type[[4]], 
-alpha = lg.alpha[[4]], 
-shape = NA
-)
-)
-}else{
-"none"
-}, 
-alpha = if(fin.lg.disp[[5]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[5]], 
-override.aes = list(
-color = lg.color[[5]], 
-size = lg.line.size[[5]], 
-linetype = lg.line.type[[5]], 
-alpha = lg.alpha[[5]], 
-shape = NA
-)
-)
-}else{
-"none"
-}, 
-size = if(fin.lg.disp[[6]] == TRUE){
-ggplot2::guide_legend(
-order = lg.order[[6]], 
-override.aes = list(
-color = lg.color[[6]], 
-size = lg.line.size[[6]], 
-linetype = lg.line.type[[6]], 
-alpha = lg.alpha[[6]], 
-shape = NA
-)
-)
-}else{
-"none"
-}
-)' # clip = "off" to have secondary ticks outside plot region does not work
-if( ! is.null(legend.width)){
-    if(any(unlist(legend.disp))){ # means some TRUE
-        tempo.graph.info <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste0(paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "), ' + ', tempo.legend.final))))) # will be recovered later again, when ylim will be considered
-        legend.final <- fun_gg_get_legend(ggplot_built = tempo.graph.info, fun.name = function.name) # get legend
-        fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend
-        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
-            legend.final <- fun_gg_empty_graph() # empty graph instead of legend
+    if( ! categ %in% names(data1)){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\ncateg ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }else{
+        if(all(is.na(data1[ , categ]))){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 CANNOT BE JUST NA")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+        tempo1 <- fun_check(data = categ, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+        tempo2 <- fun_check(data = categ, class = "factor", na.contain = TRUE, fun.name = function.name)
+        if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 MUST BE CLASS \"factor\" OR \"character\"")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+        # NA removal
+        if(any(is.na(data1[, categ]))){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") PRESENCE OF NA VALUES IN THE ", categ, " COLUMN OF THE data1 ARGUMENT AND CORRESPONDING ROWS REMOVED (SEE $removed.row.nb AND $removed.rows)")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            tempo <- which(is.na(data1.ini[, categ])) # data.ini used for the output
+            removed.row.nb <- c(removed.row.nb, tempo)
+            removed.rows <- rbind(removed.rows, data1.ini[tempo, ], stringsAsFactors = FALSE) # data.ini used for the output
+            data1 <- data1[ ! is.na(data1[, categ]), ] #
+        }
+        # end Inf and NA removal
+        if(any(duplicated(data1[, categ]))){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE categ COLUMN OF data1 CANNOT CONTAIN DUPLICATED VALUES\n", paste(data1[, categ][duplicated(data1[, categ])], collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+    }
+    
+    if( ! is.null(annotation)){
+        if( ! annotation %in% names(data1)){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nannotation ARGUMENT MUST BE A COLUMN NAME OF THE data1 ARGUMENT")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }else{
+            if(all(is.na(data1[ , annotation]))){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nIF NON NULL, THE annotation COLUMN OF data1 CANNOT BE JUST NA")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            }
+            tempo1 <- fun_check(data = annotation, class = "vector", mode = "character", na.contain = TRUE, fun.name = function.name)
+            tempo2 <- fun_check(data = annotation, class = "factor", na.contain = TRUE, fun.name = function.name)
+            if(tempo1$problem == TRUE & tempo2$problem == TRUE){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE annotation COLUMN OF data1 MUST BE CLASS \"factor\" OR \"character\"")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            }
+            if(any(duplicated(data1[, annotation]))){
+                warn.count <- warn.count + 1
+                tempo.warn <- paste0("(", warn.count,") PRESENCE OF DUPLICATED VALUES IN THE ", annotation, " COLUMN OF THE data1 ARGUMENT: ", paste0(data1[, annotation][duplicated(data1[, annotation])], collapse = " "))
+                warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            }
+        }
+    }
+    if(length(data1) == 0){
+        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE data1 ARGUMENT IS EMPTY AFTER Inf, NA AND 0 REMOVAL IN THE ", freq, ifelse(is.null(annotation), " AND ", ", "), categ, ifelse(is.null(annotation), "", " AND "), " COLUMNS")
+        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    }
+    if( ! is.null(fill.color)){
+        if( ! is.numeric(fill.color)){
+            if( ! all(fill.color[ ! is.na(fill.color)] %in% colors() | grepl(pattern = "^#", fill.color[ ! is.na(fill.color)]), na.rm = TRUE)){
+                tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE A VECTOR OF (1) HEXADECIMAL COLOR STRINGS STARTING BY #, OR (2) COLOR NAMES GIVEN BY colors(), OR (3) INTEGER VALUES")
+                stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            }else{
+                fill.color <- as.character(fill.color) # remove class factor is any
+            }
+        }
+    }
+    if( ! is.numeric(border.color)){
+        if( ! (border.color %in% colors() | grepl(pattern = "^#", border.color))){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nfill.color ARGUMENT MUST BE (1) A HEXADECIMAL COLOR STRING STARTING BY #, OR (2) A COLOR NAME GIVEN BY colors(), OR (3) AN INTEGER VALUE")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }else{
+            border.color <- as.character(border.color) # remove class factor is any
+        }
+    }
+    # legend name filling
+    if(is.null(legend.name)){
+        legend.name <- categ
+    }
+    # legend.name not NULL anymore
+    # end legend name filling
+    # verif of add
+    if( ! is.null(add)){
+        if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by +
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+            
+        }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ #
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by )
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+    }
+    # end verif of add
+    # management of add containing facet
+    facet.categ <- NULL
+    if( ! is.null(add)){
+        facet.check <- TRUE
+        tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) #
+        tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap")
+        tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid")
+        tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep")
+        
+        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"))){
+            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")])))
+            facet.categ <- list(names(tempo1$params$facets)) # list of length 1
+            tempo.text <- "facet_wrap OR facet_rep_wrap"
+            facet.check <- FALSE
+        }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){
+            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")])))
+            facet.categ <- list(c(names(tempo1$params$rows), names(tempo1$params$cols))) # list of length 1
+            tempo.text <- "facet_grid OR facet_rep_grid"
+            facet.check <- FALSE
+        }
+        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+    }
+    # if facet.categ is not NULL, it is a list of length 1 now
+    # end management of add containing facet
+    if( ! is.null(lib.path)){
+        if( ! all(dir.exists(lib.path))){ # separation to avoid the problem of tempo$problem == FALSE and lib.path == NA
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nDIRECTORY PATH INDICATED IN THE lib.path ARGUMENT DOES NOT EXISTS:\n", paste(lib.path, collapse = "\n"))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }
+    }
+    # end other checkings
+    # reserved word checking
+    if( ! (is.null(add))){
+        if(any(sapply(X = arg.names, FUN = grepl, x = add), na.rm = TRUE)){
             warn.count <- warn.count + 1
-            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n")
+            tempo.warn <- paste0("(", warn.count,") NAMES OF ", function.name, " ARGUMENTS DETECTED IN THE add STRING:\n", paste(arg.names[sapply(X = arg.names, FUN = grepl, x = add)], collapse = "\n"), "\nRISK OF WRONG OBJECT USAGE INSIDE ", function.name)
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
-    }else if(plot == TRUE){ # means all FALSE
-        legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
-        warn.count <- warn.count + 1
-        tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (NON-NULL categ ARGUMENT OR legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON-NULL legend.width ARGUMENT\n")
-        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
     }
-}
-if( ! any(unlist(legend.disp))){
-    fin.lg.disp[] <- FALSE # remove all the legends. Must be done even if fin.lg.disp is not appearing in the code thenafter. Otherwise twice the legend
-}
-assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = tempo.legend.final)))
-# end legend display
-
-
-
-
-
-# scale management
-tempo.coord <- suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ", ' + ggplot2::scale_x_continuous(expand = c(0, 0), limits = sort(x.lim), oob = scales::rescale_none) + ggplot2::scale_y_continuous(expand = c(0, 0), limits = sort(y.lim), oob = scales::rescale_none)'))))$layout$panel_params[[1]]) # here I do not need the x-axis and y-axis orientation, I just need the number of main ticks
-# x.second.tick.positions # coordinates of secondary ticks (only if x.second.tick.nb argument is non-null or if x.log argument is different from "no")
-if(x.log != "no"){ # integer main ticks for log2 and log10
-    tempo.scale <- (as.integer(min(x.lim, na.rm = TRUE)) - 1):(as.integer(max(x.lim, na.rm = TRUE)) + 1)
-}else{
-    tempo <- if(is.null(attributes(tempo.coord$x$breaks))){tempo.coord$x$breaks}else{unlist(attributes(tempo.coord$x$breaks))}
-    if(all(is.na(tempo))){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$x$breaks")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    # verif of add
+    if( ! is.null(add)){
+        if( ! grepl(pattern = "^\\s*\\+", add)){ # check that the add string start by +
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST START WITH \"+\": ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else if( ! grepl(pattern = "(ggplot2|lemon)\\s*::", add)){ #
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nFOR EASIER FUNCTION DETECTION, add ARGUMENT MUST CONTAIN \"ggplot2::\" OR \"lemon::\" IN FRONT OF EACH GGPLOT2 FUNCTION: ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }else if( ! grepl(pattern = ")\\s*$", add)){ # check that the add string finished by )
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nadd ARGUMENT MUST FINISH BY \")\": ", paste(unique(add), collapse = " "))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
     }
-    if(length(unique(x.lim)) <= 1){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT X-AXIS VALUES HAVE A NULL RANGE: ", paste(x.lim, collapse = " "), "\nPLEASE, USE THE x.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-    }else{
-        tempo.scale <- fun_scale(lim = x.lim, n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) # in ggplot 3.3.0, tempo.coord$x.major_source replaced by tempo.coord$x$breaks. If fact: n = ifelse(is.null(x.tick.nb), length(tempo[ ! is.na(tempo)]), x.tick.nb)) replaced by n = ifelse(is.null(x.tick.nb), 4, x.tick.nb))
+    # end verif of add
+    # management of add containing facet
+    facet.categ <- NULL
+    if( ! is.null(add)){
+        facet.check <- TRUE
+        tempo <- unlist(strsplit(x = add, split = "\\s*\\+\\s*(ggplot2|lemon)\\s*::\\s*")) #
+        tempo <- sub(x = tempo, pattern = "^facet_wrap", replacement = "ggplot2::facet_wrap")
+        tempo <- sub(x = tempo, pattern = "^facet_grid", replacement = "ggplot2::facet_grid")
+        tempo <- sub(x = tempo, pattern = "^facet_rep", replacement = "lemon::facet_rep")
+        if(any(grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap"), na.rm = TRUE)){
+            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_wrap|lemon::facet_rep_wrap")])))
+            facet.categ <- names(tempo1$params$facets)
+            tempo.text <- "facet_wrap OR facet_rep_wrap"
+            facet.check <- FALSE
+        }else if(grepl(x = add, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")){
+            tempo1 <- suppressWarnings(eval(parse(text = tempo[grepl(x = tempo, pattern = "ggplot2::facet_grid|lemon::facet_rep_grid")])))
+            facet.categ <- c(names(tempo1$params$rows), names(tempo1$params$cols))
+            tempo.text <- "facet_grid OR facet_rep_grid"
+            facet.check <- FALSE
+        }
+        if(facet.check == FALSE & ! all(facet.categ %in% names(data1))){ # WARNING: all(facet.categ %in% names(data1)) is TRUE when facet.categ is NULL # all() without na.rm -> ok because facet.categ cannot be NA (tested above)
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nDETECTION OF \"", tempo.text, "\" STRING IN THE add ARGUMENT BUT PROBLEM OF VARIABLE DETECTION (COLUMN NAMES OF data1)\nTHE DETECTED VARIABLES ARE:\n", paste(facet.categ, collapse = " "), "\nTHE data1 COLUMN NAMES ARE:\n", paste(names(data1), collapse = " "), "\nPLEASE REWRITE THE add STRING AND RERUN")
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+        }
     }
-}
-x.second.tick.values <- NULL
-x.second.tick.pos <- NULL
-if(x.log != "no"){
-    tempo <- fun_inter_ticks(lim = x.lim, log = x.log)
-    x.second.tick.values <- tempo$values
-    x.second.tick.pos <- tempo$coordinates
-    # if(vertical == TRUE){ # do not remove in case the bug is fixed
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-        geom = "segment", 
-        x = x.second.tick.pos, 
-        xend = x.second.tick.pos, 
-        y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, 
-        yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80}
-    ))
-    # }else{ # not working because of the ggplot2 bug
-    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", y = x.second.tick.pos, yend = x.second.tick.pos, x = tempo.coord$x.range[1], xend = tempo.coord$x.range[1] + diff(tempo.coord$x.range) / 80))
-    # }
-    coord.names <- c(coord.names, "x.second.tick.positions")
-}else if(( ! is.null(x.second.tick.nb)) & x.log == "no"){
-    # if(x.second.tick.nb > 0){ #inactivated because already checked before
-    if(length(tempo.scale) < 2){
-        tempo.cat1 <- c("x.tick.nb", "x.second.tick.nb")
-        tempo.cat2 <- sapply(list(x.tick.nb, x.second.tick.nb), FUN = paste0, collapse = " ")
-        tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE X-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
+    # end management of add containing facet
+    # end reserved word checking
+    # end second round of checking and data preparation
+    
+    # package checking
+    fun_pack(req.package = c(
+        "gridExtra", 
+        "ggplot2", 
+        "lemon",
+        "grid",
+        "ggrepel"
+    ), lib.path = lib.path)
+    # end package checking
+    
+    # main code
+    data1 <- data.frame(data1, prop = data1[ , freq] / sum(data1[ , freq]))
+    if(legend.add.prop == TRUE){
+        data1[ , categ] <- paste0(data1[ , categ], " (", round(data1$prop, 2), ")")
+    }
+    data1[ , categ] <- factor(data1[ , categ], levels = data1[ , categ][order(data1$prop, decreasing = TRUE)]) # reorder so that the donut is according to decreasing proportion starting at the top in a clockwise direction
+    data1 <- data1[order(as.numeric(data1[ , categ]), decreasing = FALSE), ] # data1[ , categ] with rows in decreasing order, according to prop
+    data1 <- data.frame(data1, x = 0) # staked bar at the origin of the donut set to x = 0
+    tempo.gg.name <- "gg.indiv.plot."
+    tempo.gg.count <- 0
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), eval(parse(text = paste0("ggplot2::ggplot()", if(is.null(add)){""}else{add})))) # add added here to have the facets
+    bar_width = 1
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_col(
+        data = data1,
+        mapping = ggplot2::aes_string(x = "x", y = freq, fill = categ), 
+        color = border.color, 
+        size = border.size, 
+        width = bar_width
+    )) # size is size of the separation in the donut
+    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::geom_text(
+    #     ggplot2::aes(label = Freq), 
+    #     position = ggplot2::position_stack(vjust = 0.5)
+    # ))
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_x_continuous(
+        expand = c(0, 0), # prevent extra limits in x axis
+        limits = c(- bar_width / 2 - (bar_width * hole.size) / (1 - hole.size), max(bar_width / 2, annotation.distance))
+    )) # must be centered on x = 0
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::ylim(c(0, max(cumsum(data1[ , freq])))))
+    if(hole.text == TRUE){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
+            geom = "text", 
+            x = - bar_width / 2 - (bar_width * hole.size) / (1 - hole.size), 
+            y = 0, 
+            label = sum(data1[ , freq]), 
+            size = hole.text.size
+        ))
+    }
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_polar(theta = "y", direction = -1, start = 0, clip = "on"))
+    if(is.null(fill.color) & ! is.null(fill.palette)){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_brewer(palette = fill.palette, name = legend.name))
+    }else if( ! is.null(fill.color)){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_manual(values = fill.color, name = legend.name, na.value = "white"))
+    }else if(! is.null(legend.name)){
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::labs(fill = legend.name))
+    }
+    
+    if( ! is.null(add)){ # if add is NULL, then = 0
+        if(grepl(pattern = "ggplot2\\s*::\\s*theme", add) == TRUE){
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") \"ggplot2::theme\" STRING DETECTED IN THE add ARGUMENT\n-> INTERNAL GGPLOT2 THEME FUNCTIONS theme_void() HAS BEEN INACTIVATED, SO THAT THE USER THEME CAN BE EFFECTIVE")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+            add.check <- FALSE
+        }else{
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void())
+        }
     }else{
-        tempo <- fun_inter_ticks(lim = x.lim, log = x.log, breaks = tempo.scale, n = x.second.tick.nb)
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::theme_void())
     }
-    x.second.tick.values <- tempo$values
-    x.second.tick.pos <- tempo$coordinates
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-        geom = "segment", 
-        x = x.second.tick.pos, 
-        xend = x.second.tick.pos, 
-        y = if(diff(y.lim) > 0){tempo.coord$y.range[1]}else{tempo.coord$y.range[2]}, 
-        yend = if(diff(y.lim) > 0){tempo.coord$y.range[1] + abs(diff(tempo.coord$y.range)) / 80}else{tempo.coord$y.range[2] - abs(diff(tempo.coord$y.range)) / 80}
-    ))
-    coord.names <- c(coord.names, "x.second.tick.positions")
-}
-# for the ggplot2 bug with x.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & x.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_x_continuous")))
-assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_x_continuous(
-    breaks = tempo.scale, 
-    minor_breaks = x.second.tick.pos, 
-    labels = if(x.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(x.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(x.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, 
-    expand = c(0, 0), # remove space after after axis limits
-    limits = sort(x.lim), # NA indicate that limits must correspond to data limits but xlim() already used
-    oob = scales::rescale_none, 
-    trans = ifelse(diff(x.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_x_reverse() but create the problem of x-axis label disappearance with x.lim decreasing. Thus, do not use. Use xlim() below and after this
-))
-# end x.second.tick.positions
-# y.second.tick.positions # coordinates of secondary ticks (only if y.second.tick.nb argument is non-null or if y.log argument is different from "no")
-if(y.log != "no"){ # integer main ticks for log2 and log10
-    tempo.scale <- (as.integer(min(y.lim, na.rm = TRUE)) - 1):(as.integer(max(y.lim, na.rm = TRUE)) + 1)
-}else{
-    tempo <- if(is.null(attributes(tempo.coord$y$breaks))){tempo.coord$y$breaks}else{unlist(attributes(tempo.coord$y$breaks))}
-    if(all(is.na(tempo))){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nONLY NA IN tempo.coord$y$breaks")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(
+        fill = ggplot2::guide_legend(override.aes = list(color = "white", size  = 2, stroke = 1))
+    )) # remove border of squares in legend
+    
+    # annotations on slices
+    if( ! is.null(annotation)){
+        tempo <- rev(cumsum(rev(data1[ , freq])))
+        data1 <- data.frame(data1, text_y = tempo - (tempo - c(tempo[-1], 0)) / 2)
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggrepel::geom_text_repel(
+            data = data1, 
+            mapping = ggplot2::aes_string(
+                x = "x", 
+                y = "text_y", 
+                label = annotation
+            ), 
+            size = annotation.size, 
+            force = annotation.force, 
+            force_pull = annotation.force.pull, 
+            nudge_x = annotation.distance, # knowing that the bar is centered on x = 0 and that the right edge is at bar_width / 2, 0 means center of the slice, 0.5 means at the edge if bar_width = 1
+            show.legend = FALSE
+        ))
     }
-    if(length(unique(y.lim)) <= 1){
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nIT SEEMS THAT Y-AXIS VALUES HAVE A NULL RANGE: ", paste(y.lim, collapse = " "), "\nPLEASE, USE THE y.lim ARGUMENT WITH 2 DIFFERENT VALUES TO SOLVE THIS")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
-    }else{
-        tempo.scale <- fun_scale(lim = y.lim, n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) # in ggplot 3.3.0, tempo.coord$y.major_source replaced by tempo.coord$y$breaks. If fact: n = ifelse(is.null(y.tick.nb), length(tempo[ ! is.na(tempo)]), y.tick.nb)) replaced by n = ifelse(is.null(y.tick.nb), 4, y.tick.nb))
+    # end annotations on slices
+    
+    # legend management
+    # removal of part of the legend 
+    if( ! is.null(legend.limit)){
+        if(sum(data1$prop >= legend.limit) == 0){
+            tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE legend.limit PARAMETER VALUE (", legend.limit, ") IS TOO HIGH FOR THE PROPORTIONS IN THE DONUT PLOT:\n", paste0(data1$prop, collapse = "\n"))
+            stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+        }else{
+            assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_fill_discrete(
+                breaks = as.character(data1[ , categ][data1$prop >= legend.limit])
+            ))
+        }
     }
-}
-y.second.tick.values <- NULL
-y.second.tick.pos <- NULL
-if(y.log != "no"){
-    tempo <- fun_inter_ticks(lim = y.lim, log = y.log)
-    y.second.tick.values <- tempo$values
-    y.second.tick.pos <- tempo$coordinates
-    # if(vertical == TRUE){ # do not remove in case the bug is fixed
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-        geom = "segment", 
-        y = y.second.tick.pos, 
-        yend = y.second.tick.pos, 
-        x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, 
-        xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80}
-    ))
-    # }else{ # not working because of the ggplot2 bug
-    # assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(geom = "segment", x = y.second.tick.pos, xend = y.second.tick.pos, y = tempo.coord$y.range[1], yend = tempo.coord$y.range[1] + diff(tempo.coord$y.range) / 80))
-    # }
-    coord.names <- c(coord.names, "y.second.tick.positions")
-}else if(( ! is.null(y.second.tick.nb)) & y.log == "no"){
-    # if(y.second.tick.nb > 0){ #inactivated because already checked before
-    if(length(tempo.scale) < 2){
-        tempo.cat1 <- c("y.tick.nb", "y.second.tick.nb")
-        tempo.cat2 <- sapply(list(y.tick.nb, y.second.tick.nb), FUN = paste0, collapse = " ")
-        tempo.sep <- sapply(mapply(" ", max(nchar(tempo.cat1)) - nchar(tempo.cat1) + 3, FUN = rep, SIMPLIFY = FALSE), FUN = paste0, collapse = "")
-        tempo.cat <- paste0("ERROR IN ", function.name, "\nTHE NUMBER OF GENERATED TICKS FOR THE Y-AXIS IS NOT CORRECT: ", length(tempo.scale), "\nUSING THESE ARGUMENT SETTINGS (NO DISPLAY MEANS NULL VALUE):\n", paste0(tempo.cat1, tempo.sep, tempo.cat2, collapse = "\n"), "\nPLEASE, TEST OTHER VALUES")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE) # == in stop() to be able to add several messages between ==
-    }else{
-        tempo <- fun_inter_ticks(lim = y.lim, log = y.log, breaks = tempo.scale, n = y.second.tick.nb)
+    # end removal of part of the legend
+    if(legend.show == FALSE){ # must be here because must be before bef.final.plot
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none")) # inactivate the initial legend
     }
-    y.second.tick.values <- tempo$values
-    y.second.tick.pos <- tempo$coordinates
-    assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::annotate(
-        geom = "segment", 
-        y = y.second.tick.pos, 
-        yend = y.second.tick.pos, 
-        x = if(diff(x.lim) > 0){tempo.coord$x.range[1]}else{tempo.coord$x.range[2]}, 
-        xend = if(diff(x.lim) > 0){tempo.coord$x.range[1] + abs(diff(tempo.coord$x.range)) / 80}else{tempo.coord$x.range[2] - abs(diff(tempo.coord$x.range)) / 80}
-    ))
-    coord.names <- c(coord.names, "y.second.tick.positions")
-}
-# for the ggplot2 bug with y.log, this does not work: eval(parse(text = ifelse(vertical == FALSE & y.log == "log10", "ggplot2::scale_x_continuous", "ggplot2::scale_y_continuous")))
-assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::scale_y_continuous(
-    breaks = tempo.scale, 
-    minor_breaks = y.second.tick.pos, 
-    labels = if(y.log == "log10"){scales::trans_format("identity", scales::math_format(10^.x))}else if(y.log == "log2"){scales::trans_format("identity", scales::math_format(2^.x))}else if(y.log == "no"){ggplot2::waiver()}else{tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, "\nCODE INCONSISTENCY 10") ; stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)}, 
-    expand = c(0, 0), # remove space after axis limits
-    limits = sort(y.lim), # NA indicate that limits must correspond to data limits but ylim() already used
-    oob = scales::rescale_none, 
-    trans = ifelse(diff(y.lim) < 0, "reverse", "identity") # equivalent to ggplot2::scale_y_reverse() but create the problem of y-axis label disappearance with y.lim decreasing. Thus, do not use. Use ylim() below and after this
-))
-# end y.second.tick.positions
-assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::coord_cartesian(xlim = x.lim, ylim = y.lim)) # clip = "off" to have secondary ticks outside plot region. The problem is that points out of bounds are also drawn outside the plot region. Thus, I cannot use it # at that stage, x.lim and y.lim not NULL anymore
-# end scale management
-
-
-
-
-# drawing
-fin.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
-grob.save <- NULL
-if(plot == TRUE){
-    if( ! is.null(legend.width)){ # any(unlist(legend.disp)) == TRUE removed to have empty legend space # not & any(unlist(fin.lg.disp)) == TRUE here because converted to FALSE
-        grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(fin.plot, legend.final, ncol=2, widths=c(1, legend.width))))
+    bef.final.plot <- suppressWarnings(suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))))
+    if( ! is.null(legend.width)){
+        legend.plot <- suppressWarnings(suppressMessages(fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path))) # get legend
+        assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none")) # inactivate the initial legend
+        if(is.null(legend.plot) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
+            legend.plot <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
+            warn.count <- warn.count + 1
+            tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n")
+            warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+        }
     }else{
-        grob.save <- suppressMessages(suppressWarnings(print(fin.plot)))
+        legend.plot <- NULL
     }
-}else{
-    warn.count <- warn.count + 1
-    tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
-    warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
-}
-# end drawing
-
-
-
-# output
-if(warn.print == TRUE & ! is.null(warn)){
-    on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
-}
-on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
-if(return == TRUE){
-    output <- suppressMessages(ggplot2::ggplot_build(fin.plot))
-    # output$data <- output$data[-1] # yes for boxplot but not for scatter # remove the first data because corresponds to the initial empty boxplot
-    if(length(output$data) != length(coord.names)){
-        tempo.cat <- paste0("INTERNAL CODE ERROR IN ", function.name, ": length(output$data) AND length(coord.names) MUST BE IDENTICAL. CODE HAS TO BE MODIFIED")
-        stop(paste0("\n\n================\n\n", tempo.cat, "\n\n================\n\n", ifelse(is.null(warn), "", paste0("IN ADDITION\nWARNING", ifelse(warn.count > 1, "S", ""), ":\n\n", warn))), call. = FALSE)
+    # end legend management
+    
+    # title
+    title.grob <- grid::textGrob(
+        label = title,
+        x = grid::unit(0, "lines"), 
+        y = grid::unit(0, "lines"),
+        hjust = 0,
+        vjust = 0,
+        gp = grid::gpar(fontsize = 7)
+    )
+    # end title
+    
+    # drawing
+    pdf(NULL)
+    grob.save <- NULL
+    main.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
+    main.plot.output <- suppressMessages(ggplot2::ggplot_build(main.plot))
+    main.grob <- suppressMessages(suppressWarnings(gridExtra::arrangeGrob(
+        main.plot, 
+        top = if(title == ""){" "}else{title.grob}, 
+        left = " ", 
+        right = " "
+    ))) # , left = " ", right = " " : trick to add margins in the plot. padding =  unit(0.5, "inch") is for top margin above the title
+    if( ! is.null(legend.width)){
+        grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(main.grob, legend.plot, ncol=2, widths=c(1, legend.width)))) # assemble grobs, ggplot, gtable into a gtable that defines the positions of the different elements (as grobs)
     }else{
-        names(output$data) <- coord.names
+        grob.save <- suppressMessages(suppressWarnings(print(main.grob)))
     }
-    if(is.null(unlist(removed.row.nb))){
-        removed.row.nb <- NULL
-        removed.rows <- NULL
+    dev.off() # inactivate the pdf(NULL) above
+    if(plot == TRUE){
+        gridExtra::grid.arrange(grob.save) # plot a gtable (grob)
     }else{
-        for(i3 in 1:length(data1)){
-            if( ! is.null(removed.row.nb[[i3]])){
-                removed.row.nb[[i3]] <- sort(removed.row.nb[[i3]])
-                removed.rows[[i3]] <- data1.ini[[i3]][removed.row.nb[[i3]], ]
-            }
+        warn.count <- warn.count + 1
+        tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
+        warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
+    }
+    # end drawing
+    
+    # output
+    if(warn.print == TRUE & ! is.null(warn)){
+        on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
+    }
+    on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
+    if(return == TRUE){
+        if(is.null(unlist(removed.row.nb))){
+            removed.row.nb <- NULL
+            removed.rows <- NULL
         }
+        tempo <- main.plot.output$layout$panel_params[[1]]
+        output <- list(
+            data = data1, 
+            removed.row.nb = removed.row.nb, 
+            removed.rows = removed.rows, 
+            plot.data = main.plot.output$data, 
+            panel = facet.categ, 
+            axes = list(
+                x.range = tempo$x.range, 
+                x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
+                x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
+                y.range = tempo$y.range, 
+                y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
+                y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
+            ), 
+            warn = paste0("\n", warn, "\n\n"), 
+            ggplot = if(return.ggplot == TRUE){main.plot}else{NULL}, # main plot -> plots the graph if return == TRUE
+            gtable = if(return.gtable == TRUE){grob.save}else{NULL} # gtable of the full graph (main + title + legend)
+        )
+        return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
     }
-    tempo <- output$layout$panel_params[[1]]
-    output <- list(
-        data = data1, 
-        removed.row.nb = removed.row.nb, 
-        removed.rows = removed.rows, 
-        plot = c(output$data, x.second.tick.values = list(x.second.tick.values), y.second.tick.values = list(y.second.tick.values)), 
-        panel = facet.categ, 
-        axes = list(
-            x.range = tempo$x.range, 
-            x.labels = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{tempo$x$scale$get_labels()}, # is.null(attributes(tempo$x$breaks)) test if it is number (TRUE) or character (FALSE)
-            x.positions = if(is.null(attributes(tempo$x$breaks))){tempo$x$breaks}else{unlist(attributes(tempo$x$breaks))}, 
-            y.range = tempo$y.range, 
-            y.labels = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{tempo$y$scale$get_labels()}, 
-            y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
-        ), 
-        warn = paste0("\n", warn, "\n\n"), 
-        ggplot = if(return.ggplot == TRUE){fin.plot}else{NULL}, # fin.plot plots the graph if return == TRUE
-        gtable = if(return.gtable == TRUE){grob.save}else{NULL} #
-    )
-    return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
-}
-# end output
-# end main code
+    # end output
+    # end main code
 }
 
 
diff --git a/examples_fun_gg_donut.R b/examples_fun_gg_donut.R
index 10ee97b..4b7cf52 100644
--- a/examples_fun_gg_donut.R
+++ b/examples_fun_gg_donut.R
@@ -109,51 +109,66 @@ fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car",
                add = "+ggplot2::theme_gray()"
 )
 fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
-               add = "+ggplot2::facet_wrap(facets = 'Country', labeller = 'label_both') + ggplot2::theme(strip.background = ggplot2::element_rect(color = 'grey', size = 0.5), strip.text = ggplot2::element_text(size = 10, face = 'bold'), panel.spacing = ggplot2::unit(0.5, 'lines'))"  # or ggplot2::vars(Country) instead of 'Country'. See https://ggplot2.tidyverse.org/reference/labeller.html
+    add = "+ggplot2::facet_wrap(facets = 'Country', labeller = 'label_both') + ggplot2::theme(strip.background = ggplot2::element_rect(color = 'grey', size = 0.5), strip.text = ggplot2::element_text(size = 10, face = 'bold'), panel.spacing = ggplot2::unit(0.5, 'lines'))"  # or ggplot2::vars(Country) instead of 'Country'. See https://ggplot2.tidyverse.org/reference/labeller.html
 )
 
-
-## Other parameters
+## The return and plot arguments
+fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
+    return = FALSE,
+    plot = FALSE,
+)
+# nothing is returned and nothing is plotted
 res <- fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
-                      return = TRUE, # output returned and assigned into res. If FALSE, res is NULL
-                      return.ggplot = TRUE, # ggplot object added in res (without the legend
-                      return.gtable = TRUE, # ggplot object as gtable of grobs in res ($gtable is NULL if plot = FALSE)
-                      plot = TRUE, # plot displayed during asignation into res
-                      warn.print = TRUE, 
-                      lib.path = NULL
-)
-# plot the result
-fun_open(pdf = FALSE)
-res$ggplot
-# The advantage of $ggplot is that it is easy to update the plot
-res$ggplot + ggplot2::annotate(geom = "text", x = -0.5, y = 0, label = "NOT GOOD", size = 20, angle = 45)
-# However, manipulation of res triggers plotting when $ggplot is not NULL (NULL $ggplot obtained using return.ggplot = FALSE), which is annoying, as explain in the function description. Thus, it is preferable to use return.ggplot = FALSE
-
-
-## Notes about the gtable output
-res2 <- fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
-                       return = TRUE, 
-                       return.ggplot = FALSE,
-                       return.gtable = TRUE, # return.gtable must be TRUE to have a non NULL $gtable output into res2
-                       plot = TRUE, # plot must be TRUE to have a non NULL $gtable output into res2
-                       warn.print = FALSE, 
-                       lib.path = NULL
+    return = TRUE, # output returned and assigned into res. If FALSE, res is NULL
+    plot = TRUE, # plot displayed during assignation into res
 )
+res # info of the plot are stored in the res object. The plot is also displayed during the assignation into res (plot = TRUE) 
 
-# display the results (does not plot the graph, contrary to $ggplot)
-fun_open(pdf = FALSE)
-res2
+## The return.ggplot argument (return argument must be TRUE)
+res2 <- fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
+    return = TRUE,
+    return.ggplot = TRUE,
+    plot = FALSE
+)
+# Nothing plotted during the assignation into res2
+res2 # the pain with return.ggplot = TRUE is that each time res2 is called, a plot appears (because res2$ggplot is not NULL, as when return.ggplot = FALSE is used)
+res2$panel # however, calling a element of res2 does not plot the graph
+res2$ggplot #except when using $ggplot
+# The advantage of $ggplot is that it is easy to update the plot
+res$ggplot + ggplot2::annotate(geom = "text", x = -0.5, y = 0, label = "NOT GOOD", size = 20, angle = 45)
 
-# replot
-fun_open(pdf = FALSE)
-gridExtra::grid.arrange(res2$gtable) # Contrary to $ggplot, $gtable cannot be easily updated (see https://stackoverflow.com/questions/26499608/inverse-of-ggplotgrob)
+## The return.gtable argument (return argument must be TRUE)
+res3 <- fun_gg_donut(data1 = obs1, freq = "Km", categ = "Car", 
+    title = "DONUT",
+    return = TRUE,
+    return.gtable = TRUE, # gtable of the full graph
+    plot = FALSE
+)
+# Nothing plotted during the assignation into res3
+res3 # no plotting, contrary to with return.ggplot = TRUE
+# replotting
+gridExtra::grid.arrange(res3$gtable) # full graph
+# But contrary to $ggplot, $gtable cannot be easily updated (see https://stackoverflow.com/questions/26499608/inverse-of-ggplotgrob)
 # plot the first grob
-fun_open(pdf = FALSE)
-gridExtra::grid.arrange(res2$gtable[1,1])
+gridExtra::grid.arrange(res3$gtable[ ,1]) # made of the main plot + title
 # plot the second grob
-fun_open(pdf = FALSE)
-gridExtra::grid.arrange(res2$gtable[1,2])
-
+gridExtra::grid.arrange(res3$gtable[ ,2]) # legend
+# plot the main plot without the title
+gridExtra::grid.arrange(res3$gtable[,1]$grob[[1]][2])
+#plot the title
+gridExtra::grid.arrange(res3$gtable[,1]$grob[[1]][1])
+
+## The warn.print argument
+fun_gg_donut(data1 = obs1, freq = "Km", categ = "Country", 
+    warn.print = TRUE
+)
+# Warning messages shown
+res4 <- fun_gg_donut(data1 = obs1, freq = "Km", categ = "Country", 
+    return = TRUE,
+    warn.print = FALSE
+)
+# Warning messages not shown
+cat(res4$warn) # but can be recover this way using return = TRUE
 
 ## All the arguments
 fun_gg_donut(
diff --git a/fun_gg_boxplot.docx b/fun_gg_boxplot.docx
index 6a4958fc34f14cfb47ac786159bdf4c53431b98e..19fac582a77139b3dd87d5049befd14aa37c515b 100755
GIT binary patch
delta 98367
zcmV(|K+(UbiwByE2e3g23P&q`jB<7X0NGlTO9>lB+eWtM_pRFhp_iFEL+(%zpQ5C>
zR*HIMT$S<U+R4p_skx~p&?FIoXc}mWV)mckbGjQKzz0anvW<p1Rk0)x0MR(V^X!wX
z2`_)u|NdY9xxDnayO^<15V%DcUoPkFo7z-}`Q_<p>`vK?#?H)lBOddya9ln+<>RsM
zvePAxywgsr-IBk}Bkr;|hVQ$f;UkS}`gZQqad-0~x`d1IhP_jFN+ZFZ*7s<C@=f}u
z7pLc&H=1oPjn7#KAC7r6qXPbnCZ{tRJuH9bCve|675>N%e6fOCw$75<@He$Z6kh6E
zoTQI}mt3kx(La(aqGP^SU2?0*A;0l7VgWn@596snPv7b6xp#vPr^!toix%-X3zGAe
z^LFp^0CB6RvcBOlkFQBN3)GVr-?7~~f+Bc}^onC1A^)Aplgy|euD|$MuO{<y+x>q}
zyFq8`24^w*v|IG`El%UrY|R?W`Q-Dc{6oYS^Yz{Q&%4{(@FBey%BxSfmqtq#!SUxk
z%!etR11rzm%eRw|M|2dx69MfBkdH_T5ag2D6-d(&_g46GPL`J-jlK78YOU7I`QZ8j
zB(Og8Q-lX^FFNhpyY%2K8`DJ~wm*OQ2@mzod!0dBe#88y2!HsY2PXt<443rJ26*<A
zh7&39@$XYS>30_nKhoe$4Np|ZB6WRq&qEQz$24|*pw<mvL_UkiJGPYf8iw)qQ8&&X
zk~bWwe{W*>)8zq=l5f4gZ^3`+wD^zYX!|@la)S?$I~Kqil4C49`QaK5pXz_7K22AT
z-?05I4PV~!Qe1sU?xKiCm&6Td923AHJWI$0nQ#sSpR$lp{h1$=h|TyT^N6=#L~!zM
zi1!lej_P%7VQ~X?>izZZ2mSsUpcadtfkpi;tAxeC4;g89&xjuq5TNb@>9jyjwmWBJ
z>5D0$o=0djk(S0MBQOKDyIz0)tlKvXY@bDi*MlZh#+UJ&x}ZDf5sO*$$Z7zw4?K7T
z;WhQ+OA_-L6aI|Fgdy+<{1(#*{OQt=j2M~m2v3DS#1#IbPsH=%c|cdBe%>Cm8V>n+
zx`>+^qYV6^70Kc88@%2Zfd?*OPGiAZ_1fM0_y2rP-o7KXCK)e67u|mYC!{mhB;el>
z5y7zs^@7yvBmNkb!^odF<ZXz|>f<#{#kNA8I%2>;z(RzN<=;yh$v=}@z)Kgz7pQXt
z=LDR{iTt*h;b)Be2`?agA?6D~z=$U3Jq1jp53!F7NQCoVzte5+R!JsM#vwy|lR?ZS
zjzLPo>ju*N7Q0jk79D>Ui5K10*>&$|rJO(AD&<{b)Bxb@6N$)|9v4oGeh2xUd;lg>
zdiIg}0lFW1r5ZwEaMrnP8NoTbUjSt>h!o+wTdOo+NmrnaSQJ0g4j;BVotw@smd}4;
zS^lu)q~Dm~%TsoEgt|ZSV|Ga}kP%;=p1KhYo#<p9@!y#%RKS0VjZT4?9?)buolNke
z==9N_gbcJ$eEMGA<hKdqv)Gx6S#TJn8+cSN_WbVbt}ns03DW#07Nmzks^3^8K#FQ>
z$|L^|SO5)Dhfvyy9>XKy3hcsg9QdwK3JolDkn@?79r8iYNPn8S*&Fiimmhy5G=@7#
zxPd7<CjSTQQ73;~1VOz)PX0U*f5Y43J=Zq|+c#n#7k<RN!*Jh^?7{Htu6=KjeLS)|
zo&NBmB4H5pNu^iAo?PZG(0d}6M8=RuEOe*1V??y%b)>l_odG{2?>~rz=W}(3Ijlj3
zQozY_xmV~Dbz9ww>)Q&vzTGxn<}DrZ7V^ld%&WOdba{U)06QFH);Qoxxg+s$0Y)(H
z`<)K@XYKA;=X41k_=!tH_<s?z6N;K2RQ&8j@Dp(PPi6~udNP(O{sheDlbC@l^iB|X
zhXvox_pCWMIhc`#`O~d5+>(YnUvgfDR7Sge>DSge@3eM@i}Rnn-q)59nu>G#H@)lr
z_1BP)FC>2mF<3U4{I;nI%vr#QKg|Q4-~2G1EW1@V`Ewxtc9Mpk>xZ?=3vzI~-IZ}q
z%fd!r#iN3@n|Tw@Lc~HO1Cf=5hlK~i<I1IiO^h`GcBA_l;8xVT>$isI-PeiuibC5d
zwhlGLOGbme$d);1j>=C{(OHZ2FM)N_U?GvNVl02h!=f|joeze)5Rm`mb?bLC9$34>
zPQWHC^kimIvEZwSTddG-?H4Q4t0vQto5y~DyRi4eA3xk#P*_k%P%M2<OaT=wr*AP~
zF(EM_4@yitC-7%6QAJE((t{KGe;_XQ*@C#Vi&p<)P#Q}xv$U(0GwAPoawExNdDHTy
z<jsGO3m0{`wd^b^)~KMtd`bZoR)<?eC`8N{4FM28zyHNb35yBs|4se)fkn;(Qsxb*
z#lovua9D8Uz>x+CEHW%IuCT5#*&WKkA+xY@yMmL5t?DhO({Jp1e(Mlg?lhP?aR=9K
z>Axr_O7dh!?A#&(ishP3zWMfi`c;x|+O2=iuzhVg^;;Vp$dX4SQ|zU1rSmIxtlsd2
zTAgbRG0F47_f|GAZ$YsI3TNbpPB;Q=^q(J88T8KXuGdYsEHHMw9Fu_YyN|8Lj!(w9
zzZl&0E;d0i>~wo;q+~#mKV5=CA9_8YP%p<MP{fR3V7~KgDbX98-QVs^%(hY@c{zVZ
z0YW9=bS)q(AS57?jsDL`;k1~j7A8hAu$0J(=OM#_z;H)HgWyvuxn462s#wYO<OFn-
zBl{M(ZT(hxS9J`_-rjWw=YtKMe$ndQWja!4J;AfVS^q4@$NCQP&eas>#=rm=7S0F?
zcL|CJUNRCCvFmeUUsfL(4)2xKM^%5<M|K~h;_e0Tus^dIMivkmKyXYstA*U=k6S<>
zAcXHes69?AC0-5`<)c54T6L<H6fZXH3<I%(@4h8Tz+H5E-7@YdUbQPl2`fIVP60vV
zhi<U&nDYmVxJ|6G(8!t_eD1I(5m9GGBOrp@_iEX$HWnR-ju97v&%P2sZq9$&y-%9-
zW)0IXVgkj23TMvqqx>>hK15nm;C;YG?RR=-=dBX1=BfrlyH<7TSOHUQ0fTKdUV((M
zw)P8R*=R6G)a%r;-skP$o(=lLVc#_3mrsB_OK^F9EU*Hlgu?5qDc*c4%t2wbBXh>$
z^;^+bf>@@?*xodsDeJ4En$mxv!Ow1UX48wxY<wS)UL@F)vQGA?H3jU>b$57iRu*7?
zMv76pv$;^i*Ys>bzCYpBa`Ha;Uap+c=R!vKLK*?J=jWbS@Vsj9IH}qI7@b=bSrqL<
zQ9xtyo`oKZnD-N%u<MA?2YKNheCftN@suy0Z<bnkRRdlJrIAL9oy&jnGVggWEDUpO
zDf+e2#<A_YEhHo)CXtVwL5|*+vF&0$6%dBCi=iIk!IpWy0Obu61rZaA2osu~yGd)a
z>dQuGO;cwwnFk!V=@e-XQ}(>mJ8!4CAqVw-{`6PL?D`dy7z2Wiw(G&m_q@-_PP^`R
z2j``%pg>Ez{4f3L2$_FieSwM_09r>_-_YVg;$cZaZOkJ-L{t<>-;4WR`?fUOpjojd
z?VWv({#p`&ZG^YX=K2xY2!TIx=2T3Jfqc<98(!R$1<0R~jIh=3<aqU?-@!{DAAw#?
z;F}GbtH%F(E;y>Q@ulCWfAo7j41+JZ!}aXPMLK^+hHw8-0+N3ZTCz>PCHWU8`EJed
zU7-zn*E;JAzmyI7S@`{_@Ui=y!R?m{T)aFD&_g_6g_Kr~s32&EjQA7&VkA|r_LiX<
z$<oCfU0NdiwOT~*xG1b{HcUp5zY?sb%rEIBOa`rD^e3;xWGEli!4SAFf@={~OSl>y
zkwi1abLRSE-=%-T=V9C=F=LlxD#Sd#JU!JX$;nTTEQ*=KqsggG>4}q%Pw*bGGZnMo
z#qn>wq*8lHzYgEOd;9K(4<(?xb(Ov%-QV+NOk8*x>WA>Q$-5!(L*h|E+y813x4wdZ
zaP)2C-(=|r0U0q8u^E449{f7y5hL@6yDX03;wGFTri6bIjQ!)y>L7#Qw16-3W6qDq
zRp+jCbKbYO+KDUua!QG-zr!nLF`)pE5ZpeO&*lpOpb7j3h6oc=M&tluSu3f%s(OQr
z7om#^jPNmeU!<sSJN@gy`EIzB?~Uwf?pWx;Y+%3aH2(m+UTkheY|J7Sy6kkq`0O`L
z^s<;6R_%X$>j>S?oJ{E>0|kQGBw#2OHg+sGSi~^ravm@mGgD(=yVvAK4D17-*_gTl
z+!2=ti&>ah0-BIg(h~~oro5Dul>a!09Ob;2`e=j5>k`#~HV0^r$zsMr;gEZ{b9%R!
z3A}6)@x>feN)bR>!}i&kIqn<)o>ePI2j=5*0g`_MMII1@#$@bAu^=p9@<82pnA61L
z^2~OVJcN9SMyQxlp$>M)Yyp?hAm(VtYKIvRBtZvX7yDu%-RFiwP@SnSt&jjp-=Cov
zyOPd?H5Oi%kNPtxg!P!w6vHkJftPV!HEImckLLkhDSg6Fa)7Ln@{}y6EF=q*G-U3L
z315E*@I?-TXs3s<A4K`nEr`l54ZkBFu-ZNWR7~oC7boyT=3p6qQ-W^&TV|r^eydcy
zi2Hu`w*RH_`ExHq;aso#{vKpmD|sJ>-w=GSKbZ#bzeqk<u>d6OCJ(0l_Dt0qO|tH;
zfL6vv$AyLrRDc;Un<>rzcj^Ufg<%URYT$p9#I=Q2;XZJ1YRB-N95~_h<ATL9=6>29
zbldHV_8^U?gCsgxguXc8;}iTIb#rNe;QjD+bj}<<96KPN9Cr#g^WBG&jHj&bbSj_H
zA-}-Q2p|2<T#?3BsOz%1U|y4q78q#>(c{Jt3JOTDa~3g}ICbVsz&^r?&C5%5j=+DL
z)zuMv$a>4^G)c`5-6@M}4fu67t2GI{(FOZw7_4-5cjJgy-t~F6cfB)*H2;a+1}m9B
zEV6YkuG@F#Uqz_B$YO4_K@c-Gi;0jXzsEyH#L{=c?+nSWfHMq#d+ZU8fcTfeNlHeb
z0v>?b{?3;S_~ekV*N`sXd$|fZtayJh0{`kz&Pr)%dnjk6v@d^8dI7)=Y|sj^S;9RI
znCXkSDUEN*K)6pt{V*o8RSZVD&lg7|Ra~F7hj(l9I!EK=U{;&tPOqXi`PLN)UntI7
z!~oarUhjHn3<Id`Q0;1>ZCSLo;YIs;SV{|7M&3D^d6?1FM62<D<W;<93YmY9Ksti(
zhO`b#BAecF*sk6Aa!QEp@5u1|55N55?%mHezr}`qzEkNfmsQ$0!WEHmL}#W(X!Z#0
zOlA_gZhAk6j|*wnJ0INKD*iKJkslW{6uzLsf2`Qv@FRwfVwDC~A%dZcQz<&F)|)CS
zTaTa}iwwsjB9>h9U5tz;_e6g=Jd&kri57IVu|KZJ=#bmRJiwH2<|VQdlP=bIc@(4l
z%(GUJwTj5<lDJbEQ5UyVV-ZQEt9DbR!IVW18G~e4ozN#A#DG4Y(bXZKF!EiscMt;(
z-{lUZsl+j$Q*L1J$dkDme4QA|q`1+0xohMCXf&XArV8OLCM;wT=A3^NF|>h3UiGqr
zq@IyrNRo2oW);v-wiA-j+pI6M;;Hi9x@74~^2lVu7#bZLaw!!Jy9HC$Mp?TI=}lrZ
zYg9Ixk=9Ga>Ucpt`~&myfcu#bM2)sBPbK>u@^+j_8O#v9K3+uf$_hz)nFA1Q*k-2F
z8by1WquG$VMWG}&=r(^chc8!&poyQ+mL%WAfhD(*<d(`_CE^~Ye5Xm4Q{SDUJf8v6
z%Vh6Efgers5>mH<rW=^wi~@nSIJSsF-Mdz|cUwjZ=>phF(dZI<e8OgPu}WPk?eIH$
zifz6|H3zAK=w?lPHl3EgmEYg^?1~R71HJ;jzFcuf5rx|qz5ai7DVsGFm!yEwgSAr6
z^JRgnj&;xgUZJ;Aj<0rHR-~R>wKZDNXu#~Kw8^dS)cW?X!nb#%NN{9A{Ur%-3b>Bz
zsfsOh|KOuE0Jx3^ZkduKViI2=h6ItVF#k+d7Lz*up_md2&}k$jW2PYjbsw1zK1C(t
za`-j<w9}B@$JT!XlQ+qTlT@ij*cD;@7+V2NnRhZ`YA~=WBOo9644v5Yv5G|A#YOkJ
zJ18STD`wPU%DkB?Di{iQHDVI566qWrXN$EGU@a_8$u4iF2UW$g!?52FtXe>OEOsM*
zj@sTT`&X&#u|_Qy&X+}NTbqptAB+YS<}n#6qDsxtdjx-!%<p&(o0q_-?{v;&Ew1JB
zufXR;pug*4U9>r%H`K<sT6-!|xTZder<h2|T<JpTl2Qz?!v&9Q2Qwe$-?L7)(^?nD
zTUIsL68$dY=1TDR#Q`|OcGf~3o`j1a-($>lnn`Ar<vN;q^I_B?@@dvBA*H~$(CsEQ
z5OoE+N(+Cu{2Zy2|Ku2{bl#P{_iWwy*APHOc<uCio%2#!`}KAsmyf~|SH>a3LIG-2
zlj+?w3xZ?QD@#bY!mYTfP)H?K%1z#6zl}#3t8f2EuyS)9!}Q`2Q;~$todK&OUZsvO
ztZje2gNkDv5YquL<)h`N>6*fpd;49el-%8^LVJIwYZ^#Xxj@y8OIJ6*V!7+njU7QV
zg|5!k$?bz$p=*V%=|)n$o0Y^#S-`eT){X1O#1~{qW0j&_oQ-OjsLoVc!NOEw#k#D#
zqG_E5>Y2+;mvcwjJdt@V`TV=&!_8Mw!X>uVn&j$a%Vs%sB%Yh8IzhG^Q(=4GIlFI5
z&9;B3BJ-bE75S|Q`5n2DkW6NjBwAq-u~og%B-8j2e=y-<+GnI2eJ1TW4<q0c_e8Db
zWUS?M-D%(UODU{=;;}Cqk@yu-nIWh>Ec@KzN#cjGVAR`LV=7a(0}eJI{G=mn@)j)u
zO!pk|g&+Y7Cz$0fb3ZY$U0_+o+vK|vCs2Qn$HIA8E0i3+O(wt1X~2YF^@i?Bj;7a)
zJ{7(3@}|7_GrD(cttwsu;6##D^)DcFCQ+e7;QBLcO_1HZGLo7_nLwN1wOKwdqS1_I
z44XP&pfF4Gd~*4F6_Qt|lPg8CbW~9@)jG-)QAw90j4(?BMfFmd&w~|~o@mNujU0bP
zcI=9Q*<gv0;xVv$W`4+2`O6U;Tta58WQ{dEJC;}4^7n7|@JHA=ok8z>Fx*gU`)AkY
zPq(vc%g>45$@b9!*!iL7V5R&1UTNwiJz)mTqm3(z_Y6u~^y44kU(<>kRCcuNLb6Gq
zKL_G(Cuv^gIG<)26J6)ol*UuRwH<%FcH7QE<3!)02npSbZm-?iEmDkm$vgW$7>c7S
zhx{7(f{|Jc2^;c+(<AnA;TLr7Df_ByF<*JqVwsEiP}GgVVYnm#pI{}sL+;Uk&=vRJ
zqprgNVQL0UHYku{D)BdDvx{t}PP{LMNLAr}5hvZ;_FHEcWqd;w^F3tqgtUJgGX}Du
zkuwUY`_RCM_>!TkiF0@*>G(nf_)OTZQ(X(c5j8VWUt2_0!vaIt>o(%J@M;l#q<T4J
z#$Dz)IPVJ?!<t}+8%n3EaT@WFoF%PC5RtffRZrzA(n}Faws(NjX(%mR)o5>(3SJys
znFws%Q1gEH<A+a?+3MpB;gEmyP{~LE&HIIE_7*W;6)_uN7=iYsV!Fn|VT-Qa?hXgi
z=Ca0{IgludX)JwDOp9mXls3C>Pq18Ny;Vvv=Njsh`Z(e<(n4w6ZWQ;*Q~X-KWnS?y
zk<i1inQr^6M(BZZNu%9IN~OVF|9-GDyF4$iUdfKKDWERSk<vspWifxhVkGg1^(2Rs
z7%DA)?q1|Zh{#CtT}^4BO<DyyRf)0IY3wQ(nY34}4h^sBW-^lMZyU2Yl`%Z+Qocwy
z*=%|iMoG#`PrV(st5g&By|NunN2^@p@Xhs>aB-NLi*XSFD~efj?fa}D>anpO9?DZN
zO%q6JVx&ecw)}+Jy^McmwcI)FznMynddSz1+V6mtJy3@3v#BH?^%;HI&)24qV6Ubu
zyNU+|vcU0~v1;%w=|e_LESX9u!6`+mqSlk4^O&^BNk-|QIh4fE<;6H9@t>GhAV4`i
z&2Ulxm`P7aWt~{0^Y8T@X}y8L_BlKR=w%+MVR?AB5nqHJjpTpuGaBI^<Vg)`T!e*1
zJ>@k_1Zg)(r%Ad=ab4O`0v7!8We9m|(*})7CHTH0g^`vjhE>Y9jG`DyH3BB4JUCU;
z@wOam9VOXcjbmI)q=e6?0wBp$Dyj7gUH^^UQzJR0$a6N9i_mt`O9}#xEOfXK#w=o9
zNpopqj@rRCh-ZJjR_C^i6(ye<CCDr$Pq1nP4h*Il#uC(i?}2E7c_iE!U@(|eD&~+O
zR)t7asJAO61`X3&1!0z)8apoB@)biZ`-2wSTG&oOe32N2A;P989Zw#%>3HRlKSh~$
z8#60qELZJ@<A7H`ETUq0qpk3Sd#7)iS=Df~zhhWx3^sprw^?UqaFxwED=TFVg-}h^
zTn^}|8KYtGpo_Bh4A}#*kZUIZ3Ds8X(+)s9jTYfUwKNyl8Q7gVQj?<tKg5<2laNQ)
zyRf($jFtRW@>|J&K=SWQEzT|G)H>s@))|-bHunFp4td$vl=ss4SgOiGqTS+H+D)-r
zUSrPWEWdv}IwPs?BWTJkr+)l!pjzB4lI>ZwACg&qJg3z#mDVr;jm3KwdJJs#pXh{L
zM~ptmtCFTVAs6m6(^cmk{=M#o^F4VhbYmUQkLLkh;nqd=jHW(-GyWog+MI`S))bx3
zBmU&iz`9WlK`}~s(f<!PEgbtE3tiQ{I)VpJwmyGm%=2lec2neC*Jt=KVNcW*aId!0
zKPOMJ+j&I(YeB(50hRI|z{E)X?SH**kW&(Wj6^+bG|AEzQ=&S7tta<MNgb<-j2ZP*
z%@A&d$fgweZ^q^`YRd;bbtN=Bjwi{676F|%aW0RZ7Avm;j+1k@z%BGAp+AN<3l4-{
zoacX!$3PBP{t?B+mJ?rvtw)3Tlv+ElG;1o{cu}SU5fKex*~rN&IAeirPtrpI&y~mc
zoPJ!eCPB%_T%2wZtQt-crH2>o>*1Qc)vMA6O(8$|nZt@_{D^gykou2O8K82Do4BFB
z=27y7w5-&AMN(S}XbkmcjHIFG$>^_=4@G|<@7(t9?@Aee!j_4KisyYYN1Ca$P>q(i
zO*t5cGccV`;whb1t+StpZbG3h=(JlMb7nk9rviP3DUbX=;6cGb0s2jN2)|T{ba|gn
zdvR;g*E}?H#W2EZ9~WDSVzWX+NvZPfwFzU*mDgvk=zym40A#o%k&KM!5K9v4bjW|5
zYJ64;d5vHqeDX=7iEYqovGCxy9yEu2(hyVn9Kt>>K+>T<<n#+eG5TgR`jop__DKk~
zTf?rM%P<u%JQf&l5*RlD<zZ7)ZKNcR>?1lyme%xstvh2|VU&y%P&FMoT&q%t(BR03
zC9+Psk>O=sGnYytyqS@3zyU>-qWXW1`W!2=D{ptCZNM+dBXb3|10U;t-R_iDNKoeo
zFGY(WNx{dy<9fDT(vlnxX`f=<oQ^%w*&IZDjPYQO+eQIj9xVC2>*5Y{8EMaGjCWh$
zHY1vB(pA*reREPESe*HI**B34c6964lnigjdf*jkbJ-PMcZ9b%cy&0a#dLqhYHHwv
zgn}FgFr|-5GUBJH2*v&Rbi6rEg-`Zmo{5|GO|R7*SlpO}n|;pUKQVZ*^s$?!ky>;2
zx~LrFo+!>^R%SFA>+MF9)H@A~|J1w1?H3HA<l5xX1g#D&Rb|A+sGcU6wJw`ueL5A%
zn&&)@{gEH|Vukcoa$h35woZS4m$#Y2)8*U#611V$XXQVWr{jg7OFRx1CZ=O4HNr6s
zOKT0BQ-pRk4xy%@3$j8+F{PnSLv;a7GqSYxUf7MHyjHkFZuUL6k+LB(B)m!j9=l0Y
zicTJz;~-M5I9^5pa)nberE-1PkF(QnH%7tZ@OWQM`YL`$+Lklr9p!(!n&W%I#9Unc
zVipoPyBFn^KgPv_>{J4F>%|rE!Oj_mrF~6TArDUw2ZoaQs#yTtZEEi&bV^<o9nz7Z
z=XyBj{qEqtlOD9O(;Y9Td|2A&;iTBOBwP-uph0R4?HK=FBpinAcJJ<bH(E@2A^Y>X
zj9gAuB-F=?NbbddvVeat?V_(D1~zrUU~(ggCQIUDrJlQmenJ&EF40~d6||k&b49B@
z>vTJ<%_6z-V$>l&PkpRglB@`;RV&>XI$JFSr&G@<3D^WJcXW_37F!|0Hqo}Cp6AQ`
zHJu!gS+1<&a;inx>X2{u0+Zd-^|<NWcrT9$>IX5QBfd~g5{rLc$cCj{c04bs8jY?Y
z3^Hr9@B;x7D%pD$%feclDgYoGTY#6aje`TtlFF=yvRkB@$Zo`BEAS+hK>M4hr8bg!
zH?iGq58GX1ux%>U_U1l;@vx4GX_OYxOQ*k>LN8>vq)*wJ^+@3u$<|G$tKtbpERkZZ
z`8=S0D5EshA>MyJuckm-mYr6q8mK&#4mn_Dr_^(k_|NaV!`^Lr&_*?Pyqpq}I}^Zk
zWH8*}k;kG;Ff)@Tx)IOBEQnlpV|i&BO~<>j^=@#t&>G64r{r&K59`<Ctn-u8ya$FD
z8n)6G*z+ncM;y23Wg0e9Uu=`p*IT^_!_tT)+0g5}80~-VP}ha4GDW7c-@=Hg=A-h4
zx$dJ|JIc@0F1FsFK)V>16<8!K+O5v8y%~}(Z;}-PT=9m3n?%JzqrA(===)u}mKg~k
zd=W4i%R8*q4a@o|jzt<}p<F%R7BNhkNL$1T6f+p?%^PiNd^)F<JG6leRxMIJ%3@%g
zQ-%Ahb(wzwk<XLD#3<Z}Z8+rD$QO*%YH0GUW8)F~xbVRc{2~@%2PdxCEUMiWp?*20
zfK3G<gzv4gElzz`Rc6L;B1NAAdtvGJQ2<H_#R=oH-{2%YgKfMBRk0@8CzOPEi-^fD
z3WXfG<Q&t!(U6yU_3QBcySMLtC>A`1Vc`-DP;7rpBKJ$9#esGKc`0Dzia#!B2v#+f
z)pM1irORco0Hl3!gs2KsR6D*gsjG?KD>dY?Xw?_pPPf%JW`WWms1hBoP)VeNb9BV3
zK}O7_s9vyvtRG?*6GM4YCeAGi6UIsl!K-Ga1fjB3s4tCX%3}BvQ0K=pG=gaybJv$y
zB(i_64h0)1mKCOO12{=0$6>jiZe9+MT@inVV`ix?+_@kA_yK#rqX5G2ZA3$l&w`b<
zovJQ-ld>jkGL$G%6u*_i5E{Li9wyH3zQ7<|nqDU(KC($kgFG-cPjk+yWkco7rHqA<
zTjJ^QI}1t#CI>6yN2;Vd{jDweA6^NFn6ZBw{OnCluqV+@UitAvz<0aT>->7!<jXDY
z2p`n}v%m>QmsmILOBCWbi3!63=G=DpdXfcPky?h~2#>6X0LN*QwUrb2Gt~#uA$Lg$
zv`kf#WtbrQal$w#1|Fut7`V0W$99CGe?|1df^j-yC?X5Wu3o{5K#-^=d~3vj(ExuS
z5?IW%1OXajCO&4e>Zpnat_9SFgD2VeRY2)VaMgDqp3xv+s`NyT9m2stuv{Efl!S9p
zD54=Nt<dMHVY|U!pjb<~cThaHYHg%mqC{Z@Ek>2UBGp~4S9?5X5&AzzNTSlcoV{6M
zpDb1li=Z43kY??J?do;A#wE#jyDfjB?9ME>5^qBv<UKRDHFY{$eqaI)ic%(0GEVN0
zs>5BV!<w#lJ?LGp3o0^7E&qv9YU+@@iB)Rf68lPZZQ{O!4;r(IjiA|X6<@-)uD$(g
z$wg(D8SwUHBs^xScv#zw$OjJjB@K_t2Bq4GmEn`LoId>&O-*H-!bM2}3#NagpiwG4
zpLM^v^gPuwNcgkNjj(B5v`|?GpctYBI@kGhmrqj~);+3>CMk*}s+sXyL&*`B`y>ml
z)v%f5X$)?ArnNa_ym}dH&@y(_Ohi2p_&{(2S^9Iv&{||!l_B9aQuS4PG>7`MY6>KA
z%gxmu`^pxS$@vG1xXe~nQx|_U71ILCtcEGhns49s2G^Ur_~oUmf^Cc7_-Q>>Km!;<
zYI19;QuMAI?G7Z@m`0v}CDSc53^DABJ*rUJqUk3h9+0}uH&4>d85N6&Sq*J9bWTI-
z0b5&bVAYnbF01s|*q6Hox}-++SkT#B2@31$Su3ihLFIzT?bs$+PN{!jH~|+9`MMyJ
z!&2i$sz9<&v@XBO;9PGb>t>2+SVPZs!AFs2IAoZtC(U+=iR_b3U7RQ&OT}ZssE5m-
znN=4_KErlJ6zPf<H0bxb*QKlo-J4Ns9v${3+0p}TcT8JR!j|)hyDY{+ywM7aoPL{3
zew)*P3Bl@C`@FKq!;^m#(9va1#3q&c5q3la_{hC!)E7^x3`G|duNxTdD)N`=qg^B=
zI(My`bCXgnk|Owg0sOjqL7Sva@;Z9_p=^V21W(=GbqD8zqj@TSy5uQ+$nsQKExICB
zxIY`5_0P&EAf6nGW?H2b;bm%cO5Zavlrg`N$kj@g#zR`*ZLWXbl!dBI%Q#!>A<c0$
zpjFMCn@rIC?Ch^(F1rf*Dao6c#Eg|V<_k={aRcr?;JiQ*aaFK=Nm}xRbzWeTT&P1y
z=2?@p&()E|9M+Fs%Q7TPxDGt=L=}o;zv`&(INAD=lYQS}4Y1c`fUQ2k0Mm8AM;TzU
z24r^wQH!eAfGU4~c1b$<ot>>7o3cD8MT<mNk)6^T^xEh5Wn8gC{wV`cGm6<Yg^TEP
z&aS(|5-#Eyv3|XlJT6<kR(Dv&1zzdFoTvq{%RQj2aG*{XEaTR-F_b#c4Mr`-5K`@4
zVXAN{L^YIrRi|4j<}KZfqT7?11j`M&<KwDRNj0vk5Nm(srlD1xdK+5%)cQuRGCT_o
z6Q5iX=F2+vTF7fnQiDUC8mXtg(}q)>nIb5+x8)>3{FjU)6nzOB;cZlgCfL6-J>N<D
z^0{+jmj=vhpy!X@-EO_im$zF-d^?<^zy7GMhUKH|aSaTOi&ckQU~iDv9|bHeC&|<`
z7tu;@#Fu}NR&p9+r6{#-w>6QTZIgHlNia%Ai%BNbt~L^<Ld@gK)6)rDwHP@rpPgcQ
zCb-5iJJn~N`f&`re|mo2K4K5?y3=jn%B~GoNmW)!3EzE??Sklh9`PrChIMG`)<7PM
zu}5+e?JJTE7Z?4T+nX}VhJcP(;q*L>bW`Y-1Y3U(bnz^EP+51JnqlKXgkfQj)i4rO
zDx#I-shA41aBbrzTl1K8vk>g^64TYBnVRX(T^K_)42b6L!|T{ELGQvJD}M^JdX+0B
zu;m<*ajmNkiuv+tsDiXzGi_IGF95l)7=E-`r-ke#tMZN4o@$21MMSMaZtxnzX2RE8
z1FL_C^QwZbgc;Xeg(~JKgHLQG_>y`V_B7`)4Y7Hu%p**qbJZe~Y9MPSrPPLJojDWm
zSlX@sY0G~*Qj5Va-DcWlt($RZRF0Kg_8a2XFW{n1t7U2nx{cw>yJ=@MM2iE}<wfU#
zKy_8fv)zBo4D!gaV`^-#6c~Qt4t5nNRcL?K7cJh}xL-4fs7H2G9gWy4&36}p>|}!z
zM-oNuI8HGf%PUjbt*TS)e>#>n<)w|&H<vnSt^@1a(&XpANPJuRX$%3f-0h5+%R_Iy
z>AAhpsm`F$N_%i4CYFqa8K*ASKr^*-)VK-(gp>hlQpe+RV}W4cQ^+)!Ut+f2iim%h
zSVSQ>hS)ucpk0XjEPukY|CE2yM-{(Gay2kwpbuq+bHS$@&khfIuhXl;tq(2sUmN!Q
z&<z$IbFimZW{~YI2OMQ)`{1k<Jdd=#-wE_BgWe+P6(Ol0k%Z43_9P<e%s|BXVcKA5
z9&sE7h$V;hM4GmwylXeI<zoOcc|Cto*T-bGhy}VQ)bo^llPFcagVOHDs-0yMPuea(
zJ3QpH-ENh$PJXs1dhUMz?Cz$Nd3ePeu%&(7!k-7eY+$qR`Av<(Z1QE|`_fTdmkjl<
z&cDft;;nzR|J@+}cgIByWIvfW!RN?aJ1RS^<bMS=#0V@|KHHG<zQ<<R{9%6w)<z2%
zb5RZ5@KOsNYc^!eh9YHgbJp(Nlya_AEU0kiyjr{Hm#GOgW1bIgtoDuRDCvN^HI>LA
zcdEKY?rhY-$<)`xQ@#uvO;Q7a0Io1q<1n=DIGE7W>t5enl#%Y}npev_h}Z=49i=xX
z360c}1edc!fW|gR_KM_m!Nh;W3QBOy*r=EYPop}80@kdqwzq0<V<FIPdLTp05xbQ`
zUa=S`D|M27cG=d@IpfDv3-9XtB6{?@owHuw9BgHx&<1Op0)N3oqWH12`g2P<wbtRQ
z^x`GH_sso~JwoC0Le_0}JKdYx-54?DHH_ieOs%!rQ}&0mMLwBq_x*qFZU3u?VLcl{
zt?B+AWJfDm>*3YI_xh7*0RM~RgB1$`F25-&ybFgr87R4P=?hk$T78NOvHJ9z09mU}
z&yPis>cr1!=<Y2C!bNtkTZ4N=R5S1%k~xc977DuAFjnv3*N<4PDTGeRTqP2|A$=Li
zh)FsmPsjf~6|%wBP=bH-|6><<3u}q2O0_EW#Z;=?+`x^f!@5<E&Dbebl7kuDnm=9Y
z*394yt6R(Ht8N0kd{3@;@zqR8Q3F!SPnO+_f<+*?>_ikh5yd)LMk#^>Cz(yt|JH|&
z**z^Gn^vy{=J&;2)NJF}uSko_w6`x^2tK2dQ-YQ3?<BY?VoHDOMA9Os?aC-{mO_s)
z4~vp)BU%$E|IHiGT5Vi;ZH%)Li?$GpWI<(i4-am*T@TLA?z_e;O4F|1Zm3GEbbMsJ
z)2P-~>A^y?(o#<KH33%@<YB#QC{eB&Qh=mG4JHwtPpcy`u0R^95sjoSx1+7jd}Z5&
z!WRLk#(iTsCEkD4NfsH_no>w`4%t-ltO{9H86#>tX~(j{>tqEz+88U+GLgOy@TdOh
zd-9Is>?3$gS@(iL43`R)4`Qe`DkJ97MT}eBaEJ!jN^C{oCM=RuGq}EGldvb3&9N&B
z+)O{g()ZAd&fK$z^9-xeNZK13YCrDX)<<)af=rn+*B^g_{B$W$zpmhsq9Ow^Jeaog
z$r0YtyG+mDXaa=@o6W^4v8*NekxVYIHL$Kjenu|WEfRt2$BTe0u_L`~6@}K9G`nI{
zr|b#LvS78||C2;-!ufEkM~2}~wo(C3^>s03C4?L0q*g~&-jL7Agir2}Bm3L1Ky8pJ
zv7$}61jB#K6@aTcS8i}5vzzrP7<X-~FV|fea(BT~9P;1NMr`ZUE-C-inV|;)&SQ_h
zn_P$E9p-_LealIw3HT1ia>F&bnOsBes9wMRl9v7w(3}zHvyHQg<mF)4z37&*Pi(VN
zIuIn7?@ra$4+lXOP$lFfbyfAaLw=2XFn?sO6~KSW4?!0Ck7yBXK~t*Kcma1rP1;O1
z4&?o3G{js`iCjcO#6B)$Prf`XlI+5D%k>OKH_$BEMm?sK<d4|@E@3exzMBB)Ia56i
zG~b}(l!TIYK+%fn*Xc@7%nraXOzK9w9ZPEuvwx~Iq~@w>)ybN2$k1S$)ssI1|A93m
z74LtCKYq|;q{Izq9D}hhbtQ-&C8X5Fs)wh{Tl2iNH&>XeURqtX`)#TAV5VC3NR~IG
z_E)W0E-xk7e$zCqH)^Rikv4lu>@1P3w0~Vj`6TkaY)90HE<rYg>mk`A>OM>&z6fO-
zO0XX=1TP!O%TM`}PFLI&OG<>zn>R|zR7ihPkUG9O60c&x)(JuK^H3bIN7cI?^se_5
z@$BHD{OO8|)FI17CEy~EygPQNDAv?UBb`f?IpYya&Hgc?VgYa=b&m_M#3j?oxqmbK
zIR?CmrcBvIHNCE}Ij&OjOT(+vIXk!T`lcrNrZ6obW0;)_dUYw&cVt{sF|_Q;91DL?
zYbr0W_Sc$9=-E7H?1z4gp#wY#>@JmI(omh{l2N;9lV_l+%dpS$XhmwN@m6!l-4mV7
z(d(-HHIW;amoDd#_uDw|=k*5399PEPXbE<nl@G5={k&CCpDhIh)|jB!P8>g7BTFKs
zrccU@noZ2L?8IoC5j~mw26i4^42XY|d9JcgVgqBKzSc}SPj*Qo3`-Rmdc*TpcUZ;&
z;8)HOkDUiazty`aWh(Z*c2Xf4B0fr=CW-lMP3v^G2P2)!$Okh&M5E+^ktbD<R~Y2^
zdPM!K&L>%F*WD>|AJ9nC;MXsr1jf6So?laX{vEWl3XtUkTT-OCbRl(h?1z7DGL9ip
z;EHF|o+-*&(8(??39pk;_=vG5KUNbSuwpQz!P@>$Zero~5^yUevb6nsskAdn8qlbG
z^gBbC_%c!=Kl-@fg2~Nv1R#PckO3BO8$-gP2n3$5RD6gV>}r!IX6dac_?1$c>+K({
zv<n6HO{?7=l#wB|sj>O+C47HUu!d5tu`cb>1W+cv+}u-XQ<#~jWxzTUYTbMjPnZOJ
z1Rt~T(T{kjOfX5})D=i=v_A*pZzrTiYe}4L$)g7pG}(*M?NCACzhoSR$wX#9CR+iG
zzhJrt?wH39WXd9$2$Gbfsf3<bXH}A)Ip8di4NtHZO70o#z5hP=Dfxdj?01HPQnosu
zFj3FDQ00W5rk|;=8C++HRX~sPg9QFv__);?r-TpQ_{(9HZ8u4(WFe|?g_&)V_iBEj
zl-aWWU=E)5jDdKM^X-Q%I9_LPRw2ewI&lwh)UK;i@QD`Z3o%~^@VjG?8$qZCW)@j=
z{Z8k$f3q7^ro6b+Fe-mmYKE-}>?yhZSv;Dc3iL}Io}z_bpAEWqhSx09U`bbU2CY9)
zoLiX{fAS!dtq^hQ{Uqb|w70T(Vq-1Yw4}_{h^X{3*+K(5AQjKtt^)UXe1Yvi(&WwR
zFcFHtIg7?Tnz6#%<|>fU+-7GVa0s{NL~W3~zx(-@_wQs1Gjf0Qhy{Ep)5v5pgU1B*
zgCjzi=e@ygr@Nc3$-#`tn?K!}yrn`(YTa(ayoAp>5p-m1FUjmst1ZW&{4amc9;9}7
zXiOAwF!(u%_^4uyqYgN9g}S}p9h{f4!PhR}nN6mw!Fhy}R$9{RUS4)jLItYQoFg<c
z)oA$%cCnKu026=83M1L`l!vkDmW%-k%4GdiEcsr@a<swHDp`&j;n1IS-H8%TzWaSz
zkVJ6rQb#?vg97?=8e{_R@(LQq-1TMYl1@cayW{hSKl+%vfyo)5lT#<#0`PSK5K-aG
zBR|Z+5dY2qMqv``ZS}@Hdt$E41Nxq<A=*r?ATt)nbb^0P&jD(oY<eKS0#)w_z=jze
zvCu7KBj#d`9h!l<R9&JR{zfCwAs;e{99v&d2lU1$Jp*6eW29=5_oo+Ojso^)0gHPM
z%pwz;FyV9Ev^`}n>G$s{)v<h0dA<nzku#^*m|-i6c%usg9mPCY2#~3CU2vY(tAjQ0
zppc4r!cu?fH=Lwyr6@V(Jw8H&HR_%M4cH2Sw>gLQ+aR&sl{d0T{2EA9cH@gv{kGNW
zw(|pPsxN;^N#J3Qwi#?5qH6h8v&RF{J6tICcJ69fa9eO23vRSh^UYWN@c*;-EzXT1
zNuU2pt$KDtj`J{jSd!fn-4S~B_|7f&`7yg+OwWIHU{RLYMxX=aw&uS5-><3w36MY_
zfv7^cu`%67SY289%SUD9<Js^fsf5w~zIY1`1r^7Xfu9uFmYt+`Rab45982gEr!c}j
zx--z2v~u2HHA$gsVL(awQ9_4@IVkZkL7&iinMv#{Mp|~)cjgQVp)jj6!=eom=@E(i
zBGrG4H0sU{>ffFgs-{%Wn8^I`N8`^wIp}W=a&S~RFfpAWt4OBtL(-q#yKU#lPnJ`I
z@kAT8xi5L+XU4I9#^s>h<#R&NFW35W)XK$lhZJnPHyo;tmWT$SogTRpLtD&r3sqTj
zK;j;;PLDr4PJ*1Du$)`(R|3uC954bHVHkgL?#KK-5$&)=m5Rc;YZ<1Z>O{S85EX4)
zHd}y<<DIDtn`jL{!D*vlCabzcRKj~d3TQNDbMzv>;RN7-6;;;XBj-=h3?~c)YcZF|
z$<g|b%?;&X8*_T9q@NLAbIG!>p6ECa>R2o#n&?0u;WwK&o~o_lBu#JtKc&Ktsy}~6
z;^)b*ZDR%y0tm%I$ftrNtW4?9zDPZU=nDi%&f(^b;0N$>!YXGwUV?hZfeaw#L=h9!
zYT+k33MIbcT(A)o#Fw8L(KCR=jFVF?1?0PG=-QITJju~jVxNW+2SC10%_77Jalc)^
z=r?#=IK8%(=0@zLb+cFNj2xJ8w)%e!t4qo8=GB)=2dz|2IdLW?Q<9E_<yV4eafQ$m
zLBY;~AB~ohg(t-Alx%hKovIn926)pvu(xQT27S@z#|6JYe}aL}BB^1g$M90pjNtTd
zMSXt8tZeMrBxmu8Zd5>DfW8Rpi&r)T?7ce-Y>S?doFS>sba+q~pe~N8E+Bu4sqiq}
zT-LGX<Tky^rpj9X(029~EdgB*I%cm0Gvb^_XYrj`n!{T2V!)#U5Pk~Qxa$pvVGN~>
z<tG#76SC2EUdj1lJ~d@J(ZE2jO{po>Qp-JvxEPQi(k#DGYflCC?PvK-b-HgXoxjr3
zp5443^y|a=RbrhVe4PQpeH4EM2=QISC&TzYlg;n<&d9m<szS)7|030!A1dEI89jOO
z-Tn96cZX~XoHv~>1#^3f=9ab&aRfBnM8uinJ&{?fUW2wOG3I1FwDFfFFI_*~vApiD
zg5@p9@^18h@i%^n<H<#*)xP2}h#hg9i0@$|C<7w{-a_`{0i}La2g`pO9T6N8VZX>u
zJ1OFfuZ~S7X5FSxmG;tMe~p~IM9J<AiF)Ajf=iO5Qi;0D#4{Q#S<DqWCXn$6xWf^5
z{4<mZiDzpED0(!3+bQ;zI!QLB6At273IR(FtZt~Uy{|7bWhkAVXsa|BCF{Deq^6io
zy(2HqWL=#vG^bGoYkPk}UT43?G_CW}MqlKu7W%SraanI9w$Q=X!9sH-B-BEO1%$(8
zmao0=W~L{H+b;Cjf%v}tyFJi&vR$plBhyS(8NS)3{Qf=tt0bEyQKZ5SkJvZ^aJWWs
zT}r=iA5TKP)ocv}l5^IjOI8$?{=>pCY%W0Tk<z76Xu@f~+R}f}7&FA}qc`gTDR^Sc
zyhJb0F~-iI=^ZgQZh8ChOjL~$A&v<TqjAW+Y&JVJaY&X>pRA6xg0_uVk#q?Ygu$N4
zC=sjTXEJVO54RM4l#v(t*mAZLf=ThmJ@HtQc(m58ltF$urYF(|V)H9`NKH&0Dh9UL
zjUGD$m!6%;1D=1wTD#F*wIvVQXYZje;2C_K1w7qH?3<lc!C;1BKu!lU{O`x>yWs$R
z>GuCu_tWs60|3B`m?Hwfy(Zg}?e%uO-CvIY2DWe696s6Z#YKVfr^R>;&xnI2LL8Z#
z2qv@{6>%mP){clhw>utx+L0Ay74=C_Q3IB@OqSQ0$l-sGY%yF1hr3v9;Pb*qkK_Ks
z_3ii`jqYwgi*PIc@Y@hg=;T2600CO;w6xgsLjN{-!ISS*Fkq+1dSf>KR#$-eC(HZ>
zF#jZ(Kb?O2_rkJ)`6tT!IXS)9HU|EmBL7)g-#69+1^{r10HDR{#P6rMv;crp1i)N|
zBj0Iq-ZXy}I{5r2$$A#DPQv;OdjLUr`j}iE{;T`>%T0nPZhzDs4f=_NJNP<dyZb1L
z?IjRyE;(#rTe2`#s`WGUD>9y}I{JrZ|N1i<=Po^)#IcJk3vsyYV;o=9ZNFZ5P-hr@
zK$EJe8|JLqsGXyFjs55B9Ic-<=(ASvS$H2rA-R7=X=w*(VKfu^&Z6_XIcM+3KApi_
zC%iQATvXOnC)G;7`%pPYmBH<&3gL!RqW%G0P<{!`nufy~O5(;Sb5Zz18PlWx=`$8l
z24Q*fLMUqp%^yQEOjmR$e<nsK^-CZYX_PaXI(@O9v;{7Y2vHXKMCjess8O$7QexjV
zYi)m`zu8}F!4{kX@vU>wxnd+-68qGzg+<G<P2%WTU27yyY{R7dWCMeUm$LF%Xdw@M
zAS#dD*!h$3Mze0K)a9Z=NxiUhv?PD4P;Z57(uC?$`m<y6lJr~dUv_#|tXGI{D|&Tu
zy4rdW3%>4n<jy1Z&1pRHoHuzw1;g9Z(TIPXAz2s9j80?j4v9jC=fq?5V%tG4Xe%nV
zc93gbHd;;AbQF%~8-Q>(hqL86d>;&ZkDoq;+g~PUeDnc5J>yFkedCt#QE7uoj`E??
zukX-H{)+wwYQ%U<Og&y+47hAM>YfJUJpsX3x_16PQAg=FpNmAe700G#uifWzz}bI3
zHRdicK~{20;l~KaRZwJp-0E;s<?<8NC4bc4*K2>edS=`uANieF^5fczB|ms>EI~9V
z8BS!c5_f=#JV^M?1|ks2fG15f*|H?VRLH;f!Rfl<Vp4LP@zp~*uU5lTtSb^tZ3uqw
ztZJIqv)O(0Lse|5mTOYWeZrQ&o=|@&VBe?eShCc=oTnY{McuHOM)!Z6tj%9!s^#6E
z+2~wHjd=f{J5&2R^}&dQS++t2%>U79O;jiidqv{Z#LH?;YP2Zh*$1vvx+Jb1ZQn2Y
z@z$-OAK0ow1>U2@dn7%nIvxM?0kyo<{9Cnc_O;HI(MMFn(9^&7qq9{__eXzkPuA~0
zv6Zd2H1s^gQo;b4u3+^eaa1+-i&RSmKO{e@v%rrAJALa0-Q?wJoR#m5(4Z@acS}jt
zC)z^6(qGs#d>cc)!k9~#R$NLm*(4q_qo77fAiKq8qDOojE2=`z&n<(Zp(r7&r7#hg
zQB>cY0H-`@WHh^K<ih(|q0N6vh{E!I_N$9mY!X42sTFW_vQ325`-(c%V76+r*DDkX
z><7vJ&Lw&3YEkRO__6*X_hHbW$n>9MU+2C>>$QCZ(8AG`E^@kv`92M{72AByIts17
z+kMOZiH@g`oX(%?-_+D6X)dz`Zgi3U1;S2c4?B57JJ@WuJ(=GabVh%paxg!zr<pL{
z-5k!AOZn`v-tqmMW8Yl1Y+S3#mP8voRcA7BXo&}@X;u6-mx(eXPFbwsr`>`FYb<cS
z2NC8Zia_2u`T;n@DQB3qJv;m3kNTf~o})_jEXlQBdr<4u!ojj=WgC2*DO>ju`{q-y
zwEO~qocx%cwOmEZ{$4qKHq4o<kv|vI$+MQ0<SFGU{lOVHRi&Rb&dC1?M1KxW`#X1@
ztnrM9feFmgO3*u_)@)yP!}BPklY&P$0WXugM;QtrC;NS>iuRp&Uz6TP90FM0ll(^~
z0<4CUB}hyd#rZgL4rL6GXu~K#5Flt5PFRcufC50-vx!I+2Y)k-brC4ocUUJ7$I2IZ
zihCxOzq4Q5rQStVSle4Wiq6Nfa%<N$sc>v^@<S5mMeujXIf}J_b2uO8@U84Ui7-p1
zp?`k8!C>e=N*7TY>a|9<-rLS~P6$h&exHmtNyAg(@o=Gh_hlPg$-r4V;aMAGmlMl+
z-23_5Pwe1><$vdc!9@aFiWoeGi-dzUJ6t4yb5oj&gzP*SN*mlRfGi7)bhtwRZWYZf
zxI^IWfCJ~(^!rwzO@KRu<2g}+n0`OIlieYRY7*DurbVJGpl#u@pxpYF`2+Q+v$GGu
zNkh<Es@0>S(osS|cdCBb=wGde9}Db_fOH5h9gTF-@qaA4(gBhXl7v>L)owQvOG5B<
zkOVmW=LcVi)j+I<J2pAir{uvJaF4`wkI3oWIX`$!?&v%M>gk|=$g}ycPFK1{_`|)A
zlo_^&XjHvpbRbO^t{vM>CbluLZQHhui8{75u_hDSwl%TsWMVsK-sd~(to8lrzuncV
zYS*>zd+#c?wVwe|=S^!06c=%S^Pxs>vc>7XZ!2h&qX(5Imz>w)EAsYqp<tK_lj|fg
zjQti*VxM{j0x_S8RN3@#U(jiIu9VxWbuQMJ`Iv|NMv?1?+`-0{r<_sOBX@J8M{`b8
zb8{n{<(y4H@s;aLlhA<m%O7Muufz_5->(X;1*F0x)1<J)TL~QoYcLnTJHBdGFB2#x
zIMMIzp*XMFVN&-NPIB=;jO=EdIS<#ZUp8jy=SD;T_(fw19KjM@TgTRI6+a$yp*pYd
zXy`2RrpbTa1!Ze+<qi-CCK<4e`0=vm5nT}}IYzV}aLob4CSsr4UsXPm{y19&+u>Uc
zT(QKXiGL?9J{|_HM0b(&miNPVOE6hQ#ttR*fo4{tO%#O8Z)v}iej$4IqyI2AE;7<?
zNPIT~I-!tL7ZCFef5=R$c#qELqYZ1X@oR>?s^H&SK;axf2d@~5ogY!Kf9=MFDr$^0
z^BpoB$VDe5zUrkun4hCM*h1v=`22yegbsXf)5>eywlj$F=Cl6{M=tds!+KwpPvUlE
zN<=$Ub4(4f;>G{B3N5!egwD@O&lPYw_N;~gOqdTCPzIQf6zcl{UVW5f8q@5OgE(>T
zQJ6z5Gpu9SJ`gU0t%T`aG{Y?}WXZ~{mRtUbz8}ogRZ@>(uNp$paHw2kKhe00-b~Z3
z>Hfu2q8xmB)?2Rx<);z8vcvW_(;u}!!r7sj__TI7zQx`wt&Eq0r?V19PRe+dKK=_J
zFEm{pO*VRfHF;cKpvo%nrgQgxI-yeR=?(?r;M`ZGKV;lrM`MmHdAZW)i7m-R3g2fy
zXrhe*%3YnrpAXC1vqgW={8?$EGy7L?1K!Z{?|e)dPa^xR*ud+8Kgpjn?btYO*s|$H
zPP*gYMap3Eko)1~D@?~$4tRI!07L+QA#?^J#Zm#5048M)O(VZpXI2L6a%Gh=S*)m0
zkz}=ArqnyIg26ysJ*9LSZq`yU?R*Al5rq_g$HUUj>KWas2>1;m+ebfyFu{WZmtd1{
z_FeiR9((aMPX3fP@Tb?D{bX5y%1f>qUAwSZo5?ENhbd)``m|7EA2c65#F-2L9U=m%
zbDE!Oahu_U*&Jnjr)=24<oTHAllGfSgj>R)TKIjV0n<M^!+UsE;KnFb2;mkpcofuf
zeUP~1#==BcfNhN4-zwv+^!}u~oISiNnvYxCuD*geF(RaFte1hYwTX^hn~TG~Y8}<`
zg=V<w#&W7pTeinB@hFD#5e*uc>>g+jlCShlENQhgdEtFyz!~(w^IBz1Pn;s=5I(n#
zN=Aq=MjsTF;SQ@ZutluV!e76VgyjjFq6qV(B*7UNPCTaSZ63gB?o?n&M1qSk=FK$^
zW3iLu6A_~OyN~zg1pMVvUA<g@*4(9a_?$$I`8ff!Q7xKgXCXw`ZTJ8<j>EkB`4<l~
zyd<Y2WO3+V`%@rLDK*;n{YXPlew9f6{aP2%U42tS%Gy&t(fW>QxHq?ZQ}jT?M6=KD
zclz2+)^RSKZ9?#|=B;uu-Rb)?j6$xqx(+w+F^@C--91R=xXM<@0j}?;tq{%`--pd*
zE;Wk6W0V}kEu1sx{~Q9nHyd2JqA-*`k>So}+O>GYiK#kC#`xOY-2bL^ptG4B&1xU=
zaH2Vs6{kP={%9|OO#axoV4R)hFxqd_u*95ZF^5eI1)MxTL~aB!a0l~g1{?{ffl&^7
zFa)xR*e>Q6`mU8LGu9pf!`eF1JDbyd{gV^TORg2zCUXPhb}Il3Nifd-syN;6SdV#8
z1O&olwNnzAI1RDRE+YFP&`&|5k*5~^h_@)(99v$`A(ud3U~orKH`(rgFGXf~E-V9e
zpxD70+#!MLu|_9`%KPR+0+nZQ?pd$hiqkpBCp^ZCX(z(heNflAGQ%4x))~2nz41+n
zF*BjF{~E<x5Fq)N=JQ?&gTr)*o)Cap?9imns1}+DX?-iYm*U}N-zTZmXJhZJg4hVE
z<p^owv<C}may4ZNX_}AzP~eD0TZ)?#PYLs`nyorsOS(<KKu1)pt9D}9b20qB^P0aA
zzK1aX5>FN%G+}Ju36n4mWZuF*!NTr9Kf$)qv(EYU0)Ai{R4IlJ7CSsQqH-#NkuG2k
zC_;?!MZZ;92^~<PilJIl19Y8&K{#QuI6xgxTuhlZh|NX~tYlpDKK?>~f0(yd*T(Al
zqzBK)WKpN8Pm{%AC0}%cq=uUGJUUaxO~_RPi!8C9ssHI}$2Tu|`JRH(ieY+FWGvlG
z{^TmX1hDNxYiwxSA4`p>M$$V3o?^$hb3P5=NJ&VOZ5~WWe3*<P`H-Ib4xW<6j=J+;
zKj;8iqmY6$ZBqwk;widku1$PX)c%=SP>Gwkuf6U<v1JIem2OGvpK5y-0w#v_;+;Pt
zf(WdXx%UWB?+QUZOLhNd-jj{HH+v*06v^)(08XQUrqi>Vyx(X_bH!HIM_2u;C27%9
zxh_dL7c$(6wvA@a9#>(i#crZ@{=%lX_JMaBsW59cn~xOjaJ3<DBLroUP8UEn1aTKx
z@Y8VxW?w5zK6||1J>MKbxsQOSJLr+KY>k>02tC|9T~7USf;LU3@>7|%^iKHSR`W6m
z*yXyxK=nEcaOV#3oeofJ`C;?{qiR#}U$FH=D)pL^S@`~<U2lXMBsCQ1(+->XNUL3U
zI`7-%_NI6EQ}U+W?c>t|6?=utZ6~2ujc-*tZ-p(0s(s4@a@_GCs*t8K$qa8uf8jXl
zZpCx=aw89~&Rdd}jCt=LB(Q%DkTBf?6r)-cb7;j<*+a5e!*C@NPc^K{FV7n0X!iMY
zbjfHN-=R561S>sl`+tW!u2gx&_G2ds*ffW0#2N0nk3xn^>E?kLt}hhKSn-G``~s2s
zU4fNSMO1)1@c6+Oi6Yy*m*QbR6CTJw!TT&s6gCNRVWW96Kpb@z+5nx+zjM9|kkMS8
z3&RF^;N}a&o5|e;a5hf-M_X1uK`*}lTg8nNqp@8H8VUa`shvhJ*$?;;cc&R_R=e_K
zB_SSl*bfqY0uWzwsJwqU%fW`K4u}dBL>UzaCHw-O5i=)($Sp=qwCC?0Gx|izFbfO;
zGVWzDmUPzsc%4b|>Vb2>&94W`FP~M|GELNy&|kFSOD>&yjpp!SLgLG25dDy^b;gJc
zt`-%s7WJKp&`!)>%-oo)`4IVP8dpG?Ztkpc?c#_d81+2_7vUdWCw>1GQ=#L!(t#r7
zrfLutJvTw4eFo+f4exLvqmL3Nulo%)y5U{owi5oc(i?1oy|M+U4$HAmvz@tISg#5=
z<VpRd(6xMm+$K{SVZAufy3%3QG|<NSuDm;VwS?G)o-jCjMu3d{A(}e{xVICszjqCB
zzU_t3xQNA)MWGGOh`y2p&|eiOT0<CG$_z+8V*OUm<xt3uTDMqU8Aq*hUZc2LX2&Yz
z95H-9m2hlDzmEW1x67%jmlc|I1V#1l*RKsX`)YxYHlN_v7EZB^OQOr_k-R8Mo_V+u
zf)eUaw}%hWr^`Sm(1%a>E?&tHx<#nNw1EQWr#lqC_D?^q;yl(EokiB;Cdz-#dFNJj
zk0&SIolg;~DW9F*f({=lp?;m>@_Jh-vbSE9=YaQZTqQsrE(DD!EK|tT7gWQmu!X1j
z+v8SdGl!iAt2TC@YH#Uh1FtPY1*f9ZA>y0U;NsUz^*5_yDay}a9)~zryKHhfGRXsS
zX?i&d{zLVYcPINdl3wXDEcskBsjnlO8<dmL_)@#+S3vWjrv_>0q-S<v(6x%^r?stV
z&QZ;Ko+ps1|5Q$_N~}snqgwDR)Xgl@(d=3@@sHY)CyXrN-CY}c-a72+)4?`A%>Nj+
zzJcH6tnc;t9!simFtTiXz#dTbZf-9<^lxtW!5{5j*03oUv|W7$F@wb2jotj({^+|H
zLT$~=_aoF>fg5EzF&NX>o&8gx5~_{gu?yY6<pOA19FSmHIQP=WO{&*eHK3XB__lsJ
z^1VWqy@WDSp!^l!P_L_VkU@(f0iAFr7bHav7{Ec>(U{!`Vj*V^Gn=RE#Yx&&fj5=<
zZpH*Sx3e+Ky*#LGlgs<ldf${`S{C~DW!EGX{Iu~$h;yu(V*bl(vdejPs8wD-b^dKq
zm=plh^D&1`2{U@7dt}25eH62A82HwQS~5M+SLC|@zd|BI#rAO^jVc_iVKu7*k(DoQ
zYYekaCW=ju{X2OAtE%HQ%cXIRd`j+#|8^t=CfDJZni_S|Xe>Mh=C<UmMFQ+a+pvGG
z{#hTp*vMg^Y#)aDX?b6fQ*_}UyDwh|A^=z8b%!NN-lM1y5qtQbe<wDtKR;hR@SFa+
z{VBM-+<~OJKC6+a+A_a&^1OCG;%<vy;rzMkO6$I<|9``#^rn8MD_@EeL12YazdF`S
z2wL^#bue1BUK;KXoB%XN>RsBNvrYY9sr8-GGn4%7ZlxKZ%KS-t3R7OC88kp9-i`*C
zgYXo>!ld0AL}|uYY!|Evaj<cNEAH-+ow4J!`>??i>Yww?k;o`B)*1mK=cjYtO1cA#
z^8bfyRrDWT?cl#WN;YloTEG{gshH>C6fq{Itpz0?UE*RbU!@l9`G)Q@Oz3mj=*Q9y
z1d`3<S^bTM?28F2f%TaiHPWvguz4mAfw;*gi5^n9t$+T6^>R~%MumV(C2vp->*lV2
z$+n)wJh4s}L3n)~y}h;mr3nOReJ2jSsTSGai#TdqDiTxfpNv6n60%g1)Lemc;!60d
zjCkcj+<iU*nRt4XGE@tgw?*>>a2Vx6(MITMfmmU+<ev0KKwGmDTDcIiuDyhMzV4r%
zS@rxfP<ZwH7vX$2_5AZ}SoNq8OL#S!Bgub{SI?YFZZ#UhP9r4Jb)|;167xTmGAsiF
z&`x}UahA%zVUFdR;o@2|iz?j1<bp#o$()mzQYb1_HqKDQN?xOiS@UI3%P|x&-^?|-
zaLqW6rDA4)mpI8P0SMHCzV{+aJ~bjNc|<2V(3bpHrQWlF4SO&(qN8Dh^p#W+AMpxe
zp2ss}r-nuC*=WToE_4cq0|s5ELjG!S)$wj#k@4+j9{n08A;ZbqGKF_Ysn|+52k0D&
zu0DqqA#ccDj=BWp(MRWGGCf`@IK!l!QXkhmdRb;5rw$fu)tans3(>$a4cU&Y5A~NP
zF>72F{&+?ulj+zg;#v;n&ktI)wZ2{LMfGn#BS|Jpp)a%}@-<qd5mCZz<Mo@3d||8|
zlm|(TE%s-vr2WKCk3qYF*)8jf8BWd5TeAY9h&!RW6k29?5*lGRhBm40rfBmx7?FPp
zXqd7Ccr6<%hOiO+IHtybuss6u4R2u&&abPOvg`yv8?ER@>q95p_L#T+19*>^cX6o=
znO|Ho?H39atB2OJiPE2LZ6CtF46g1PH{8JQxKP6&o-*}9AR65DH8E{JF1>tZKU)a|
z-C&RXyO;7%%}HPO|8dCvwSaP=+zfHiBJk1tZq^2PyD>i~_@{p}lVr5Xf&WQ=rCu;K
zgf#k68Nu~E$@~L4|8!wCMciKc#K9yU=K3gfs9+^1{{~ffP_!Ld&*t!Ar&o{Nf!iC0
z>TZ$UdKt=faYdy*m`IHt6Z?Lm#Ds?8QSw3JuAT|$aiY?jq{kM~G`)hdGh<vy4JgnE
zgI9-0l_zzEr_F+>v>V@~6f#$JI1e2!Mp%XafQV9S8WiL#zBx-Bd4)GsaO^1iCQzo~
z_RA%XHY6_W&9X~_Tb2uia<i8e#YQ=_o$WBHndgP^lp-et>PeK!j!hUUnwyU@i~Fpl
zxX{I6c|+~yJs<2$W<NPnc<2auA4osrH%M&ns%MLeLYee(HqJ-#05)C+W609dDv&%5
z)0yMp7vuI_M4e%&-UT~bLwfL_9Zlrhb)at`IQZtE+-ILXVus^6IZytR9v-elMLq|v
z=dY1uAii6|rNDq>g2+2t1)+foXi>05<csyLE+vgnq1AGy$F`FxF8To&L&~je6XqFN
z*^`>fMf$q^Xn`nm9*3(%;Y2Ag2lbPq;0!4+<cz4)-2eM`y8;jQ;}Al6KbX}|agoiz
z#8tsJWO^B>(NSvUh&{7F%)#2~u*V_3o7Gt14skQO0g~(e=%SkGJp?s$FAtR@Pq<*)
zO3V;zg_Uby^^GL5K0rrk0KI*I5rX#G5SMPk?=C?HKa*ip*<^ET*XGnn{Zjpag9JvV
zAw9WjUutA3(*E0om%KzY3~;CeGox)|{XR;K?orhZ*1Gqc53B`<N~^s%+e2<+(Fc*b
z^MEr*A@qD2BzdK-Ug(MtC{#5ju63Q;RsuDq>+#tEu8f*$>14q4Cn#70wiyv9*qTcN
zm$@=1z{^e;7~~i39$M;g7|l1tk3O-z%G6Ilx<P{V=bs7}!aFE$3+fs1Z={zftY%Cb
zg7}(WuYq<ZKE`L_HH6hD!Xd&TteD0~*tGH`u2Z$bt9I%f4;l^ZMrXu{^|0MY<4`Gx
z)LHrUb%#_^abSO-Qetbzp*ln7v3BHCO}&2s(G$A~*y&WUfesOkrS<3Kg+%pbvaYI&
zAF-qHjMTp@?78f6iu2Ef42s@ceJJDJAzYEzAEYxU9^qrq{)l(Lg~zE_FRSHTKv-r|
zjTYkd7e!VFJx3;Zr1F|XkkH|sTmL-Y*#0BGFFn9|-*@3V5qxU@Pr4Ha<8bGpgzlXJ
z2Jw_V<<xRMILXnmRg~pnd&<)6tGdRhqi|5(a;8I*bNg}-=gh~*gT^w)BF9r}>41&j
zR}7<xXhB;f@P8tqh3xt35y^Y-%7f0a<#fMT<Zbw{sYP*<+BXG=GpKEZwcBrHY1sD}
zp~Eig$4K#&HayHkM`{`5&3Fq_JBT2F|HYF(i(*A_Z4+s+wu|G}mzR%7`tgab7}|M)
z)5?bxjHP2M)3tpMo3&p~K2L@xvQN95B6mCO+kQAm)DOr#h`S&RAjry3m-G9_yU(DA
z4&hH$zHb3xU+vvMEAXXG1!GaV<s5~UmUwJsVwK6)qJ<DR^X)X%<!Ux?RqkJ#>t6aq
zyj@J3D(z|N&q|`4U#YtXJ35kFSEpvx?pB8iD54cq$LCxV!DuvGqm3<SjL342RWqZv
zHd>9k-G1&cJxJnU7(#FHB3%C>QrFjY9f#FdUs@8ud;W){Gs{$Pbg3fh8*aJ5XwfcH
zJ46N>em$Ib<3UA4e#;2UDhfcb8m2%G<_ynMCq$Jdp9PYm<zjl(?c7}-3rw@EO<Wn(
z=d{)OGVqW2g>~y~8)eU)7i@C>c)z9Mq4=ZyuYt(Uwe=Eaxq?{-X<Y1=F^wyw?Z1kE
zlE-Ory4m~7^W^H6RG|ruBX!Q`3M+=BNDPhcGs6eug0ET2RQ+j=7CB%|p)|RuaXRAJ
z)Ew(`L+*wq_pz}z=IB@LFypj<+gp`G%?LU;OhkloLV{*&veARE!u_`gW!4fH%jO^V
zgV8GPgKE3DQ$;|3gh#Nk^g~n-{d<(WwkWHe_AQ3)f$JOeFwaXDSY5iLI4E>8k%FY8
zs?-}{7*m89C)95vPaz;8VwR2iZl!0fjNNuKvn95J(_`BX$CDM3>JPV+mKi9zUe*2f
z*R7OW)WZZ7u5AxO%$P|StaQ&{MuNxJo(mi8fPk;ch2x;nKg!hIw%cFt@2^Wi$F*W(
zoDgaY%EMBn9SN}$ZRqCzcEov?6=VqbSIOoWo*05N)sD>RcLJyk@d0WYL(i_OjriL+
zt(s}}{ys?z?zgdC*VBGnn+**oEz4IQN*6yJD{S4KmbXZLm{io%%k!mUFN$QAqlbrC
z=?95v5S^%><kNomd7o2lpNvu$u3?Q~;R$YSFk6m}X=wKtt=4zdt~{0fx&NDXBJMvl
zNJC1P=p!%sNdfemZGQ;dmyVN^_d2@o^M;H~`=krIs^*Lx%?U%VzHJ&=Eshlw;)02q
z&fvy22a!DUfCs-(v*U;vb#|cYec$Rw54LNxrXK}jk1_vb^c!Wc9Q5v{GJvDVz=f0|
zjB*KDF_$A}R-%Z&l>RZ(&owFDhM5#~cAe#JYJTbvzXx1qsy&z4Q5d^4W0hU`V-~3j
z`<62z+W(O+HeF%h$E1W<PVA0dTS|!nD1mWf7VF@g&B`I^4Z8eTf3tkEkEFX*^}!+P
zp2)!iWV2O#jHWtC_1S#V%V5gUe7`r+QG{mYNRz<~f+9%2)Ny=V{E~^nvg3y;hkFpR
z?MP0J5r9y4@S>-y(bP*s$L<~{5pU`OmTU5soy}z6S*&%e=x6iDz#t89L>40EL=5e2
zjJOt^F|DXsGxxXQ-r|U6m5f*9nfbK|lZ78djE9%r8~)(<!VH9nq!CsSwM|=6k1vcP
zNacC(ehZRZbAU6GVZ1D4;%Z;C6hqf8!5*&k2AHHDG4OwG<e*N37PS?zy|^5*RVOiV
zCgZ&5`t`>Q`C|DS{Y&9<rOfO}=c5sh?A891qR>{xrGd{z%jq5TdSg+$;N~tZ#olu0
z{E0k3B5z~ZI4{y<3$-?X>XM>gh3$o)DSEFH@+-ceEMGEdZr1Db@3uT4Qhn3p5&7-p
z3tZS{68V4ILe}K}xWxTMi`=8XU6aerU0Xe_l@tD{Wk1%OqJ6vV@5$HgC-il9^9lM8
zHuwtDBo3P*7v%FYhb!OrzPLbTW+(yZeSaU%t;ecDt;wO70-FXnTjgFDBusWfen#U;
zCm_S0!qin_G7tn>(1=^J$)FUGUk_BV0W`40#lO+!Z=+83r>X;9B4Mq&R@>Sqb*01^
zC|v$$L#qgcyF`jyghv?zr^Oa76Q2<>c9|Et$Rm>=@KlkZ6gwbJLQc$y_I!}^*%(;=
zjNz+M<!@P9FCv}W|DOa@sB~-ADgCEh!m<-wNyu7>>s<-+4e}bq%ju>!7XTv!L1rYh
zN^JNi#e-6Upc7KM4dez)f$({GB{aQ6BY1)EwR)u+{~#-Y-*IY}jQPK6yD?%fm=S6a
zWVTRO?DVG$kER~JoADcxI>tbnmLqllyQkD**QLyOIaXg4=p1$?)CowHFsNw-+{O<O
z{CB5+LZSX#-lL>=1u$>ZvWyIhsmmcrt@c0DSZ-?EK}Zk5CRVw1=r&)q$F7e2Co^%T
z-|aX3K#;^P#a#y^%ZXSBG2cvG{5SD}1mUfIuYD&9q`v}3hhI7MHpojf{8@tWmk<lj
zyrnB7m`={lX4lt;VuTR6-~(&<-gCc7#LAEV3AJmN$ZRvYKnGKL`D$zwXQW*hlobeD
z?WDdAQ63C@!Td=znawqN7-ZfyvgAVHz?SUT=^?}F*+B<-OV9}?bqoZY=>K!2d!s<8
zaj5M~8Fx9?Q+{XIp)rCmgWw?GD2Ca8Yq(zl@%*KBDG{*S{d+$2Y5gJEKX@c`-dbvI
z111gNg>SssSqRKTA}M4R=CfLrL$MaI>?Dt}AncO{qdGMW4g5yh7yvCuh_P4?iW~rq
zjEvF<E#wv8HB?-sU`Cw}&85UOv)Z>K2tuggrDV^H84${jMLvTP04GSx#y8YXs?9Wv
zEHvots`&e0%DhMO7KX$`371zrOfDXOM*{@F!@(;qh7-RBfrNuZUx!D(<p)8qWtN&h
z)ng_0MNGdo?Jp+t0znUe4T4odE`bh>PbK<33;qv~E{yPGT&9W4$D>QL|F~z43TG=a
zN7U+Gu`v$3<j$Hd{!3*&I^tCtbYYgF*yY^Wf6V6`*^b?{HCNh96ID2q9$VdWNe}@u
z80*I{e`^kEK=Uzb5v>#L{|h&VxYbx({5Q;}e3Ip#LGp_RQ-Sw5q9smDY>0y`m3zAH
z8*kvIs|7bVf@*I=qV`h6(Si0wK4XEOv?$fMI=Jb$ObKlv88@K~AQr=x%&fU_(LiS#
zcVFPm?}qdsZtpT=YG!8tu5as$fY&tm{;ri;<>0xksFBfoL1k#*O=xbMG2TOz>y*Y@
zJ+?#b8qrcmQ-yudzk}6a%ikjW8jOP42X3NM3_XFt{C-$yd4y1$3+z=1*ZJC;5c;Kl
z1zjDJmB`2zL*`7IVRV)wEkIpfv;+nW1T+921ht4*y8+=obZ3N^N=OU=xc@`xUsb^L
z-5Hd<vJ4nwgAko{qoNr;K%5~X^ag7*3n6@Hi`rJ3XfDS*Ran9_J$e|hC=bKWEK-zv
zKzLYgn0jtssYn0OsFIfma3+`}+$B4#jILI=7Oh2ic+}!|Rec*Zb0jlWnAVe<Gcfbs
zBOPJ5Jy>P7JkU*BDF&kk{>r+IWKbS+2RMJPKM7&QC+BW;y@YvJ;$x@`*oy0qDv%Y!
zUR7VMUnuJ?=;X{lIHY}Uda-^1$@eU*y(jW+Lu0!z3RN&@sGQDcgF#op8u@)&n!8<I
zo<LZE0VaCS+M(_Cz2Dl&&t&olkCKV&Onqc264F)3qxsi=YBlixldG}OA&+baEGDpn
zJ)4Q|XP^^zs0u}ke7)U%3l3ze;-j4~$ALZg3+VNYM{;+g@Wg@zyZWT*zISa|yWv%#
zM2P&W7lQNe7jkp{F?+&=Jyj+|ZU+r#3D2eLP}K8`G<!yTMn)((Lgdy^epX&pzhVaV
z04&=ZaJ1OA_fmlcu+_Ty1f?{jwry+}XEI5~O0d=-Z9`XWV&X~v&)?i#;9sM8cW_!t
zS52%hw-iiGAka+}tx&CK=|4`H5e2}McaqU(ar&l6IP6yluSb25yMz()kx%Nk$|$ar
zZGXnD8R7JcIJ!A=>j~cHX@l4NM1>y%{480p&S@gN4v$>)aFR^eUnS<W(GK8|Z{;xb
z|JKji9O+DT#XBcc4vRd&RoirE`#iTzdBz30ev3bDuH`{DKXhL07O5{Rf<E!C+O)2c
zrNCd(Yy1}M;aLF(+j&`va{R9hgpF1V!hP2PBAm}Z9N{h0ck=*Ju{${y6C@25U?ghs
zKCQ0W3X*U#;?ky^=TT??H)mO1fVKC~{cXOWp`K{pM(D?&7-@}^IbdK=Y{X;|#NKfe
z79*$;a%UKw$WE9Eq^d0yX0qasz@0Z8sBcFw#A0<r5nzJiFnhhbxNcqvAV8M_K6lSQ
zVum;W4EVV5ipXtO(eBPZKZ3dgjt&lA-1wcAO}#OipqtN<Dbon4W4~mQ)J9WWHu2cQ
z2l^sM*(G7d8{OXV+hVCYFZu2pT_pSm2EM&;Mx3}q2mcNKyMCkjk+<1#^YA?Xiv6(*
zT_ZTefALpnVvg==6A1OQ?_{`o)Ya$A3HYX)Tlvpbf6c%C52F8fB4f6U&-K*M19lSm
z1E`O<fOkPp!5P|z{^D007$ZbB#Q2mq7c7R{l*`2#;NNLERnh**+IJ?U1iJk}EPW7h
zywgEsThiwVz5OxB;5%11LVw5CYdo=R?3%;A_u*@b?KP=OB@@RD2W0;`%=^mga&%Vs
zCjIoG=`G~}f9cJ~2gDGq)>lA8r28;GgD7~!CFdjwuc!&#%8(LnH@x+1UuwXUCxoD&
zxkWqL&_)XHTz>j}bP-YAZ>uqo{!`o~e`{Hxr?V<x&XYTSs)eUH>}XmP34!$FPg$0;
z;2v3Fm_|HR7)_gme=i+B^sSP7TQlJ2)<D~j$gDk-ggx7@018ZvS>Uah{ZR^(81v>m
z9D2o7_DGE$3G6sOb+Y3bMp=jnI_C^3E~T(y5K`%bNSx6Js<(zhurex~L-5jP-gvB;
z3x=2rgh>>F)=1`>(385u!LGqNeON6o`n&nY!n`dSRK`&TCppfIrU<<_@0%H7H95kM
zHC{=7bvP(W0KvLt!Q-E0aVX1^F}DyBtKABCG2bI>3EO5$oaN0*s&_k#Y<;s>S2P$U
zKi)glm|Poc0(?-6kqvqo52GhcbaLPxmCHIgQ#@#8$Wt`)lkFechN8O91<GIJD^0_+
zFO)L&WvR*CK7H!jq&yM^zRivDLDRGIqK)g)YGg}j0Rd-%rV!_S)L2piLCTXqmCwc8
z{9<=h-RMkt+t`Jwx;rN0{h`%vD12PG3sGy+pWNq+Sb~Rq5AcLTb~MYBtJyUykDvzr
z`fWHIoAy9lUlMGYUq_o_#=EU~jvR%^Vn?IN%6(u3uBPC1XRu~raVpeiVa=kBqauCV
z%PXvF2S7kfpq&Hh;Ea;OVm;7SwPI~P1FN8ig|L48!jGPcp`lhLo!M|C#fRH9op`U}
z(;(*7RW?=TsNK;l+)COs>CYdIuD}d%4R);S(5+23#SkBg3_)y(>jDo)I7+}5hQEv<
zpC;VE-I-nq<~3#Q)czwU<<BEu&V0p{6UGAR3%s|p+~%pcrl=+C%3d;ItZ3o1h)#Lb
z5$Ghx@J22aoU4kgZe>VR>nHH{YGs*eB2QJaR#WuC(6rjl2s;Uai2fK0^mH90E6NWy
zAjN<fqIyd{RPqJmzgIb=>kjXq#oXx&9s%!k-hhG*;jaEUFwN&`)>nT9J{e?Z=o;gb
z066Vun|aDU?jpmUkvB0BAbVb-RWOBjxE6}Zn~+>QP_z2@rayIe&h$fe<xXTp#0CoD
zZT4lB1bU_i#ireBW>>}mUNUPm?W3p^L+e7IbQ*)1Dw2bh^#*E=ttmL_9#IT+gGU3{
zW(t*UA{+WtQo@<9623fl8DU$L^E6E}K=xHVzvNQ1Nur@JpD>xw+l-YfahI^Ef;@eI
zF~YxzT-a~>HvG``z~f13Y(`6zm<=F|1^VpMQ^8By+aAsYGtH;1YH{8se^(xm$<uPg
z;_UOywn|R;Y%I)xgv&g8rE`7Fat-GBU^onyQD3msCnC{T?V?f4pEip*;8!*W6qmLh
z*rUlABfp$gK0kBn<p228v|A@~25QvH(a{_J<&R+FxW7DNw4{@`x>S3rz2ewhlsVlS
z@0RwpKY~imNTA*rv|*fX-tYR1T?<viDnV`89~hrG6uyf1>#zYGH*2P|&?N{Z!@Y$`
zoFBDN+MjbPVcq-Czpa5c4El@<fNytSLENNbobk!JHp1?bYZ7x$RIl#5T>EbKN7C8O
zPuM6ZA#KX|sU;sGosB)&moaiGU3ey;UQGB~4O_}r%Od;XKInXWk@i$SUgPdEoqArG
zHA7v!77_YuTuyW`Jx}y__#lP|$^%n78jrHw5>w#{IcA~7%<xE3xvc)cF@?uh(eB}*
zRuRLB;LQ6zWZml_7*Uu-uXF<5loN{3F4$Iqz#^>HeG6uElS$JFejZ9IRsCmrTx679
zWj4Fm$|fRRG|XD8mDQ$iy$=bgUyM4$kXP;>waSg|GptYBLb;NwQ6<9?oGH|eJNgFa
zqrXkA)_vPq<AlB2f0e!|J^S=+`#dwOP9phsOVZv_zsDD#A^Y}7$`b6n@ta(?+<jEb
zp&ibBb<|5-gMy#FCHk-U;$9<xvj=)|ZM6k@u28~UBSoJPG-JYCqYyFBC3E|*0M5Ve
z_xV&n)RG8Oorq{auIIsl4ynGR*lPlYYweO1RPa`?)7u`oVaVRHf1?!A1KA0!MSf}(
zU7<p2v*bE&Sb*S0{t&`$l521c;N<se^E3i^J`L;%1^-0_q<JIKpLC*ErT-1|f>kJG
z4d_14G;2ER)XLHpm+#d*jqW@GCSYE%_U2XwPU!O0rdQOkJ`jXhPiJFt8td~>%-Lx3
z0j4Car7XYx_pHhJ0oFS0TfihEuq@CCW<rI<v@MRj@-t^YM5327qh12ymnw2|Rv2G`
z^+Cj7$@_q*SB2O-IT<`QYWZh=xVvg$kPP^Hoq^)zwkIzl+X*AM<|8ZMw=g(z=%bNq
zD5<XUev=t<)}nIUu<=}+Xy~*3cWLc5hs|oS>o1w`_kUysKZN|4!FW#i7f&A7;>}hH
z9G2ZD=_8i@dqm6NR<oz*mJ1PtK;YKZV|5wf)RM*<zY}iv8r%HOQRyb0XT|zWhmoag
z{y?F2`zOyD1r8U0#Xkm!Mr{JSQ;c(<jVaw+mL7;$6O&lrsG0slJB9J^eP&a&0}Qou
z#(Dt0_*HmUe-_R|q+X31oLs!EbZdmB^PD2U^XmHbd4cprOl3sR=x7qVvuJA~_!v(;
z?N4;-9Hyk&X^nj5_}{<x{9nkG95t8WztA;fEYAXZi(T#tf|q3g{S>N*7ivgR6LaEX
z+r9;awn{b|YxSHVolDq?#de9{t>8(P416a6=SfIO6u4XM-;7N5>eydT-J&moK9T-o
zJYUECk5Q}wK7ua9mqu4P4Wb&v&1Fqq8kJILJJSr8pJ49Mc8Q@QggpZ+nhzl$kFC?K
z%ZX}BT)CeD^`aEOEh(uGzL|#_v7T1#-;v$VjX;RFF^hr*{xBjo>hXMPNsSXOovap%
z9~aCoheTUWWM2rIUjj+YG_k4N=9z{*`e>(BABK~cv!aHt^rmUsj_e1$cyL>RXjtEr
zEYXUBVA?>81R=7CxEY%9kP`XT+8J9jkVPo<pdyK(Mp-(6vg1V}oUIl9G9>Ujg`7VE
z#F%-V$<N_ZdqjBMr2|CIJDjRPZpz)%jG=8^s}^PCsWtD^eQ(doDqy_$pVTA@zd0>7
z4%fKj%|2AwDfeMyvGKAIvB+CG+Ga;=4f3eZ#myA<xSP&b;I_8<*?y6WoX=?5LC~SB
z2P3Zf(~uDX91dXfgS4i5@h+y$Y6i0~D^pC++tbbGgN!%28jpvcFBe%aK)2tU?q!Hn
zOoGDV=@s>=-j8xqvcKYW*F@zxM2@)W+Pgg%10sr7CywEkMG2UBgjLXLbWo^Vt72Ws
zjHzdQ_M|_nz$6s4_clfI7AR%9KwsDJHVPfvdi*8;Khkea!5gmjw>51tskvMP49%S#
z>zd=RzMkBqu%#*M%hIC1^+;1)jPxQM^t3s@NIVGwNruK8z8gs$=H(fdw5xwUspXvN
z1g5L!37Ain^YFPs1k><mLIp<(%1I@oYM-?nY?;`A$l4b(NG@HJdAG18TwnGa;)uT9
zpXwq3yN)kuzp^7gBeoqMuY<_}g|xGlle^<L4^Jh7ccj|>UN~?%`qX47I)uvF7#cjI
z&?*Ci+WyCW+2u-R8&5yJTqBLr>r#!0Sk})LDjX@!{YVZL!v$1W63QBoVDZVZ?|r04
z=*jNMEvsjZuPtUfTb%SD#6d94bSu@T*=#F7we?DMe^wc9@us(Qh^ec4&uCPdl6;az
zljSkH`rCu6{Z~C%ALK~z2Biu0ENt6u|Id?%dKZ-+Sd@j33`}TAQKO@yQtaH_LwU!y
z+GUsP2uM+aIRm>1F@pISg~ksF3eZikAXx>OzvbAHA_a#3-4Z4aBLZ7T9UwA!G`J=J
z6ithEAf{X5-g&Y6l#%6T1W>lLWJ_3|>vk`(U^D;0Ia<gXoyoPGkF-9NpI|mDLAT`=
zjsKJ?;axTNIAGeAQ@%YeSM;t4uRZ3QjZy8UUb~o1ah%ShZkk`-U?p??Dw4a2=M?=h
zl|zhHIwGhlj?>dND(}XR;rWcKB>z|n9Id9uaHt^q!IVOk4j16bG5!@BsL%bW2r^`c
z94dq{P~1o#j$4x)9e-b!%7UHoJVw5JIkqL9wbIAyqL)6lDqXUdlJOI3<2^MU;Yf3c
zcv^|IW>4|Z5OaW>)$5DAGfUtreMbJ1uuPIR;b-eX)1~cihMSAX>EH3`jVT!~K%#&-
z7>!xpe6|XQiK>Xh7_k(~2AnI12^U!R&h7NvdDB2Jl7*0WK9fy!+8Ox6C=?y?U`}l{
z---g%jGQp*rZg)e8RLvxgMz`|%v0v8k}SN}ENx<m_g7s}KO3+N{E{V^l@Lwa-!b43
zyD(l*R#lR}n+W+kGvX|aT?hTDfEsNA%-4$VJ|DcVkTmmUcOZ+HpWkOc`x^rjO=y1i
z4`+H9tB8r9&(pcoxh(BiVRKfFE7lJ5ykG<t%?uXplHNZS+BC=Y<qkPknpad4s|yM;
zEl#}8>xS@q)pe#YF-E({Oqy=Hn$3w>w;=ow$%VY<s57{48!G(SGgq(u0Z1;fh$@~b
zWn`77&%9iXO$pQoZ%--BvTc%z_Y%MwPi!<UpsM8F=h3DgDC*`>b5oG8HP_P6gLVl&
za(8;_(r*1mg#pIz3lS+#O<rQ}Ewx@1N5fo71y77~V(q+~=zM(4mk6Owx|ge(4tZ~H
zm{e*gc8O+gW$zvw-gc{l0jBqy4XQsYXLFpfzCS<GH?(|f<9l>I#dvvFswDMdoT?sR
z_#JN9@97ecv8#Mzbye@aa^ITijnNCKfQu%mTayyIE0vPK?K6Hi6)B%!f2wd2-xJUJ
zn&dEls>pj@(D<TKI20m6=~8um^3GV3Ca<IQc?mA^_=+bG>o3p`06KuSQ!Vgjq;O-f
zn5Y0|T{ZY3>P3z<63Qrx9364OPmVCnA)N)5T=ecNnGE4`%{tDVOpqkt7n*?m#j5w1
zYGy><Z5sLYOb9>2&Icl>BH@Nt^@lKc>cUq*Eb;g^LPZWYyK447jE9Y#t%O)O<`bxy
zoyy+G+vvd4qqE8@@c9uUBBJkwlS_<D-a9aMA(9i`k-0&3xeR-gMBf=TsQNvdwpbe9
zM^K&A<A#;hPmJGh0DGvnP>Z6*dYndeM=O!&;eZEY$N3D30GGRV&b-Zk@gOpHznw~W
zEy9PWw2>yb5!4#|8ee8@MS@TYD}mlb1}jPRR80owbzUt8NK}HFvTSIewI^wKr9@t0
zkaLiZr;VfM4Hg8^+clj*K|+#5KqRq5UM?Gx5|J+x=xvPrs@azLcdNnY40c8*7H<{A
z@X3t2lbb0?m6;(GqBX_o?^H8MTSaEVu{`fut3S2X9qXQ?q7A$mPgtvQn=o#E?jFjw
z1cdv3&|>@DKq=<@Y4YcB*>^ub<$vwcrL9DFiG2hi7Zg~aK_-8FsPREBH3UjOPP}&=
zOCfeFao!WOhG<BFG2=yYtqVBZvk%|~Lj<$0>szu<^scXM7!7K!%k86;3z{!r84|VD
z>MCFNG|o(QH7|GacnuE-%Pw%-GIo7}x|^X|N*RDeVBL>=@$aTVFoxz1^r)xw#hY#!
zC9;ltS*-m*82$(?ltph1`Sq3;FyJvpgauXo0p65|ZPAd|g(nWXK`kW2$`?b>fUFJ@
zBn!giMe_Ljl=}yj+C@PmvO*XfV(R)(dhQcEv|8L=aiHR`R0dx8cW+6!^;Os#b*n$L
zb8dejfn+@AA;wMYBiPJx!MYRCai`L6WGX^JonC{K{wh^tN*Sc@^>{;lpf7v9sF2~F
zrHbxgSReL7CRJJtJR?*Pmywm*78ILbh4x_y4~5kW$tR{7iiJ4kstQ5FVFIE|<-77^
zJTfve-M7^0{nGh_454RaWj_pB>@(E8ue-_M0L#ez4(Ah4Bp4*>Q1J4+9Cq7dI^SoR
zay|XmB}gBl<F5s@gPwCGF74te&o1^})eqbZEp6nfz0{iNiNln7uVjQ8+VlvWH5}YC
zxe=lbdU{`)Hl6m?^4F#BM3@vM^`u#3LolsSi6w~I4O-Qhs%I#8j$>vg_*rlKxmM`1
zfIb)8Ru48DJ?<(lLbcbHo-D|omF_ommh+AQSaxXFC}dfq+B%ant0J8<l$r)IKNq7_
ztd=3qlePPt>jLJq%%jHME_azj`#lmzpOsmWIXBZVhEvaX<T<OPMsfW)a~8w=+v(x~
z7!q<rZS0K}Qy*<;`=2VN9lTWeAkTY2z*R`pi#q)Ki$lmp`djYCPhR(o6ufsCHcyq?
zpf5Ph@2$b3z?WgsWAKLOYit;7!J7CX7;qZMiBYfu3I6OK#F27+4f^M36T%AK0}93R
zC&Qz|^^i_;n*MzUB6O`9-Lg4vR|Gni)a%kx2md6)Wd}i~-Qr*Wi(k3E%O9kifKoTp
z#0go`>*8!#K~Ij2V&ihvTT?1Y&AV%A7WEHPx*!5Gt{ynOm`)ad@z1Gg8vAZq>%&ep
zfeLcbX|V7Q?tX5OknQc(QbV<GQ}o1R71->vl@8^^*$R!Y`zp=-wZbNAC?yR;Y+-xp
zX)L%p^PX(DWtlm@%uekp&hkVbfJKpi8GV(#ZVCaT$WhRq$DKl$Tw+2>4vx@pdBP;y
zBz&nN=GWUtO6SJ}`?$4tNaD}#$}+7sYQxu~wYOTzN9K<sJ6PO*jZP)^jxn!AJqPLZ
zkCf>$kX%pHi!pr`BN=*a?P!d~en0VHp*3@*oc2pemloM0IP`H`TUv-U0ZE}52f=>6
z&<|f?G91KI3)v)n`P%lm#8NgrL`H`nl0$BYW*2cf(Q84TCB?njJ!oLeJtky$c?}tW
z218va@mI@L@lNDkxbt9?8cmJ*F6M+~>XA4P$Y2b<<O;ap1mcc=CZhlvsIF>L-U(jD
z)lnG-M@mK}Q??OQ{Ev@8z$FtT^pEHfqKJtXnu5C4By-N$D>B<stoUD^q-l9NFsTw{
zn;%)CAM7G?z!IDME`Jbcb+~hGQK$ViAM@z*>f{uYnLpiP_!mLK9YqiX{65xc#SJfB
zI^AD;jdIPylvb}G2%_-<)lJb%ZFAkb{Xy19BVT84?D#jf?;yY*K+i5(K*A>_czw>j
z<1K^z>@-<>s%koa*`9CVP@<KC6h@*T!?}}*kg4OB-C4j8QOW(mWvH}wT00e`vG~eL
z;8U%S0_idFkit3ou7tP{ay<jddQ!ap756%t+U9+Jh~0#IN|tXk#j{3o?~gaT2~+vR
z2bJ%D2%Eze{kk+3up{4{r(DillIYw{lvtlE>Gc$bi#-(mIuuGY=Di)LcIC%HWR>1G
z(VTBAA%e(SK4nvtu5#jKh{!s(Av!z|6z69wmuq+(-;;(+P0@S|5eOGcoV$l<8E>!S
zI_bV`KsoSJ2vrKHo7ym2wxUbyB{_<Po9+)73ndecrfe!2u!zVyB#XutH@Hth`IT*^
zjEy@e=k*XM;Qiwf_LZ<3+@pt!<Q7HTjcKqRIaaCEwIr4LozQQ=hZZh1TH?S}mB2iB
zhJm1qT2MW+k`WK?>IBcd0Tg@@$%^H3-HeAKI4FZpHwEX!M<y)lN}T|&T*QW`zAq_-
zB|}H{ER#VLn4M&~!LGcxY#Duh&(Nb+_dDjmuMx-_kIFTfWlAmC4WW}s%fjIZGqUu%
zd;Ti{9_%v?Bj`F@pt35C7Mjy*+c0PgdCvkf<rsQ@3i1at;2171vB~sTET-7R;>3iS
zqw+?%wquw^yC>Bpp7he$<3&30D_$%|1BRb_^1zQWz)g9V_FMhkjKc`|ezc7=Eqh?h
zrWrlT5Y*7~M(c!%G~=z>t)lv%Y6rLMO26&lxOmz0e7|(Gs(sbpB%i5NlQ!jJYnwk)
zI=hhDlJpt!935%we^H|w_bH8-#ixJVtTgFOYcyBSNJuts(T^S46pr4K8GB~hbrqm2
zh$D4V0kkL0U_-Ca3vR{kc@S=ySe_G{MRk;^Z$<Dtv7oj*nWtfR6`K}E>6B`B5lT~{
z#pPE9v&jXdY{H}LSS#8W3DbX2p+mW(E*}cw1v08uPWOwb4UAMWCn3Q1i&1_}_}_MN
zgWGqB7pN=|r=rohN9zCDqeX3P7UdwzQUAjT3E1=kwO!|0uM$o)Gf|v=4kYs2>=q}>
zJp#Ib<ZC>P&#j8lFAtK`348}O5VkZ08gdU%-TJGQ{|GhX7eOT33;P%w$)WUD+3-UE
z#psWQ`434H{t<_ttZLcQC55!S3c&Q@gKMi=+cKFv9WS+Ky%ji6PQ#kY!LsKDjtabs
z>78Va$z1koxEG%1GrIF^g0`o2kDdSr20t5RFGllz3&Z#HX8wrsKJ59&42@O!_N+{`
zXA|ITCrRrR<3*UhH-eE-VD{}2Z8cQ`dHDqbIkYv!QPs*whbElH9kdz;nU2P;P1=cv
z!--ay*_9DORywb?Td0jin_Ca#0FaC2t5ss8{{lvMcg3IXItu%RL{Iix;upJ>JMVU5
zjY`e@nXiHW%w|&#4h6;gu78{__)DhZdCRjqn43OR`N(pGN^C`lhHqrb>HPw1k)(Fl
z)9u2<ZwrdwLb~-AuGKYTF!(Y$kl)?0{(bGd8pW5I1esa6pt@*AVTMiSIM86T_f*BH
zzIc6%c1*WJ<%0CDjsh+FOZ1Y@W%#tU1K3%%Lj*}yLHg3CJ%am^{c5IA3G`3tRPivN
z4hW~o=yF=oqv?CgA#fCXeR>7ig7MEt{ncw{d3M*?VRjBUpst&JJstFk)`&tGvJH4$
zr23Mmw-t?U^QbiEM_PT%1JRTZ$~}xt=4w}4n}P`BCQA7ld3=L*k?N3pgKzQmrYiJy
zn>aGiTEk;9TnZWxX*U9jc=V>VG4mK)jGucxT-gjib%ybKb27{%E)jmOTyq>Y$c{1o
z1^3cK3MKR6Y$;Oo8yi~-1Z7y+_)%3UStIr4A^PagGaEMlBE|m!0ObYf-25l}5E>mf
zGG2A)49RBnV2YA^1lw+5c;L^Z0Ypdov1|)mGi6B(+UC&|eOxVqtt+)91C)@M<-$6_
zl5-YdrHiuSZqR8Oh%1pn`id4O%1jvLWA3M*U;2%%XI>7s!{5Pdzi$ojuS!_fS`&5>
z?<8=2%kWav|6nOz0)m3bVuDzQqGS~pCCUrpE>kU=EK0|bU&~fR9p*F0;TPOcY-C33
zTgt4ueoh%*LsRM5h@gheQNq-qe1NFvZGOrx=-i#Fr2Q~1b=Du${Sp4c5b<1ww`5i-
z7iq}Vh=^Cy0!e#4v=_#UUS4%nIm8U|9242+MhXVf4YPBF51=~f>1|RbDP8Qc-Zb+G
z_9(bl_y(n`maRH$-kradyMK{9L86a<w$ItOEWhSCSQ0m__pG<oqP>2&?S4ibC}CCN
z`H<9zsg^Ev4T~)eKPrI@V1l_<zbEXJq6ArxJ{KHR+_|*1fnBAlwY8_b?>q=h3wFXb
zE1QS+k9~)&2KJ_`G@6uK8xJoNz&XCFvK?yYl&vhxj=1TRM5;$NJsNCW1+$rMmB&}<
z4a+&C8bU;aU+vHMXJj3!d<L>X#;VEw4lPQ-!Xe0ims*xGz2wxOF_}i>flH6EB|HlY
z6bvdZf3)jL&<v0IEg(5V@g7;CT!!`U)C3dscr8_QPJpWw9Ja#CT{#I^pq{MS$G=qZ
znDxT#xey5uOV@W*r@>~Tdv|=asFwd^M<%o+OVKq-2dE%jo?RR#7w8SxWUZU_<<N3J
zhA&lO9SJfA_n_bkoBOdC5Pq=ERjKmui>jHyDf3iEyDV~Yn3OmvPf>LG{82CSW;BHB
zUB~)14#3oG9UsiFSvw?OAABDea#tSq5}c^mdp#8_c)0Z~wIlrW;KEO;IyBsHE2-hK
z9+0PdRUX1g946Lkp-jWgA;g4I>GI!dJ@{9WWIq<*X(6j3RwB;rg4rcstXeO>WZ@e{
zbPu<_O`4TcuV$kbzk2RAosEyHJM>vCYS%2b20Wm47hM+*`#?CaRH2W9`EHPRh&hXR
zy(1Pz?q3-rQu3eEr;1b2bv(K_?nmarJZYr34)gRyC?p&_cYgd7n60(blz_hoosPsn
z!03dn97<m>uP$>e9blQIE6lNnMnqI6dEK7pMeIU!zbJ|NS!r81%QJ4P6+V)eUSaVs
z15>ZjI7k919xHfEP&qc5^5OR9l@h~9Dp%2_>~lZHumCaCyXLf{3wLg)*wYr5Vdts4
z@TXo)+OV1I#k&=%Mm~U&uBi50NA}E#A*c1svLofOR#3Tm^mh5`Ubk-K$y2U*Yl`>2
z)qjHD^863A<t>#YOTs=q%K2yiv>F1e@XGZTyK&J|JIIOb9ZZ?KeTV7RV0eEe{8cD(
zTxIjlLrM^pZJi=6KvShmbLP8)nf2KC1N!EB9H-#;KKN$l_lw^77uFTia>~glxTC-<
zmEgV~*Q-EgBu{U`-$UbPMh?m!4>Zb78JET4A%CaAXwnBQx+%|+Mk^M5_2U7Wo2=xL
zp~(mtExey4Soa2s<g2vGerEAq&}<wMZy+8_3xf@7ds|7;&_Vql0HQ!$zyH^9V|^~4
zmFml3S%5V?KW<>9+&Wy!Fa-Hobbyt)xpwfpSSrd5o^Dm7B0=r|z{{RaOe;TCUSFs0
z&R;{p{d4>s^P>n0PSg>{9|qqGC#d<*L4Vh_=V$TL+skD55<caUKXwyWA7{lO&Wl+Q
z;tAGB2qGj`@*w<0CPxIjE>k!^_LURn_-L7>9-eLLz4snwkjhq)tuR=jb|+id(%Czt
z<4sJCSgw{TrG%=+fFtt35zPoDge;Zh;ssX>=V`LuOpBxSiOj)GuKD+(Dn_&uUN*C9
zI9i*C4iQWX-@}+ztz5Y%9Xw;hyI|8YuzU+!b){Ht)VUhxvc?YhK4i&kDnm;*OcXFN
zlWmog0<*D`0+u)ej0uxGmLPw}UxA?}yJ#PuBtNT*)k&N2_*8Zgw?yfy)a#I*2b4r?
z=M(9asp-~g^wY7+i4i|lEMhJW@#>X{hGshcm^Pn1sRlLbHH$t)jL6t+H9;V<-_Ef>
z6fkxqv``c>RtJI+1mp1t#-NRoZSvP5dxIDk47C8-+gTM!(6_~tDzZQQ{U%4oR^fX>
zoGg=oOxx{LwrIBl$?*GdlEwCqC3y!kQ(-NLcFlV5pkELxS8`{_ZEP0<7xPfKJx(g$
zzZa7lm(YKE;X7@OIaOXw6`Q*`l#AWW4(ta3V4q^~*Hg8TYN(@o<yoy6s`7(=inG}f
z;7AvMnz(3}mz~a+056nT0K5QiQt%Fm&+n8@muGUl$PeI<T9PvQTTgNVt7{La@R3>q
zDpP6zPsVV~4vzvE@nJijsQ80?u4B`5+^=vE69<2un;!h&g~&9#AX0fifr`}YYg?c`
zy7=q&DAKjrs9d;ap=`Q;-p4_@@)z8r(4P*cifNwe#z-|hB*CPxJw8GGsZLD@{c#Sc
zJ&N-Y&=LqOh^_N0E58>oK8rS1q`FUeGF8nY@pjtqjlMQ~Q4D2B${T;Gq>1TfNeXsq
z#w1E9+*`i!p_iE{G-KLnIIozei;D|<mmB;T-#tOL5`(TC(YUUxdbHK=T%@Uh2O%}O
zh~OYf-9SAoyvH~3WHQbRTj}x(ox(<P?vpZ^Dg!cI(UV`9fq(y=Mhpj}de)>mTwDG*
zEyQpHz%MX{<EuDF3EY6EMObDFft#a;H{)T~EXU;_ahnSimbFk=^HKeMk9*xlvSFS#
z-S}am$=JO+;uh{awA<J`o0w>(S}*ztnJ@Z$I<jX;60^<ryy;^{(|CJPHQUs~d;ppH
zOl9wIz@$LMNPp{kgf-M;gaOW3(JVbkV;9#03U)cqc6q@&)Cr{FF-SwWg?!@!-c7-{
zsLQ+Q)-)jtJ<Gh6r*>=5ee3X87n^6&d1VFb6>s)VSeGhifc25wYliiq;APqLqIk1o
zFPGr_0jXrds)9zUr4Fr$t|2mVm3j`9Dawy@O+}jihJP%b<e8gugQl0$OcC8M@JEo{
zk8hE59#`(yn<cqSO`!}j3p>Sz{kCGUR;(5sbT;F)L5mEeI(DLhZY$70_<)**KBMD-
zPwYu2`y3*k@|JJN?IW8abH~w(@{`;?drELeLijg#l+NmI-B&BkdB+FDoMflHuiJbA
z<4k#Frhk#VIdN4O<tJnNC;B^ou_Ib*lN2*Yw&XUuLV24-qtraFvvJi+M!q61U82$Z
zxB$A9{bzD@n2zv^a+pVTZg4<J98mGHTx`fI*Rgt8u2zbEr{;b97*6eNRq14h!`X5P
z+a)#h%OYW)?8^uhY^l>jW+n)vPX{sZkJLJzUw;Z-quE1mWZMlCh~~#Y@U754$fe6p
zn1DaT?T8S@dS#aHD&=ytWVzSt_Q*WW+zA@bHR=>QB^?szP%zQ1+<JMg)cgE!CwB(u
z?x9b|ib|;6!vE?vQCseO9^4}NzaOMF8g!kX@jLWuzG=5|Pe!c)YW3NBpU5czTRkva
zC4XaozsSA*L*E4lW3Y+y*J`sO%Z(%9uN<LlTKa3=;h$tW;q}UYo{5DReKy1};4%Ms
zh&gAhF#a8w1xk%ZYtZU;zG+{jUZ>Uhy!_yD5<B|qv-NjW!XNJE$TyI$`NV#U-{3^o
zjiRPzy(-nXih-SAb~l}#qHENY+I>07>VLhLr(P~KD=GQzl1(_jz9z0!N?HXL<58Zb
zD*2}}8X;5tsT#@@%~aD=o>ddSZ@sCihCg?By$y_|^sc~v$Q=jWJkuD{p=y;ahN^)I
zm!1m8g&~}oTx8w=4jWZH884JDxPAw^evkG5c^)odcG{ir2eaE|q3$Tx3y}t^0e|@7
zf^XZ!1o0JLWS-fnHX)qk3TSXE+a52E<E-AjyO(+c>N_KLaD}*CT78<zaEhe%{jF4!
z2d%o)M%`XR?xC-(cAGlb?(f?zxsglW7Xt*w8F%rLmcNf>Nr($yi~R$2*DO3Zey)>a
zq?rWsaJi$I@Deq9IvvqWBGf}I9}+GUHWWvtc?@yWkSAlLOtDrxlj)rs8-pf&4v5!k
z`ud_gsn%quOzo}+aDha+Ku2zZlOLW>1sKF@hAxwHo>YI|3+Pvx{X)u{R^LN!iisxL
z#F{A6>HC_-dqS#cH8j+<pV*qEmA59de33FH&ygzIj^q^MlIQ0pqcv?ld%{oXKTc6=
z^RIU7X<AA+nvAh=Y?t~*{xzFbw5n8W)K^>A)|zLR)%xN^cNUrF@uTuwvP0qMnoh=f
zs-=B=HS~X{sX8NRL4)3d93>v+i+iy7wLR`v=k#$fzmms&tCYf3R?g8rK3eLZmS~DY
zSb8QTO!BG-q?s`3&q}>7f(Yf*leHOMa8v@k056yDVg*>y-t(jR>y+3=>IhQt4jTsx
zRUzutid?$lY0=oUR!$9UH&L@mv?GVS$BeT#sKS3e@NRrl2c5E7|LWYj)ai5w=vMk7
zqsGI1yH%G4as$=dQok?vY27V_5hT@NCDjR;pE*@tP3rGQIUXu*Mdqa(0Spzl`_rGm
zfs|UsZLwIcUHI{pV>UtUL*eKO&{Qa7H>t`in~T9x-@v})I(JC0OiaH?<`EJx$)0Mp
zb9{e~_+qeI7^u7<;?GM9=&#D`_3wz>m0*}`7~w7*^e(|&+2865;LQPV4qYZkOFnvU
zj?-(6xo^nRa$ENQhQ+7c{nkEF<IeW@1SB7?c_QfHHu1r4vEWW){r3e+*RWsNez>D}
zFCJ&VlzvU@TG&#pT=$<T16~sFl7N>4yd-}~^OYm^lCVSJ=nCxAmn=byvr|RJ9mer$
z;@97I&7}-iFw;MQJ(>T4K*=&Z4v0$JCIyCSYssWy!lt{U=S1<P4anxK$!1W!M1nW2
zP$F&{bKM6am}t5w5GllTh=tO?0R?BK;Vm#IA9%CVvzj+}!&<EJ06Y(qzQ8E}mz#eR
zj1z6>u^z{;<+^A(iQO0=pPNoh%co%k-Dd3kQXq68XuuAW9GY^I>+53QMA9P{=r+rA
zOQ%Vcx0ogYZr9YXh@S$KHA^<p*|q{v?BnC4yxL7vG%8*R>6ACM*W(u+4pcU{jo=~O
z-a}em@sO6mL%RK09*}~;_@Klqoalec7!J#<zA~8rPEzt{79P#X3(#G_&5>zN6Tu~a
zCY8#UG@QGNr1u?BkW5tjVMoB(atWl9asEmF^OO$aAh*4^Cu4MZAfWyplmOD1vIfK;
zBr)tfb*(QC2CdHLZ3e9&Qh-wH{=<G3f7o5j0;=FX;rCGmcW##gS6>`CaEO0-cq@A2
zCh>8U$|Q&60!cdC3y1@91%Nl+xnc3A6R_xSD;8_TYSB49A#hJ*{e%)e_$YgtZc!}{
zDcbu?H&m1-sW3daf_NaEBhecs<AsLc0p7I*x`AR@Dy3SVyUhkj0;=}xyrycuey*+V
zi+Dw*>a(hiLMQo%70g@#rFfIRq6Zce2U!cD2s`qk!|gUHku6<s!qOOI^OM`6TmsZe
zlR2X_0`eh~Z=+}dl9Sk@PXeY<lPRQ!0-jitvZO(O(X|&2dU1i<B0cC~TEUU|C`nEb
z_$iO~!rSq}Jeg<jRyF7Vdz&8e1@v$9Y^bl({HucL!643|+&@1!Q+Dl76xK$7?{)LZ
zW;r?$9K}NTU|2eZN#ZzD{f(WFX(?Epe|kqBf_6>SmJ|ogC%ETeC#-7|UNkS|i_0Q6
zHrU309foH)r3uq+9GYCr0(zTRFZ$eKdfBgZ#<2b)>zJV@b*Riub$jCE7taW00oNM{
zKq@<=h!N6AZrziLIugoTs%fbsq&(?ARp_X_y^bM}Dzumtzf$bEO4}SQS?=|^J=E%u
z5=BpVj#ilkT}oa7a4Vov@AJc*+!>&|hyH+nw%J3s@;{`8TyNb;ZB*~J@y5T%^+C6H
z=xrujnkpJ-{eje@wY0V0vF0pw8oyy+Y<TRV+Rv>oa)-R@m=j^LqIy}ns-`9P5=05_
zP9nQ}xa-hulG0&P0#3(ApnIu@UmM5=C(Uiabk#b~waLHctmK)QczdXPt~M*O+&B_{
zfyfc?rlr5;9f3#cldo6)^VBS3qF`mr0gw4tMCTmh!oLH{EUD3G4O-pKH|?v`>tGhL
z{Ghuv2eEZX^Y*^eyFK@b{T9E{=@ot=)vHo1)$*0?o$Etv)14v~?~by1@8zkNOU+73
zO}LYFqg-E;$y}7Q3dqulO07IiRq{`NWi&#j`cpNODVnLKsXVJDe&2diRSkbGalQgR
zM~<!l1Q{|7?_``;w0Fw<6_5UK;3jU#ur<4R8($sIwnZq|Q%?m<wIYjv#KHEkW5`X@
zjTu$}-tj+(i;4JlX~C9qc(w@crSvWx_wD8Y{Pe)zqLZAj7p&gx45U`4j~PaP4&2b0
z=Mw_-7Xy`>8=9G7^n0o{QVn%<k1gq%p(;P<r@+<{CXs8G1TLsJK2q?Pz}ubH0C>TG
zkHhYrKq-cNF>Bq`X3qmX|5UtPt3j5cI>x#&tt3`yR^Bmm9ORV=Z3ZSnm%;PP^XjE9
z-|@Z!YUZImyX=0^IQJvV0A}@nA^5HD-wPz_!ycNdKe6Ew@@%+6`26CcR^({=@uzv%
zJK-H&H|t~``_)jKpb%vToZ!23IJpsTLs2TDSH!|59o0uxu$SmJugeN>W2J}h&hBmi
zUtI8)uE$lj#Lpr_JJW{`dg(me)#M(@`K!03eqSb&Lj>c$5hW$>3nplPur$m}0N@nW
zxkUR3%5e+}MPP9sf-n$CS?qPc(n&yr+g6{^R@k=6rSj#KZH;ohEveJkIHamrD#;R0
z*^>Tyaj{>LXWkFplOXu8{Bhsluhf%~)NUjBzaOObX%uUQpWWWny8m!sVpFw*QwgNO
zpx3HB4CIDbXYaoqsb24Y_8O#OgAiO>{vx+2zl*Rn=Otd%PJihs)_pk<ABXr}7~c2L
z>0)lYyg%L0J)Kpm4JoAnLxOuET)@$J#JVb>H(QX?5AVFdHo^IQx8sp6rW>>+ZheuS
z1^>2lgbEx_hx9rgB6%*DJSz24<Lbgg9@!0GazqJ`_?G1q7=K}Zx{Ob+1TmvWqznIh
zKu2QSG_V)da5BKWJD-?H{nuQXl9Ef3B{rIj$M{*zLfU-xgnzY@qjC7la$u>L@kGHu
zif6)dtPCyP2utfAbxRg9FgZ;Ob+l;BWRi4K+9?gw8vb8lUlo&}YUgK?+&nIVirdSW
zKkduzeqmD-`S<C6*idGwO;w(5f#^DzB7@j3RkO+$<@&1s<6J%~)l>Ca$8B?e;<OHw
z%qG1RWb%oKqd`Iwwb-5IXSs2DlzI6MV@8<XiYXCRlI`UKQ)o3EX)0#cYg9-T29m33
zT8ee5;Vb!!6qB)T6La|)nE}IojLFKIrAoEAXypRB0na>t`=gsTuWJPEw@=VidBRGx
zcd%0^gIJ*i4|#9`SlBHht#9$GRDoX1*rn;s!CKYpoQ!&~KjJ!C{O5e8k*=|;?_j-M
zBMnmCjERMH`N<FR`zE9(imhk4pT#dP%ROw9CUiAXivR5H3x1c?^UhJ#hH}@Ef*82o
z&J+8YFXK9Y2tU9tE7fcOp1~|i0AF12w`dw*w*d7^9qTsjmlx?ICp7Ng?6%w8uOy0T
zCue-3UT?egpGVrSwEUwU@>}VPjB2vnai>NMy5x)dvXc|({zm3^+vsITqQxLkGrz3t
zAbkzIs?A0Ycue{ZN8|zVnoQsR|B$PWHU?-yM1yF5(?z1jWi&&7v-e$KlO68#(PT{C
zdQpD*e?D4J2kjmWi+L%l#bUX3k;tXYjycH=g`-QkjlbUYQ_w{un>-E=J%3QbP0jR8
zbq!2r_m%Z$x0yG>x7FtIUhB_wX>)~0en6lRVQGYoa-;f$i(cFk->0d6fi*CQ@UbPW
z`DhA%Tui#ojQP$Tb@@j}f=1?q&!urI7U6Q1KRaeolyxihSpiMw^Wc`a*=%j9*Zs=s
zA-`$0hXHE!@j2AHy`Eejw0}oZzu&F5B<3)arTVSyFI)QQ{QY=tZyLLfGv)Pl`krq-
zbcnBvzJ(ugF6r7GUB2ZJ3u{g{{EN64>>==fUFL{hVX()(P8tS#2r<|LsxQ*J0@TR|
zdLZNybCF97hJ^r3Bcyk=zxZ(*59^)~cc$8-Zs4yCp~LUN4MO?7ePRU%5=<-0%oT*R
zvPiiYmFgL-d<LGIJmB1?l>zwafxqi4nkb$x*70YVDAtqv_i}yE`XcY(1j8RpCaa8p
zIyoZcyX`ms49GrRo|nn|GjNn9K^`3BpMh&C)9^xiVT$l>NH6rNw5|=2?W8#gB;Ixo
zI0Yo$g1LzPLzQSM%RFAMFUaH67zU)CX0$m(l$#2dl20VpV=IRG>UEE#7#&F7GKNa@
z)JO#?(T|KbKqdMSgP%mmfQ1XNc6TR#OC&204Q{0Y9c)0N35`~>Dfj5C`^8M1fG|LE
zApkm&fCeeZ68h2|ohH-maTy!{0#BE_L2)`?G{X^j{D1bog*j~{TlcT@CVS3-I^Jx+
z&t%H35+D+)2>1d{W>=D%lA*D60um$1aXfS9zu#Uh2_z8WVT8KHIyEQT27$YOd%Yk1
zEzBNgTKa|QTgBKmVHakwbX6%tb+NQg{4J`*g^NyrNXPKwU9V57D#@1%0SA%oI$v=|
zEiN{hv|&`qs;$Gu&r7Atvr~aKzG|pcqpx;JTluqTj7V4g*!iSLSM_lzb-G9?K9tdO
z;u-hO=9;bQ>T!qc70CzY|C1(HzcR<YaQ`cFv$RvNtOuX`q;`9E>}jR1zF&TNbvVF#
ziQ0AVgKqE9(#J!$URMMaOb&0mYENy~RM>f2k%~2MfcYe2x)tJ7>)f^Kph<w+Zo8do
zOZ}LWxvu+v12Mzaka$~k=eO@#t>_kgcl3;h+Y?6X3C(y5p!KYB6-4#&R$eH8E+uYs
z)85gp+GDHfeT$bTWFuqt(n4upEJ4+NP&=Q+izZnJ76fD$Y(?4LMx>|}ki5H{o9l~u
zf2~ximd;E5@kv4Vy{sQN;Z4XnhmtS%*fry2Vp<e`xh2cWU^)G)*~M9&GIaL=83EPC
zYPG@pKxT~NmEa)z4H*N&fnR)Rc)u0t=ot#jTjBDSTa38&hbsh?PiFcj%^$O>jP7;f
z#YqtF<0E{i1es_*s4F{g_rv|6azdyxDEdcMn0>inlHt>ItixYmUpyY0&vb|?994K&
zg)d8gX`rExJnK*<<jglO7P?8>z(>U4DocNOWWWAVL8YFER$~l9*|ASFLwleT81UkE
z6C)=a9VdjjU}!p`@c^cllU****`5FF=aJ)`v})t*N>xSK1=KuQP~yEtjX&O37dIEp
z(-KifhymHyLztu%Hm88lqK%n%_5iloRmI+a#h;@m{t@e9uDrlC6G90f%2YDT<a9Um
z;*8q<MF9WzqbGfqlWfshCRYzg>a#>|Y0lnhZ);oO{F`q4jR1a{2VB0WjSsqo{iZSv
zlNgi^0lEY-jv6yM^ZcU^hc&Z8WN{!)I?4#~Y!hR3L_0(kdN3omgyyJtkba~Mh}u4X
zc4H9W&uv(hl&Ew1$z}mX0K~&_mHVJ10^%L&q!AEL3P3yzZIcs$*>p|x!i1m)4`kg?
z?uaAUBB`6aMHZfz6LWqzUqVo4-@I-KYFR=Im&EAzusjw~CHD(8`Y25m;OVqIZdC-$
zwp$TxF1#c^7k(g;?RG#x3!be_D9_`65zV6He@)$BdV@JG4-eC^>4d@b4ok}Yk#GT-
z5mi*iTq*(X`y=tw&CYIgVV}>^h)AZ9X1m^m@fCG{#Kv6d&+>8bb+eaQ9A4mq_^PVf
zCSCQmrPSD`tA9bl)muQ?s`J$eIkw5~^-g~&&n-Gf@QEb-4pBaJnsxF~?Y?J!q-m+|
z)i$Tl`}eo4PQQ}CKnv6h0&ziVm&3EoGugk}vl4Q0ZWQK#le5<lX;SzS%|REu--OPb
z^>mx3X*3@-kB4qP&K=1Q9_io*i{P|-^CxwqJnws$1~ILnjT$V|R&TLi4t>$A6^YSd
z5Qn_c`a0MZ_a*oX)C6-gmd^Qq0}JHMFuoB$zq}^nE0TWMZ36{#2#!TV?JA<FwlD6s
zXkptIvAncqJF|3YO=-6~F%67FX?0bl{ucpo)MleO&%K88uBkUS?#;e@HJa_Zs@8*v
zgxPU5d*q{1S0QcrLHVSTk9Vzp^R}fDW|Vv&Svb$G+TwX?_8gp5wet^uNM^dTmpk32
z+LO<Mq5a9W{n=}GCH6atnxevf#{=?KDhgmX5fA{D6Umz#dAZj1YbG!;j|ZGQ;SpIT
zIdKasJW9RxV-aEQfb6v=HvKy}+Y&S9yXwA|4mS>5K(=d=`$?e|#4Pp19CC2=8K@h3
z&CrSiws?)%0Jm1eBR8La&Y>VK&Ip#TDz&!_k<LT}{}lCCLgbAZ58`UJ$_mXE>&Ts-
zSLz9kdfthLx13HUbaft$i7e!bHzqC;BN`4g98ww%j!NYEzQ0c`lLfjHh=75A$$x2+
z>EGRSMrH9$Aux+D-IW+u92r(bsc}}SCpw5V3Ft$fdUuVJWJYy=g(o-1ifAacKBFf4
zU+sg^WxuFh@=1l)R;Ny-Dlm~Zdsm<cyO56JLD+>uoivsqundu~UWq&ufkXhy5XaYu
zxtuVHnAH+`f8nO^K{vi@eO(_BZER6(_%a)CaRT#Jj0c#O;ym(cD=?GA&(_<sku=(1
zU|K^RW*9N`2=mf^nL5;&t#F59pho2X;#id#ug$|JuiJFiXsX-(aFvl|PRJt_VZt5`
z<2_i`dKF4{k!O61T%U-6*p0W35_7T`4`{|qem0<M$oU{WllnT2UgF=hH}bj)L}0-@
zq&q)Vy)ov3ESJ}Utck35O+`>{r)Q-!CqrdgxgiX6%Dahw^eN^o^xlmF-acQyyKQ-h
zF141@>#4nWGh`ODNhzly&GA^9+Ps5|<Pw0?*gBstv(^-dw3yF7pb%j`UxIu-hMI|q
zzzjy2{6_KsaEn$wJ!-6wUJgL&rkbO2?FFw>3aGdGDfe5Bg1WGcYW0HY6cM)}dw4EH
zeE#-=e-T=Lg#A+(<8!g&bH7Q=3&?p}_|irAug2nBqj}7}^xaFbBtNzWJJJ<PY1fy(
zd9~%);OqSscL0YCjjQAUvGd0Fy=xwfaWTfl7#CyQ)IA!Eag!4P9^+zFDf^JaTlJ{T
zy0@;6BPy0E=OJSQp+#Ny7{<Z4T!&^Q%PI$3p|eeYUO~JDAw*cR=F4qq4GnjGw<YiL
z1%zQ3>6P=7>%nP%@I-7JRlh8kFGNDFxMBM#w<PlnZdMPHg!Lw$;onbOy1^d8gt$^K
zuS#k{8{0qf>YGh=DBfmn+-aVG<ZM1UUQNcG#%cb@I?ZN7Ot;>sxb&Q<W+~Y*^U8w-
zss;dmtlv}8R!H8Vw&TO83xM!~4ZoW&^VLI0Ld7IEOY>xmb=ygOzRIhUFAL+Hk+XMa
znG`u3laIUdzhnZ8$wyQ@*)Xoc&u&I$I2bcTYqc#0JV$$LJ+<F&w%@~?jrLuu#an^q
zSu^(PSDrTenk$k{x2|@fcPA4zVfiRzwb`G4n~ZJ%a&~*$f^jj>h~-Unt~YzPE#<F;
zJ6`Mi#n8<?I$MJyp^=An=b<>faO~Cz9PbgQglyAt{H|ijT!@H-fUMpE+IBhhGN!OL
z+&HGN_Pq^EVWCU73kP5d3z=cY3`u0GibQGMeo(4vzgbgSUV;kKA;_w)*ISqlK^}U4
z4G&>2xqE&YjjoWq?ztfZw8szu2<VW<0v&R^u%;O=6Vsw3KZ6@5wqr5E6##>LesNy6
zXP-eH%d!f=D+(e~CH{(nQo6XjPy|}<4qe9H9>Rpk)D?w9sg%kUg)Go)qbmx+l5WKy
zm6`I+4)w(D>&N@giFzb3sbWYCKI<lbzX06C|FhT+kKpdv<_QFkOL;e+Kd9|MN9GJQ
zF3_5J!ZYR`;#ynzsKVyw5#<ulT(i!8;~DdQhkvxCus0SyAO38Sgy839Umi%MFKUYj
zh`7a%L_dn4aIexR0`V?_!X4_Q5flzV;f~u=iRPC%8hYAxf3H_Rep|Dh+T~e)<MLF%
z?c6Yj?EAo+Ouv_hdGC(!+B>&_CbiJiyx(V`3e)O_!+w^{xx?rw(>e*GCmEuruZ9=@
ze7se<eg1Z*;l@AFfZek@a;qQlW^S=ytKMuhSm@O5zuWNd1mdI36=YPj6}dzKdY>>F
z!)OenF^tAi_k1uKOHKr^LCQUUH*9x2DspF3nn$LU*H1yym*0haPaIsIZNlrL)UWF9
zs*>h)?6h~V?aJLkyMFp@VRn+Nl<@l*gS`cr!3ShyT9u%bonCWl@vHlpA{nC@-&D2;
zw=&Yi1UO!;6M;kVN-#3Rj2U*g5-g?hnU@6u_m^+GYL8bU+I({|J9sO9l7ox8J|We+
z{v1V+_pEs%PvJgyr_W#NDIe8$(iNIE#29=hEy&<vO-f2ZT=D{CrN-)lAIgoOawDzv
z%0OTg&1KO-#O#pD43QGBRML|T>?_#TPFp2+Jr5HFUQqtQ-s!5IFQP!DR$4(rke1yK
z5Kc>~;2{og6yaNtyI!k*O|YJc<VJF5mE8JhW(!uiJA4DBic(Irv2523Krk_;56l4a
z)?8VAwj|mIJfdwOc<Gwe?c4DJ0w)Mf!5=do{MV@Q$NTDz9J`X>r<Jo>Ez#ky``z%0
zoT%%TZoPZ)VoXq>hF%nKCwt8i%x_U=fM$QwjlV^sIK+Y;WkMb^=ak%FtMcs1ovjh-
z5(@jWX>;mcyUq9REMN?dy#mM#(pu*&8wlbQqgb)t{v2vKTL%`CR=X*G4QBs$KR-!5
zk4#pO@QPF-%jA(5l9&IBi102$>;hbzya%&4ynDgi&t$2#35tLE;$OZjl~KDVq}>IX
zVT)DbgI0;*%A)7;MMIF`{paK23Cu|%GsOD}dzo_>#3^fc`rP+$ug@$xS^~<5*K9m5
zo%*{;+ezGYLthOy{}@ewE1*xU)9y9vYS&GDwL5<H%TJ2cyUORb2p!6(9x#iP2_F=*
zNSP)tW|2IGctfgB&I-%(tdGY4xHmA3pVWdWJ&x(7Demdmd?tBFTNvgu<|a^QWSYFM
z>Y+Uxd3PJf^@dP((&iL^n6)u+ldi<%GehA9qiNa50NG}M!6O}i{2<OFC5GlaSrn+x
zDUXWX^UWn;fZdntGJ<X}AAo(KEB5fKNgf`)Pqi_1oNy5a&Ai}dK7p(g*Ar_7QVQzD
zL~qNDp83oW*jj$5IU2d11Nxs3?hnKLeNQxEdc;J}7Yxm(`!OwQcG25QQ5!Qip1Jkc
zq~E<$C8Oxg2@JS@g~2SEZg^YZBPDpjn7t61F0pEJd}~*g+S`UmTaylF@As)?GUl4>
zXd<eOvnzG}qVy(WkNuA;Er@q!a_j=QLSn4nW%GCK&0dJ#k#sZ?qTZaj9)v{MU~89u
z12%Z$=luMLz>5W5l@8dWl9!G}jz-DPTxYj3C7B<yqCF3P0)%-OoS$gF)>txm$tD@W
z+#tZ3c>aH(@MX4>Z|PeyOjjgFc&thsS#A=l5}2PnUV&%fqvrR?`XLw}v6=JdNqC?W
zvnOKb;?*z9<%=_sHpuo<ZpmhU<Dos2(#7S4vLJ6)>g82Qm}n@lnO&~bcsmqtGdCbC
z1@f#rkLHtq<9SJLpP~69>og<xbf7)iR8G>xKn2B?UgV?R%|J7tiRhk^wnFj_wV}&M
zyElJQY($jBuiJ_63wZ*6Om(^;=zd4nf^Xt_s?bjX-4$%pHqmVU^T4#&!&B23GS(vZ
z<}Xn?F0lPJIj$G~dJhsmG2(A=b7!s{XE${|C_PetRn<0N*N}dPeC4sv*W6d5|KYB8
z!tqbs`yd~bPcUJ?9d$!$omQvYgKHyvAWo;-ReQIcb{*hvs-1t_wmN+wE{-7MC8~9@
zcu@qiEy?i=zmQJcE6NLb7Z>J}XW&?X`Txj+MW^HZRC63^K_gD?|Gvq26H9p(rm%R6
z`Jmf>->*;ScgkuGVTa;jGuYtYjm>B8d~8RvSV;4-fF~K)b<)x29`_s%iH7-|)HZ|p
z9n9}weg}O{p@W@)<_Efdth3150xenXb~{~|mzv{k#Pi-^?f|Ff!R)r1h!FDQU9S&l
z0r_%)DCC3s52dcwnje*x8zhoXYOUYtroGXBz1~}pZnj>puXH&yZS^nm=7cb+<SPY_
zK5Z7y_sIL^C$$Z4`!2Zq)M<gSZeM+`c6<B<o&e}~*;mDylS<|MOr$DkM#wnN&U{e2
zDro}J$~#aj{*YV!pHJ@0PB%tcniaW!^LXAkRn_{Dm`8zF!FJ4P-Zy`cNV#74zff|2
zg;`85-?HMd=+sTlXD#+Om<3bn^(GA4{Azqwx^1Qd<{u2EpdnZ&vk&)j@D1=a@4UYf
zmE&1d9kp|%nkbm$wkOSmhy_JO;<e^)vih*_)GFo1>3K3++HW4>`kLql;jC2vR0if?
zYxiRcKWf7vvFT50X=5^>wyix-o4xOUJX310&+T2WVA@U~5x}m<Op_OS;EWfp{I!7h
z5R+i#YVG_~Elbd5pV}T?*_mpX)`S_J7xn|BIUpqmu%08`&_!rU#WY890*M9vqa}Xa
zi162y`fHtbUum{`OqpP5KKuFuOY_Jx=;J3FOQ%BK*W7eMNUkF#{+#gcbtWW#{81?N
z6~iFJ7yyCyAQBY#S0Znoo?TwPjiht<9ZqNOB{AuoW%-WIcrl^(@2qN$R{pZwY$whR
z)(<$B>gc}}poZd|{+Rw`K^GDs!G(WYE+&f3BHpcX*-qNyX|b7DwnOy_EsOP28MrtT
z>Ke`DAvE*`vBrYCxh~e&-Uk(bVSo@8tC09vBmTTVAe{x<WX@gt?qgN$l1_uvT1u~{
z_Slb^uzUjBy66SF*Nj_tAKTe*Jt7d_#aVL3qVf}xlak-(+Naz34629eq-M_-1EpPG
z)KTuGeEys_$uismpmbHDv|2>{KX*#22$%c)&-f!Bm43h5tlsruBxJ3B*6H#K{qSuq
z^^@A--;Lsj;<swyOwt5C))O?{37>GAX_SE8=y28HD=+{0zKo2$LG#N#OC}hxBu2@!
zdZV^t54=Kk08YTEwOJPQwYAaNJqPc`+M2=)Y_f5FTD}lUoBc*s&I|}FSa4F!^6(o$
zEDJ}36>i$~bykXe5}v+)e-+xR?9BYmZw0`XLXnY9WaJ}_T!th<QsOpnadlpPdn(cf
zq#JH1L=Pqgnu*c(Xt0Vnn<<xN<x-&IkG3$MKd5ceQrqwQAN(}AWNKp_%G)g0-|WTO
zQb`E*vC{?AVWka-BF#D26t7_T3C4K^q+IZ_ja^bHNaudfYb-N=E9@70pgcW$kx66W
z-B)8q^Z*3V?6oE6A1=c4?{)uH;FAm8ssTShNE9K74VOS1hbTgaI%z}^+6#)%#ntKC
zv(t_BO89ZESG<>2=@q<`l$Z$2z`xgIVArNL#Mx8kQ-1=jFeat9NhqDT=D<)oG17CI
zdto&ETpU>}D0NwXAutnw8P{d*h!?~Q^{iY=)KS<Gc$PT~-WUMIZfFAs29vteh-K3(
z8$=6{vbpf0#Il*~k!v7vzt3gRmC_MsSyw{mZ=R5`cF(*35Y)AWi{v-FU$=K?5&)%#
z`2n+CE+O(3NymAl*0XL?7RXQ*6d2@D`$=Kk2{p>@e=6jE`H}g<^E1_LGRWundk_t|
zKkO=3=q*bjSLiJZOdJVr!$!Et=p}D|ma<vm&Aep)=Xkk8=U4>ic*|5e8gYTh8*VWT
zEO;GZ1=_<(KO}B%^~-YkLZk^GH_Xc@x8x^Bv=3GIcWklu;9J`jwaPKckZNNW?Am79
zXzb0pj@#XT<z=~kC4Ob3NIq+p!S?hnmR8<w!n6`@i#N-`hdXM0tl4(aw1#vRr(}S1
z#G(`PCmqfP1e)_8o=;%so)?SKApyX7ckXSmf1f6X8_{Qg?y(fl0;$1Hg!TdM27Dmo
zlwH<KZ>y31SNI+5!4uC|ZXPGa@$*%stg4GedI5ufLJai;p#);?XIa(YZNg);Bnwq9
z%`OfeHH)b!YR#rGhD%^yxTh4=#Q;~6($i_XMr;=48*<&?xzp>P0{KZnXCJ+6WID^0
z@#K(HDk-8WxGFG@NgBzXAxcNZU?7*6fG%2Ei%x@g9b(xX9q&xD20GrECa-t(gwL;W
zWk1w^2ux1MU@rG3BsbFvxx&H~=3``yBk?;ISt7s%5LSg6reGpY-muK)e1Nrra~sxu
z8UL1Tz97`99y3)}P_Juc$K#6a%C)_?ft%56<QIYT5iyPN3;V}&LE(;<!LK_xj-}r}
zIdlmAD%A#$ggYeO6rKnOJakyIX|}R|`(ONjh#`0-zS*vIx?Q!_Z~aA-UawPYDu8v{
z?+{g~eQ;5}!xylZZeDv=vfuH$sP5fqPXkW0>2&;(Z``%vqBMUCKD=xO<}ZddO!%fX
z`vZ8@`gK}DAb!NF)`vQ2ylTCdtJWAaOPmt{8Ap7nOw;()b9a#VvWFMTSk@79Y?vQ^
zF!yF>zA!we8+tM1+MaMYrADXK>U;(O_xV7HdZ*tDdc1+$d(dU|afUu9pHxy+)pl^8
zgp1bHQ~g;w!S72IeF!;QFtwHjK0CTQ61zJ{UjDC}@9_RwsZ=eUm;B?Cg6?}+KX3xv
z#`n`dx=pk|Lz3G~3I${cqzngs;mu2b`e@{4X{m5|TI4OUcxwUjf^(RQc;WV?xGiyv
ziCnja{J~Q!$WcB;B&ka7+M`6-Nt;thpKD{r0uTImu2I}Nk)1vRlrKx=)Kv;5Ur@*;
zrLo&(Hl}{4;+4Yr%b7tm@(bL2S7m<H(eN}F89|r#+*|S=(ZS{gjw1F7Rg!&wz`N=0
z>uXX;u@88+MXx7J1(i{TmC+WHuI4>T3kf&QCSPV+?HdQxl(0Sgky>;a5(_+Z`p+lk
zjXCBl*kex`4l$l4_munU*pwrYpzO*YEBQSFw=hyHm|28N{uW~=-3lNA*iVe{oJY+8
zUJC?-N6%2(t2Bw+!0mx)mpCea&Oy4UTvlqO1=hf;+KH1DOHA39+Py*6MzY7-%d^v~
zd0$b06T<Ht6(@LG7l;#>lYDRfDr)2DqegzaCZ)p+WeXfc1i??wN);6SWC$)m(+m<n
zE#hz2G?QC))=o$cnqk7NG|^2n2WXaA8>{T2cLsS`Tt${fo6Le^vrhki9hJ8u5PM)p
zOs1tjW;nn+#T#Q7WV?d8S!S2t$>qB)cr6zn&7nSmxA=^W<xg*x6eh3DqzfR7m`+Gc
z825;~4;#d!-|;5>&}61yGUJ31q008L=vb(---yP-m4KRLp{mUkXfDQ0fn6%4i^~f|
z{9SIahcNlB)XS@qn$X67JT1S+Mxi&G>`=VT+yK`Vc)zas#9ThDXiYq?(fpBhn$61A
zQj%^R7;AQ@cQeooXg9d0q^*#=Lv6s6(eBNk6oU$ERl!!3{fep_47L3-F-pZK6{S=P
zDP3qviN?9bl;e@J31WX7E%D><Dz|8Gm@AqYB*+J)M_Ow8eg6Z0pn-i<6+jFl{SQi?
zD6JOna;P^O4Ydmodu<8O4iVibv6p@Y{ym?Y?631C30Oi;`KY=*78`X~pd!q?B1E(h
z<e6=F2Eoy?Ph*7N6(*3G`4=DWdVNw=NxobN_!(@^`AUJCcJD=i-{r}QcDM7nw`$|C
z?ekLU(i?0p#<o{~WZRWSU+t2%@@LZ+k*@l&^GT7dgm>do>U5D(@SE=oGTwz>-JY2h
z?Xxa;a*=#sZX{`POlI!P`Cpj}4|CxKTKL>2wcER6&nkWOeZ;dXeM<PO(yn_Sbn%^+
zJ|04_U*VZD8NBVPJ+)m^Vc%^<D#pA4){~6oRtQtAbJwbWgB}51yX|(WE%jp#ak95R
z;($39h*1$I`*0c^ak3F7J5jw7c_;#j0Jg~=0Ti1%I}AF!U3ZW7buZO7QMNNC+l!^N
z&~WB$?s6Xm6Sybk{WXM+b-!*0=vXu;nbs-WAxF6Auh+AQl{2N28HhmhM{pe88p+{6
zbsjkGMEgO1y`gw4q;h|#oDk{^iln*xP3HJ%VsH>uw_k}P-g)QytdGaw(GM)DIdph3
z>*>{vfj*?hfU@uXHK0>xwnm<zvZr*Qk90aDd7D!94P*1UQ1ppam~G53$?$19*5Ok&
zG^?A>bckjH&BlV+cm*o77~Uv?0w-rV1c)?KCNF}2ey7d=<<m2i?}X&rzh0Q0XXh?_
z*A8hvLmt2WYUAumP3or1*6W9EI2MC>uTkTV_tnL1t!-u(oi6^s@!!a-h><}OEpADk
z)$>?FcINUqYZe?8c|=y^F6-h$t2WEGTB;~zA#Z@d)?Bm9n`)$`Sc<6wcja5JSb$bL
zjBJvBqp$FH5s>(a5kERFxa~Cro{j5tQye$&DxDm29evT1#_bZ#XIi96o$<CMM(D~8
zM-J`Xs<mdLsn$vLt{-)|r`aRzyH<<j+Z`C@OKw}8eqXH@66l;m1uYP(Wc7j!=CYFG
z84+^TB9Ir0TOLCMdm^arJi?#i#so^ErbDKGW7Bcyus~q;*kZdxerh_<ETN6ZFI$Y=
zMID1oxX6Ud6;_`c6=1ztxiueGZ>3Y_!~*-7Mj-qE&gIuWLgNd+oxmF={0(u~(b69t
z*{^>fwt7PLPan;^qq#Z~DUF33kT3^C2k)*M%`M-XKzEQs*nfvaGhUvx7gQ5R!z2QK
zxyj<t8r>C-J3OCxNr?pg=TJK$^JZpFwbwr~)a~h5d&#b6Edt*jL0{5DGqeXffzbv&
zBM8!Qh*_LNc(EZur?B+b6dl}#d9MWlhV_l0-TT_RwCcSM54uDAz(>0#rDCaap7yS>
zP>uo_q%RiAkC^yd)WpN%1pNwLk9-Dy$dsPiCkjjhoTtIAXO%q{sIT5(4?`KS({BAm
zZaaPU>88>mox6UksdnA>>&@P6OZiK!7rq*Et?DUNC=xcBt=4RuAzM$ER6G3-?4N(A
zJ&vdn{l$g!p^9E(ZCoRA?O`x|w-`-^JPE|#I3Z^)5{#QlTyeA?o9{pL2~eYds*+F5
zUbEVQ`HnT^jvcMi`q=69Nx!SKd$$TeDavN1uwtBjGldmn<e6=F1}nxHCT|?#@UaD%
z_dJPLwDo^cc)KK<@x~Z4+lm-*PgC9v25+fo@zp)pA&pH+{CKLJZe4ZHMK=D5CS0bd
zR`BC%CQCgoG=0X)jBl9)d1BIk`3#-TUPuihmklx!KI$NxIS>KbRA!4U5YZ9Z^iU^_
z(56yAn_|qKoCs`^lBrkTHfo!bH}tp!<lTAlYC2?o)u7lnOba;lVWCgi|3|2`?sN&y
zV?F^-J`iN24K$JfRTx*9<l*7_R2x&r3E%rXUYoH?ypy8s*clUOlqbJ`t;PLtcOfI}
z%wcq85{vgVPR2+gIZ{&s$@S`0S*f0GqaHC-f9;BZgggXa#fa<ZD9%1s7<CV}_{sRr
zvGF-_F^iSA-ftFke#9o_+0Pm?kR8IsTLyUq5;9X9>-w7Hep0xs3i-a5DK$s%k{83V
z*M{cP{g@UtyXd`E^nj;-y}9-N>@xOinb*yD$&CFLDxt+4Sox|_d)pA{Y<A6bFXS<#
zH=lcy9Wkvdiz_l>U~hh(S|$_J$*fr<Q}!h`ah?s!6870s-{RIOob_8=SvnY)#!qU&
zoCI!RY&Y5SDzs|vZ2T9cpKp+iDwbSiFmoc_t^)ID;<qMCj0(|z={)b8pJ=}@0f}OC
z5HXTWfLKgvXhmz{MTUjK7Zel}6cm(YLHQP1@SkaTwHZz@NY2uDhiQAET1kEEhr)iS
z)Rqf$c7boOCn}xO$DXK=VyRx8HI!|N{=vslu?VJE42=w_LwS{rXp9UNotW%tk{_A|
z075={ec1MsMFDVs3((jGre(2@1V_+sh{YDoJoVG_k_(}>FXoX8i)~l8wDk6}tR|&1
z_qF<!s>hv!T;yFFUKdA-s&#)Vb&$__NQq!AawoB^D3=$NN;!2B26aNXPjY^te4!!C
z|3{2PFWm_ioO4!>a+Kjv#bn%YP$U5lq&WR<zt^^QwEL=mQm&sLv)x~7;Kk!vW*s!+
zh2PWEmTFi6t&@*ekW3*&%h#9H4uo7@V94Q<O;>*_L^wd<MSJQjkwqt{ualD79g{8V
z<yMw+=7SU-DGQs$J@4qH>xS)6ZOHMpfgVNO(AOv-;fTUEK?a)5a~1}DCT)S4UX$%?
z+hkx-7UK?o$^8o%n-5<1ovoXOY`jkL27Msx6HnBR?GUY<pQ>dE-0`&b@ajyYX-)X>
z2g8IBSbXXgcQZp9`ZJZ3G=jV(vP_`stAWIk`0^5e3m|c(66$JSt@YJ9;Oo^ot-Fuy
z9^mZp&#dXE#2D!S)I5Q9`>{5);p$e7aO5S@X}CUrBuDD2b-G=(ciU;#0no48`P?I2
z^<(D~qiMdUs^oWGeWJsnWj+^;d;Xc=YxYJXo>3#nZ4S}^>^>CF;2^t%8E9l$dA$NK
zV&!+<_)|0u>6bb-fs;sD28FnUg>?a}3t(L!Gm7ntUilVV8{o^XU6jgo;W;Wcb(H8H
zvYR@8s$89(qs>W1_Eu&O(pDn7f^IS1<>9c>BK13!^gCXf1F{@JB?(F=7+cGxG(`yF
zQ81IE?`A=KoVBTzPHPpR@O<J^4$?`Wa0wC&&XS|{2G_+f|8?ZFzEID~wfW=#q%}W3
zVaOT_F&3hrIE#)2?2NPis&aOAA=C_xCqw^#=hM0(qi_UZ%O2~tlOJo->G;L}&p`70
zoOs8)2BSob1@AQxCms#H6;4Rwu1&7p%+Kt%=SO|;7(enyJax0HKaNJ+`|ANCE;!>x
z8_9(}h-i~35W_Cp6v2;u;e-V~S}#)HRV7r;%ctc%U9u8LCG7E%m3%6}-oS^hR!%Q}
z#k!wq-7{tqSkPovVPS|{vCj2mlOlx#PQ5^DlQA2>_#g~f7g(~G3hRW}j>T+S+^SDY
zm3qy!n1ZZ&Nfn5F$co+2U~~jbk%+{c7ig_(F6?`*Rw4`D?*&l7ZzXP$1(M~k1wLhd
zLG_*vH1>mw;AC3*KpT6#1~v;kY=?$_UA8~d%tc`OVgEW!ALUoHNei?{DgB)S`R$sN
zc5Zw=Q%mLZ(q81pudPcTxC=yG8DpBQ3UYYXuUqt&rP&n%EiNkoq&TbF3G~*}@rmqW
z*JkR^O1BNSGdYX6kpkd?8m8l7gU+iGzUv5(pkvK(*k2#8)oZ#zCMNrs@ie)Ahe=Eh
zGc^u>YGhiI1q5R>h+ET1V1o!qF=!O=ywNZoa?xlQkJ>QqG1DTFVceeE0)|m^k>%Ia
zPq(<Hj!k5TNZIV1t=XpxX69y+B)*<^cb)u3yf)upVSVj2YZT11==McF{r0Uu{-zs$
z^C`!jVkDUWB=e}VYSpl2;jYDhFASeV>$QXRIz20))0hx}lfg<v;>$q%XcM>vfMT<q
zx>ej27r{kA$b`=VP!8yv2;rOr5j0Ln|AR^zomQ*!8FD<emeT8~J@P^Mq>_5Gr`+DE
zN>{BDrO{Wrq^&G&SfIWA*)&GkkQE;cCEslLNGNwb7ylK=b=&InfpE=#o=Y$Cx!G!w
zcBfCOoM^W#<uA2fc*pS|U6fc1@Z<~dzOcYtuI-E&uXU+0q!u0CY7ew(QSAr+)QXKF
zTVmb;Nwz=J{;lwB35;5RJGB@dB}aClfTOV*AO+6|fR&RS*efJ2-ds3V@?yJ0!nEER
z)gN8$15ibbb_ukgi4j<TAEB6~PX&N`Cf6*A<##mQvGF<VRLZpIMQ5G@4B*AOWBQXt
zxtM<voN(@#h%ZPjiKvAMUUALgx578_#%sIh4fNW*O$`TfzDzZ;Facg0JZhFUfbRRB
z6d*>>U~YZh@)*njkU_vo#`Yi9FU#eNgdjwA+XHj*%B@VOQ1V}Y+GP57B1o*hYvYlJ
zpDde(^aNn6*>=&ihSd5Jl#If+LV^6(gY$P%F8rHmQO6`hs*T;TNj6hUW9sVB^z|hc
z%o1mkKrCU=X;62bp!5nVy;zpav}B4<mPGO%)?dQ-Qf&J0pwqLHBMw-Z*X-gf!o>W5
zF!b~C1K_<tvW;AS`zf4YSSW%8#ky6dEhv&VG4f(rQUof@%92CV!J_0b2;K_U)h9oe
zB&G9cu_TElNhH}`Od*yecRBAY&v6QrBxg1f3z9+<Bso16^X!4DSbAID6`gQp6|YE|
zBNDk^aNgT@trp4iA(=$CiTz?bbfP&h24ei;sw~Z>L=Sv_h5xa_2{BAwG@5butYj2@
zzUgZ_EGQ<YD;DE`Z8!rfTA5}ItY~GKyjanakv>?_l7)UND_ZlY0_#~>R<L;06=@wq
z>sOJreCiGdN3v#LggsiPMP9FsQ<jw9&dYTZG8AS*Xk<toI&gH;ATTU~r-!vMVW!)l
z#4w*BZ*xz7iTz|zVvgK|i)mTxBVND^ueyz&J2FuDvT|ABp-%>tg`02i{u<j+>Ad7q
zXFa{vc#SK$uGT24>_jEq*2;k34QW{Bgbz0HMz$Yv{87SNz5(fjO~J$@@~Hg;>2s&C
zv=zesa$9S!7qHsf^K$9!f`FY}RO%N>lz@dF=K|(`y|hNamSsu?ZR{O{V9Sl+_rtLF
z{#OpBWpVA2kmAN<Ce9)}3}+lJJ_T;<9o0gGS<*@a)q;Nz0+JTW^<1RbQJdB<nkzS<
z_%RVzL^^6?i)zCc!o6zk-uy{XXtOJ{VE%R$+|pI0R9^~_s+F1=Db3->k21#g4o6r{
zu<5{m1cor5*uM}gd%Qq=kDoBdX{uQo1mVaauVxTDH9R#`MiI)0MQJ~@2k7zK)EG6+
z%Z;nZJic_K4xOO*q~3YCK+@5Z@g0c0<`Po1adxGuqHJK-te{2zS@b#kdQH|rT#^mK
zv`SnWMSy)i5^2mmJWR)?Q^~F3%>Zr1TYj{E&@FV`Y{{<ScaDTzWg%_u!P1B7FUQgr
zC&5KiS-rR@N0xuX?{F%6FJ+j@{y7M*lopkdfNLDn$wEMk2aKv+lmnDe=<R_vrgoS$
zBMF@K!2gJ-9xdgC<-Tb_mcBu&#8|&)A)oUYZyT}^-bTc<StFFV3Uh;&HcXKUr78D+
zP_NlOO^Q6Qh6gq|H9Vlj15zuDgCcAF;dk(WHj_I%HPlu+pW0dF{OT$)&lP^>NKCq5
zD|4;)MAHp2Fozp7S49fJ>Z>g1wh4We<%Ln*w1&E&In*W=9hg6<^+NIki?UaUZs^5(
z^H-5Sbm9Vqz>fqva3<zZAHBHu33l~=_Y+_ox$e8|dQ(N3+t~}lTO>e9+F(=CfG2IR
zmram86PMkTbJ@qG^q=*i^T-tcWHNnnc=O~2HzBgl0-=)?p9OYaE@%YX=~81zEjql_
z9%z+Jonw$>ZLqD|Hm7adwry+LwpndV+qP}n*0gO*+s5thoO>f~)Q?>iv42(U*zd~B
zXRTa&Blu96f&b+_gO83VVDbdwG<NF{J_*TNcvUYno=radF%(ev4Yq=G#j*j2O|kp<
zEntN(A1bo%x1Ym3n}mJX&zxK00!1r&iTZRW=-n`gEWRtc%bRq6FU_a)-n*i#cm~AX
zsl38CIJCzRq|_HX3F{xl=LI4$3qg+y#IV+8fi@(DgqMyn^zLR72$XJO{z;S1vfB`T
z4&{&+5??IGn{<boYN`9c=5z^YSirnqVmPwj*}wP{J8Dp-5o3w)KXDMY3FG2LY%${C
ztilIsdF*trsPgNZQG1KL6T3u9?v8T$@K)5<OJBYyu&cxLwHVtap}BV@l1GYSujF&X
zA8zwLQJPHXApWo`&pr9d?i?3w)T}C0c&Inaj@v^$1!B&}V5F5+1n~vX7ceKEuoX_a
zz*RgYM=_fkj09Sngo8z;2e+)`CzfmZcu-<bxjtRk#e7bj=6}-;u(j^jY#oguJ2?;a
z`7q-O%>LnZki~B|+jt9#&$Vh1k$H`a;UtQOYq;6}j&s@zR(yKAkfF_NxPCdRD_eXQ
z|Ax5ibiYD;6}}2SE$jl=VMdqUa@N9!g$(pMw|~#o%Y9G#y(<_xLVOx3upp-rNTXhS
z|BE|mCj!}}j92XcRbPrX<%yrTA8nS%C$+D;UB>mw&!(k2ZEPjL;wDVK?gDdr@c5w>
zy}q+GrTm5Iy%j5_Fb4a6c{G&C!T~7kLsdFP^tbg_e9Q?W5YqvCW9ra+bYjd1(eB&K
z6@hF7XOA~k@4hL7v*++TvH#}T6JSy1vr@@ZEAARk3X?=v)u5L?!L;~s2GUg$`k6nz
zE4D&nxSl>onDH`k1W@6az2N!+jh|=nY`zBHl$kUy`!CR@QvZPzB204-Y8*)8S2P|&
zl+nlQ-}WdFO!WsuVo0DUyn%1Ra;P<+4CHp*+ow6NsGEUaVU*m@IE_r19wV^EO?#u2
zr(i4@De&KZ?dxGOh>G0Kn2OR<j@?n!e~i3QZJtJxa^psPHFB4u><x$MzMDKHN#+dG
z;M`k0j+cn^5*|a;wd?>(vyRgBtj9d{>28=$;1fgeLWTg;FSmW}1W>q}1ekFCtj$$B
zGtzSIrq61a8;ZYq;K~oc$#OeoyKa+xmu(1?seJ%n3|@H7Fgk|-Qq6J<4_R7ROc0YJ
zPi|x7nhY-LOPKyBkv5o_zqY?s^c(VoSsu;i5v&5*vG;;zeT_Qka8(;0Zzp+UWxijF
z8hd51P(1*REgujkoZULg6PTD%z*}7vep4F_O7E9d@`nq4`-j1TgiK_lzojWXVlfK0
znlP@#JAsL9@B!fbO%`MG?dS&Gy%PoANLs2NHH<?_NVbnOTS3E3jj;7d){UkEnq`C|
zAk^Mm$$Z;YNx9&{k=tY-5#q{(fD!H}A~l2=<39jy#`xe^vF3UTg5z*S+nX^x;w{47
zuQ@-VzoT>6s0DJeC2Cw#KtFjveaVl+mYE3e1x%mi^@JJ({bMIprana!&A-|dY6~Wu
zivIk0rvzbZ+WC16cuZf{=_dxu=VaOtI5GmwPTwm()+mf(nf3Qp`n<a}_+0;*h`4Wc
z-f;$evGDkSe?<9s$ol*Wo2$oz^wj}-a7f>v;cM>|9C)X|6Hyz8{2h2b(%O-PU99ap
z^LrojH_{y_deGsYJ+(N3(^Gu0uAmuu<_T?aAz4^fdf-q&S=hsHbX+Du*+`P30MYkf
z&u}jB$8@h68Ad}*g84jgb9g`t1%YW8PBt`vHou`JN%?V^(m<ooKZNVU`ojqG^uZgG
zw|RQ1NSDHt6~j5a+uu?}fuR<dmJL4-8}vN=Ut;6}y`OsnputPY)VfH~*TEHLe}3~f
zK@{kThBbw|-NLvM=RS!vMfzr-h<5f_aO4l$N4OzJ^&(vwDUdVcJ$_AquodWCPy))m
z%jio^5iiQwvE%!NQWfoHR_z~o*8^O(gv>k!QtO?j-3%^Uk}=9%&(0DODW*&87wuXX
z$fns1@y}1kfAw{%&hpLpvh8n@JYE#Ir7X%0leSDyE!nJaU+OomXlyk1N@|f)SOw7?
zKV73`bI7YT$G{+9r0)i{x`x?|W&z#|=};GIM;-^h`>nYIMoix#C+{alACS{VwdOy5
z7C&J;9k22@he#xOb+6gGts6xDR5x2Ty}1A+{@t*7#F-tbbl<krAbbB>&y!E|ujbow
zpJKNfRh4IoKnnPPF%Ml6A`=nI$3;1Hf|P3Q;!RFI1v8m<IkJW}WezCwY5<4{^mTp)
z<8gQ19G;cRv7z<<Wg{phIe(6#3ullQwNp3Pe{QuWQH^(&LB0lHsKGmXvJP5wG?<UB
zvV_h(YIVuLj}J;YAL8u)%6<gFtW`4UtJ4bKYRoZO=`jjng1-!%4ailiwiPcjwDh#)
z!9#V%KkAS&+~;0}7C@u)@1m+Q563!L@AW>}`nl)#!9jN^DpycljOEGN6|C*S51ABK
z@nWa`UMQMOs&OKoXGc|ECBdu1KXJ7jDj7GVK2Ma6%G#F4D0gR!3dXo8c^aiBl5>M&
zX9`waKc%L~AJD!Ow>Uiw30w54KXTu`s<Fd`-*QQm0>(*RzW`jIuYY)61>eL&FAs5@
zGj!(AQItsB?YU#P0(g0Cit4&hbJ>ZGQl_5t*FN{IO_$H!roJE*w~*dXNlG^JUB-2e
zx#B>t<HG!2yt&zPJ!LAzB!UgtDQ08ec9GwV_e|Yb?@Kgn-!w^mp9;B5jmmhE0LJp2
z9BTmu(}I~q+kpMX-z>ah=Xdn)47K}*%l+k0)wKvBF&fn<@;ZM}^$l-YS`bhaBdT$1
zP>~|n%4O$iW@u#uv1*FLVQCZTI!F%@4M((MyatpLBR#B#vMzEwlm(xhuWgx2<I>OI
zlVsCuBn)7lXNBm=8Y9YTtg#3Eh?polg%(ykFF!6f+5uA?NqQbkt0A~v=8<lF7rN7T
zF@iq^`~Is!Gm#U@sB`~zPev&J9}R&K?fGmzK3CKdscNVc{hE+sDc>eCZeJSIF>9ZP
zZuR(Md(}qE4Bm<Nmt(E~@>uZ<j{|mtsOa+5sZb9Vous<6<UqY#*}ogsv9<|H@s4DV
zfk<t30+drvf#(pyd*J6$@&nub%ABg9>Af+Oz|rhkNKlSg)X!1$xXyc$R`af21VwH_
zvq!X63*wlS0l}K2Ws@J%>@4+<XzDNdNYyo{tIATZh`4gmp=P46E8t=?DI2^9*vN#d
zn})_ZL*$OqE2|@*bJljEl_Ov=PNCO(Dzsw}1mxXJ^B0L|yEtTG=+R9Im0;9(F<ele
zi!co@&)^&~>gRsL90oCdC!2lIX#eYq&-2IYcIwm8CUY++B}U8)kLp^`=jY+;wh@KC
zopE1BHlX#)zTD%#3x(YJMQ6$YuFI@biqBByv`wd%*7fxHB}IUEKly{YWPvKcfEQsy
z0#voutf)KE+A3p5ROotPgG!ykPyR#Y9h8_PzQTrhs?8rKBky3sk+d{DTqF5xj?W$n
zv@uYWStpSYofLl~KMGmxwY}PtU$XG^^!K1_4z$SRw)<ldOH_$QN`Oetz<9j9J38&-
zVLH+rWS&f$k}GrpV=AcbQ#hI=OEVe*evk=$k~l#ZZD=-XQk`yL_M$_IG$j&Dfspu_
z_*`X@V2z<!Ag+C~-Ma#%PPBi!sdkzWsWiHle~%L3vg=4?@j$%@;}asLEhUAaKMmp_
z4yCYvDbFSbmHc46KfS(}cpjok#%YAkweDlklU4II*Bt!4gyt#}9mEAH!;TYxyL<xN
zY19SGv;rFTd~j$Ysh!CF@v+gl)jqrQr~78ZFx^t{H3vPd&gMj8k!*vRk)#W#d4lLF
zU=rOE6qNlmj#oJ{cH`yy!WY_oeJth1pq%^)%R}y7vuo&|@oYaK$_yvxL2>Hr?ForU
zz#}cGHcGu``p0v*P6Ci&#54#%En=fo0WQJ6!*!*DhG2?EURoJ`(8U8XvrqiP`CnEM
z{M1@sR?)PI->3_W&zPYDJKZs*?Kq*o=E|k*Kf8#E9>tc*Y;+m2?OOYk%NWFocZsX6
zj4eYRU?tw;x^B$aQB}!+e=0K8=B0KsOM>_r!=Uzf9QJW0pf0q(Oo5RA*Cp6Jma_6K
zu1oqxSKgn$0Ubg9ut!tFRCe&&@ixDf?r><P3A<dwJmr7A$ut*dF|bVx4R+rk<%Yu0
zBbXQ6tn&ufBk%AotYHG{Fom%rA`k9^Hrtd$ppZ^;_VX_N<EWU0H^gm3-RKYXQW}Dh
z4#<MX`>P@0!%2*#c^o7Uc%N%*n4CL(LTjeHh8mA?{%+Pp-_spxEiaghM)iIaM7dI`
zQBWIf<?-f=d{PZFBWkrI?w9bq16rf%Fym1P>^4sy6!JUc()fEKbx<;#6Us1yDnh5}
zTbscAHQx8P9bpr&!dQ$-+UknPt{89V4UR1c*S?{l;jwd>I+ZN}@K2wWZf+KB6)Q^E
zDoltaw1p{#uFlf?h$w0hDH@1bnjvFrQ@a<toM(xwQ20=(L{ah-7S%N+!n#ZvN_VxC
zcr4J)L83NK9&seY7nW>$zeX5l$KbTEdxf^j>E;Qv^S=MdL82rPqz@#Tdo-PSE9F*Q
zvUT^52U14SF{YRV;1&wM$G7CJddfG$;yJ(Msnos}jndwD)lSF6@jZ`st$;0_%Z+~v
zaj$!FXA~gauvQ5QrI+iZYwbl2HFaWdOdl+FX!Qp4y*SZ7cdLB@FD&)cidr5pjBS2u
z2F=aEBpq_rfyixc-FZ82=zIBIg9Wn^G+)r=S+GD`@5ojI4&ZN$ApOdO^p};h7qS?m
z0zhL)Kw-s}Cm(EON0@(jglgpeKrql(lT5&(lPuKzx`^aNNJKp0Wn05t7}~lO0XZDc
zY*L2`wge-0Y$aU*c8!M%!>fe1Bd18sVUg&Xv0hlrITXRc_H&d1y~Ai*(dNN$UAvOH
zG*b8@#<j#xCc&M(%z4R+(Jx`?XmlI*(O2Zt-z|_j%r5@?@W=y72RdI&E^8`hX&EV8
z<r57`H0~A>nXW9_?jiSWoYZnPha$j-YZZ%KLql>yuF?*8O?n>d_5RDSIrL=gh9%)J
z(Z7Jj1OrE8y2N2sTRTHgL6_31k=Hz5ldaf}y<%|zX*2sUHOtelT>Aj?n^{eP%49=r
zg$Ud3d1w5PM$?I-!RLrV17-zT+Kjzq8%b@UT5Cs-AZ5}<1`HxH?n`wCpJ31wDwB`-
zKg~9pSdX@MK((}b6a}nmZ5>M!?hfLf%iJ?0joZ;rg;u792t35{Fhk&0%4_d6H3K(+
zvP_A9NwSD4j*_cZxdWVLTz=2EY)jTbrU;3cD0QszGu2DMn8n_5(>vqYK4wT8@DwZF
z{zvy<zRe(BQ0^2lE=j=~a<qz}yxHv)%rQ3aiFc`K)?KDwuB*m7<^mZa-6FJSz6d?F
zw%QlnA|A-#wC4doWhhwp2)QmO6}(8M8`L3yuFpc$ZSq^$tf*NnQ<!-0#ggB2o<c3*
zF_W=rn!j0Uh53E%ug!<)=4@ZjhoNYP$icQV4<iz;OtUzyyxkL-{lgycZ<z#nuw1r%
z?CK9_)pPAvR4Hf)P4YZbmX}gRA{qp<jNk%Jsd~;^k>7Y$oZ31|{ZFaMc6|LrnSCCB
zC1cuaR|;^fhNOGDUN21*;q#Utcaz(N>TxTOJ(sN6f91xro^0+gQ@rui2o`%!5{<6a
zx-AtaMQ%zO$La~*iZH=6M|XGo39LF`x*8&d@S}qEaL&h-L;@pyB`8RL;H5kxZW>&i
zrC#gMkc(?t&L*KC=aE}ihZNvP_;+*xdWkF{tmo3_9a@zTgA+~#wYyg33QIIjzQBFH
zJwGgpTrodvrl9rA5P+P=LKUs}YDZ|pL8q(<&pGZ9exP<l<G|!tpe^MV5qK)}&`t8t
zSQ}FtBx(bkw=xA-?fW9;8)vGieEEF({8;R~@>b%pVadif%LnOagTvh1?-nb->}zz=
zYFI7ve{3;9m%*L^55P;Z*-eoT_0Rl$aJI!G2p3WG+3S+*&dkjcWO}JOJsUO7*)qQU
z)5<t>Ffym*ts-R8qVL=*5L!6i>H|)*YW739Z6zN}cHE}$Wa4hq66Ybzn-`<3fB9v@
z{#%=-(#*(#D$+`=<K<<XU>jh$<Ad^_MK&@eNDgA1-Hbuji-O+c(XkSPEV^BkOWZ>{
z(bpHz{@;ElGFn2at1YyYrS;{lEw5;i`W!I_VNJ!AhWU_4y`;zlI;!!fk)ghb#SV8d
zTo(iwT-VMx_LCd;+1Lx0Nz`XTGNiGQZiLrBys?5H`#CuBQT5uS^+$j(RKGgADM%+)
zl7RKdtUk)&+3{><+<T7`A&=9iFOr^dt<}G&GZA1Rr{Q&<VLBZkODmv6uwM?D2NH&a
z({9!N)&_isA%C<uNju;xHAYM>V$4;i%QAL=l)2UJ&u!l8s5eAknw@++vPgSJ08J@T
zyd@PLzS!yX5RaL@;_84n`nS2P9~*^?+|Fr{hxH?|)Ao8%7RJnh*aOW%e+{h8ULBA5
zoH6Hiv)%If_WoPVammDTtaPym5eXs%MbQVfoNAxz%nQU(JoaSyG1oL6hOb486$U}=
zUKbnBt3~H(%5=%9>%9<0g?MxTwsCnfOQLe%C;Tz(L}T~f6czP=qQpZ%_@!99Kh>z=
zqbk#hbW6RgruMvFDt3g$Uy5?=r^pEMhwC`2^hC8Q3uxdN%4!IISiGIYkt->Xv!g`h
zYjqF+OQYpA02pwm!Yw*^hPut0b09i_vwqaWLdEPS;|F3aM<aU$An5&wzksT2wpfGJ
z^iXtPvy<J;@k)K!OieRER6)R-4DJc}P<3p*8t<x6eWGEC?#39K(EieGv?HC};H=j`
zB)yICpWSu?b!-2<Y5Io1W9|%j3z5ZZQ8!CQ*3Nc8+61`icyp^RyHyX{9m2AYP^QZQ
zcW?+*JY(_13X5P6Rvzg%DoJAsOG1{%iXIUvSB*|)4z?H^*CY6<#fUT?4l3yFjC}Sc
z((>pehsCWauI3){H2Gdd;3@Fl!iaePq!uaM$O`Rk9Kq{+Kz{$F(t;=w=0@lT@}xqg
zfIo#<TMmeLR(j6n+=ni$w{>f$mr`oEw==FPZxNq+oBj~5MjrQ~+wDtcDM_Q!V5Z3F
zS)1=5Nv#vD^8x#7pXrWflh9`5h8E>g{S$ESjTQkGombZ7urta{&FG{>jmR^iSff}<
zKF+EX<$`ntvyWC!n0KDv=HY=v&%+M&)-i*$A_Z7#T<kX%V(D=H<i)oD5$;c^DTbo*
z?%-w)o7om*EAtajq-(a{8Tb{^t+dw+vq4{ITjc4Gx)+P(Rf7|Q<?m|-v@W{jx($54
zQZj$kg8xy>V9V_gzD!oqAA>nVg1UAQ>dkIh$<C9(+n5>`gZ^?Our>~+f49Sy#cPPe
z`wPJR@n!E2WKa8EfpFQmwWy=^g&L_Q$myyUyG`H#&0mQb82j$ke?kjfnQE#Ee%^rJ
z2z<<v+9s^XCa%RGe{q*+KOFacRByZl5KvIY=ZLYo=FqM{wf+YRI*nt~3Z}SG(w{b^
z9K%NC=#~*A8|4(Zer8)~z<4r+dYh^BDh_zKKrNerEO~@GovUT}v`j0q*G{Sw-x1*0
zNubWfTQUvw#8xf&+ipb=ImVkW-&v<QJp0m)=?NAX>x?dNbdTN)#xa5>EXDf0aR;KG
z`ZmK5kqmLlHfd}sa1PUU=n1LwPoe&z`Gqokj;1+s*|mAC10)%w%E;x!Qf{rv3Ic$P
zW}ib-9lMe%StLYQDrr!$$nqddfOm<Y^m0G@VEHSpr$4!x+S-KvRktPlZ?RBS8l2%Q
zXP5P_D4AtlO+IjFkvghbk^66C&+)x|#3n)MGk(m@A)JY?_likNr(gbsfp&Mq^-+f6
zEhh<ZGPP3>x2aurVIvcUuNPInVzB{8`Al_fW-lEbKHORb64~H)g)ZtfRHFrBu^NF!
zkx|WVMFX&xVwWr@8CJ!*D2}Nl2uL!0D5Ql(p1prL*WvuF&DzLAVmT3=MmDnuxfqdw
zvDtd+Ra*IsMtxdKVzY<Y4&t>l4su6Qi9`Y@WANI9wvSg0JE{j%wYFGuUv2=JJJE>d
zPW&o58n!s=J1CvD2<i%dh6gns_GB(X_6>RLeBQl-A$fRLhhvJ16Xp=t@-5i;43GC&
zK_Ras9c2FqfX;7#^ruWlk4TNckhUhU>!g^TF|utl;u+1cb52zwD@7w0T~*PJcZ;}`
ztJ1IRBVSW-iiX+6{GNO?VLt?Th8BB@msYk@ZvNh1))CY`c%iUCS!cZuEzTsHtz@3{
zT4j$N5guzSH@dGzaRiSgc=405O4FR{W_#3jh!PuHIy)N8oRvP$SgfdC9%g%0{nR7+
zR%q|?FUn21tM}S6!St)m*`d@pj6Hs&aQc)YsxjpIx=40{&)&j<zQF<5$p_`)?VT0!
zx`56t1;Cr~VGW=X^k5szB%dWGP&6w@U-n9Md+9v_sNav2+m*H1LUdV2&MjN8w?8-6
zt2+xqP9{Fr92MnpolxEz?QKiz1>TR}8V`V_CqNfFvSTK$WB-`0M82B2L@xf5#GZAZ
zD#;e?p8GraB)sytJ_ro3fO|*$$<Y2ZR)56tjmu>_F#kKl9&ML{8GM4_sZudKSQy=o
zxC3&Ey^V&LLP)+tBXJ#>S)u6>8A*oQ=RPS&iO*On7|3-+%A8NF@>9dy(X<Lrmo^>P
z;1;H!xZZaR6+t?e+AjH*fi{-ts3o|T!AE~ALf+%(9rMN*mfrxfqgC@d(Ng|62G%1%
zQoiD`bh7uaKAO%3)hG*85!kVU5@QGsY~41<2kR+M#>hD2yN8^U<p_XjaGK=!)qIf&
z#K_MQ^PI>G37hbvCFB*Tcei6p*kh++=5EE%AB;_-dl$=EEsu+x`_O|=1oA@6r0;_O
z<_S+Q3C7i`z4!r`h0!B^w>BzU-bP@T%CEVZ$HgG4ncxzH1E_{R%-R9<c_@R<8|#*E
zv*S$sSf3BXAtzSXlgS4oe@_wkV@@$(whRr^>9o1d>2=rwZ3D>G9kE(gF4bwy5#Il;
zqS!nH>Wl1OF_m;gq9H+*bl{vB;l;$~tGRNDhwASL<@N#C)xlP^DVSMj<uM_fTuDUm
zT?#-O#7!j4x~4@#i-dQ}&r|HntDB6>%PF%xJhYIC6G2H}3tSSy8^M*hr4*6fljJcD
z#YF!pqwQ<Yb3_o;kSdn596Tmq<5|tmU^Yw4(pK=_Dj42adtU_i*J=hD{DRk_^%Uli
zs5=75sV4_mrq1!5fax&Om1Of8zydDWJf#{+A8@jRWmjD(6P;=@SAP@Fni<`QHO8O1
zOO432LbS+DLZ9n)CW;~f#stO3-E}(d!;L?lG|Xt%=+ie-_NoCC5?^~8<9O8@t^|%^
z<}+-mk#l*6{VuqT26+rI#TaiD!*#H!>1-Hn<c$EEXpr3WBs@8`O~EJ%XC6wI_#N^O
zb%Z+{@ApZZSKE|&Vh^NOIxIibeCX@D_ZT@3G4><Y!fHgGrW<>!^xdPjp00?8S((t(
zkilFIC`p?OrdcY}w4pp%?s{jf|Ekn3e0%--Lq|uyQC3Rt1-;${n=EEp*XV>p&azv_
zYQS2{&RT8Sq<1&9ZptibYj^oG`*_trE9H}<I<%k!?K(%VQf67l3<Z_o;I9H^>X6=t
zVtAJ2W_l)g2BM0-pFv)Q$a;m`{b71HSgif-;5RGXm*-b!Xd=b&92Oj&-#15FzfxaM
zg~)piY_ZyrKtSlQxT^n0Xo%p9NfDf*D4^XtEt9(w%-n$_I}a?KJ43M6vlUw;j{F{n
z?GGv_UB9SL)2aqBzEsh72Q5<a$(D^CO=|TSSGnGz!K`Q^gat#b_tMp)zk%s@U5i0H
zWN#_1%C!1rf$LINhr0GpBb^IIkW1=UrhHWax(I0+Aq@zc(e_r08}|KGkrj7<ISKJY
z#cx6eOUp(u(=L)EB1^CwjH^oYFvX(rI?V)e2bPuk=hS!{U^GCe+Ng5w8hxQyvyO#*
zX6R*Es0c*G5#b%EH#Ps=ia!1Bn(O_e*CxmHFfsE5-C)>LHRGIIDbfXB5L!JA+PqtL
zLl)^DNzhJF&<a~?zClD(Fq2>ctTdvy^vK>gWqJk5cdsY16uC9`F3v*}T(r|i!KNeu
z9|r>yXI^4P1QI}5wmkhaU*an;Q{eT*7UC~7%)I`~t7Gv7yneQtwW30;rpr?j5X<n`
z#eKVbDR2sJ>7Y|8?{B$g)+-H;&aQZ=oYQqwvf0>0LXk`+@P7>kj#u6(%T#eGQV=QQ
z_}3ySMbF06q#_^~xomon0(>7xQQ3c|%?}+9Z<9TJrp5sb9dB{j`+KP%oqcU>)!a=#
z&%Lu}y1^&Y;H0Frq&3a_x<%-+IDU1sF7bhV{8-%Bwx~&WP9YaJi{7pQX^EcmABV`~
zOIDWFw~G9yv?Y_FYg{gF8Z|@>=^*I<QDhraU5_qCy}R0cyrCPke)@rG4@R*p9^H8g
zt~8k<Xcs`(h;OZXuA2|Yw3!~0D>G(g^kCs&kO=S|w=dPrV=11>edp~}E;^!&@VXl4
zf?3hgk+ol8N@26w7K89?V$_Bch!eZebrED_qt@(SLRy8KJ|j*dtT`)U1ay5&X4V1s
zDX1{n-pgP69!;397CZmYHn*{A9^WcHq5lLXsrUnyrxS8Ls*gzd(TZ11q9Nr9SgSMd
z?|DV7;7W$Y!n>t)y~&V6o@u%L{mTy&VHvN(q)xOQnw{E7_u*O=Y7H&{q+<p4+V5eA
zZ6_Q0JczQ5#-vd0-yU2fhp5PYM|XSPXnwa0s5jhtU*mM0<*+}`#Ko9E4ep|}osz~|
zJOcnJXG;=?(7j|wXu0yr)iG!GaBM{~EF^MsU8vrLR2JZGNjMrWLO*Vnk7VD!7kat^
z*WS@%)54$7;Z(g?lg!=Z*!I=5WQSHS2r|+n@b72W7ELV!!Sl+?%kAXruqzhOdsW<s
zTqOkhSuMpHH0WWLAvZFexIauIm$K~=rV9XwUIX!*OiVOopK-I*HT<&G^r-CO9AT=U
zai;X_8iq?H3Bx#z`XFkjQWoKG{1LoU(yk^=mh(Mw671>i?@2W!u})Dbco7eJ0WLRQ
zB;uXK{Ri$37;K}bOYI6Q<ZJ8#dI8ZEP~!SW_B^j1eY?`sC?ndtrOn=BBh`^2tqy=e
zE6EbLAglZ3w%teWxmbwf<h&0eQBXb0jN-&u;9WWgk7qW@ymeol@U8eBv=eTB26N~*
z{0|fwl4NI^Z&EGE$kbpAp<Ca(zic=%=lU)nmIXVlrCKOCcZF~E{@_LK0k>M=62wyP
z<?>-{aBhzTp-)8g_n+|b9G@1}u=SrE_TyjmqQ9boZMykj`Hzj#{7LBF^VT1>m%re-
zM&okS{8xmN#t0*97fSqvjp3FGf<1$R|24vHf9BOe%UjEYW^HRA#ATsGWqIh4j06k0
z=6rY~t~Ay@%*qA~%O^}B9Q|~%;q0}BWZW=O5O`I?NpmmlhB9a1ufs1jOXdNEjAycJ
zoHB$o7hMmFY$Qq0yB)J&!vnw*(|C=WF6)Kjr8lAyhKs<{;SIJb9CTxXetwI%k`oVO
z#Y5vq+3(92PS3(Cfy<8DIM1E&Id1xh_kmt<tRYIk?A*#oz~%5<)qJN`@6gA7W0hg~
z0o1^CHh<Z+`U^>fMiL5x(L(_w`Y?9W4>NZ|uy;c>w?qGaiX;8qYUuZ~UfIf<flipN
zGs?Jkge7}Bl3ItWa3gU4UR?pssDfX>9Hs-Oe33ER1XFa&&%aTTqUwD$X>svLDa*IP
zM6V?9$^OdAka8l1NRuXs5Y<6LiAx_Mt@!G~(i6oWD`fy2rHR1_Bq9K$6`~o%`oJ0+
zD{u=5$&>2nPeRaCz-}=?z9gi=(=<mUyV#|ya-JHj@asM0^Q_Lis=S2Qp6rORW%LVx
zzvnJAggCSppVVZ`&z#D4hscB?o8ciiF-g3yp>0wq{(L>(W#CG%q1OFlBm=mJY-b`u
z^MBru9*OqNsC>h<9X<iRNeZwz>oLXfFo;&~<>u)k+g!Z7G*z%H2lA4Y85G9unw4)A
zG94a-;w`!%!C?F>v9f)b{x9`Wrr!LFRoWX=KAmPfcWte{JoCJ`GE0iVLlW|u6`?4U
z7zzE#Y_w`3|00U5Bx&RKL1dQVa@gs8jA+zE?xfy@?&!#LC8+_FmW=%{pcCPlbh{9s
z6U5+2O#Lv;gyfp}{GywA{F{cGzXO-#t@=Gss}k}trX<%<ny0Yt!`h{J$T=J&C++fa
z5O^K%wT)C^bH_p&wcR2G-H2l0;iF9y{-7M!8<N{LkOq0$3>?<yKggz_7fl$87_DU-
zwV-<1NGd>|8=?a`M(M824zs6X5W%F3Mb3~3scGynNE2cy5ATn!o+JgST0?ck*cM>?
z(pxfNu~AnhA&-=mM8iwh#b!YBT-pGd$PJBm@mcTm*48r)SdoEFE8wQ5x5(hdf<SqB
zqCydY%23p4DLHc${F_rI=12!rk!V3^sKQKn_9xH}L3e<?etw0x_<<Zv^QL~*wo%Ap
z<!*9fllcV%Qqcw7jp-n<K2sU{&g7%bXUPq@pOu44vdRe|Vdto<)NT`H9?k{5P_9Ih
z^dMl$brOQyXh6%NFi|*F5oo|bOx|F57-(X!MzKJPVc|H7VVxz2i5h{L>#btFsZhmR
zn&+KtKE?pc)?Vj^#p&v9-M)JsWn<WSRcldPXLXi{*6nalsjYm+o%1b$GK`_gjS9mw
z1mK0tm3-1Nhn?U|$or7N-<I7HVZV-@b9m=kxSOafQG)&=gw+@~G!Hl<bj~eRVS$2j
zaUOy2l&rtsDm6<?e|1ZEt;L*`P3?yY6!ZS83<3s-oVeTw7C-TUVMgg=bPd=p<#%uV
z)ywJo=P}f?%p4YU3!jb6cfvHeb9l8{iy*7x%({^FCH=kbJ9_b8@uYRm8H*J1yVUBp
zT`e{|G1?bh%Q^@ko8sVQIBJU+aYG^|*cqQKv+f%&adk1-hEE2MH=#;BFN9YK+61mS
z_csqfcg>vHQ)m$~dq4&Whr#Auwz3cl4)`PT#aTCycdo`#>4mm={&0f{$G;)_fAo<o
zKAbA7+Smp(kURYUeCTXDpJ?039_?zrUp|%oRZ$kD9Jk~wlV{AcMTl)2b4(yyYE4_5
z4<cwD`Bq?Cbad~rCc}l)c5h0;JkJ2!1sTBQ=~(ENHZh+OMdYqcsNT)oJeq7W9=UBi
zDWtwA_BFNMka$HpPMG!ZR*_bw>PW~I<-aN3JM)c;-KQciRk55BSY=Bu4fbn+#n)Yf
zdOo<^@VyJv_n^bSg9B{S(l?*?+q8^MSy~nh5tCsc?+MN;8GiVn`Oo;wZBamH_1QyD
z;hJqE_d`!Dwn!*ni`E6>sf8^LJ3$NiU<zWi=JDu+^srMiX^{fx)6lsrsDmglQHM3T
z!fG@TcoU<jLZW3rClj$A8j$*DnJPCLb9E-N#Xnu;EYYMR-`3OL*ess=hSnAq07NQG
z&0-w~x?T)U_x7w*Qr1Us0VlxsIQ<E!9mz8s@l(KC3B)B*c7Qo@bEy?XV%m?UBhfpL
zB>;mA9NGA3r#G-iOqd6G9FLq9|1Ov9_ZkVcXNroB<I}@S7csZb!&dDzhX`A5wNIDs
z-N30w#OCwT=^-)Rd4M6H`Jv<lG+y~MPqBMX08yX#f1eTN-P=ZhU;!cTe}kBkq45Gn
z+Duv9C@NvXx%qg=Jym;^@BwvUpZviwwqf->({zfp&jj|}+;}=>C+gKeoe=JQ9e!pS
zX&p@-lz}t}3TPlm`oWFW*fmeD-em!I|1y?!ja%F_*K3o-REz7lz}-G1(ec{eaw&d1
zL?}y6N>{XnAJ#{oUI1faNWB-D(WB3x1IE2yKDs`8lxmBf>81l0h;F>kHA)97vF1Y9
z=^WT5w3O!K1}=G73*DFsZIy2Qzn;pAn}ecHO@~ttHMNk|o=L~W3AJ)sF-1_YPM_6w
zVMX85`I{k|s>``8S@;|CD>)L|JJ$S+C~Ei{71=geckAtIY5|58K@8D&LvZ>jb(**Y
zm@@B>$ykoM0;>YtWg!s<y9YGk#yKM{39rqxAvXH{y+1+sypyzbzn^<lbtc*=&W;;X
zB+v&$e7GfyB6~eT)06THGx{(jFXaZB)XKZjy*7A7qkQqpOj$N4gwl~H(n`5M#UC|F
z8E;qg2+c*p9so<ov&ST$lRk5n1U5nRgXx2_`WU7@BiT*g%_Rf5?P(Yx1eu(f$}j!V
ziL=Mgs-G{PbftLs;VwsP3hT)V_Qw>2p41cyR~U_h@-J2@hcRj5?aR^1^15u71XRN$
z0n0cwR*i?H?KRUPM|o~pZS5T0m+^LS<wr9KF&2zb3V<>FQ>eeCPc!siSt-Q2NXK17
zsrdxEPRGr=@vzwu&+Iy${hS7@OGaC!buDKaw4PcPPL66Ojj}@Se7an0Ix7>Vm0t2z
zn69QgVH=MvI_+IuZKTr}<A?e9P4MG>`W;5cl}xcN2o*=->G#akwB?q`iwu(I`x!nJ
zUo~;(3ILe)3P1I+>!o{~5MABnWEnI<ohFj~xe3VVKLv~KqBO9<QX5Q=epeicIR?>*
z>EFBBBee`>e==a##zvdy4<*dlZYIvXp0t|b+}LO9c)siz+x4nfGFPS}mo*Mz&rB=V
z-Z)<gefk&-uef^SQQmLVAbNm%j)R_QKCBDR{on&UDw>1N(iM3@xr#eddys)K0Cd0k
zxrj1^I4zIN{v<BRhyDMf9J28lmSTu^-3ur1Y)+7R|4iO)@|ffirK7rEp$E^=g^h#B
zAImvclGb_LE(a+3pTg}-y}I`JQsD?#E7`CESAm0Vb`JHI%kutyvwy(fH<bAR#61xt
zT#7QHI@~1(DIG;n0KoRR=V)8J>{tp(aw^-;=wKVSmrNkwvIL#{xi{9}>xGF$PB;fI
zbA&IvU|jrW?#+XyWE=ebpg4kanZIWxdBTruaVljU%sbrLzEqa69`md|z$&U?m$SL_
zBb4i|(d=WY^3SDIN#Ewe+1LBs+Pt{V?Fbk%nhYlGFIa?6K&QD73gGiz5C2|M-Dh=~
z4?}#iC^`1aZ}M|X(mWxQSyu^7?3EaprTZ*GG}-Wo1_xfnLG~ZuHor|zc?W!9yl7Z^
zi0k*i-`;aIGO341xIIh35g%j1uf!zFccg>;Q$v6fT^=RaOOAX=u#<4w-fY-<de7&m
zS7n8+a5C0s0Hq5Zc^4`J)DE0}0bfT!feDvR*(&AoStAx2F-M$pCgHkTXMPLbLZG^n
zp*;0`eGpw@5z13#fjhL`72C2NMtA>ceQg8dWb`6lEh^iP&MOrhcX)NYQ?<SgE`F$z
z{msnGlkzD106dCakQ+(obyfYhz7l&OuaO+L$7m)b0LA$fF0@v>5T^G7O2RAUc}UF_
zN1W})tLdFDkRoL&3QZmA!s?w@SxCkzXAzPRwvV*USwVnCBQb`PDc8Bmi`PxUFc*s7
zaA0X}7n3YOm@r_OHhb4Roo)ce04pqym*^rZZ|bM9(xX$H&ka7fIGy7YE;Zt^6MZv4
ze)(q=piV$#(N>Op<%EG>AX$1p0*&j;ELL<hJG~tEN{^FmicVoVxf#t416<Q)5-M2l
zMhOff?mOLfZ8ai0sOh<8Odc2_>_icA_`RvhP%A@8F+4PrW5=%o%Z1LBoX#TMttA#I
zt}1_NogE{eYPeLWTz<>n<m_{U&`|L*)Yp~(xIZk?LDNke&6gM+RCX;?ft|MyecKM4
z*A!ACEu+RpfUsaCE_Eubf1_xy+?UzGd0YZVl_|On%149?F_{QHB*@Ls`1akgVN+Bk
zXP`cb9Je|s%kn{7C)@RR0EUr5OV}<r#7r>91Qdj$Ve<W=5>$V*%y)Ivdk>zXW^BX;
zlq~p(#zb4@E)3Lba2UiHiydpaRE;#v-Z|5z4UX0B-_bN!$?DJwRCZkPnW#IaqYsdb
zRgP7rFGOaZ-<LRfhE!3L``NJ^&&{vJuBUgJaz5?0dFaC8W#D%O;qsK&$7`?B3hgDf
zD7V_G0JesI7N#TwWVOkfoQTWA<5WQaws<wF@;gD4zLA~;L)cUKOr%nwN>kT|Ux!W4
z)O0f#TUM4_@Z=V^n3WV#|IxK|)$P<?5a6~a-`JDG+ejoZTGFOLUhZjMz{e)JnS9Ci
z%T<(UzU$JKXP5}7?>>m-&`mp*3AtQCcO+OaBD7Q^*h;g8)wxr58c(h;o;3*qzDRK`
zGxE6?kFzx(M8ZCO@kLG*1}>GCu$UV{pxfEuK{_2Dv+i2RjTq37F7es(O{HdV2>J{L
ze;rwhJ8q<#_^c>9Snw)KO_Y;%%tmi?OBFzz%V<@dxru%qD~WE7gyE;KijZ`f6ZTGS
zfOp^_+nSvGTH}&@8Z{ptd^UytITFszB)|z#Z`iTT6o)4JKuX)mj$iWaiPU<)4O+HJ
zM4y`)QSP=*%xK6qa>svqFw>Cw?OVd6qEI^C(nu)F(83_h(&Ja}n~K18af<Y6-?EUz
z1JUnO=dV%?!pt@J@BvcF+wpM*U3aXa{)$rpxbd3_I)3QC3J95SNT;a*y5f}+M-i2o
z43>M)RJJ;`Zscs7k`WE!dfBT9qh!>UT)w^1vGGY1AE&n%{aWs5HOs{va^0Lz!F_8x
z%qmDnyj1AU-``7&__q;IdWXWa*m{Sj#5-t~`H{R@+`^dcKyi7Iy25J21ie33(PP0F
zLCjZ#LZJV-i&h~I5UC4<TO_JgSR;<<sS7lILM+g|akuLrtBk1xCBJ#+ec&cHiKfJM
zc68t*1~Rj_)GtVIYcznxd0*rRda&IgQg~@m6938a>?^c+G)qv)BA53@jv+rlJrrzt
zHku*2Ti)1&+)th%9&DGjS{b&{;w%-7&}?xMxn5|Fp_cmsfXrTS58m1pi_@<r8NWl3
zRBO%O+^P-oVKji~_=l9t2glbcDbWbo!L6-vML13;;nY?*et8NZ?Ukq;rr#k!oY}7O
z9*Pr;lXME<>6_?g5tP$<maXO8UQ_>wV!^Yh0dE%bSl(YnU)Mlurl0=lFLm@yf<}lO
zBU6WjCoZxHkOuQ;(`xT3*7Px;UrxrN!?j%jBal$Wj9ScXj=3Kj>)tUhg+b@Vl4(*B
z`oTKDKqB>%!8n1@^2%e*+pT-05x3b%nO?O<=)RvY;B+xCIQBrgYJ1`qUCI;Cq+|q_
zSE4p;sT!8r!!ryoHBWg8&bS@%sXX#@lZ{Y++ACiGK2_H{Njhyp%uxRQSr@iRw{T0v
z{W66`LD$BXs5telCb`K1+ffxRRP+Z#!aNWP#ybUNj4)P*#o(8D$?8lpfoWR}+7X)w
zOSf>=mXZ{n$ig7)JLlO_!#VB?i>hWdU%cO>^(wnjUG8qkT4sdE9e27T9pW0ZKh+lQ
zD`GQ1;3c{$SRw!j=G=vvNdM_SrT5C}_7XDobZ10tEX3jC>C}*NI?!&wS4EXiQXS~7
zF6n@>-2_!n1XjTBzNxD7NH-Z^#nbfCvFL^!c`qyJC<x2pnNe+h)S$*k(Hz;qyz+3H
zhX$oFadb3HX4xW1E-+I-EqMRLJMP2w;|Q3Wrx)&ox-8%bs*ll2ij$39nw9zCWBxxp
zCTFsCSRS^f<W04kcm)$^^CZ%%abESTu@1WFSoT9`K1f_l>Ff)y39*&pCtZA*1Ev@I
zf5j7aqr`|CX{#79!97Y3Tjy^$SHAZuZSff2z^z+AP&&twSGOl%tINWd?W;_`C8Ihv
z5pe)AO%?}xO8@eI<r8{Rx#_Mozen#tFFS|Ewwg8M5=;IsK83V_McBh&)`}C23Fb;f
zW`$~9D^&z`Q01(DXxD@b1}sn%)wof)UU{Ovb5vcbJlVIw75vS%%@T&3srRzw{Xf%P
zU7_!%s0-*V!2c2sphJTw_~FrsVYF5hk7)ggO}eGx60VEFD@GJYy8)!gFjT3CJ0u}c
zf<*J^UXyS%;>>H7%&&=SmMZ0(l1!3Js7v=J-75^#lBB>WQa~N9EQ)Van%~2dT(@_M
z2@s}<Rnc8hUA&W%KSzx6z$7mwh9{P%CxvJG5x~7#2Ao5JbJdTGs!9^5?60NFky9<R
z`_ZtbiGr|yftQ0~&Glr3jAHUo%jiEPGD{{n63H)2RkAYaxjYFf+6pxM)=`Nd{Ng%J
z<yE>#Z@<M03M=%NAGbMWvbzj*p>32hR`DG>WB7j;VsgOA(W!9e!bVHE$X;A_w(cY#
zK3D#Cok4SQ`{X?YI))k#$=FyCUIBmF4rRhm$ex1yPi`^T>i<fhhV+u&o-U;X3goFi
zv=C8Ii)ac*^zb6h6Y!Yk$oohcESt&@@uXDY%_C)cI*8sTP9yf8NO~o_#p$`ZFZ{nY
zI<6M6Sg#$H(@7tBLu;0g2W}k70M~2<lC{VJmzbk{q5sr{>_MI}H`qLG{AA|EjM)0S
z_P;tGTHwFRY;p83rd>Azwoxe=w<SuNLD=gqOzXHwz_d&Id-H~BAewhWim>Ve7n*c*
z=s9oj!hP`8Y4Y#L(gDCdrWq3X_EI&C`>gw==TBP*C5b<+aTH>K=`nYJ@09;-I=Qh{
ztwxXx)T?ZauAYxN)JpdRdbvLJq3r8<ff|KuL!!d;vcyqE-{G*q#q$4*s=c%m+I&&j
z=e)rpq!^`sX8AWc#Ha;0&lrw7H&3JoF=U3-!YKJz4>;jQMYK$IP6tE&2fRq{t9pRJ
zNpN0w+0q+^A5S>P8(=}#AuEz{HJxLIENvq(=as&Ex~GF~D;^LB-Ae|w31K$WO@g8J
zNOc1fz}r!WmoC)|OY*bmGGjWdug!UMsz!jVL@=Yj-&n;Pil=Sfs1yDoujs)+`WxTs
z0t1f6WeP~#92s{S9c0>8DAD<z^Z<ckR4CB#EZb!4_vO#@60il-eBBDPv}elnn_v(R
z74phow+>$v37FWUz)b=SXopAJ94w!)^`2p5pvXnAdl<Z5ZYXex%2Pj_zu9Y11^Qw}
zmEn8CjeX%qrex^fw|w0MDj4`<GNf<fd8Z+x8qQ8{&K^bBYR?QHEd0&s+x)j#!$=oL
z!`QG}H}pI$0N@e>o?a``=H}g8982XWhYIz>_FPXyHOT0XkwJZs^?G<q1F@BCxR^WS
zFG75W@T7`z3Tfx8q8y&^(-Xz@n!Zp88Agn62*tC{l5jpnLQ>~{!?)1ETTi+P>()j3
z>U#F)09|1W>y{{39}0$UV|-Shky}vPXg5&~*KWEO1N=Y0cZTY{FD~&dvX=|=sqxC}
z*WeeZ_n;(z_6hdz&C%i{!tlR#TO0QJMPFI{b<Vb76E29POz)$G=JEZIJP>T)$}Knp
zgKI&QBfG(?@6Q@rY)#bXz$&P^kj7UGK`Nx0<=kls$PxKk0jlkCQVDF1YHYYuqDM0T
zUPJR(v6HX!8nIev`=m^<+w=ipB8%J)u0R$&d1ek?s^u8bamPxNRQ5APFV(WOE_KXE
zMq8+w)85HxrWZ8BD)9C`ZCUyfEX^LMgloJ}+294%5}-~-;8^GI)FVv4pb?YTLdO$n
zzh0gLvWZ&*Wf_&s5gZwTes!g;gj@$yHk_ZqcH(zsI8)5EYt9aQa`y5n*j(<g{+L_2
z=hF4DX>^iUd2j@&d+|Ln^HO+AX>$igQlIr<NbX=g=^BqCij4|2D`(ej_w7&*^2xj~
z#z}iobYH|lJlI`7#$p*haLEO8Z0d^JImxIKnN(Wo$n_~-M#}r=B7u8VAwB?#nbmaU
zq>E-#W`E0n*xRicev4led77eQg3_x8?J^KW=T~3>7bgUs-OVM{l5O6TT6Zlj?y1kh
zOBABbu(Ayc_|0ABeUB%6Y^k2}KC)?(D&_)jE|6p_c7yy$#@319!A>?E#Z0MU?Henp
zeFg#;iv43904@dQkS7vb0cgOyEC{rPHP}P6_C*vGu-vYvi4H4rjZIr4Chu!N;e@V1
z(42WPbB>yulqlr+T<3Cav<!5+3ox3q0{b|soN2G7cmZ;}MTvYjNZk1CUcIVG0=wEZ
z7k>$y(6ibrf^>D9pj)`0yoitf_HUr;rZ`@%3j$SyMA9(4yUftZ$Qpp<qgrW7^h0FI
z^_7!n?fW1!(|T-0@?|AMBtPR80_W(qyKD34w%MOJTI{^e%d6eYfvoGiKM~m-c(Vp4
z5YW+vx!T&9sd32J6?o!fhdYbRp1vo2%=xMl%Fz^VcwBa2U=Ez4rZ8B@XiU=EmsI;9
z!We-HG|*blpw7XxKlR_CCXFSId#a|jUokX<E$T>2kr5@c*`zg$=ZFTDC}QeaS5%3u
z5_wvLG~C-o1bxowNhHgEu(3i0>iazI8J>~SwX+=p)^!7BSp}wG+W7HHqE;GNtevch
z@V_>TJy>S#3HPE(trn4{ALo==V*aHY$iK>;!D&z?xlS5986cv2TaW_}glb_SovD`p
z@vic%0D8^?fd#K+lq!un2&_pCggOjW5ewQv1KNUnrQ&i_L3n7QEnOID_&4>ke0-FS
zQKXU4ksKU3UpQ+cl{h8w<!Kqe_Wiq@6zBtvmjcy$So7x}-WR^3G^-XX%<9VHrRKjC
zpJLQXKoe2}s{lKzMi9FoSbeo@LyijDOIEkS0Xx-9R(Ud}Jn{#YS{5d^*fpx$R~Ldb
z>6aJaQ<{THqN~+gaX7(?=?2S<ad~p2i;=Pb$4X=lMoSS`*wzVua}5u=&XaPi3zgpd
zBLJb?U725@pGH-TOeMbIKqi+7&y}LRa3z*7-DH!bIbdw*q|uZabs?Ip^BW{na2rmg
z4VYdSxT8fhLD4_-@xC(H)sW&_=JzfpPr**>^w)0BCbZwkWsSp0%V~!0QOw`Y&79@@
z&_X7Wlr3X48_v~bUr!?x6ES}}oAVwZ=>Ufi+bxof4vo6%_bSohma9`q5sCGwv!JP<
z7*^Kr1v=9&*j@TT!|n}dhPK8|$KMQ2JWicxr+y@@_w!RmH(cUkEfF4mGfQww`B$o`
z1USIbAcGC5);Pf9fUnqY219urt@SS&XUQ{+O&P|>RL;`<T1J_DFTAXVZ102#$95A|
ze|^{p8fXR%`>%HnJ{AxuagfHCJcT3DJoxE{1wOv+5i`ocN@Li`<gP9lpU{x6WeI!a
zb*{SU6Z(ZU{RhEs7th<vbN7~PYx8c}v>?DSk$~iK5)(BCz|$$NGvH4z)1z=b=e&Hs
zRI)S+r7$~<@Va{xc0*A*Z(=UFNP_zN!GV);FLu97yT_Od5PIohF=v*c!&x*5d5uXV
z6nYGoL`Ru$>LnFh4E1bFB`-`I4xOr`9qBXm7+%#h4f<ME?LpwzAB$BoR@wR#Q8=<o
zo8$M(f5XokfJSxHzna6TmG%b4J;C%r3<C{!2g53fXx-1ve+^$XhsP5Or7N`|i~1u*
zBLGK3EC{1NUnG<g|3qNY*j?^_{CLtTCSyS?OZ<=&44sLz@kLO@sxOr&aQp~6fvzGx
zA@LLMEA6AI%3yf36;btYl%o-nMDJy)U7FY*^8{f5;?ftmC>~=|ev~HSY@B%<^67L|
zT{1Oz!P5%H-{gp5+L*j~UN%6)&W$BaxynBcr__5M12M*s$qo8^h|5b!JGi$UZKuk=
z>2O*;-8dLgw-SWEqrT1b@#R1|hs>quA~9mW7?K%32;xgJxWoRMHbRVQgnq*kB@{vb
z{`e*bj0D_%OQufGfggM;ok&5q&kIKXY$C9yX=nN(n2+mP(USiFobPu$aS?G4aw!qs
zgpZ#v77F&Sj4~RCQS@l?QZ6TLVn%!LPOE$uD+yG#{FMirVpxsa{qO(UVT;JUE8jtg
zh^;sd1w%ATntnEv^0a9D4#Kjw;E6Z@o6-b`kV29E9>E?U0)lL2US4+oKTN%2bY#)i
zwjJ9}I<{@wwr$%<#kOr*9ox3ivDvZv%Q@%y#(2m2G1tG^wX15?p7XxvHGv)ukpA(1
zGch12J$Nq{5a3M2dO1nnT$8`hSN1w|ERc)7!owY>ibOz*;Zae_*3Q^_OLocRCi60#
zgR&V}Ftx<|gR6w<Vra2jawjHwX|K3S0hb3ESk`Sb<l%N(ZWt<*@6hMLB{wEP5{AXY
z0<3;6*EGl(fszQ@nQBC!&8sd-7h=*}%&{g(M1GPjgUl}n{M?Uv)9<6{Ye|vk-r8Sy
z!53h@QuC>LZKX>QEp{&JZIWpiouO2~#^tv!mt$t=5}Lt<DeUdz^pDSNPtljMagX73
z5;v4v(Z6R3XCp?Q&5vgNe;fxAVf2S6fCH>|mU((zX`tgLd2Bm&s0WRB5NSlF?Mahm
z@0W8{Zch5BezeRoCveCFl7I8>!2+OYI8qv{<=6P-ap|i9{LEVRXrq>b$(!<l`))#Z
z#lkj9BnnU>jcH(UVdjnM1c#zeZ;|l+GS|`ppFJ|4d`y-O#*Gp21XAL69;1lcfL~6m
z#^y%T1=^tyG~2ht9VFm4^Zc3Qd|DhWy>{sDs@b(wag^3eFzDUT_z%^`q8B?6O(maP
znGCVk(RAat5&yQ~YUXao&vf?wa&X-%RAvbXpyST4*v=b_#X|!~{%MbhnC!C3n_vhs
z;Newi4&Iwis*=KA+%<UWqHkBP0tyazw^-oF={`GGx>}bR6|QV(1a_ya>n1%^8S<EA
z{NH1&Jd|izUw)-dMwPiDBPz>|FJ+eu$kIY|FItb$U>U)ocbv-7K5{urY;De$MuKQ6
zv-4t4CVh}rpxUAu!lzs6UXtiu(j>svOXNxnm2L$eNvSKa+fO}1sra&$19+}freKNI
zTgO5Dw1oT-+XlLhp4IBKAvXI~!$P2y-Zgp6H1-*LGF6JGeo}9#C)NU%sRPIcJ~ZPs
zOf#W>nm{3_)p}ra3U!`JN`(jD^dV<Z!p<Ze8XSPG6QDt6D9J)}MN05eB<a*IIqB?_
zj$BOU;T|GeRIyFlmm};}0Js*Demul<{5C}TwT<nXpM^jofa%J01COhyaf>(|Sf;`-
zcZ<>YB#ntmw<ShCZJ=nLNf!0s<5gW9m<JA`u0|!uvd+e}_Dax&PdlY+)xHfVTeA<$
z9WyPx@K{QW(D}}wBxNeu9y6(Xl7Qc}a;Gnq%Rr0Xgx^_7Wiq)Jz>Xz|us-&81;@=H
zRE_Kd%wMsWf_~>{g5~2mCw!ZqJo^*1@E+f=WPd!@GpT2>-yNPF6SVJ?plijhN2WPG
z?B;3xZME&e#XbBVdV3IH7Ip2FAyq<ls0jQu57Izlt1kf%#!-srE_%OgGUs?(snQN<
zC~{R}X5riy)!pn0xMHg-Z;qA@4L+KB-$d=Yn!fm)MV-NYGz{Ap{^nA+Dzsn@LEFw_
z570o|+LP#z?%1wZS99&8Pxp^^c(Cw&w{yIT(||%M2SH2>$^3@Pc<uLdZngNZYv=T=
zKV5%m-lf~+y+;D@Tlhfqy?fA0{J9HFHy|cVrdYPY{6yCR@b8di4W(IBW#XSJg=6T;
zdRI&HW}mTpl>M!lSg4KXv;I3Q^^G)R8r}OnUH`X2pHOaSQ8w8rCg1*Cac2RaF3F9G
z%wxHQpizERzV?O&`HGo<up4$8DrMfhgPl^29d`UTUlM3o|GJl<ti}T~(j)|q{t4vA
zyxY}2P;Uehpbe~)UW;Iw=pD(AA{pHsY!m_b@MxrtQuMoD&=5;vEH(Jh@~DKjJ*%;n
z@a7KQt0mVbeLd}Mdd7=T?2TV-c)zDYz7ZK;@Co3@8(@~`5&g$*%iXoN%jMX;E*Bnm
zDtKqS0Y;Y890}X1`DYSP%mmG-bPXu|weVS+!*gf=pqsOrVHeVOE}7!a@UKDxfuweM
zWAz>P-+?nC%4E@`r9<gj*8*ax?{{DKCjqM)4&FZ)W43NzxsE5Z%bPEmoC+hbYMe!w
zH6GIHMQ;bz>>P`&%5zTPpu1LKRR~qw6+f`SkHwua-)Oa5G>&u$HApo)RkssmH@NEH
zs#*IW00{~nx$xTe!qYdY*mSLUtp;-Ki+f3K%#(rF)y3A@c^0E~#%eSU>xW$>eGY}A
z`aJuP|KK7^1-`>>69>nuas_bDB#LH%cY0L;NwINA)B*yfxFxA*QNhn1nOdBhS{%5m
zkU0t&2XQSNsYbe4<tVMySYJ+|P!zBYW2XsV(Nuc=*UxUg1q1FfxLD~-rq(na)3oiG
zds&H7liy%acPE`poXJ<xn*>O@rxZdUS6^m?Iq$T}lE~<z>QNIi|JzuOO3>is)yR^G
zRMGG18W>k7<5rP0)jo8oG-nA31M=7cDGi4xFQNvlGz~f886m0Do8;i5ERfc?Q<Djx
zNm(YSYKYle_i`i+Zwo4WFsV7ZTV=N{IU=!VkN#EJ(2GPp|19S!(kYZvU~x07k>YbB
z(waeWFehWi)r*>Y;S<PFl#?#@HE8H>;YK+Roek>D@i<<*|1{i?{_{&Z*%z`>y)=|C
zE%&|?$UMr90)1j-uIA{@#E~qbZny^MtPB&=W)lf$Q?H8qqhG`m_NX~?2T#Vwo$LsM
zb7y&2>6DAxmOi>q;p`AdHGBK7jDNlR(<J}A;ECYr^KLQH^sr8SuOIv_3}nd8rA)EE
zx?QX>*jN1&lol$#z_i^+sL|;%_>Q8eQGXsOPbs4zWHFnzsL{xy55`#-4<q2Wg|h??
zyQ?%5E;G09b#fh8grP_QEgV`7+yAqE<0-a+;GkD26k_k(+o|~quz))Tn5VlP07cwl
zzq|DJc6h-Rl-t*0K|zDq$E$;YVdXa33n85aLEj5TE;^-t8DkqI7>DPM|5E{y919<V
zD)|jW#gr{tkqy|}a(aw{F$#c+fd@4S!$<_mq7Vpt_VRwFEwVmUA{(L*mQ?hWWib`}
zsGVJ6t*I;#_80T#`wNSo5{HZc6|VHIA*tuf)1*c$?hZpz{D}+{K>S9M-6~r$t@Rt}
zt&QQQzYK%{{63T0wOh$~D@)43ZOD#ixwy@52FKc*eGKp{hQWIn7nR>$Y!~Zy$09cp
zuOjz-cH#vc+jDtHhTIx=?~-mAm!=1XqV0~1Wdi~66V#?8Rw`J{q4xtEIkD9wVnY}S
zIgxNlSd2-hr3Qx)ePWoHqjHl(AQ^5ZP^TIR%+5->8PY#u6GX-f50UZW$Lpy;0;h?^
zBv6IH0C;lACoUeia#HZP^WdTMlxR&;$W1WIPyM*XI}e=1(e?2s8OkAyf@3ToR#Jk(
zKj9F)KV$4K?)DojfJGR(FyzFS^^~NqEYg4}-jZXVk<LsKn6ZEeU}yOAkCb(()IcAe
z&S+x#Vi_041BQZggY=&_@e`z{n86D60rYhLy`v*7^dX>SZbY2sAdo-dNFAbs3Y!SV
z5cmt~Od{j?<V-?PAjr78M0ovQ*iQ#bkxLVhcB_F=6BdpX8)i(%wqS<QB;H3NSTxeK
zd8KAuT8b(Dyg&(Qn3JuenzkjD*PuKc)%?g>KbP0NlxihDZp!6H<$!0EEg(+lVBA`U
zM8vz$j@h5?4o?}O-QM!7U&y2$AGaO7VR{pB*=hYHXmr8`r!F17a?v;ZDXVtNvKgZ%
z$Zq1`8k9lV)1~8j>3|f|N|p??N7~;^J1t}P%4E*+C1U@++1UGts%*u{g4R`0J|#3&
zys^nZ!%<oOyGp-xRy$u=8nBd%Xo5|-5raCqbW>T7&UUq{GzOm9J%?~_@`;0~Z2);|
z1hy(EVdF!6CBsziT<&URyVB0@49kN?>0hTBy|vNb$m<c^m3hY;Ua{^@1T=|ksa$aJ
zYR6m8&__2v^3dM2TVpq3-4ImUnt?l_k9qbP_Snw}sm+Dmi6lq@2e7;S@7bFs_sWjV
z7NUoOKohxV8U)cxjWiS)wgs=lRm7Cz;v*#!K4Kmm9l9KBoX%^qB*HdygZGbNq=Zxz
zn_)+mQZO&F*RQH0HJ%zZHpY*MdLlZ?DO}O1zm}6(4kmQ$h29wydYw8H-qu%btMJOe
zctPS|G4RAP&qxPAU#IT^YOAAOA9#%u5L)nhAJ0T5al3d`T<G(|0)s34e2{fIKE7D6
z>Oc{i<=^5`9d(2ur0{){Czg<x*G_P;(BxMv@mQFiuKgGW1r?Kq2`uEp#zifVAS8Cj
zN3JXXed`9gotw%2oA-i{9tkruU99X>e8j17k#k2vRaO)bnv>H~K#b$w9YXvF6@MK#
zwuowsgi8e`AsqQ$S~5AEOx`RRHH{JI_%>=_Mz$Qe)l6W<Nj;?gpQA7$c29Rm6QhxK
zG;Dn&7E(O<ex1`S(&>IcK^VB%9?J}Aow4Z^=Mx5*zMN~vD1Y#BOJpt-NiDW>19xHD
z$vax6X&YcnJ6t%54|SnlCs^krlAe?JJ^!>$<gQB+zu9~%mY;OZTs|)%;v6)uvUccZ
zq1h|P`Ju}g`#pCL?<FxjwkQ{O<+K9_;skTG8RW*n!0*lp6VCVN>{<4??=;`Dtjy{G
zvJ8t?Cqoho3hFNznO6lGb?5;kxsp#70`N4#^%ejz<?ErJw8_Y+aq74}Vc20D)Gcp<
zgbh~`<H`yVg2&p!IA5PW<(^b$GFClEBG`$v1ot&=i}pk9dVt+Evm;~bK`n?c8dB1W
za2-CKfmv_sL@NQ`?VZ>&ud?~4qYOzInjw=e9smV`FFjn^a@(};=$9eav}*t0)5)lL
z^8`qvH*+@_OB2$%%Qmi@c*yjMnAruUtYO_T16P!!YEZx$2&hx)no{VernvQteqa$O
zx|GX1L;IZ66-oG;#)TY6^DL41s~=>p=$NY>h#3Nf-3WB+KrwOP(?J*~j&Bb^s`8&?
zFE+*ANu*uoobWz2+4W?L_8XYzE-w>&^a%j^X%ig%5jEyjXA31ckI+eU<XzFob#Ni;
z+d#U<Y<luu!ditYxRqTgMKN?$qg_Xr0Hl@o8z-8{L6frz3lN(c$qpBV9n@qX&ik<s
zbx{r74Ff?SB>faLp*qxHD>m5Vq_o9IQA0!PlL&Qn4H}sP@+8R2wazWCKB$<LIt0)*
zQ%iDw^VBG8qMYoWF*f9KI*f?878+(g97V?53WhzejH&}%^n6QI<FrYZJ0B!|Bz%kl
zua5)%vuq#(2`1vM!mq;XLe+ZgX`x!V3H5gff4>I2k~$kg?R!@Qb3s>7*=-8;mgR(8
zM+!@CDEA`qGhPENAuwJGQsn{zmJl2xHv)GK$hrA|A$m@M)e1J2go!~swPVc{rX<8m
zK+$y6&9({!4N%Y4S?rxj=%=?cwLkb--tN6{_9G%#IVjR}>&NQOw>lS)2kCt)&qAOi
zhx538dHIQXpBUbYakug<sq7cU6zmb+uzsnZnfyB13>7aBts|-m%6`HJ7){M_bx7dR
zITjijPR+GAUA6ERl(|dD6`T&!ls!Q|!Oev<D<;k|!yMKm#P_1pRP3MHo4Rqo5?e2Z
z-bAJ4$;@o`btV}I16I%|S;+kihkX*G@-T?lnI}pOt9HwLl4izxWoM-Rlo*&5V&?w_
zXPHDxp`uh#EStVRSTFShG}XTYceNCcV0X1JNX+-!PPA!}gkdB{OWvep3SgpOI|L>2
z@=AhuI|nBtJ2i-y-=P}yooFUtTsM-%0NQ(g;gsc-UVZ;$U%FXOXUSvgIQFFROsVPo
zA=d|7y~!-U17Ts*!#<-;1;f2zflao`B*8(meam2&w_Lxe@lp{1@iNHYQMhn-8n1Bo
z>B^sGu1$-g)97cKpg=K2zS_2FQOKQB!7vCjD-fU$Tpzhku7XiVQV%-Oco%TB?4p-u
zeW(6bt1f9g%^R4ExfY*_jyKQLoPGk5cOKepsZ1kH&Q^>b|B$ouk|(7UhEmd%xeqC<
z8h+0nKc_^WS&KIV3d-tK;#>JTtZrGzrh{GS>RKRM+8P2!8)&N@0(=*pityhewZ}wj
zd!N70TAXA4vlY>ro=!&RN&KYxWu&^1gfj_t4c0m6q$b)D9^8!{arh;%_Q7&tb1!3@
z9&+6N$@4-}c$jvvIDA<v5l|n{PQN9kl$iQx$@5~^@iW2+zm-fSeI5309bw!YM;KzU
z-*j$%VuxknhEUBE0U-fIQAk=o_?(ia8mxIJc^D2~JdYJJ_Up@BPYSgMuG@&x(vL@g
z0twRK84^PM>blT2=G$241V!OE82nGE-BysVpk&(Ocy6WE%eU(^BYGt^8^K+%$wNxR
z1bd2Wb0a{u5Yhmi!gkD2H{L?`nKiyWjN=GBy!_JPV`WI&#CT)<F0&=%-&|!8)xEfH
zExLF3$ry}`&Uv)wcjcMhm%k&`wD)SK{r^EN9HyI=sYR<t|D)1#*(A#Pn|3CRUQO>B
z-9*Qjm3IEQk67aD`rdB$?Ey(ax$^61w*@3z&aJ3B1NB(9ME#_KxrFCMLq|-Ek@KDF
z+G&+~zHiz+-QSCrdgU=z{2Kpqk!pi;n$?X}|I-OeHZygR4)GRU%WUY27V2N6A&buW
z@3~Y=`|4<QG1>~=)=o`Ho|c4q@AK*+%QRaSLqC57knldjMshJ?_Eqz9Ug#df0ED+b
zrY-&agyer6O^JQgc&%1Ql#39J*AcEg6lhtoUVLM3o76_9)L9^Y5~7t={Im0m`Gg!}
z_P_rbUgG4(|IAzry4sv*bgkq<OQ}0F``Y(2h*G;gUoT&;<-65%`|MS`eP9j|T*o<b
z$2T>QqThsn++aUG7Lf?zPW8tL08mLaB_K7iEYl+VUwV;od_6R+LX(~a_dtWa=q%#N
z?+*qF7p{(Q;B`Z}a3c#tz@|q*?^h6Kp2Y8_S1Y6E{>P_e+4zM{ug-9~=Dq`L$x7eb
zfS0#M+!CJ)%e6La)-h{J#EEscvlBS*FZE-m;3>x;vt_rc8I>xOU<yF^gDd?5nYw&O
z5Qr${MQeM7ZAJ#kL_6O$-v6r6MfRd+l#mbm>90{t#5>y2zY!CDDT4L-6q)jB<I_-x
zFs00BQL`$!t>qKSKii_ax;^Q`lwBP~R|wYOzs*&%SffhKlk3eE#F%^`XxgYo$`Zwt
z{POs`K1em};o)%oF0cUCkLje6Q&$RU84mgS<j^5fiac~_(lkxkwA8E;WTReg&b?%r
z^q}$4rC-5LB~e&rUxxqA3fQF}!WaOdaIP`-@qT`769|aS3bZL&#yU)1G1|BMxkbJ?
z53%WtyMN(6es5am$Z%y%cc3gyB0idaqz9*<hzHnBikqhvYh?jGqjs5Neh|!(5i385
zL2F^huN1^7+_D<|RPOs6wDI$K62I%rF30iy##`CV@N1p$h3Uf;`%iH)?l-`a8s_7l
z;fOu7DvGIkPvuH9oA6@Wj@;?UWR-&T%h9L{F6JO*c44FLpiSKYVglK<%f+iR%RD%`
zLje&S`_tqZ$7=xSAg1SOMU3s=<gSV1fu%>ML9(kD8y-ahQBi1Fxm1}A*QimEf7mz;
zhL)=?1UQ;+?^B*>Y)ge%-?E#wlvzd{#s{rXwiHFsOK_mlux4JCW9@@14lRT3C~dfF
zZ9z5NkYx&XMpXEiN+4!MCOpGWtaEe;>%Egp7-=oC83=$}%L>s}qu7PMQZ=9@iMC|K
zM&m4>f7e8;c5HH#v#eJ-B&IU3;j09nR6Y<<uwbK=W{6#U>pL;VDEI}RgzeIKpAteL
zLG5Z+VZ=blvEQ`hB9zB+NSOtusFQWD-E-UTTIY|BMP$e{iEqipgz5uuhMVzll-gS?
z_!+8!{pbK-NliDtaG|S43A!ScnUDYs{C?)N_0ENJ#y49gSsr(T@zPR}vopUAU+;Dk
zBxOW8nWdAJW60v<;{_vRaDIvJ1S69}k;E!qR44p={7-L5ht;tf{FF5aBPmfgh{kE$
z(-o!zlylTsC;3^NuoA+?g;}9jHF#$T$*&rDZA`%0QIpfxb(2`tl~8(mthO_Z{-mge
zsHq(d21uj>i-l0-%nU54IGuvg;EI`(a&M=J%I0cr>4~2ad0N)J8%d^S`-9GJ!J*vU
zPP}BxS82H;Wv-*bXi>J~G4|#BYg-80Qo|*`{jw?ESU)$Gu8RXs%G6otR`+%^UC!R*
zFi61ti~cB9XnWO<c|di+#BFdK-{6Tp7mS&<H6nfjR0<6|Pjp`eCMz+4wmxL63q4S0
zzw$1JhBOh$N47RO!ul<p*gSlrTUJ(axw=*%b_#LOu3xX4_;2d@`W)D2Kb#xs)Lbv)
z=pbycN0H4IcejXW2IhDs<i7c{pKXE(QV-z&YW52{!6T;0W|ta-fy%Ox_wGu@C=O1m
z#=b1nUcXY%4&ox*!QkdR;hRpweWS-WG!LcxvM@Wc8VoEpnO>3Knmu7f1lasj`~)u2
zP6r2vcLT<r28pu2j$@{K*Euff!Q$yOHvMjOMgPS2xD=zpdY?9U-kQhQYIXxWNSyp!
z7OcMydaTOUCwHQ$m@;f>{7#)1sE1QDO`U93g=w4G>oUdH@G-P>kMFS)7*HuB8rQ(N
z=Pu!GR1xAXP-$igIi~Y}ayt%n-EJbeUG^zb)A-g~uQEAJ<eKpV+4wN%!C^z2#6v42
z1o~v>2zYe30asA*A5*EaQVyV(f&c{hgD+NR!`m>##Mq!M^vO@OUeI05wH+qT_R0b2
zE4!U}*v97|2tDi1H3$48eaawcKB@VIMCsywZ&b12h1NN*)_8?Xkd|y6jRhC2LKnL?
zq-ibd+WXs8aRrEy*&Bt0R7Fcp`gcjZBJ5nly3)nCjp-SjZt8gVh&iB9J>I^5Ik$vY
zM-?wjwC#fZcdG?Y9qnC~HvMjA0vUlvlQ4WS@?>KNQIaqh`6Lr$iPp{Ol!zf0HgrXU
z$D}|GMJC8Dd2K&bG>|fuplz^?>tX=(c6<#iYBJ`pKb>IOJK?R=k>tpwFVtq+)mKRo
z2y5S$oVQu&R7Z<c#gc&S4O@KH)UN|_8X6KyV<FV_Cu*Q%6D1Vn^mll=r^P#DMX-_b
zdw5bvB0+a3LJCw{{lVG2a1UF2gbzscJhN!`CHRTxxa&u@`n1^J(P<6^KoypMKNB^t
z^G-}ZHkVp{3G<b%zr<C#ZLG;654sDt>)gW8A3geja*5flT}uIcawTn&M;^s)f~45+
zcys=h#?AO;gN}tgl1Vh*Y&adY<Dv8?bJ44eS2Q3MAL;E1^Qw&pJ)Elvt8XU|nko(3
z$NgrnJ-k7&AshQFT5=y+ma4>xdfK>0!^KzAgjg6V<wmr$=ShJ!FG7yymtFuTGFF@l
zh-MjT-D9SjiTu$&!^`Axsc%u$5OE>1lp$Dl(2>loSUyX&GVJoUvBpCdoi?KtA}y(Q
z$NBHYEqsR^C+5-JxWuiVKTBrxr$do8?b9s|MWEvhO{b%~^8Q#Uguy_+Vx2dz%MU1G
zgUxdTtLDKKFoRfX0HekHw*L4|hBh`!S>*<1!ixGWk{|`3oTNQyWJEc8TKa_$E@`uM
zMO7TJ(r{R{Ose4`HB*3DakaQEP;*^hVfrQ=Hc92STBX-gH(OWtt9BEyiGZWuRTxVq
zs7AfN_Ab<*`j^p2g4+zMI@~}MQMIa$N|7MA7Rcr4ZORO<HaKaozyF4TW4n_C#wg^!
z?8C*@bi8!H+-@Vt+aTAuaG+hHRX+)BYe#zh1rr0oIQF>}qODyqJ*%)B5EaiD{eovO
zD1xO|kzlXoT!VP)H9wh$_D5cQW*`lUT4->vv!B|8K8>J@HuMZyR_n?A=JT=>q41vT
z8PmZfL_-;{bPVOMIr@28C%N|7?qZD$98&>B-KP_PzPNJxg&e)0UZ!e5)+fP4l0d(|
z^OWhpUTclSEs7bF{ry6ewm?Vh2l>nDeC08=ZTY|~vy!Qo=8Ctw<j*syAYF|$pIHjb
zg7sFlc3)G#lO`(_=Ob6CXN*$5gGp;Zzh*yo)0Gjh4XeaY3;zAH(TR`?>&<ejO?lYK
zSxY9M7_;fDo5)ZdK860|i?;9(i?wB&7eiV?OdlOj3qYaSr&@JI(vi4Ld9Fcv?skT0
z)efrSBYw{y>;J#{EZV#b%Dl&OvgH{K80!QM9;f==S6^u?F$4D-KMws2>Wpi}sH<B$
zEbi@gGj}l2-C4V{Bd@z#WM(9;Y?xdnK#7|!S`$X~ko{U4&qOp$Idfo3i;q56oh#8i
z33q>r3yf6%fUp7lSKCVWMvySbrm#?gY{W3bowsfct9%<FF*&gfTCsQyg|-uM4)5kH
zUnAW^<{H6)lAEl*s6_Ezw8SMzDgG-Gq=i7K;mH!Uqr7QyGxB`lqGO=0lC*aofWuMX
z>e>yyo}-(Fv9c4gYz`7ie;f4}chV>95}~q7gJ05iCkF$YO$k%e7n4a0Z@Ym$p-E5l
ztTdh2jCGBzGwoId!~f7;BkVM$w>30AJzQH1;X69T*-LA`<a0lGYgN{Js+Gzv-21V$
zUQ1+V+Irax?gZPe;ypDGoD7r<@co|RCwG)<+@Y<k+y*hSS)yls?pv2cR?w*v!fQm4
zfka|tU>^cU17D=iU&etg+JnSKY$uBqp4l4ghq?CezQX_k4=gf)B8f53-}f-7wD1oL
z2S<mle2NehFX5GPq++9T73*`n@{qw_wK~vX=w$5;<UH5#FodajOYlQTfPJX#+-BUY
zf0U3{vXn}+kk@2(eF`(pI(m=Mq&V|LM$%2gEjWA{oAFQiGu2!<nV+#YMSHfNJ-@5<
zsny-d-)TY>YqqQ)Z#xr*!76zwk;7_cER)yExpoa#$zI;y>_e96x#o<=zWwtmx4)MY
zT<of1rw{x$B+EJb-**CWKvtHf>u{4W?{yxGcVtzSeLEbH?qOUN`lJt5;BT)j=L2&@
z4xjqXE2r^&dxgdweBiIvQ-0HEFXC^@lY;U_R7TZrt{3fG4~SAmx|kpw`zXa8Hyrv*
ziILc!30GL!mMu%E{V7$F$>peQn>&k^*n`)F7C0CTY(Q<}vG|HPfCq?<U*1JKFc#7C
zkAY>74&j9}Ta}nJD*0$H!auXR{v?>)hJ7A`^*WnnLbGcXRMx{o#7qu==hE%86_Lq5
zPoxKQ|6xXJ^}^3CCb!!9Vf!Pypj@zS)eUSlf%(q~xL0p^r<;F;>7MKICQ&1`_2%Y7
za>ot4&kCGI+I;@d6sUVMO)f=%dmo?zS^-4=c!k_RoacX7lOh-RK4Crzxr2NSj}m3B
zTJGd8)tuJi0)KJVmNvvn2Zip<{H`PQ+|AwmPe-Ij<vaQ8fa+&&hyX|e1{$g;K$uE5
z8r45g-VU_(nX&oDt#x!qO-o5%<2}8oLs$@tPyv!V7L=GJ003MP6*skD?D1m{OBc6$
z&E@K2;}!vq_6HI>+sqei@RfZzPa-E8K&?=*M*sRA=G&>*==LAAl&JtiKcQ3&d_}y?
z3ke4C7H{W$y||k_$y1Uv15=}3Z&6ddNfw^9)zB7SBBQs*NF-fVMHrt6UAvu_ksU7P
z9LMIwN1PH+ok?&Nyqm!1;tf2O+_GkwZVR?f4`SQI9QR+zblsPT+8rOAkc3bsx0cbi
zw9%CeXNH7&wGAEw=(FA~Ku(6(@ZoXH?ryZZTk@8!yp1w(lII6$jS3NWJfLW;30_84
zF<+0e!$rjr(b2M1zB=x3=&i`(a!;Od>tFh$58y`pq)zPX-EI21z1FIxnjYPC(7{~$
zJ@+Y?#|sTk{&)4q88p;32;4aLy;za2rgPQo8S`&t*K5r7_g5}wjN~x4f_q59k2W}5
zkci|`i%CucT_<D|I3;O`UD#Qu)gE!ROx%?Bde)(yc3T{J(3SAv1cF8W>R0Qk$*-RH
z7jR_UI<R8$_KH>8BlR<er*2uAhWfJ`>;D8-D)PyR#S}bMSH*=5F0nB@gilM7Wp~9b
zAv=r6Z3>UCit7pyl)~$hbjphBg4W>8+3n3ZBR9pZ4LL_q;!81dAO}UYoYL>>Rtflm
zgX)k2A0B1JQR)U>_owSiS2L_@S-_<jJkGyX4ZPW8%)e$8L=L-?930=u6<|G+`2SN?
z$&2knIu{Vm#4V|RRMnq7@`ZTRg*fnKA#*2k7SfNZiq2g?+_7fIbfW^lxjDB@1@9pu
zEo6v1W2p920MW{{HEQ8({gp2M8-2Vke&OcxUho*^;|g;$6KO|43;-*etPIIku#WqG
zh8Jn2J#+ro6zXb<VrGNd3sFq|B^_|aF#46k?){2;=Yl##1)qZ9yFFGkiX^p|35$z)
ztI!-5IEVjNMT5nQBq-1NR7y#AsTZo$VNQl=uyY$IdPxNpU5B8PD-06rEPS$yuC|j-
z0e}iR+G5~NIF%JBeqmiI=hHLv`l%ITARm}FxBq=Qz*IiA$TMxv%jwVpRvh#La|ffm
zFgh?zBH|Uj0_rVK<f2QE=m>XKSWwo9h?*%9p7~GwT^mlFwkf<c`+in0TuE2bCVk_$
z4qFK8+ol*uf7kLRfJxNG4KFfq8u|qgR%^xO1<Q^Ia=3T%6L#rHamS0gB>gSfH7bbN
zmHEM9EF+@E_Z-D~J{pRJc`$-$94De1nTjQDxWLA}v6n@?7Dsxq{DZU{@<R`Q`b$EU
z>yYZ6@&PWb6u?}+5-0k!+YjnLSF8`lz4dq0nYkkMQ{)oyn^cQHh})0y?lSYoi3dYE
zPV+jNpKp(d#|fJYO|X$<DA1XS`zFAA4lRXMqelG${MUkg$F;|VAcL>Z+X*sqyRyBp
z81-`xEXn*OKi=mneB#_E4dO_GIq#q-qEZDIDSM$UDzvdL+*!gci&UZ@byhNv@WXp@
zdn}Ys;%dK<KkmUZ@K~}2<?#Z5CMh)MO;AYudV~TkTEK6kY=vAlws0e-$`nnZ*eFMi
zcmNAA3)ggi1$HD^nP7Q(y^c?*uV@uJiVcw?+geKU$T3(i5m1bN;k2Nsop&G6mcI$x
z*|QVDBo^+-peUV@F_%5prcmB!s^wb`n@u}mq~&)+a#^>O_kdcV0xAXkWZH_5y{@=j
z3eqJ*{V@AT^b_83=1gf87F%;2yp|woyx@YNteLIXoGsUxb5~q9{mXe`>a(2J;w?`Y
z5h2CtM@wxiNue5-UVr+(8rs&_y8qn8ouhs6&CTa$>R&AKRkqw<*G;9Q_S}C9acL0J
zWg}AG$;QGxA{g_Mt>*z?m*TTbLa%%SLH`K@x2+84*SJB6F!T!&3*$txXtl1!EhJi8
z9F|XAtj$@lDLZ_A)T|Qj*5SB0FA%HUuW42L|6N1xcz1H!c_m(5@2kC>vc9~qd1_jy
z`Xa=XbN=cOnw`^*rEwnpZmIfx*$mLA-(Z>5KF~c>DORIc!#o09njP-OuDb0_JdJ9&
zBgEzUe~30|#EM&^7pz5H$X&MA;~B&$eFy1&b~$Nvy)GK2m@S)ACZ<e-+DiU*8=&+*
zC!v~J&9#kVWuV8#sH>~4)h5A2z}fC^KScnD5Ot0Sr_>i1Nm;q}gJrR82eGlv(CPxg
z{E@6X`GOrupWX$m?y8e}3N0IVB2O30SojeiJT*GODz&O^NK96VY%B`Za%EbGSa6yh
z6NyoE`P%d^I34e`7ZIA7T(^+SVP>2~sldC?;OzySAcQ|(Lpn0^#l{#l8Hbw<DP5ra
z_;YGCOn6xz?SQ_SMYEES=3TA4SGe(g=ruD%Yvz`gmf8Vzuo*vt=M1Y4sDZ+2mvoiM
z!PE5{!F@07)zulIlFJ#DeoLP>R33=tn$m6+sXRAOkvy~mVI`S<Im`u%!O7V8j0T@X
zDuOgh&rt=UKF4*n<CcR%QXi?=?X-%RQIM_;g*F}YKuE2g-BrOlN7|~-)#G>>vt;h^
z;?Oe;(R~8W9n&S;iguW0^ucLVHVcEB!~VNk?V6u6%E*vmkUngP>a4m%K1!Mz9iT*I
zxfUHux327N!|)q#ACBUl1N1*L=s>Cj8HA5Yz?fX5po^2q07$!3zlz-UuJ?Cr!b`uj
zwMso8-_)_zjkc^|PIx(F)`I_QZuK-Tg8>-iVMsiuF|Q2n2TOiFqU<7SMoN=~>N|NO
znM{gW%a%zlI;(SS*Q;7j#uq+aPgxz_Q#(<{zLZWnsgU7U<m>tI)qh(qw*`Qdcy}yN
zZgxN+UG_;W3`@Wi$>WDygfI?f5?84lfEly48E!7MCkNigpXa}05xpyQ8Z3^$djZbi
z)qh;0b<Ce6zUz<54LJcHXUq8*)nsgV?}clBvW?n8ev!C&jJTA5kq$h!duvkHwVW(o
zdH^!|8=9dwxGjbB#(c&uv9RaxP0g*Tb!hSW7$nwf7I>?0F({g}&YYiIrqa%isR^z*
z5H-{=th|;njnle#M{tsdBg3DR9{`Lv=jGdi3WH41iA*`{2wCL|p~5{(mPU}q+|Yjq
zGqQz+(YWJQ%Nt=+s_Nl!d%&CXDFntP?*FI3$}I*&%^26>qlR`50?%{_9QHJ9z?`b@
zkk?N^nEffhS;Ii0bDE+x;NXR*6sj?Wo1$Cw6!*?TDD~My2o>~-y(<IM>q_2Dlj*ZA
z6f11tI@ujOtq*P*#qXp07P#h%!NF4hYuVbiNNN9*_N%lw_ytBzrYVpSuY?b~%^>u9
zKPDnZwNp+{pIoXuO9~&po}v<CT<Itz?B2kiLZgpU<v?GD3uiP1f<6JZ!sle68_O$j
zEgozcXO)%^UPX`ZC=MO4<LacF$BUKK5*DGqc&FEf9asRXY{}@uV!NT-&ExZB4Teg*
zOB|!X8btd!sFKTY{@5Z~Lx{|b%ZgA98KotEc$9Ua@~!?FQMQf;7A3tR{iaGqfVuSa
zo~7Y4(DKV)49dj3Dw0>c)+1k@g1OEt+nvKK=aPinPH_Tt=L`pczqOaTAu)!J#wWod
z+Zt3BYo;<*O5hZx!8>HwJU%p=E+z7Zh`~)Mfud+abX(`JsZTegFVquB_c8q~YOCk0
z8ukt~z<HA~W)sSqq#~BGCN@Oh(;-RfwneE9U!MKkh{?c>vLk2G%;`0hNdpmK(k=CI
zv12&GJLOkLL=rimZ<Y74TD|gqbMXc1@<7q8cL?T*#w@QSn3pAb+Q)!PvU*x2!g3E$
z?jXJ-LDww|`4mBtUPP0hw*s$nSjhM|qPJO?fpg{|%e+Ojh*QzORM`K`2sf~|8b>bX
zI03;Wbks5Xv2$$3xpYzS+)ahOi&t2|D3hCvnvHDn!D<p<T#Zk!Ps&35Xus=3tr4~Q
z8Zo2FwDW*gmRvYWO>{hlVMNv+6E$=?ynIt3rJqqp5hLp6YzxYk1Zz@3v0IKlDml^O
zU}6;GD*FWz%CvkKrep-yM=##(0|QZ@zb-Y5*Vtx#s~SazS9wJ9W6d<nB1$K?^J1F9
zSk!Uz{@??6Fng4(_RdWU-$*w*Z}0tPEILBS-#<zVZ%x>)Qc07mgs#EWGf;p1BXbQo
zB8EgTj9ZXW8D7+XSV!V^P*Q=g`{y5mUc_FcO8hEYhSx(Em#-0=!<!^KUyND?O9HY-
zATmK~81kEB8Hwl0Py1u}aH6+)mSk!B@7}-8m$Jiv*kFhDKermu;gj(ooVV8=%&wL4
z-phr>+Av}AbaR$A<jV_K6y$et)O<6?bgI3WNmE!HQK?_(=zN(B3jgXa%r0x5sLWn@
z-hfi+XeT92JtD5pmJ{(ZIyQFk7U152?}I=c@*`yU<VpO!0$Dd!2(@DoiYqPF)${a(
zq~;y~5I5mHeByi0Jzs*kaaICN7LPCAEG6{1r#C<9EY0mp{0qKM){A`Gu9Mz*uKD?G
zdqbGWk1Mq2(&j~0wRh={P|j#qA*_Mu{OIh|!<)t!{N$4trc0b4zF;nvRFik`r*c~-
z^J`ZwJRE}pbCqglP8SJtnjJc7d+8*GLN*ydv)hgDHdnDbwQV=I)|ckS4$ZJF6i4Nr
zZLoorp$Y7!?xtDhJIirbNC|wpC0F-m4Pt2VWQ4_*g-|E*7GD(AX>l#Dpgj|0UR>j7
zGR-i|IH1nrY|hE8ja~R*(m(N|G4W)o{H@(kYM=qG;}|=YiI&qF+{-`vTws$9jEexU
z_>}n9fT?(2ngQ=r?}1s&Gvof5B?1~K4kXBU$aBM-V77%F9Vog@A@o#nB&0vG9F2NV
zA90elOBnIl)>$Hx-B$S(s`Ujr7;?qS(uoO1n)P$)*SrPZX89M-vvT*nXNc2lZ5XBN
z&Tl(Ctc#9=+JIUFja~G*Udcap<P7Nm_?!h9WqRwKI*~5LZTv-=A(SJF6E2rz3XcTP
z=gUgLT1YR{rCC{Pw;ug8On^d+Ts=Xmbhz|L8H-<&YcJQnz^=@xe`eSvS=KyFdFshu
z>1^3L<H#HCXUDyCE*!isZ9BHvHqy}h<0f3Zn>pV7n8#*Um*ih|0m8ItSjPnb()iTG
zv4k;o!P2!xm8%Zs2AsEKM+Wb-dV4P2KPkdaHyS+I`|GjfIie8_2K=-u%ZQ0Hj&U+r
zGF27HMmg!&E;w~MzjWKgeUn(XRyKv2ao{@?px2Y$4B}*ok|fm?ekI+L4z3)oBr&A<
zW`5>U%s9}sQe6E~&#TP`FK6%ppcFa%)IJfKNq2?aT%pvc3`NGje7zzhaQH0jOXyR9
zmxw{7GtiN6bRc4xKqf6U2jv*-y*U^SS!s#liQ?^2#unKvRa|;xtk0JV-!*Dw6!5Bc
z9=eL$*;}$py)F%?ZU}BAc<dcUR{wiJB`%i)950r~BI*+AyVUA{T0p9Tk1)+{(f@x1
zqH^A520h7NmHK%Wo}Jg6?tT4sew)cK&^-|eZp25Sc<r>;{YJVWv4dCrvxlbD)mJh5
zihpG=w;Ma=;;chHe_D!1*+i1k{yATtsaCREC%a);p=277RwXl(nsV(+Yi+tRw?;6t
z(e6nmcQ=A_0$|{*A+dPjRSh=*Xb#64f6I;vCUyEL$ML9tI0|v3#<&FOzdlx=3MPNZ
zB35F@&%+fbwGCh~2y~O%Ra?uOdDo=sBT2z34!AC^Hd5GyC|#$7dhFtuj6S7uB|k~>
zQcq?q*q9*$KMtNKNnjQudTJEw(sbNpk9j77(K)aUK>7GuB@ee*Oe;qch4y%I!Rbr0
z6xdoZq*4K|IJcEON9d>MD|Vjs0zcXJrjJFKhsB~qhmVg}rwZ)(MFWW^QmI2kLS{z1
zgr_AaTs<i*-nebXLg?XKd_PST7lwjpr{bcQ6o|j3+P4DVcC8q1E-C(pCpgmoXIh4h
zDmh&XV4p=tUsHz2FnYeW^(2(fBhm>}Dh|Ufj2-*hqV{t1f2^b@apxC#Ch1VimG=M8
zk}T+wCX#38twjc0^f!OHAOSk(<?B)pTPqu4(YmZ-zxPGL?0;CD!C{ihwJY@Wmlm}O
zNAJX%3jD5^3`(dS$pk!I#I?~jVIfmf0P*WiLc+ft-r$Hlb_ooaPt7M<#rV0Jq2#vU
zHxI$)r3+Z#;Dv<xm{fW{Pgs8EA8k)<PfYg&xKpplBeI4rW$``JXM&G<s|=U#6<io#
z1`RAfKqW5a+O@el?6EJq33b%NU;7ZFG$PS-aC)~2%ElZ9LeUFwyeGk*=-=y9fGo@F
z0`IDf`)T$#^T1a%;eP@?y9YV!Nlzd3j|t0djv`PFJb#+1JtKzQ>~d^!a<eC=Ppu1p
z?Lvkrptr8J-*z25WI&`Og(EcD&MNgFWdhvZKWB>-@+D%*UtkofbuaexzJ(+=>rFiT
z;7!pLEsuTm`u7~6;2wiqD*~vP0dSn+rmW#_Q3+83Z5}<ui_Tamfx|srRjISb)F#U#
z`nS0cFezFk&2<Pxkb+{3*?5!ok`4}2q0KKeb$Zm5JQL=BA`+ds18HTVa??o<=Y4c3
z`WgD?pkHgJCsLs}J5uUsSW_2)znH_5dVvjySfo)XlwlrS0=~07PQzS00Cf$aF0ZYq
zv!Rf`0$ki#<n8{1Q^53zNevSX<i|90)5MusNY>IjX4g~4$VbJ>d+XIT2m^M>P2C1o
z`Kw`XccDH9czE88r`>S;GW#(_A@j28apvFuiG#|P)-1|Eu)D7ks%|EK(XFm)k77D6
z(dxu?C(|$nP+NH@5L;G@0?;hcOjkrr7+xwN#-JQ`o+@9qFNUAg*eQU{JIhX~kRY<+
zm^hYHKP4bcR~ylDd_a*d+1`!HA9_KvXRKGtl{nsbfvs0ZkQ4v8s)}^#lOD(UYi5nP
zSl_0zyIXFwR%C;w-L<aAc~GT&A}M5|Xna-7b?-LU`*G`DC3H<B0*q5+a(v{7m51xs
zT8y$hOZ7Tz{hu43S^lWADf|f^=h;H2B<N#3r0UxZpb_!6IlxAEE=@~Z0MBK&x>7;L
zwvj6=u{#qU33nCaTo&$q78P5x9(;sZmP7c&%$fZ@cZLWpMM-j{r9KlE)TH?)mDQwi
zuT|>|rS6tZp_svJ0+_YfI(d6hbi)+_$18{(5e3{aholpk%8su?lm~a6p!KRl=gT`4
z42mjUu-iXEkS2(=5Ztd{?UJB_p>MI{8loLbb;340JVNV0H&i+4LT8rD0*Za6%g;it
zwl@)5q6gMS`$+E|)j)bid%zBcV>^g&yHZv=_>AaLz1Sp-0JfL=Hy?hO|AJGcU!+>R
zIV;ro_COu_UXXGL<C+(XP(#kZoS<ww9;Nq+7)e34Jc8CElaJp^_l}r2&zJOn8-Eq;
z7@a#s+C?H$HW2Uv`TpTS^QET_#o=n28??g<!3&2<#_ydCa8ECMq;?F;z#O{JZ@s+s
zZdggB&gEV)1q4}9tl?j>c`2E<tJNI%fyr2#Ldj`jojXQ#LmuiFFOpBkN`1^2WgM+z
zxr9jV8rAGkL+Yv(81WOkMYytlhH)Iq;SCr(st|pC1J{Y6p9<yOpEAR^t1_X`xgnP0
z=5kV6;j_)7$<1Atk@R>d&+3Nz%T%|YnTqNkBM(i60L~H_+2qoib?XgQ>9w?HuN3iP
zo1%k=6<pNs`o?eG$tT23<_P9!SHtw0Ll1cS<0-3q4<%RE*f2OlPB63non_@OfWu>8
z#S0mF4n)=-OfH9olwq^Cr4a=bMv6ZJznRr8&^MP*=1DZcpogucmZ&4a(qOpn!u}!&
zmL49|1RQ47>-EVXoefA7J8QKf@Oyf&+9`t+oc1I;pK(1lXH1@tmI7qG=MAR>`F-MV
zaouhf=<QDomg)I5dZsPjTO8u?IPDg(2PoCVl{&}AM1I`dS{IHA4|s&LxaTqE)>w6@
z8?T{{ygioK?x(AYEF31h)>I&j_=Y-qmuc290G9ChY>%6|rsvGo%anDOc1ig>eWV4z
z#xdGDuVMl>JH=~Sl}-H(qUb54fwU(T4*&c*Cbl*KvyOA~#c99-)3#ClUn%0TQ{!sR
zg@*;Xx8n5~Bu7Y8)>pMxqf2s2G&DO|9iH}VgMZ^j+3fSnm?hZ(rfm$<&|z2;LH_~)
zfQ+ov2gD|Mf(3%%!Pt{1kQU@u#KqrVnA>-03^P(3PbKaM`>Es!z-Y|t6HJLjo_Y3p
zD*lLOCT0Dhdv2Xxd?N3XOXZeO(;8j@b^Vg7@X61w`aqwRT+C;c&u)qCaq4;+=+3LT
z2G1F*s%F51jEwPUrajPtVhj3zJ(BCQ0iQl`eOOf_cf$I1@HH;Al}1OAP;Kljk@hPx
zD;O$&-7l_WkyVHAvlo{85`cz(tAt<|Q&ue1p$n+Yf0T~-giFW!QOSCXrhFUn{)SCS
zv?lH;C|1e=^E!$9tvNfAIXP5_lBqKK?v1b8EjDmI!1?AejUr!Id#=(j#8p_U0Jyh(
zQ7)!&b>ntp6OoNp(y=C)=`h8{s?@D#Q@2CW%X$3M7<a0!Dd(m+M4p>C2Dh@aVDv@l
z-YkN>5m=9YDw?R~uslV-GDNMlzy<2tKt5aMD^rT%dO}dEs?4ILPMLa0w03Wee!X(M
zs0~gFmmw8s8F&^FkvD!AiMjaQ1ZZ3m)b{iTS=@5aG8Rt?i6)w#N{luln}v}O+2b~g
zgW&1MxCsM#e!dws-%FeN9qnv}xtBJ1T;&Q8X;2AdkJg0^Qg>(-Q*8M+cf3IwmW}rF
zfwZZ-h8O0nAo+vl#F0rSxM6lYe@Y5$s`zj`Nw4=!dQnV>$#7_r;z3uD7r?;32)2tA
zCx}Ln8$F0D8B5Y1xlWG<jN%?)XB$Miod{Mn&)&PX>YqJA-r#6V)7Yrs5xRay$e!~$
zm-pVEuLS0FAX@(@$lZPZONPMDHN8OMzEZ17itV2&dEVQwlybBQUd|8Fw4iOHN{nf&
zgR(~3x(0`$>Fl>P{u<-c1psX+gV@eGpJ;SmG}XJquW_-GzVxtX$ba)mUOJozzqi;0
zEu0yH&26uCB5FtoHtXET1^{Rs{?BLeXr<ftY;;DoD`7nvtzGO?P)9$3{{q=1lWFOQ
zt97his%~%@Jc^zNT49vSa0>`P{B^S5Fb3jrb_3A*#%UzmL#0Fq^yn3Myg=$M!vgjk
zzk#e6T5=VT&ip^<J2fwVjsOh?N2QE|7r`YrR>lD|c(V!+Imi+oNcZdAZg``NL8}NW
z5<eHi7=HG1;L~U8k9lv3Orpd}?1j<2Hk$;c-?BN2wYBYMD16V#aw>x(u`YNb@%QZT
z%yr9`wbUwhq*uTI9}LTSCd<El$Fle-59?18UoYAk<YD-HM$cf?IS~tY%4a^Sj97<^
z`j3`SNCI`@MV%d9<9BXNknzPI>t5TU1Gum(rQ)UAwv8>1Y59L(?DQxb{6T7(5wdu(
zCQUZL)`rJ$E;ef{FBH`q_)q1ny>#Af8V4cujhG{e9L5*`jDsx2oF;wlh~0MU=+XUK
zLij9bt(TE_H3CeL7!p%BK1_2iF`hSh8@}{(`znc`7$f#!q)PZ^g`MNx53(+#IpSAF
zIarRhnthfY?gPJV)WGVr#@v3smV>{+ExB7v_>vc*pM7ImI(AOI7~$`~zXfOY$ANId
zc-5O(EHf$qmNuP#+Y1J%oS%<g*8<r!@M>P^{een#@ngnV#8{>@G!hq;s7>AsBj1@8
z%jTHCvI->-oI$l9GdU74#uPi3Lto@^-G~rmUItj1bCXyKLg9rXTQ>L|Vh-2oHFP6c
zx^*bQ-_hU-9bqh3x_vcE(l7{!4>z)QCbzt#7#Vi~Adldeb2(m9uSbz<D+adnt3Tsb
z#6^i8g7^PoHM%l<Xm%(W5>OHHo$}fWZvY^sZk%_I?(@P)R+F|l?RA4p@`)XlG%+be
zz7YqX13L$g+;9n$0|?Cs3s7apv0V)UgAM%qD3CyFm1_^O`fu#buRBPdktvR8OI!lt
zFZ^BrI-~e2s0%VF5yMfHsthYSf#?<%H5pf8MtHre?H6a)GR;a3x~;*Pv-s0=qNq)O
zR1O9y52NH7O`DZ$TQC2bEj_qXXFrn>W!p05A|xnO+bSQlvUB3*kKZs|2lOaP(7U$m
z?B{#tR~js2qM%o5R<>dn9h|(3n!L1rgf@==`Z_?WAXG!b1b+d`5fJlblUR8KgiXWE
zOMr56Af4Gbto~j2;4maa%|JEn@oPS?T&pV89xBHgYMcMl7X67pUIT8%CwIws#TzHI
z+Ia%W8;qzxz=O!Vd?aR-s@|h&hv&NPqKsnxLpKtx&o#XRxy4$6A|Bza!$74P9b?%A
zNSwdZ1<(X^IqcOPkJ4;js#pHK_O4X6y9TlO_%&!qH~N4V@dm>>Em3mN1B&AxY_hzJ
z{3#}o3U#L>Xb5%3lzh^NH9V1tc_u(pItx9jpa|dpo@7glyeID0lgV^{hgh3Vy+Vv<
zM)jC9FT<lO4ujVJyEZU_DbA&d;*RbPFq7vTCtpp0ea<?RO)6pGzlZ70eWksW!>N@h
z3=#t7D6Ey_s{rf(>>y7mlQ7|Zrr99Kp>lRYf`OZhVC6yVFVsneSa1*$@lc>yu|Tjl
zWX&uh4;6?6LLX|FX^a)>!LfWETAZU3E!imep+q@#d|>v~NI&^Y8JJ61x_lA=fc8ES
zoWW|>PQ31iG?|mqU<!hi_cAW7ye~ftPKxe4T@@5HI-kM>_WyPDmO*(nO|&R3!QCAm
z+}$O(ySoH;*Nr7ef(Hoh5Zr@3xI=Jvhv4pZc+Xe2PSu@XyK44K)lALwbg%ASEya^F
zDPWyE)}$r;)B=&(u8(B@M6LwcBlv;+;FTPBALiT4Rgdl<zTNK7Q{lZXmdhs&&DOf3
z{HHyu#74CX3(vt&BcM|uaiEiqDc`C`8<Y+<2-E=c9vKyHg+nz9KhPKu7*6U}tOWA1
zJUst?QTH$Y_ve{f*o(+J_jWQR_~zl^0>P<`JPE#_MS#spsgZM-1Ndz=kgf4WJ=(dg
zGQx><BqpKkRy#S(ZkvzkR9J3p9!+%zFFDE^YfHa*zEpOa<A>+E{zEVtxYGa2ujRkP
zM*R%HKHBZ_Jy}bNUTiugqJ^4sJ@jLpn(9GxV&K=-^xm8<@667SWP^gWj=<Kye{Xrq
z;=Ob5!|<7gOSO+Gsy$?9Rztk;)h%`JpI(<Q(w##qrOW4SIJ63~;h}%i!MdOb#I$8&
z9~`#V%`v13BrluEF7Fg$gQ*62>psynxOL3}Vq}nuI_SP~1Mx)vU=LLxH7xO6e#lDB
z1xU&E=Loi6dmH<Q^$MaqFB4HqN|)+6$%SlHo+hZ3X|{04616z9qFTf;E}j~<2*X97
zj+|xnEXsvSR%$nb9-oy0S+om5Rv9_(>rFW{@2CA{iSM5Xu@DsVqJ=~0)h%wm$O}RM
zn2eJ%5agFTm?EPEC@~jHemD52-(mlSvQHUDi7EyLWy^X@VJ0?T;oaSoPy<M9_gIAm
zR_5XEwQ<sf9mLYy$z99+R(q7X-w49#i+{5vYW>ER?UyTCB;fLT;j&v;4@PYBr=&h<
z%k>pXjTjn=wfy?iM9MZyyGZk#ZHbQqD6nH#o;-}=<cUCQAn!BeDIeqb$U{}g(_l=L
z9xD1OlQB?sQ7o9e6ppQn#O(c2vM~#jvUKWfLrmhI4y_Rs0?HrmuVcvI&^&3ci_qQn
zk3YvG+V$|WbA`&MBp<+TyGKV?LdQn>5gie>`I5hVsiTxh6RCw&J`kM#<#E*xfcyce
z8LuD{b^=jWwjUdedg)A!zwNhhhp2rJiwIg$S#>+9wLpm1mYn>R7lLRBjofgkm;k@%
zP6ic8Abvq!zHbO>6o8%lJhXD40ud|&5w5kD+mr@%xgS$_ZtRHQGOK{s_!RQZ@8sKA
zl<oMBa+REcI{vhKB>d?dMc-y0K)f|XWNB(mrOxbc!e!}Iiyu8imcH1`^p{GsT#;-I
zqLLV<wtKJ-=_YOJTni6xT?>ltdBf4Kaz%@^P){?&o6zDo(L<YXk3!T*+VB5xhQCJf
zYr$M-AO%2EK;V2{fp9pi(`CWqSKDqi8;_{Fvt<*X<N_h24U}}`--;>?15r6|Q8x&$
z50pgiC$ldR%QbO(5~XZJe%1zqkgbl}Wqay^XTi!LZ&<nE5G0I^6tj3;KjAyUG*_4W
zM&-soi5kh3{jr37b{aerZQkdU;WbP=Qj#eUd`JTf5c(7xm#BA6FmTgO>qgXaS+k$r
z^$5OTycM4yn;+-UkaY190|!f^-Q)_ka56T=BtS(2;wNQAdL!R@RX1NSq7F76r>x*C
z@&iPGlw)m5=+c&v?*$r*I00p>fjeAA+O$-J)OY~Il}%+*mUDAlT#r|yb|p?AwzjCw
zh!p7Hin;Mi7cP4Hm@N315V@{jh|Zi|bGP(I&J!Viy3-dt*cZVLK;A29VV-olxwTHh
z=3m+_nFDLliFx;tO^qSzvgDy};ZXev6f3kbggrkvq758*r9_OfyNBIYOw*eF(}<M1
z#`ffjJUYd$QQuat4fc$AS=^Ri5<k57ej_wb8jZuoSrQX7;2B}V5*qz$XN|GlgPCR$
z97_JR!YR###LT(~Kx$@mSC>aMy9Tw~<JCfa6tFx9XcL9d|J1VQRKkl~MV4K?V*c(7
zJ$71$&8ENHl>2-E)8V}`ZE;>Y<rr2{jcFsix}mT}d!gm`Q%u8-yW^hvq79}sadv>0
zjC5rpOYkwnIzgmJs5o8sHU4XD&HxgDS2B1_Hq=KVNZj-su-!==d6>(<IWu>8ZK5|b
z$CEn8X`bBQe<sjOxDpBz$Rrd6Qr>>BOR0zdPE!X(xah<d=z2vrvwqU<YZns=@A=40
z-{$=Kr+6<BLnpY*HxVHO+0XJs*b+idNefqu)OC<edC5aV)yVo`(MX3I$D?y&&8#!4
z#!S5U(r=v&_$hPqZv}?RC?oVo8I2Q43MJ0*mgLAQx38qR7d~#Mk--UCjHO#?flYEu
z)V)=!f3I}gtNjiKxghULzNRI_4Q<tT5!%U>Yc<<1)q5w_d1^HwiCH6MID_NRIJh2|
zgGKCb#19DaEsPTeTLwc0atRHSg$FQYYs*9n(J|9UK%^{PVsO7%{DEe|A_I(pdI>9t
zOUdYLqSnBH%)-Fk68HRBAhh#`A<Mnaq$iTNyH)~<`?a^j#^=*jwSMAYY@1(kvLxns
z0S_p#YDqOILD2$V&_73seR8tN?rYAw6%`7bkfY{sh#CIG8-4q|6>#`l<l(9(G+VmG
zz76`Q=2a*27bPK-*e7``iig*c_n7LGwBoDN>`YI+)dVw(9-d-S(NvE;;=|6`SQ%x_
z%HlGEzxq_>ck>RZ%ytNkL;s8nIJnWzi<3y_{}7~OY}E4$Xw8H?&Vx+VDDMk|$p*aP
zL~?}Fan~ooz1UHcV^zN>U4O2P{4vGVppI>vHV2~nD2inxyWllcq?=QHm@NI24f#fQ
z7r(BlWmiAr!?lG@+}u|%0wQQNlg}{&JtGyW&xJ6Mr55hyU*&+%jo`-K!p{A-zI&bX
zHO=nvvndh&!G_}{u_9G^?9};rH+CeA2WKUK=fZ>}KAVOUmh}$(25-ITt55z32Jw$y
zP(U+D2nO-5jAbX{hA)=1(r0L+V2-IUW#uJ&#U;4)x`~<$hH7&h7J`yXX33O)dM%0c
zL-nTd^%c}sXOfbTCOJE=<$GI+9*QA4Av)hK1!iD$?@_dke|1ujJaD}nFhiw22qn?$
zo_J&*Mjyl*>b<6cj91i1jJc^|q%UtS<N&>2v~rgLobp5^-_<(K5G62>suO4^C3+fJ
z&ni_$U=o*G?s30L@O*W+0%ept8Z*GoXk{=JCe^!KRM8fRqdav9mUJe>7B3?aG{M15
z)<{?19$2WO3MX!;{^+)cVphzvdT({zpmMYI<C~XUVxJy@<Fm+Rr|+KF?WQ!%=m*w0
zidIq0dE&(#La_#aupe|28M}IWjTndz5~95_y+)nuAtQFg?0xsBy1<rR_IBUIYG&ea
zg<)!Vz;Nu)68s(`RC`gK{`MW&p{~57r!ahS_V?U~V{Gw2w(7R`c05mhY>FdhRIjHW
zqOj*>7lE9OuG|rZFtfR1ti^^m=1+i&D?j?G!J(Ih&y|?c;eP%=p4l)v<@5<|1NExm
zq^)O0Ktn}=ZM8o35H)^PHLV102@wyiuFBpwo<EjhBMRYe+4W2^)@Itrm_wGgiMk@I
zkx@d{3ewqpu)Idk)8ZYrq#jr%(X5toTJ>!e(S?em@-*L&&?lJLexu}XO#-y|*!J)Q
zoY!olU~ovfx1f+gHW%NG$!uGB=ke$gW85k=7@wuC>Wq!Jvt7Oy-k*Z`OR}TL`pxTg
zSIoC%wuQ9q+4*VXHW84(M)57;u@AY6O6lbf0yqu*a(RVB_^PoM&GuRHrQ#&3y81oO
zUS%{V3O5-GjgmemY48wPs=&=X4FplTYx4G<^d;?mn8O!6dIi6IpQeEa{U7Z`=&?VM
z2IWyl{t)B4@Mz#3a)vc5T&a`G;<V@xyZKH&uEQ>P*)wd+Zq;MsQc#mpuuuMFq|!Lf
zK|XYpg@gGWhB!NOB!;QsYd3u-jY)DzzSxh_)&;NXdQ%_!#dor;{T=Y~UU{rN$-ygX
z-=RBS3xmC*c^X@xJEwv}3YTbtXfy(h%fCAQVpAJojn2}K+&l2%@A11!+^SwXzgIfM
z5c254D`gbs$u7Jb=2i8d;ZbR-d|wk3^a?|T<KH@si;H>pUx9uA)*@&p{d@v`>Z|C$
zASKwVGx8ROp~|mqC<EjIlc`W2q;ik00}VrPE#Ug^M|0=xD`$9=A;O4Uq@4jJGmZY!
zGguwOm9foR=8^luUSwj=f9_AKE}>TyVw{}Y$ztMm-AXj~({};xvknCbE`F;GoKrzk
zDKAZ?f5DdLft-iK3m|`aTD=wWwi>OA$+oSY(WIcj*HdM?8=z3gC(%!36Y}^}MxpGp
zC%3m{bl^2bUo=y}>#tSHumgS!S6Wu$Y<lgUzahdgz2he6{5-DwG;R)F=^1TW3^X#J
zz1+#IioEYOoWIm@i}S}!Np-2XgBO?tM1T32-`S1atHdx4*rLRm<uI3)CdsBwN6)y;
z9Q-RDC{b#5w*t^p@(sBwGjRenT4u}goy+2tvaK|3lyIh7Vm#bJ?|Ql0g@k`JBb{C%
z6_^&atj91?&=IB<Eq{fOVxv3h--5+1q@UBbNa-6wKMMWqnP+No9XU%eJ#|4uTmV8W
znpXL0CVg`#+_DP7)}Tmm%1%w+OSwrS$|kG!-B*^tTLd0O^&fs&g=_e19|w*>;z23j
z@EhG}Q7Mw9WX<592@>Pt?pH7&^JcFR;1d8VCaBGIJkXRe+6V!rIam~Opee_~Ceb1s
zGb@o`aEk2M3+I4%j`bWY^<(Jn<DZ5vskBiB`sW&O8Vl3uMnSYnqw*MSr^zc8=euEb
zf|onro`CRkh^6bVwoDSL?OJxJXj*%9NaD4#&H<XhhxM>ucBPe@Zm$`~wI+Ik3?A9<
zvm?tHy#LUGxlaQ5dXK6gdH_nnu-1?Z(BT-&f-**d;H(%U)`I@j4xk{VKHsd^p1X8D
z;XHw#TU;^T*n**!dL~XDmbg!AzXf7U^?6eTq9w=chx{>G!tyEpGeXV<6>Lc~<gSzN
z5P#1)!ll7#)}VUJw`Nx1YI7$*3@kxgFqEgifx`#irc+^Lo4I5@omjBO_}S}q%A`Dh
zKSJ3#8kx<r6S8P>`84JrO-kut{}=XdIFont80#RY^kL&`lc5v;_3(A$9{vX5{=Wnu
zS?FK$&g=IT`mFgZ7mU6|H++*rrqUvrt4~%Z*VdK1PJEr__K@52_%K`=?WZDgaccu4
zHo^@wrj6RS9)vMajhdBBWkOo|&CA8=e*f1DJ^yo0?P(w?CTt{xmU5zq`r@up&UILW
zqWmYtHnxpOYB^lHa>}8uVcON7eG2%eS~+X$soMYv$QYR0&$ZXB{m)R0T(XEf?piCD
zJ|jODl^m&}3Sa*c5As6!PmSG|unN6ov;0l|&!()MT+A7gkK@v$0q7WpQ{63fsB>W;
zzY8x}8`c%;ax=9NAm9rJCN7DR0QgpviuVYPJGYwg98+K&<RZYU2(V3hC_X6mLiF94
zA}IY^J)2Of>8h@sa@qGDN8!STa=U}dw+*Bux$7V>DGu?~(vaG`{c8wExS&rGJl&_J
zV5Wa9SWA{L6$RLm*RAQnb?`Je>`gR^y>q5s%CPxL##E8$E!hWa(NC2@S16UTNShYh
zFXH4tIl|zQG4qCp1N7ahAgmGezT@wKA859Ib|?j94i8%m(xxp3@0IEAse0Rr45ZDr
z5aO(uPAPxbzP$PXVz<PxV3+9Yg#yG9K4($ZAZHdYj62;zLx*HHagE9xcvap6iqG5%
z4oJcZG$0X2X`vs)5{jj2&b+ADr-Cr-rRFiCa{81r9~;pNVE6x-7_)29u#BH10%IKf
zl;H)%E9DmCp_;T{r)8id<ALc}aM}f8DmNU_Uv@70IHPlCx`RIVh|^4ghL)a?FN3Th
zE|Jo^pA)j{9NY5b@fh+Y>u12tzbgj(bz$VQ<gcd*Te^^VoW!3)#+zT(9Tno*OQf4n
zvOCtL6V_h<Lp#q{_2L>{1}Yi&;_ze&@{Ux$_5nP^m$%zHWvHf2e-)_8h={>E_wvgJ
z>RTtGwcq{x>jIgL3trc#$k@Kpt#Eb)YTqQgK=H&4Q{f7~Ss}7+AsbqvO2vblT^2*|
zN7IigD=#MB<@y+>+x6CL)8i3TU52lFVd$iFp9=*6o?2R1BGTQO`6A<T&)9ksk(!;F
z9I6z$jnK}bR{G$TKOSg=V-bKaEgn*1hUDodc$!nx=nEPCYbCGM{&-S&B*t1;#k^00
zkqu(-OOTH7Dwx*KHid@>L|QW(b>-esz4L3HVY=M4b`tEcbRgK6C?|beqC5vL+>9Nl
z57+_t4|iaCK;hey!NvbneKnc6EBS3uPn?3<Vojn?Ry%Jyy<Uh^G%n%MVQoyGJ?Y@h
zP+N9_5Y535No>iet+~Pw2Kt-$WtVcAlz)gSzBGPnW|34Qm(Vx8FAEC-G^Nq2tbcR)
zm-tfBnA0t=*93lMaYQ!(#-ZL2-wPZ^dF_Ey`6B(&2!uTMsiMYqXFN0KyUUt$-3K2Q
zw1fnb;pjT?&L)N;zlh;sF9LO`y7`%8d!B<3oGu(Dvq&1%G^JeAVYzIPzzFxn`@Yzv
z@9x{w<#qHHDneC7&dIEHG13kS=YefCH;N%d&U1m!9<7|iOO;}_lYINsm~Xof?c@N?
z5LMOHFLMW(+}5FSEh!#F=U%xWzI{oAZ*}Yn1nzDP2*E=$hd7Q;AdT8Go2G1OLUc+b
zEEoyzdQC8scYtgyD@POl{+Jz_>_>rKvJ{<~&m2+{tm#63i&-*5n|k-5et7a_n3f<p
z7i`5BKGtk|v*)4msX?z$uCJ-b$kzkMJ@BfSe^uD8!)`-xh^g2vxGVlHx8E{(>~iwE
z&;};7I$qGd=A5ga%h`oF+~*;+Zk(S%942zY#SF4;oEM#8`S>UrIjnJn4@mkOB`hUn
zcg^&+;`KA)x&gn(&Q&&3R6%R^T{8=Cpo?;tjx}MSkm?xHe<FNzHPQWr&(45~Xr@BD
zb4&`1ew=z(Z~=WIVd2GY-setpL>%SH(l7|WAaFQ@wL|CQ=NQg!@^ro(%wU})_$D)>
z!&fUo?PI*<DbJ-{?%!=ka%{ble<bDEsW`lnGQMHe@t4wIo$NFJ>Cix*k)OnEiGNP@
zt+g|s3-3Wx<trSH=|opWP#6K$q7y2n71jOk&Hh?b*O{)apj_3Df8DrDfx7M)s!|Cr
zBua>%t(s2ejboJI4F3<1NVJ9vfUwjy&#aWMS7+hYF2QCA@IB9@={TV=MK`f9I>tkO
z2o)ms3SzshppaorF^#ADHTmP9kYG=ZhcaxB>jZCDN#-U(dQsK3j|zZ_uAuBqe*AOP
zRUDHdjAuC~;5#)X<_}p&&?sy*(CT*gZwrQ7Zpz%c**gB|N)ahB{?P&YgFZ443F$#O
zB}!xt3SOowWDvtxdOtOm9WG4qY8QHwlHAP+oglt4fr#oR6e0>jjt4s8^AF?~jV^J%
z5=Nh2)r4LQ>qEheI7Wb(NwT7_xM^8}MyOCTtQT1-0l}aIsBtr+PnhQHL@`y1)3>mC
zmZ_<50*W)h48+%ZIo@vVsx?#1^&Mx?{oN4h%Gagg4<(PGk6r>Fh62ZlS6X_AmOINm
zUKE<1lE~#bu;cwg$u~$=^A+{@0x|Tn<PC2YX+~3DO|D%zqb^YE%;X}v>ju)0hTM<T
zkX+q`Sh_n79)X^Ys%v<();*y#c>ikjEpv>y4VyZPhep#joMBJV6?{G5FVS!)O7|mQ
zMa_#(VW}eR0(jT8Ezt^>u%RVwoN<f4ja@FCD)TIiRuMGvI%=HYI+&@>N^hJ#7*Ti5
zD)OqsixB=5ZVQyK%{rTlhJ~8mB8(mlWgc642@2=dmd93p|4L_fTq*xJopU?F(zr9U
z{x`FB+`SOj7)P1D?e`x+Jg_RRe)9n_j$T)vT^bYQTQ;Kl1X_h*?JShNYJX!K)0>!_
zeOPk8UExnH0!wmiTiO89kp9kPs}^lDw~I^~V?(#IVpE{S)im-)2+Kqy*#Sj3A80o#
zgEwu<iP$?saiX>Ee1Im%aAkPO8E+PW*u$<7*UCYWxG?V#+Mfw+ets0W6~B@=|6!cG
zkJmcm8^)-Bm4y+hymK*Xb=A-))T}2HLtX+rs&A?%M}&pd`KR8Wq`eI@Oq^!n_`DK9
z{_fiQAW6WH)zfIbYN+0iaW6z*^5?M|sp`bvW>QOqQSZg^bk$hvVq)HUgTCd!<EFf%
zQCn4ViHo*vA=OGx`6540seQdHAKLzw_Addd2@r1;Ic-jNMEHEODknB4obWWBd$#bf
zR@;MXzIdRkSmplfENl!aZrelgiL_i09lK*h=og?H_~2|w*<t>rqMeMjPiy`(E*u7X
z{b==udm3V!w8-)XoFDg|8%_SJ6o4JH6<|>9X^Q5Pno+AovO*JtNA@nsT03Zjq&cPY
zw#TNW4(#{)?~eQEH-0Hye*_D)^JDG(T};yR)6K}J_nr3Y>Unar3)H}S!}-Tg>334c
z4@}&RSEaj~)OT%l?uaY1_~N$m`E6qhA8$c9V?z`uh#xIx`iM)8L`QGFLq2EYN#C8W
zn%wa&zx(8R&Ppv?w5seIAKem~6q5cE5?K{2I2J$fZ@Tq(o;)>zY_;Lq4(-44;5vu<
z0Wp>U@2>Th!+*t2^qD4Uv?R2Nemf&a2EeGewk%dHrRA-_1HN}{q%A?G2iFy;)OMGm
zi+sJHH-E-HIOe*rb90#4zQhif!#FbaPui|0!hn<cg>9xTp%}dwT|8X&z{}KAgHTk%
z!4$fc?P-~_6-Aahz$2Z4CN}C8@VXUr-Ha$l41@QHMW9i;{%-<n4bnY<WkWIUB#`0o
zP5)3Sbc|dMCK;a~_C3KYzM=hrdKRnODPpE@wkl%Oa4ATCMEA&(Kj4N-zfrn=7=6^K
z<{bv7Xp`&R$lvc9sTQC6@YHagW8Bt=R*`w5{&nq&_f)^rc^22x*(K5hftk-Vq5B}2
zm~(xw_8xM+UO@~A?>#?bDx)ImFwoGR9EziE26TUpb*~Po7=i5$rxJFFMQwnZ@Axrw
zuvQToe&A}H9U*k(nTZ#H^b83R2L2<z43${%*loYd=l+bac{Jj8xtcRN;xaeQNl|tD
zK`WtuTPAw{nX}5(@j4ODu#-#z>zjuxZU7$wipMYz#PsSb0p-Py_lYDn7bqyb3;j3r
zH=%+El)LD5#P|_!wjne#n+K^F(}Cnnsz|^9!8vCb+;J!U^Y>(4)YRT|9vGpQp;F8y
zXU8^&_~CT9lnLJ^U8JxCLApYt-L7EWjeFGt9!A*P9Dxuk>-UnodSbU5L{<uSR9qKo
z+;aP`^p&%un?=XfkonIB(SZAAKYDfH;9!J|%L<A>&+})icni5(#Yk;Y7H`zjfBd^q
zMbAy3-%F*&^0cY>h1Nv5Q;yC6!#LQqWsIl!ecc~Q332sL(b<e+`Ie*lAlyeSUcH*x
z=LKmrAz}3GGqUBTEQ8Y1l9)Ve<<%#~fP3|0P3qFqRSf01T3y>;%YZ|gqNJOBRp7gC
zx{ukxsB#jDuYt?=8pSV5giK+9q0()E3Yg#DRRL!qFXuKBZ8DEWibBK%nwKr(fhtx*
z&^yd6=C#~M1r|<7m2;eI?UvcnS=7(dK~&TWyIt5H;f}KCg>eukjFDh!cz-xQaRE*3
znbPN>0I5jgOzJttIl!$nE4CJLexhAks-oZQ#1%0Ii-&&WYrE~QPyI;#r3+tFxN3L^
zyfg=vJKGG~X1=#~_1pgWkRw4FL+hL}*(u-j+`1!PMXyzi@dOoDE@AFBs>F-YA|~b$
zbqD<GyfGV@!HraY_MvMBeNJ{H4oC;J*q;nHb<V9-rADrij{q0DmlwS!4V$p~&<I#K
zBC=?Qhq4pp9$>4FEbg7_a4&to!)Neovkf^m7sK2{av0opqPl9l7XQgGfyoCI(qA1M
zdP%l5N_=Ff-f+tX%b&6-q6-Y_ukqNY2f0bp>Z;sQ*Qi+NZt0l(dy*peJs$QGLQMab
z?Zr<m5O0mqe*t{XiulHo_)us)DI4)Zu0OtaKM{=hK_lPUwCZ6ab4|{gb$|F<_!>8P
zVFLnVBYmoz-TLdubVwbHcikieKb`t;hB2tu7{*?BCKZD6YP<UNwzlCqzS`{1xV+V=
z??hj(Ge5|_FZ%AHqZ9o`m?LwBB>`eRAd1fB{GYu+9>7pkf@Sp^=?dWX#bEu@rxU8L
zba=iEiq>C?OGjT@4lTLc0soB!j2El;5SevGZMzKjF?=!Db~38#rKe?wvvVa9PqbFk
zs7HnDhqXGXFMWQdX&j1RFR=kpl-%)>z!w<3Wjtsb0er<=RCrJeZagaE?7i=&wGXes
z@}-?j3y@JhOB4B!Y4QfM9o}1mn1htlYx>R-@1fkP5c2Ku|6wU=bT=A#zoYi#ndUXq
z-TP1FnarCpp@Qi*ZduG%hLv(Z)hCrtu*cufLNU<ZjY>9a<&uA1i8S}DFT-g>Vo!C~
zz|0M|7RwC$(RYM0Btx}l<vt#IJ*y1FDVLPyS)sRmsI_3wL_bYW!RV1x+uuT+8XkdE
zJYq+LaJf@AH33bFJrZa$RG%(=nWnJHcV!i@wqM_{dIUrS*8<V83lBwzwDa}aSn*Jb
z<@u)vme!M```8*4#lw!q7ge~NDQaf){Jl6%_+X?NvX_4>K=75-9@-DjN)v!>6UmJq
z2H63Sw4J%6<{r?~Y}W=mXxyG1RNTjIGHGNsT8h}ys7rfomL`v$>VGHGA`Zi4P2#)0
zUGL9}7dChL8%2ENQSy~Hq>W5uF)3&r<|N_4GMS`BPPq4BcbvecCD&Hdkc~E+G%`!I
z0!tG`tT%q}2)i_@1oOnTAPHzq&^l@DT_HPb<y&Ez6U=Esj4U!M7Pk0s2i&8QgNGoM
zz|fC+tVWvA_S?tp|JKL+&JaL3DZ?6c<)*nP?Ph(<dps%9`;_l&cIuUAo5%-ba9$#j
zd1>C*`W^gC+L9GRBHETo&wf>-9tf<WB%_wOA&-u>#ypicG{7oV9B>)^ZrWnVtR8xC
zyi60M^#0}$fHusVzKm>Aox<|Zdw2)&vgE|%Io;N7X72n?lhr$qxJ%^4MmybfQba;=
zT9wJ5d(xZTdrL@hJWUO6_^$BHl`2udl-1GjFvAll1N!A>vcT&kZG_WPT{Q~c4eZ-c
z<_V$mTPMUvdw+*++W%m{f&<{Ub#?S{As|?sARsV4dc2+8ELq*HJUncjtle3B932{r
zR@@9^yaH9U-o*P4Y-Zb^$91!h?6>I!lRgQPK_te9bFeT$G*rm#ZPcU6>DIOTjU<%{
zMfLO?&RB0a2VTsP0n3giugxOaJQ1%ejIZ#Xw|4EFf~(GV=Nrbaz~1wvfN{G(md^9@
z++B@HXZurwh|%liG4NJWEn=KAf56${<@c55uH<-aZKWLT;JWuVgSVs&?Rf3zCZf03
z5A9-YtJxsya%1P2w{7k+VlZOCJ7TT(2;8#fz<Z$LEJ+XC*8SZ&o!fB*UhDdf*Y?kR
z_(;~XQWdJu&et7*vw7Bc!z#YF>}L^^X;kmHw%fNCk3rtSTU>v>6BRNZw31P~w)bN@
z2vI}a$4h77zPPs2J%6&sCz815OBe9_WAsLS`(9|o&X!Z4K!|wTsa06U>jBZq2Gh-T
z`+Kl|r_lC{db449NoU7u%kwjC{07se%5kSN;(c9T#!LyY_Ea(|QF3}tG0b^$x_{R!
zAcC}tB<$tAumRp6i!EPa{sY|#?kQ;ZTV9)6h`;J2MQzg|t0tQt6s#d+$e*3dfNkE$
zB>hvaV&!??`@FN|3INw;w>3fE=Fhd`R<7^MS2C`*Aa&lGdq81eL*IKdPs6pWg~5!7
zITCzLv}fS@vZg)Y{Oy7)!u@L~ad&4bWA^OcwF&P;Sp?%<Imw?_B+sWa7qqnppI4vN
zm!KyWJfStC6;|iRV<Hk$hS-ef+k2!Ir}3OW$01`I4sWgF&(EU5+2ceN4~3fVU8ryK
zaP!Z5;{aS(=le|!>G9knhUF8`x`ahL;B|emwh#CS5qB<ae1>_O%4mN~%&KX-FBv6!
z_vFiDHPMoXwJHGC*F%Fs=(6%%BwvPz*@wY3cMFjk$s01q*+?>mB*YlC6fS+dTY_v1
z^W7MT<1T$=?0RGKxcuAl2ea%V`+W)IwHkedNqi_3!%6LW8VsFrC{Ou*of4Uv{obdF
zJU{^Vfrkz8<!3%X=y%uo4hnTF)x5Vd$$6PKF=8HAx5tFSJqm3Zd3*-lZ%c)6?gC3v
zbEi$Yj4mjrFlpakp;gI}E2K2j2@!c4GbhwOps_-JM{sD_|4m5mM6x~_wZly^VAD-i
zk=4+v><Fu}T0n)mBid#>hXI{07Qfce3rHtS(<qT6e35H&4Xd6X`z7m)CgQG-rbW~Q
z-y6LrM8floxNTU(H7S2D>s^^_spOrTOc3QJ&Y0SA3qOt3C`-t2^03~3k@o~m9Wl(U
zOu3P9*JE3`WhG#uRESjc8+V3iuN2q%3Fb0S^|d=JUA_oU)>{*3K(y`QM>0^h0yKL^
zVLG7grLK((h-hD2%XA6(Z5FAV=(|X@_*JrgaJ3A`ZV7n~aI<K!U@JIA-r0F{KvxIu
zt=^e8v+9MHxs_fG_$!k<@N>3+Jht36d)YDt#1*!<5Vo-n4CBc&SJZsZnmlKzTw0|{
zvBt&S1WLr!=Uv~Zk3W@g3vGs%0K<2Gz2gn%lk5XZS39hRh!%1j9JV}0i<S9O9SJj$
zmaMjBhdZo-a<<L4^-UVHE=u%avWYLF{gu`l>NE3|KE=^{)aP9s*cua!&s2?uI~n;E
zxiLq!P!`47RQm2RvyG;WaK{fdT8cj;eS(s8!h$^-dX0;;Od|(f*`N>o27bD`=>5@n
zi)`)+#EsAV9Ul2$&gV%@y`^G@zIgfE#mW6v2x{o6cD0incPv4funLw`IQ`AXm@tE)
zUI{9vaNESb)pixeIak`6*a`H=oELeJIE{tb5!x`QNsyTm1NlZt&V*~wQb=rFrNE9S
zPz^!yd)8r%=5X~AyBuM216+;f>V-0ILj4p!4-gW?Se$Vn@k%^3Tz(T{IJWeKWv_C|
zaeC&LjQM7hKp|o&Os!H&B<w3}VovvLVqAt^fF%Va*qvnB>{b&M7R)FYgnx|a5Jjps
z-#{H^I(?2n#QS!f3HN0<ovsPxch@ypf#Nnnr<%5pCUl=CgP>SI5zqjac(N<fElKEz
zUpcZVI60&UTCnM5&Y3E{{x?ict*lv<K-Pv>NkeB`4Q0*=s27x#U^*icqaVbDTprwa
zOF6lbA#F%vAbsxk-qb|O%)bx?fBIxk(n7;)tm5XeQ*_~n6CdTwMQTg5k#X-Sk(Hai
zbB{PctDLpT83(5n3Xr?B(pY@_J*;K?nH>?chdzBv9u$dT{0fEXyEbiE4r*Yjc{5y(
z9!Yo4`;`ESR7nxFy-a>)(g?ly4JW(pnqNA?qnuUdmt$D46278^M(RnKGzI=+M!1`9
zQ23<_jEL!bhgx{;Fv3L#oqD{VprBg#xxM8EHOVJ%@CSq+z}d%gNnRAI)!|-_EQN1~
z$*dSzY&;W<vpxv1?c;!G*~8B`?oy3NE&W!w&0RgGix}cf%JehA-^1Q;G|66~el}Xq
z{%CWeUhKuU!du7$BJop9a|KG&iv4k)I}*}6p6{7M`yX&CTHzid)cH`(pugafr)r3S
z6_E$)i%4V^;KO1Ob{%z8ieiky(%y~c<n1tvP)IAg=}Apgbm4><-)-N=BXRt@2!f-)
zrXge@+rb0jnGe%$r@VXOGM!|*wTf?T6H+j>3_JQm05x9xTKzHw?y%T`^o@1m<VwD#
zH5j$e;%Vc`Gq_70Z0CW`YDw8uB{4z4-h72MC~QRpFqz`8F4?lJO>x<2&tsPed-^E3
z3kd??m3rgd`d~;gR=x<w%{#isdglg9u79m(_7VOJci$baI#wV~KJVsxJWbnCnDO%g
zbu-n+2w665(v)0c>xna-_{s=P@X!b9@Z0D=gjnk;CwdCiCCF`oMp-eaxQh@)N*4I`
z10l~5KrY|VWh6p6XV$jJ=M_cvz*D3|>hkZ9ZApuvAPMGzG%bTmmV3T%j=Kxxm>R!J
zWO%bM#I|n<lGBRbI9cIb^m*6szcf<>LJY~q5idXO5w|M`j%CHZWmmv$?KON`*;fm$
z7x$6r;!h>KEkQ69JXdOAtA|IuggmE!mRo&l1A<>7sMKhkUX9eFp2<=hb{^vtKn%)Z
zmQ?4BEfs`0_#edPCd|j<v~Y$gUG|V-A8Ti!!aI=d59C8BQ#=|$4T@cmG5R{VIiX=B
zpJWf-nqwtT)<H3nvFp2qAKEkwPEC_Fzh81iIm=U~CPm2UcImar4um+&5<PUWGN=Cr
zoP!6UED>Thz@e8#_m}$c*(83aSaIaSEL;B2+dgX>-~WIKw_v-;8wJG#CIB$Z7S31^
zeGlUb>9@@Lv0(i5*ZX;7mWB1@zrso03|C7+Xz+d4e&lI7nIW1^kHN^sGFRhDcE{`G
zKTGHRYYg1=BZ{~=#o-n($-Yb_p|Q9FuX@t^KE~Sx>_XB|9JQAs0%OPKV#wzR5C2Xj
z6-;}5bYKL`ebN3>@$TGpQB7>)Am_AS;13_5w@h5Y)?UHXe$(L!`SJH&+jA$&*+lB{
z2{L|Q2ihd&Vx+Ws{_-E9>~1nsp?a)LR!3<*A4>Z8Dl`;9w!KAt*gcUj2bD-3u$@7+
z%KKD>lp}Ftw98@pPGrf^xs&jE@rX{IpwjYqW|w|KPG<~Uf0iQs{)i#4;TimT5X3^)
z7xmNK3~|164@qB&g+iQ4wP*sX!FDwf-Vx~>9{v<Lt~rz(w_uv6Pe}kx4D;L?@^F=x
z1zJsL`_q8;hYwI|cOFxApQOhW=q4>g5+khkg+GSMNk^FXHoirW<)JO7SjrTNn@Kwu
zhdhLDDi8y?Qle8QV^M>~H4f~vR%r_n*T4w(>0*R&cA+vI!s1=dESDGELhFbZ4PXwu
zP(<(!L6=j%HwpM)$}f^KU<4v^C(S1kboCL`wpW5J(dYLeI(B*3#ry$~kdht8Gftwb
zkO+dFeLpwf_pKyhii|$0xp=ep7Q<Al>{u#H=nc<=?(*C`rex}9&B|u$PjgBPB{joI
z!{L*f+ky3yKQ8#UCo8tHswP)0;m~Ebv(6{M7qMO2sXkdk9=vIoD8fIYittCp3lOIT
z76$U3!kv1xSfVhtxDNnZO}OzCsv3&f8R=cU%y}`t62DJhIfarU>u%IRvd#g{(tE?)
zvSGe>M36z7*UnQ9J%ZCy68|1iE3kTPO+W9<!^(+oYK8uD*x~G+QaGT6#0^&&r%D)F
zbsnVg*=j>=y+F*54E`lUgn>J+o`GKwC*&XJT8o1%$B}mMeHa0V8@ABqzMhN5Sf_{M
z@X=w%HF#*SV5+(rF=V<332^qr@Qbw()vHXOeLyX%2d@)l90{St4vBzT*A(|&6BjEK
z8wx3|is#8>_vc=l|1!B09t%w6IJxhs&m~x091t*Em!|M3?$=|u`V3xg@};QSgxah(
zLaF<p(9k<Rw(tPq*jYIyvZGTn@=b-C^}n-0;n)FA(6X@s@XLHG6mlznpM&KDC#<<b
zkVzS75rih(5+g6hKVud=z>uPE=|fp^q-CYBOTHC;q1SSeoDFw^%P43Kj%QWLo^2j!
zSF9Ti*o2A7zMdWDxZG`U3gD*ol2ALqiQ@^jSvQegr!@xVl!AtX=`b(E49h~R10-m~
zLmmoY<9qC<=;aQw>6smJ?+9hLT+QXZibZV?<=p#*!<~lb5P?J@@Ig9y)MY&@J>w{|
z`bg`=Ck+WuH{%z2|FKGdVdT30N^Dy4Ao{G{zT)D@l_7GTU#fimWmg!zS6Cpv`+8IJ
zb@%UQi94X1v7vNnfHA$)K26qH=IpP|F{Y1nTC18+E3CQiTT*M5rl*E?*2Gc~DTw4Z
zm&g8X6BqiBdnuvzwfmfHbI&on7())R9F^v}%!E9AcB9oEZITXwnNXZje565k?XJ~i
zrW?gEc>omacLpKOk$fD$L<~30<EsAeQ;A;11u?+(oe2CbCq`4`I6G!4shuFhhnJpc
zqlI^Lk+6xy_fju_`xxZRG&D}WwI7KU1jW<)e!*$PFS~gjJOqgwrJz+4Gbqb%3O@_I
zze&~P%+Juf0h?Ih74V64X!oaWta!7@kiz0{jt8RPV^tCh_)j(#$K`Nf=5~8rX3>?g
z@;HD%^00n2&73PM{(Gb)cr{C9cfX8F6f&b6_slj#_pHesC+bD6e022*ngEQ~N)Bd8
z$;eVfhM*5nrt>a<?)>sFB5bNxyjH5&J%>~ci!Heu;4DS8vR7{Vn>V?MHr%bshWP~s
zHhyS_B(hr)HQFnEA`@y@2n}x9x4*?klpjD*gJH)6ruTFqllv&YV6H(Sg3nA+s2-E-
zEB5E>B98;ibD0hdY0JNI<Z-)!oOV*{kj{H3nn}U2;Tej>r{Cw2u*}7ns+oT7krU4G
zho_`1WRFY_9b~$LF8B)h@O&G8Hy07fV^niC3HJBKyC3-+u4dpkbLLFbi{O*?LIIcG
zM#WW=9*;4}qduOm1I}QYva}!4BtEI|vinBim}9#?#6q~ym9uQFC65anU#KDCyN_9D
zo(2Nt<XTack1ZL1dRcLi!SXGd@*x%CpA{>8+EzKfv1;)dmk*WLoQF6+&DSJ}^2D%E
zSVXQK<EMY_7)g=G+$+dujK!t=X#iO1r)#-8!m0?*FR3(hz^-K;%{jw%r~*Rbl%i{p
zmWqYm|GGGLIV{bM%IeZ4aK#rv*15sD#`o3v48VdUjxcg(964pNx>i5IdLU$?Z)I_`
zcR)((2*;2VMggr?384k8Kj)-$5ZTU4cs}m;OCtk3--9-Ry~p9%qnq6dbHGz}{9!%w
z@a4w(R+f_w1^h?I9d_DRQLR1A!=n7&uTFn5sh*X4v8xeg=+}urbDujWd6+>VVZhb_
zcZlLTnR<ft<ss+Cldofk-OE`+sI-H{pfa?Zz7(Jp!*V_16_a;?U?uLeR|2v;pS@y6
zXd2<B(B8k7o77Nl!Ul;n11y|KFLLg(FH9=Tqve0;K9ZF@t;kabWyOer9hu6i%f&iv
z(KPSU(L=iN(_*;l*#(eazB@{B=nUZ!R2+E-wFsw<!4TNMxJ?OsZWxgcZm&UId&D*;
znfBdlpj^mYT;GDP_T_B^hlfx_^3bV>PY$=CXYg%dh{X34#C$OYHt5!Z@vCCS1V&pX
z<C8B4EyLt?KRQLkGmnJyot0JA?H4oWUfO-dK<aau8bX-*#wPtLpD3|s0%FU%Such$
z$P;uj4cO8r_#te;7Bf8+8msx9FImFOeuK}!%!g4jGIhC+VBmB3gnu40bG6O3Y)wE=
zgO%vv<{xwTYGI(Ul{j)UGFuY0@>`FvhCPeQ-JhC}ncRslZ2G(@1^{_TXL%9I8VFOg
z?;`D#$6?EFIXOGi<7h;MLDA^@KH+Mk2KXM8`szPhpoFDWc5g{Aew=e5RyVD4uaZbO
zt?5J>8!%8bX<htTB1W9!52YYj?clvbT-^zYDD?bq6hJZt30|ZuFt_3KzN$1$N{^G-
zGpDMNhviV5%=>1)dmJ;0Vn7vM|H@}&#n{Po7r3ThKcjZZIv!WruWr*x@>cSwhL0Qe
zuSw!%yxRoTR|+$1Y4wucQfr@0Svq<9P4{dZOeVzye95Cdcs#LGG4^3@wYGc-Xx!VS
zN_}u(21I+OkR-#e8%B03ZQ7rDCZg^^(F<_??g<OY{yIJOMD5@Auo&8@Ts9ldL@xEk
z$cirnq{!-huWG^hac6!^&fNt9ygxHC1BgfI>4M0ZlL#+$i!&YqZ<vCJmU*4fzM-af
zrdbEO8g55Cbi1e3g3EI@2AKxaX=S$qm(2q90Au$>^2`zo{%EDjQ5S?^{)0?gaGQ{j
zx|1?AYmCYOg&83yHNtr;<fV`_AtV9Pfvro=#&U4(h-<k}zRGF=^dY!}H&JnYfj^NQ
zj3<DZ?>T<D3+k}!>bNY!jvs!W4%QVZ5m5gJ7Uiv*TZN9bI!ZooE$b5=`2A6m!-uO9
z@STS%ym~B~qP{#_o>JV@`V-<2ts^hZo8~PfbD!kh_;IQT+5ParL2Lt8vFx$B+<8RU
z$<L1DJ=YcNVE?M#4&QIeCO~7Gagg4jXk2P%3R$Laqkl(!l~>c?*wLK#s55K3UWcpy
zOP<3W-iz1knTsJ1wJNl3+_g}h6EXuhHLk_(9GyW4)q33DK#MxQo*jO0A(ihuPaJmy
z?8u6?n{+<UOxQDv5;lmutz2F_T>J<Usv^w>WcROdUO5n&+SX4dHk`atCsg8^cvH*o
z%6Cdwa|d5va{wWcsY6cR4-iwd!dpdj==${NP{$#=qiwzOZFG#Y_WaLa#vU+eH@{Iy
z`Z3?X{k!%c|Hd`&yUYCO#BAne=ko`gS@OQxk&367&E`9h{Z`*<kd3?%^tf|v?e=)`
z{XA{ddjHz`?ker|C9N%BUj*1^&3W9bN6R6dA3Xi?-n&ws^KK{tcrQAkkuBW6zY)BT
z$xKM{F^kU3W&?SM+nzpez~&xphH`Zf*!bN8Ztjftpj@XUV(iuFcIK*FWkM-9)!A7m
z2Q}`%B*42ng;n&$?d$WLujk`dj)~z5^>*j-=+&s2H&NuySjpPy0a^G{>OqV%Z^`x8
z(+AF&g*J$ESFQ5v&%%8~n{%$*!?7N&64U9`o9}7Rw>8d}4Th&8iU)jS>w+=+l^q`v
zCC6&P9&q$eZo<R&>-@-6GQa0105)|KfW&F4%Y<CPhav9S=S@v$gj50SdfCt=&%eHf
zEw)h#YSv;gMHE+2EKEyOMKi5HDcDoDH2i$?xAK#Z6K1aOJtHa>2wIE++mj&+Kt6(?
z^Rs%!o5j0U>=hKqDdZQ15wmbN(PM94jpn|7rY%6mXcv)jl+*vkH4zic`VqKe?f58p
zFEuq8f#rN?J3(f<e!-!8Al04pib9=d<c9_GgMA!+Ae68%*(|KyYxrc!fFS7xy0?Q@
zp7%Fyxsl}x(+YbdHMo5d@rZlk{Pz1<Z>3=Y^ZHAw8~4KS=H{MI5L7wi9em?a2E!5A
z|6MK2J$!~+)8rpWT1db|p_dK2e*?Dj^RRzLVHStPO2~-hqH0b418!m)^I{=I)bsVJ
zV_T&=U%+4CgEzM`rK@4>g(<>Jofvv_C4}g%NNI+8rbz?tm_fR$5TfDZ0kIIT&8b3g
z$H-T!eAkO@LL?Tt3pE9fY(#@xErb-alm1U!_j1Q<mCZJp`+0zHE6pFnfJ>UqpYaA2
z6fVQigCuN!pfO0|)sbbNPmf5+VZRY_{n}(#q7(6j<~wEOxfx+C(KfT*%D<&UJ*(6S
zbFE*Z&Zubm^GN^pYr^<h&BChH2vN+61r`g{$Hwq+C~y3-TA~S@{><il4^|~V_iCq&
z-dCw#a!=mr^jlEcHmz*+W}qiXSm)Bb>Ro+i6q9x|{*!a|8*F`#Hiw-|FDIuc%|6DE
zUYGF$<7%l$@K_Y`mZJB)j|rrgImJ-KM3RFQ7hI<j@Mb;ozuj%pX@S&$g(3CPP;aX0
zfMkONI(X}LxKN;^`s2<2nHeDUIZmW2()L)&#V_VAEyUFkO)Pn(tDGm?%wWk~t~8BX
z${?r-whfQ!C0Ls#+c&H^UzI#v&oN9;%CE@oFgaXd^}T2ZymXO;U8Adh{wv!vpcEu_
zV?U5DAV$0MV>*SS5-CeE-)zEg#SYe6x!ubYNErKWKD>$2%`<8EsK!Qyy&&^qGBr@T
z%PO)!tKt{BFzHK$(bsfSlE#fHI6gpEO(L&xjM{hW>G?S=;rfFFmo9~I9InsA&?f&^
z^+<nWeaOYBI9zG`6Wxea?Qe#05XIZDe?hXOUEM>OmdjM*FX5Z<dg|__hVU6juUz9W
zU?7Q(FGKxJ%D_!c8z)P|v<`n)L>7q5Z`A`8P#p-;pg5Eg<R^omtUIA!NG%C(3TB6Q
zF&1Q0|4HE9su11^O>~SRlzz>4p$kjFz0PwW7v>Q$M{gVuTc$eSYH*0YmG~J588X_F
zeM5?1(prFp+!v8@_U)a`KzZzr6s{c0NKnK2fb&gtt#w>MLFo%sKJI&@N{5%Xt=hW3
z4?(8lyVQ=^L6JO`e&v3DKdEA3fQ*`rP0fz(07Per0!-3JD8ngfy8GBhRD<3`?{QIo
z2F}fGxkv02%Z~~97pT<cZph_NGK@h#+`J$mmh@9IdmvRHXH!plAnAzy|K6|vd}IG>
zA0_pAAw?ilQk{Asg<!O*AR$sisvt>I|Mo&MKpLdp_d?SAPaKi=LE=KYHbSOmHbN4l
z!t_F-6a9C6H6bA&f}#HBQ~Uqsgn+0?4ea}vRWKFY2Pp>mIrX&<QWUZwRk$CLmgv6&
edGayp6dD2o{r?U5_=-&h`XQ;{YPvsqA^s0&ex=3$

delta 98386
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
ZAs{gRZ<CL!>@=qVNNTvQo{zf_{~vwXVL1Q*

diff --git a/fun_gg_donut.R b/fun_gg_donut.R
index 8dccfee..47981ff 100644
--- a/fun_gg_donut.R
+++ b/fun_gg_donut.R
@@ -28,7 +28,7 @@ fun_gg_donut <- function(
     return.ggplot = FALSE,
     return.gtable = TRUE,
     plot = TRUE, 
-    warn.print = FALSE, 
+    warn.print = TRUE, 
     lib.path = NULL
 ){
     # AIM
@@ -43,7 +43,7 @@ fun_gg_donut <- function(
     # categ: single character string of the data1 column name of categories (qualitative variable)
     # fill.palette: single character string of a palette name (see ?ggplot2::scale_fill_brewer() for the list).Ignored if fill.color is not NULL
     # fill.color: either (1) NULL, or (2) a vector of character strings or integers of same length as the number of classes in categ. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette). The order of the elements will be used according to the frequency values, from highest to lowest. An easy way to use this argument is to sort data1 according to the frequencies values, add a color column with the corresponding desired colors and use the content of this column as values of fill.color. If color is NULL and fill.palette is NULL, default colors of ggplot2 are used. If color is not NULL, it overrides fill.palette
-    # hole.size: single positive proportion of donut central hole, 0 meaning no hole and 1 no donut
+    # hole.size: single positive proportion of donut central hole, 0 meaning no hole (pie chart) and 1 no plot (donut with a null thickness)
     # hole.text: logical (either TRUE or FALSE). Display the sum of frequencies (column of data1 indicated in the freq argument) ?
     # hole.text.size: single positive numeric value of the title font size in mm. Ignored if hole.text is FALSE
     # border.color: a single character string or integer. Colors can be color names (see ?colors() in R), hexadecimal color codes, or integers (according to the ggplot2 palette)
@@ -67,7 +67,7 @@ fun_gg_donut <- function(
     # WARNING: the call of objects inside the quotes of add can lead to an error if the name of these objects are some of the fun_gg_donut() arguments. Indeed, the function will use the internal argument instead of the global environment object. Example article <- "a" in the working environment and add = '+ ggplot2::ggtitle(article)'. The risk here is to have TRUE as title. To solve this, use add = '+ ggplot2::ggtitle(get("article", envir = .GlobalEnv))'
     # return: logical (either TRUE or FALSE). Return the graph parameters?
     # return.ggplot: logical (either TRUE or FALSE). Return the ggplot object in the output list? Ignored if return argument is FALSE. WARNING: always assign the fun_gg_donut() function (e.g., a <- fun_gg_donut()) into something if the return.ggplot argument is TRUE, otherwise, double plotting is performed. See $ggplot in the RETURN section below for more details
-    # return.gtable: logical (either TRUE or FALSE). Return the ggplot object as gtable of grobs in the output list? Ignored if plot argument is FALSE. Indeed, the graph must be plotted to get the grobs dispositions. See $gtable in the RETURN section below for more details
+    # return.gtable: logical (either TRUE or FALSE). Return the full graph (main, title and legend) as a gtable of grobs in the output list? See $gtable in the RETURN section below for more details
     # plot: logical (either TRUE or FALSE). Plot the graphic? If FALSE and return argument is TRUE, graphical parameters and associated warnings are provided without plotting
     # warn.print: logical (either TRUE or FALSE). Print warnings at the end of the execution? ? If FALSE, warning messages are never printed, but can still be recovered in the returned list. Some of the warning messages (those delivered by the internal ggplot2 functions) are not apparent when using the argument plot = FALSE
     # lib.path: vector of character strings indicating the absolute path of the required packages (see below). if NULL, the function will use the R library default folders
@@ -77,11 +77,12 @@ fun_gg_donut <- function(
     # $data: the initial data with modifications and with graphic information added
     # $removed.row.nb: a list of the removed rows numbers in data frames (because of NA). NULL if no row removed
     # $removed.rows: a list of the removed rows in data frames (because of NA). NULL if no row removed
+    # $plot.data
     # $panel: the variable names used for the panels (NULL if no panels). WARNING: NA can be present according to ggplot2 upgrade to v3.3.0
     # $axes: the x-axis and y-axis info
     # $warn: the warning messages. Use cat() for proper display. NULL if no warning. WARNING: warning messages delivered by the internal ggplot2 functions are not apparent when using the argument plot = FALSE
     # $ggplot: ggplot object that can be used for reprint (use print($ggplot) or update (use $ggplot + ggplot2::...). NULL if return.ggplot argument is FALSE. Warning: the legend is not in $ggplot as it is in a separated grob (use $gtable to get it). Of note, a non-null $ggplot in the output list is sometimes annoying as the manipulation of this list prints the plot
-    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). NULL if return.ggplot argument is FALSE. Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
+    # $gtable: gtable object that can be used for reprint (use gridExtra::grid.arrange(...$ggplot) or with additionnal grobs (see the grob decomposition in the examples). Contrary to $ggplot, a non-NULL $gtable in the output list is not annoying as the manipulation of this list does not print the plot
     # REQUIRED PACKAGES
     # ggplot2
     # gridExtra
@@ -290,6 +291,13 @@ fun_gg_donut <- function(
     }
     # end management of NULL arguments
     # code that protects set.seed() in the global environment
+    if(exists(".Random.seed", envir = .GlobalEnv)){ # if .Random.seed does not exists, it means that no random operation has been performed yet in any R environment
+        tempo.random.seed <- .Random.seed
+        on.exit(assign(".Random.seed", tempo.random.seed, env = .GlobalEnv))
+    }else{
+        on.exit(set.seed(NULL)) # inactivate seeding -> return to complete randomness
+    }
+    set.seed(1)
     # end code that protects set.seed() in the global environment
     # warning initiation
     ini.warning.length <- options()$warning.length
@@ -628,43 +636,49 @@ fun_gg_donut <- function(
     }
     bef.final.plot <- suppressWarnings(suppressMessages(ggplot2::ggplot_build(eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + "))))))
     if( ! is.null(legend.width)){
-        legend.final <- suppressWarnings(suppressMessages(fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path))) # get legend
+        legend.plot <- suppressWarnings(suppressMessages(fun_gg_get_legend(ggplot_built = bef.final.plot, fun.name = function.name, lib.path = lib.path))) # get legend
         assign(paste0(tempo.gg.name, tempo.gg.count <- tempo.gg.count + 1), ggplot2::guides(fill = "none")) # inactivate the initial legend
-        if(is.null(legend.final) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
-            legend.final <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
+        if(is.null(legend.plot) & plot == TRUE){ # even if any(unlist(legend.disp)) is TRUE
+            legend.plot <- ggplot2::ggplot()+ggplot2::theme_void() # empty graph instead of legend
             warn.count <- warn.count + 1
             tempo.warn <- paste0("(", warn.count,") LEGEND REQUESTED (legend.show ARGUMENT SET TO TRUE)\nBUT IT SEEMS THAT THE PLOT HAS NO LEGEND -> EMPTY LEGEND SPACE CREATED BECAUSE OF THE NON NULL legend.width ARGUMENT\n")
             warn <- paste0(ifelse(is.null(warn), tempo.warn, paste0(warn, "\n\n", tempo.warn)))
         }
+    }else{
+        legend.plot <- NULL
     }
     # end legend management
 
-    # drawing
-    final.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
-
     # title
-    if(title != ""){
-        title.grob <- grid::textGrob(
-            label = title,
-            x = grid::unit(0, "lines"), 
-            y = grid::unit(0, "lines"),
-            hjust = 0,
-            vjust = 0,
-            gp = grid::gpar(fontsize = title.text.size)
-        )
-        pdf(NULL)
-        final.plot <- suppressMessages(suppressWarnings(gridExtra::arrangeGrob(final.plot, top = title.grob, left = " ", right = " "))) # , left = " ", right = " " : trick to add margins in the plot. padding =  unit(0.5, "inch") is for top margin above the title
-        dev.off()
-    }
+    title.grob <- grid::textGrob(
+        label = title,
+        x = grid::unit(0, "lines"), 
+        y = grid::unit(0, "lines"),
+        hjust = 0,
+        vjust = 0,
+        gp = grid::gpar(fontsize = 7)
+    )
     # end title
 
+    # drawing
+    pdf(NULL)
     grob.save <- NULL
+    main.plot <- eval(parse(text = paste(paste0(tempo.gg.name, 1:tempo.gg.count), collapse = " + ")))
+    main.plot.output <- suppressMessages(ggplot2::ggplot_build(main.plot))
+    main.grob <- suppressMessages(suppressWarnings(gridExtra::arrangeGrob(
+        main.plot, 
+        top = if(title == ""){" "}else{title.grob}, 
+        left = " ", 
+        right = " "
+    ))) # , left = " ", right = " " : trick to add margins in the plot. padding =  unit(0.5, "inch") is for top margin above the title
+    if( ! is.null(legend.width)){
+        grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(main.grob, legend.plot, ncol=2, widths=c(1, legend.width)))) # assemble grobs, ggplot, gtable into a gtable that defines the positions of the different elements (as grobs)
+    }else{
+        grob.save <- suppressMessages(suppressWarnings(print(main.grob)))
+    }
+    dev.off() # inactivate the pdf(NULL) above
     if(plot == TRUE){
-        if( ! is.null(legend.width)){
-            grob.save <- suppressMessages(suppressWarnings(gridExtra::grid.arrange(final.plot, legend.final, ncol=2, widths=c(1, legend.width))))
-        }else{
-            grob.save <- suppressMessages(suppressWarnings(print(final.plot)))
-        }
+        gridExtra::grid.arrange(grob.save) # plot a gtable (grob)
     }else{
         warn.count <- warn.count + 1
         tempo.warn <- paste0("(", warn.count,") PLOT NOT SHOWN AS REQUESTED")
@@ -672,25 +686,22 @@ fun_gg_donut <- function(
     }
     # end drawing
 
-
-
     # output
     if(warn.print == TRUE & ! is.null(warn)){
         on.exit(warning(paste0("FROM ", function.name, ":\n\n", warn), call. = FALSE))
     }
     on.exit(exp = options(warning.length = ini.warning.length), add = TRUE)
     if(return == TRUE){
-        output <- suppressMessages(ggplot2::ggplot_build(final.plot))
         if(is.null(unlist(removed.row.nb))){
             removed.row.nb <- NULL
             removed.rows <- NULL
         }
-        tempo <- output$layout$panel_params[[1]]
+        tempo <- main.plot.output$layout$panel_params[[1]]
         output <- list(
             data = data1, 
             removed.row.nb = removed.row.nb, 
             removed.rows = removed.rows, 
-            plot = output$data, 
+            plot.data = main.plot.output$data, 
             panel = facet.categ, 
             axes = list(
                 x.range = tempo$x.range, 
@@ -701,8 +712,8 @@ fun_gg_donut <- function(
                 y.positions = if(is.null(attributes(tempo$y$breaks))){tempo$y$breaks}else{unlist(attributes(tempo$y$breaks))}
             ), 
             warn = paste0("\n", warn, "\n\n"), 
-            ggplot = if(return.ggplot == TRUE){final.plot}else{NULL}, # final.plot plots the graph if return == TRUE
-            gtable = if(return.gtable == TRUE){grob.save}else{NULL} #
+            ggplot = if(return.ggplot == TRUE){main.plot}else{NULL}, # main plot -> plots the graph if return == TRUE
+            gtable = if(return.gtable == TRUE){grob.save}else{NULL} # gtable of the full graph (main + title + legend)
         )
         return(output) # this plots the graph if return.ggplot is TRUE and if no assignment
     }
diff --git a/~$te_little_R_functions.docx b/~$te_little_R_functions.docx
new file mode 100644
index 0000000000000000000000000000000000000000..866e9a8776f5c8fc6a7c738e6231617be8801463
GIT binary patch
literal 162
zcmZQ^PfX2WAQiAMxHBX&q%weX8ZqcGqySkUcF$g2M+OCkjt{dwY5uKeU|<A_f$)L;
oc|86=M%P|_M+QZP4xkViBp6<+2Z=*LL&si4M+PN?JSR{B0EC+q(f|Me

literal 0
HcmV?d00001

-- 
GitLab