Oleksandr Zaitsev
email: oleksandr.zaitsev@arolla.fr

Stéphane Ducasse
email: stephane.ducasse@inria.fr

Nicolas Anquetil
email: nicolas.anquetil@inria.fr

Arnaud Thiefaine
email: arnaud.thiefaine@arolla.fr

DepMiner: Automatic Recommendation of Transformation Rules for Method Deprecation !

Software applications often depend on external libraries and must be updated when one of those libraries releases a new version. To make this process easier, library developers try to reduce the negative effect of breaking changes by deprecating the API elements before removing them and suggesting replacements to the clients. Modern programming languages and IDEs provide powerful tools for deprecations that can reference the replacement or incorporate the rules written by library developers and use them to automatically update the client code. However, in practice library developers often miss the deprecation opportunities and fail to document the deprecations. In this work, we propose to help library developers support their clients with better deprecations. We rely on the transforming deprecations offered by Pharo and use data mining to detect the missing deprecation opportunities and generate the transformation rules. We implemented our approach for Pharo in a prototype tool called DepMiner. We have applied our tool to five open-source projects and proposed the generated deprecations to core developers of those projects. 63 recommended deprecations were accepted as pull requests.

Introduction

Most modern software depends on multiple external libraries [START_REF] Baldassarre | An industrial case study on reuse oriented development[END_REF]. Each one of those libraries is a separate project that is managed by its own team of developers. Like any other software, libraries evolve from one version to another, parts of their Application Programming Interfaces (API) are changed (classes, methods, or fields get renamed, deleted, or moved around, new functionalities are introduced, etc. [START_REF] Dig | How do APIs evolve? a story of refactoring[END_REF]). As a result, developers depending on those libraries must either update their code or continue having outdated and no longer maintained dependencies.

Deprecation is a common practice for supporting library evolution by notifying client systems about the changed or removed features and helping them adapt to the new API. Instead of removing a feature in release n, it is marked as deprecated ("to be removed") and only actually removed in a later release n+k. Client systems that call a deprecated feature receive a deprecation warning which gives developers time to update their code.

It is a good practice for library developers to supply deprecations with code comments or warning messages that suggest a replacement for an obsolete item. For example, "Method a() is deprecated, use b() instead". To support this practice, Java provides the @Deprecated annotation as well as the @deprecated Javadoc tag that can mark a method or class as deprecated while the @link or @see tags can reference the correct replacement in the source code [START_REF]Oracle. how and when to deprecate APIs. java se documentation[END_REF]. Pharo3 has a powerful deprecation engine called Deprewriter [START_REF] Ducasse | Deprewriter: On the fly rewriting method deprecations[END_REF]. It allows library developers to add transformation rules to their method deprecations specifying the replacements. When a deprecated method is invoked, Deprewriter identifies the call-site at run-time and uses the rule to update the client code without interrupting its execution [START_REF] Renggli | Embedding languages without breaking tools[END_REF][START_REF] Roberts | A refactoring tool for Smalltalk[END_REF][START_REF] Roberts | An automated refactoring tool[END_REF].

However, developers of real projects do not always follow good deprecation practices. They tend to introduce breaking changes to the APIs by renaming or removing certain classes, methods, or fields without deprecating them first [START_REF] Brito | You broke my code: understanding the motivations for breaking changes in APIs[END_REF][START_REF] Xavier | Historical and impact analysis of API breaking changes: A large-scale study[END_REF][START_REF] Xavier | Why do we break APIs? first answers from developers[END_REF]. Also, several large-scale studies of popular software projects have revealed that the proportion of deprecations that contain a helpful replacement message (in a form of comment, string, annotation, etc.) is only 66.7% for Java, 77.8% for C# [START_REF] Brito | On the use of replacement messages in API deprecation: An empirical study[END_REF], and 67% for JavaScript [START_REF] Nascimento | JavaScript API deprecation in the wild: A first assessment[END_REF].

Multiple approaches have been proposed to support client developers by automatically inferring missing messages. Dig et al., [START_REF] Dig | Automated detection of refactorings in evolving components[END_REF] proposed to detect refactorings between the two versions of the library based of the textual similarity of source code and the similarity of references. Schaffer et al. [START_REF] Schäfer | Mining framework usage changes from instantiation code[END_REF], Dagenais et al., [START_REF] Dagenais | Recommending adaptive changes for framework evolution[END_REF], and Hora et al. [START_REF] Hora | Apievolutionminer: Keeping api evolution under control[END_REF] mined frequent method call replacements in the commit history of a library to learn how it adapted to its own changes. Pandita et al., [START_REF] Pandita | Discovering likely mappings between APIs using text mining[END_REF] and Alrubaye et al., [START_REF] Alrubaye | On the use of information retrieval to automate the detection of third-party java library migration at the method level[END_REF] used a similar technique to help client developers replace dependencies to one library with dependencies to another one. Teyton et al. [START_REF] Teyton | Automatic discovery of function mappings between similar libraries[END_REF] and Brito et al., [START_REF] Brito | On the use of replacement messages in API deprecation: An empirical study[END_REF] recommend replacements by learning from client systems that have already updated their code.

In this work, we look at the problem from the perspective of library developers. We propose an approach and a tool called DepMiner to help them identify breaking changes before the release, understand when and by whom they were introduced, and find the potential replacements that could be suggested to the clients. We generate the recommendations in the form of transformation rules that can be used by Pharo's Deprewriter. Inspired by the existing approaches that were proposed to support the client developers [START_REF] Dagenais | Recommending adaptive changes for framework evolution[END_REF][START_REF] Hora | Apievolutionminer: Keeping api evolution under control[END_REF][START_REF] Schäfer | Mining framework usage changes from instantiation code[END_REF], our approach is based on the frequent method call analysis. The main differences are: (a) we recommend replacements before the release which makes it impossible to rely on the clients that were already updated; (b) Pharo is a dynamically-typed language, which means that we can not rely of type information when analyzing method call replacements; (c) Pharo has no explicit method visibility (i.e., public or private specifiers), which makes it hard to define the API. DepMiner can be extended to work with other increasingly popular dynamically-typed languages such as JavaScript, Python, Ruby, etc.

To evaluate our approach, we applied DepMiner to 5 diverse open-source projects that were implemented in Pharo and suggested its recommendations to the developers of those projects. 138 recommendations generated by our tool were confirmed by developers. 63 generated deprecations were accepted as pull requests into the projects.

The rest of this paper is structured as follows. In Section 2, we briefly describe the Deprewriter tool in Pharo. In Section 3, we discuss the problem of supporting library developers and the challenges that arise when dealing with this problem in Pharo. In Section 4, we describe our proposed approach and explain the underlying data mining algorithm. In Section 5, we evaluate our approach by comparing the generated transformation rules to the ones that are already present in the source code and by performing a developer study. Finally, in Section 6, we explain the limitations of our approach.

Deprewriter: Transforming Deprecations in Pharo

Pharo allows developers to enrich deprecations with code transformation rules [START_REF] Ducasse | Deprewriter: On the fly rewriting method deprecations[END_REF]. If a client system invokes the deprecated method, its source code is automatically fixed during execution to call the replacement: ↑ s e l f needsFullDefinition Lines 2-5 of the code above demonstrate the syntax of transforming deprecations in Pharo: method isSpecial (name in the first line) is deprecated with a message for the user 'Renamed to #needsFullDefinition' and a transformation rule that replaces method calls to isSpectial with calls to needsFullDefinition. The transformation rule consists of two parts: the antecedent, matches the method call that should be replaced ; the consequent, defines the replacement. '@rec and '@arg are rewriting variables matching respectively the receiver of the invocation and its argument.

1 isSpecial 2 s e l f
Transforming deprecations are a powerful technique that can save time for client developers. Because now, instead of reading the source code of a library and looking for the correct replacement, they only need to run the unit tests of their project to have their code fixed automatically.

3 Why Do We Need to Support Library Developers?

To understand the propagation of transforming deprecations, we have extracted all deprecated methods from v8.0.0 of the Pharo Project4 . We discovered that out of 470 valid deprecations in Pharo 8, 190 deprecations (40%) do not contain transformation rules. Out of those 190 non-transforming deprecations, 41 (22%) can have a simple transformation rule that can be generated automatically; 85 deprecations(45%) require developers with project expertise to provide extra information (additional argument, default value, etc.) and write a rule manually; the other 64 deprecations (34%) are complex and can not be expressed using the language for transformation rules that is used in Pharo. This indicates that developers don't always write transformation rules for their deprecations. Similar trends can be observed in other programming languages. For example, according to large-scale studies of software systems, the proportion of deprecations that do not contain a helpful replacement message (in a form of comment, string, annotation, etc.) is 33% for Java, 22% for C# [START_REF] Brito | On the use of replacement messages in API deprecation: An empirical study[END_REF], and 33% for JavaScript [START_REF] Nascimento | JavaScript API deprecation in the wild: A first assessment[END_REF].

Those observations demonstrate the need for an automated tool to recommend the replacement messages for method deprecations. There are two main challenges when implementing such a tool for Pharo: Challenge 1: Absence of method visibility. Languages like Java and C++ have public, private, and protected keywords that can help identify methods that are meant to be used by clients and can be considered as part of API. However, in languages like Python or Pharo all methods are public [START_REF] Schärli | Object-oriented encapsulation for dynamically typed languages[END_REF]. Sometimes Python developers use underscores at the beginning of method names to mark them as "private" but it is more of a good practice than a strict requirement and this practice is not always followed. Although Pharo developers often adopt different practices to mark methods as private, none of those practices are universally adopted by the Pharo community.

Challenge 2: Absence of static type information. Pharo is a dynamically-typed programming language [START_REF] Milner | A theory of type polymorphism in programming[END_REF][START_REF] Suzuki | Inferring types in smalltalk[END_REF]. The absence of static type information complicates the task of identifying correct method mappings between the old and the new version because it is not easy to map method calls in the source code to the actual method implementations. We also do not know the argument types. This has an important implication that we can get a combinatorial explosion when analysing a sequence of messages. The research community has proposed type inference for dynamically-typed languages [START_REF] Furr | Static type inference for Ruby[END_REF][START_REF] Passerini | An extensible constraint-based type inference algorithm for object-oriented dynamic languages supporting blocks and generic types[END_REF][START_REF] Pluquet | Fast type reconstruction for dynamically typed programming languages[END_REF][START_REF] Ren | Just-in-time static type checking for dynamic languages[END_REF][START_REF] Spoon | Demand-driven type inference with subgoal pruning: Trading precision for scalability[END_REF][START_REF] Suzuki | Inferring types in smalltalk[END_REF] or use dynamic type information collected by the Virtual Machine to get concrete types [START_REF] Milojković | Inferring Types by Mining Class Usage Frequency from Inline Caches[END_REF]. But such type inferencers often do not cover the full language [START_REF] Suzuki | Inferring types in smalltalk[END_REF] or are not applicable to large code bases [START_REF] Spoon | Demand-driven type inference with subgoal pruning: Trading precision for scalability[END_REF]. In this paper, we do not perform type inference and consider that the type information is missing, which constitutes a challenge for the data mining algorithm.

DepMiner: Recommending Transforming Deprecations by Mining the Commit History

We propose to assist library developers in the task of detecting the missed deprecation opportunities and finding proper replacements for the deprecated methods by mining frequent method call replacements from the commit history. Our approach consists of four steps:

1. Identifying the methods that belong to the old API and the new API of the project. 2. Collecting the database of method call replacements from the commit history. 3. Mining frequent method call replacements using the A-Priori algorithm for frequent itemsets mining. 4. Generating deprecations with transformation rules.

Identifying Methods of the Old and the New API. As we have discussed in Section 3 (Challenge 1), all methods in Pharo are public in the sense that clients can access them, however not all of those methods are meant to be used. To deal with this challenge, we define several categories of methods in Pharo that can be considered private: (a) initialize methods -they act like constructors in Pharo; (b) unit test methods, including setUp, tearDown, and methods of mock classes; (c) example methods; (d) baseline methods -define project structure and dependencies; (e) help methods -a form of documentation; (f) methods in "private" protocols -any protocol that includes the word "private". We implemented heuristics to infer the visibility of methods in Pharo and released them in a public repository 5 . With that information, we define two sets of methods: AP I old -"public" methods in the old version, and AP I new -"public" methods in the new version.

Collecting Method Changes from the Commit History. Given the history of commits between the old version and the new version, we extract method changes from every commit. A method change describes how one specific method was changed by a given commit. For each method change, we parse the source code of a method before and after it was changed and extract a set of method calls from each version. As a difference between those two sets, we get the sets of deleted and added method calls for every method change. We remove all deleted method calls that were not part of AP I old and all added method calls that are not part of AP I new . Because Pharo is a dynamically-typed language, we do not know which implementation of a method will be executed (see Section 3, Challenge 2). As a result, many method calls in our dataset are false positives, because they call the method with the same name as the one in AP I old and AP I new , but in reality that method is called from a different library (e.g., methods such as add() or asString() can be implemented by different classes). To deal with this problem and reduce the noise in our data, we choose a threshold K and remove all method changes that have more than K added or more than K deleted method calls (by experimenting with different values of K, for this project we selected K = 3). We also removed all calls to highly polymorphic methods such as = and printOn:. Finally, we removed all method changes for which either the set of deleted or the the set of added calls was empty.

Mining Frequent Method Call Replacements. After collecting the dataset of method changes from the commit history, we apply a data mining algorithm to find all frequent subsets of method call replacements. This technique was inspired by the work of Schäfer et al., [START_REF] Schäfer | Mining framework usage changes from instantiation code[END_REF], Hora et al., [START_REF] Hora | Apievolutionminer: Keeping api evolution under control[END_REF], and Dagenais et al., [START_REF] Dagenais | Recommending adaptive changes for framework evolution[END_REF] who proposed similar history-based approaches to support the clients of Java libraries. In terms of market basket analysis, each method change can be represented as a transaction or an itemset. To do that, we merge the sets of added and deleted calls in a method into a single set. For example, {deleted(isEmpty), deleted(not), deleted(add), added(new), added(isNotEmpty) }. By selecting a minimum support threshold min sup , we use a data mining algorithm such as A-Priori, Eclat, or FP-Growth to find all combinations of method calls that appear in different method changes at least min sup times (frequent itemsets). Then we construct association rules by putting all deleted method calls into the antecedent (left hand side) and all added method calls into the consequent (right hand side). We remove the rules with empty antecedent or empty consequent. For each association rule I → J, we calculate its confidence -the probability that a set of deleted calls I appear jointly with added calls J and not with something else:

conf idence(I → J) = support(I ∪ J) support(I)
We select a confidence threshold min conf and filter out all association rules that do not reach this threshold. The current implementation of Deprewriter supports only one-to-one (one antecedent, one consequent) and one-to-many rules (one antecedent, several consequents) -the ones that define the replacement of one method call (the method from the old API that is being deprecated) with one or more method calls. Therefore, we remove all many-to-one and many-to-many rules from the collection of association rules.

Generating Recommendations. Based on two sets of methods, AP I old and AP I new , and the collection of association rules Assoc, mined from the method changes, we can now provide recommendations to library developers:

1. Proposed deprecations -we find all methods of the old API that were deleted without being deprecated (every method m such that m ∈ AP I old and m / ∈ AP I new ). If we can find at least one association rule in Assoc that defines the replacement for a given method m, then we recommend to reintroduce m into the new version of a project with deprecation and a transformation rule if it can be generated. 2. Transformation rules for existing deprecations -first we identify all manually deprecated methods from AP I new that do not contain a transformation rule. For every such method m, if we can find at least one association rule a ∈ Assoc that defines the replacement for m, we recommend to insert a transformation rule into the deprecation of m either automatically (in case the transformation rule can be inferred from a, as we will discuss below) or semi-automatically (in case we can only show the association rule a to developers and ask them to write a transformation rule manually).

Transformation rules of the form '@rec selector1: '@arg → '@rec selector2: '@arg are generated automatically from the association rule such as {selector1:} → {selector2:} only if:

association rule is one-to-one (one deleted method call replaced with one added method call), -deleted and added method calls have the same number of arguments, -deleted and added method calls are defined in the same class of the new version of the project (and therefore can have the same receiver).

If one of those conditions is not satisfied, the transformation rule can not be generated and must be written manually by a developer. In those cases, we only show to developers the association rule together with the examples of method changes in which those rules appeared and ask them to write a transformation rule manually.

Evaluation

We have implemented our approach in a prototype tool for Pharo called Dep-Miner. 6 Our implementation is based on the A-Priori algorithm for mining frequent itemsets. We have applied DepMiner to several open-source projects and asked core developers of those projects to review the recommendations produced by DepMiner.

Evaluation Setup

Selected projects. For this study, we have selected five open-source projects:

-Pharo7 -a large and mature system with more than 150 contributors, containing the language core, the IDE, and various libraries.

-Moose Core8 -Moose is a large platform for data and source code analysis. It consists of multiple repositories, we focus only on the core repository of Moose. -Famix9 -generic library that provides an abstract representation of source code in multiple programming languages. Famix is part of the Moose project. -Pillar10 -a markup syntax and tool-suite to generate documentation, books, websites and slides. -DataFrame11 -a specialized collection for data analysis that implements a rich API for querying and transforming datasets.

We selected such projects because: (1) we were able to interview and ask maintainers to validate the proposed deprecations, (2) the projects evolved over several versions and are still under active development, (3) we wanted to compare the performance of DepMiner on the projects with different maturity and complexity levels.

For this study, we define three types of projects:

-Tool -a project that is designed for the end users (in the experiment: Moose, Pillar). For example, a text editor, a website, or a smartphone app. In many cases, APIs of those projects do not change that much (e.g. poorly named method that is not called by external projects might not be renamed) and when they do change, deprecations are rarely introduced. -Library -a project that is supposed to be used as dependency by other projects (in the experiment: Famix, DataFrame). For example, a data structure, a networking library, or a library for numeric computations. Projects of this type must have a stable API and good versioning. They are most likely to introduce deprecations. -SDK -a special type of project that describes Pharo. It is a combination of multiple different projects. Pharo has many users and even small changes to API can break software that is built with Pharo. This means that deprecations are very important for this type of projects. Two versions of each project. To mine the repetitive changes and propose deprecations, we must first select two versions of each project: the new version for which we will propose the deprecations and the old version to which we compare the new version of the project. All patterns will then be mined from the slice of the commit history between those two versions. Table 1 lists the two versions of each project that we have loaded as well as the number of commits between those two versions.

Mining frequent method call replacements. We used DepMiner to mine frequent method call replacements from the histories of those projects and recommend deprecations with transformation rules. In Table 2, we report the minimum support and minimum confidence thresholds that were used to initialize the A-Priori algorithm. The minimum support threshold for each project was selected experimentally. We started with a large support threshold = 15 (meaning that we are only interested in replacements that happened at least 15 times) and decreased it until the number of generated recommendation seemed sufficiently large. The confidence threshold was selected based on the number of method changes and the number of rules that DepMiner generated for a selected support value. For Pharo and Famix we can expect rules with confidence of at least 0.4. For other projects, we limit confidence to 0.1. In the last two columns of Table 2, we present the number of association rules (frequent method call replacements) that were found by DepMiner given the settings discussed above, and the number of rules that can automatically generate the transformation rules of the form '@rec delet-edSelector: '@arg → '@rec addedSelector: '@arg (only one-to-one rules where deleted and added selectors have the same number of arguments).

Evaluation by Project Developers

We have performed a first developer study of our tool involving the core developers from each project listed in Section 5.1. We asked 4 developers with different areas of expertise to validate the recommendations generated for Pharo and one developer for each of the other 4 projects (two developers had expertise in two projects each so in total, our study involved 6 developers).

To each developer, we showed the pretrained DepMiner tool with recommended methods to deprecate and recommended transformation rules to insert into the existing deprecations. The developers had to select the changes which, in their opinion, should be merged into the project. DepMiner allows its users to browse multiple version of the project as well as the commits history. Each recommendation is supported by the list of commits in which the given method call replacement has appeared. This allowed developers who participated in our study to make an informed decision. For the Pharo project we considered recommendation accepted if it was accepted by at least one developer (because different developers might know different parts of the whole system).

Proposed deprecations. Table 3 reports the numbers of deprecations that were recommended to developers for each project (column Recommended), the number of those recommendations that were accepted (column Accepted), and the number of those accepted recommendations that contain an automatically generated transformation rule (column Transforming). Each recommended deprecation is a method that was deleted from the project without being deprecated first and which we propose to re-introduce with the recommended replacement. One can see that DepMiner was very effective in generating recommendations for Pharo (113 recommendations, 61 accepted), Famix (87 recommendations, 68 accepted), and DataFrame library (11 recommendations, 4 accepted) but rather ineffective on Moose Core (33 recommendations, 1 accepted) and Pillar (1 recommendation, 0 accepted).

The different performance on those projects can not be explained by their size. For example, the DataFrame project is the smallest one in our list, but out of 11 deprecations generated by DepMiner, 4 deprecations were accepted. On the other hand, for the Pillar project, which is 10 times larger in terms of the number of methods, only 1 deprecation was generated and it was not accepted. Further study is required to explain the differences between DataFrame and Pillar, but we can speculate that bad performance on Pillar is caused by the low variability of API. Methods of DataFrame were often renamed, removed, or reorganised, which was reflected in test cases and picked up by DepMiner. On the other hand, the API of Pillar remained stable even though new functionality was added to it and many bugs were fixed.

Missing rules. The other type of recommendations that we showed to developers were transformation rules for existing non-transforming deprecations. Table 4 re-ports the number of existing deprecations that are missing a transformation rule, the number of recommendations that DepMiner managed to generate for those deprecations, and finally the number of recommendations that were accepted by developers. Deprecations that are missing the transformation rule (the non-transforming deprecations) represent either complicated cases for which the transformation rule can not be provided (e.g. method was deleted without replacement) or simple cases for which developers forgot to write a rule. As we mentioned in Section 3, for 22% of non-transforming deprecations the transformation rule could be generated automatically (assuming that we know the correct replacement), the other 78% of non-transforming deprecations require a complex rule that must be written manually. DepMiner proposed 6 transformation rules for existing non-transforming deprecations in Pharo (2 of which were accepted) as well as 2 transformation rules for Famix (both were accepted).

Pull Requests. Out of 5 projects that we used in our study, only Pharo Project was preparing an upcoming release. We applied DepMiner to the latest commit of the development version of Pharo and this allowed us to submit the recommendations that were confirmed by developers as pull requests. All 61 confirmed deprecations and 2 confirmed transformation rules for existing deprecations were merged into the v9.0.0 release of of Pharo.

Limitations of Our Approach

Unused/untested methods. Our approach is based on library's usage of its own API. This means that we can not infer anything for methods that are not used by the library itself but only intended for clients. Test cases play the role of clients of the library's API, so for the methods that are well tested, we can have enough input to identify the correct replacement for them. But if a method is not used by the library and not covered by test, then its deletion or renaming will not be reflected anywhere else in the source code.

Reflective operations. Modern programming languages offer reflective operations [START_REF] Callau | How developers use the dynamic features of programming languages: the case of smalltalk[END_REF][START_REF] Richards | The eval that men do: A large-scale study of the use of eval in javascript applications[END_REF]. They allow developers to invoke methods programmatically and create generic and powerful tools. However, since some methods can be invoked reflectively for example passing the name of the method to be invoked in a variable, when a different argument is passed to a reflective call, our tool cannot identify such change. Most static analysers ignore such case [START_REF] Bodden | Taming reflection: Aiding static analysis in the presence of reflection and custom class loaders[END_REF].

Unordered set of method calls. Our tool is based on mining method call replacement by comparing the set of calls that were deleted from the source code of a modified method to the set of calls that were added to it. We do not take into account the order of method calls, the distance between them or how they are composed: a().b() or a(b()). This is a limitation of our approach because: (1) sometimes deleted and added method calls are located far away in source code and not related to each other; (2) if one method call is being replaced with two or more method calls, we do not know how they should be composed.

Related Work

Breaking changes. Breaking changes are the code modifications in all API elements that break backward compatibility [START_REF] Dig | How do APIs evolve? a story of refactoring[END_REF]. In their large-scale analysis of Java libraries, Xavier et al., [START_REF] Xavier | Historical and impact analysis of API breaking changes: A large-scale study[END_REF] discovered that 28% of API changes break backward compatibility however, on the median, only 2.54% of clients are impacted by them. In their follow-up study, Xavier et al., [START_REF] Xavier | Why do we break APIs? first answers from developers[END_REF] performed a survey of developers to understand why they introduce breaking changes into their projects. They identified five reasons: library simplification, refactoring, bug fixes, dependency changes, project policy. This study was later extended by Brito et al., [START_REF] Brito | You broke my code: understanding the motivations for breaking changes in APIs[END_REF] who reported that 47% of breaking changes are due to refactorings. These results can be complemented by the previous study by Dig and Johnson [START_REF] Dig | How do APIs evolve? a story of refactoring[END_REF] who analysed breaking changes in five Java systems and discovered that 81-100% of them are caused by refactorings. Those findings are important for our study because changes that are introduced by refactorings (e.g. renaming, replacement, spitting, etc.) ofter require simple repetitive fixes in the client code that can be expressed with transformation rules. Impact of deprecations. Robbes et al., [START_REF] Robbes | Extensions during software evolution: do objects meet their promise?[END_REF] studied the impact of API changes, and in particular deprecations, on Pharo and Squeak ecosystems. They report that the majority of client systems are updated over a day, but in some cases the update takes longer and is performed only partially. Sawant et al., [START_REF] Sawant | On the reaction to deprecation of 25,357 clients of 4+1 popular java APIs[END_REF] report similar results for Java. Hora et al., [START_REF] Hora | How do developers react to api evolution? the Pharo ecosystem case[END_REF][START_REF] Hora | How do developers react to api evolution? a large-scale empirical study[END_REF] complemented the previous studies by analysing the impact of API evolution on Pharo ecosystem, but focusing only on those changes which are not related to deprecations. They claim that API changes have large impact on the ecosystem and most of the changes that they found can be implemented as rules in static analysis tools. Several authors have also explored the effectiveness of deprecation messages. Large-scale empirical studies of software written in Java and C# [START_REF] Brito | Do developers deprecate APIs with replacement messages? a large-scale analysis on java systems[END_REF][START_REF] Brito | On the use of replacement messages in API deprecation: An empirical study[END_REF] as well as JavaScript [START_REF] Nascimento | JavaScript API deprecation in the wild: A first assessment[END_REF] revealed that 22-33% of deprecations in those languages are not supported by replacement messages. In their study of Pharo ecosystem, Robbes et al., [START_REF] Robbes | Extensions during software evolution: do objects meet their promise?[END_REF] also showed that almost 50% of deprecation messages do not help to identify the correct replacement.

Library migration and update. Mining the commit history to find a mapping between two versions of API is not a new idea. Similar problems were targeted in the context of library update (updating client system to depend on a new version of the external library) and library migration (replacing the dependency on one library with that on a different library). First approaches in this area were based on static code analysis and the textual similarity of method signatures [START_REF] Kim | Automatic inference of structural changes for matching across program versions[END_REF][START_REF] Xing | API-evolution support with diff-catchup[END_REF]. Wu et al., [START_REF] Wu | Aura: a hybrid approach to identify framework evolution[END_REF] proposed an approach that combined call dependency and text similarity analyses. Schäfer et al., [START_REF] Schäfer | Mining framework usage changes from instantiation code[END_REF] proposed to mine library update rules from already updated client systems. The following studies analysed the commit history between the two versions of a library [START_REF] Dagenais | Recommending adaptive changes for framework evolution[END_REF][START_REF] Meng | A history-based matching approach to identification of framework evolution[END_REF] and proposed changes that should be applied to client systems. Teyton et al., [START_REF] Teyton | Automatic discovery of function mappings between similar libraries[END_REF] used data mining and propose rules for library migration by learning from the commit histories of already migrated clients. Hora et al., [START_REF] Hora | Apievolutionminer: Keeping api evolution under control[END_REF] proposed a similar approach to find method mappings between different releases of the same library. Pandita et al., [START_REF] Pandita | Discovering likely mappings between APIs using text mining[END_REF] approached the problem of library migration by analysing textual similarity of documentation from different libraries. Alrubaye et al., [START_REF] Alrubaye | Learning to recommend third-party library migration opportunities at the API level[END_REF] proposed a novel machine learning approach that inferred the mapping between the API elements of two different library by extracting various features from library documentation and solving a classification problem. Our approach of mining the commit history was inspired by previous works in the domain of library migration and update. However, instead of proposing migration rules to client developers, we propose transforming deprecations to library developers.

Recommending deprecations and replacements. To the best of our knowledge, our study is the first to propose supporting library developers by recommending deprecations and generating replacement rules. Brito et al., [START_REF] Brito | On the use of replacement messages in API deprecation: An empirical study[END_REF] recommend replacement messages for deprecations by learning from client systems that have already identified the correct replacements and updated their code. Our approach takes information from the history of a library and therefore does not depend on migrated clients (which might not be available or may not cover the full API). Xi et al., [START_REF] Xi | Migrating deprecated API to documented replacement: Patterns and tool[END_REF] designed an automatic process for migrating deprecating API to its replacement in client systems. However, their approach is based on the replacement messages in code documentation. One of the goals of our approach is to generate those replacement messages when they are not known.

Conclusion

Method deprecation is a powerful technique for supporting the evolution of software libraries and informing client developers about the upcoming breaking changes to the API. We proposed to mine the frequent method call replacements from the commit history of a library and use them to recommend method deprecations and transformation rules. We implemented our approach for Pharo IDE in a tool called DepMiner. We applied our tool to five open-source projects and asked 6 core developers from those projects to accept or reject the recommended changes. In total, 134 proposed deprecations were accepted by developers as well as 4 transformation rules for the existing deprecations. 61 new deprecations and 2 transformations rules for existing deprecations were integrated into the Pharo project.

3

  deprecated : ' Renamed t o # n e e d s F u l l D e f i n i t i o n ' 4 transformWith : ' `@rec i s S p e c i a l ' 5 -> ' `@rec n e e d s F u l l D e f i n i t i o n ' .

6

 6

Table 1 .

 1 Selected software projects

	Project	Type Old version New version Commits
	Pharo	SDK	v8.0.0	af41f85	3,465
	Moose Core Tool	v7.0.0	v8.0.0	1,519
	Famix	Library	a5c90ff	v1.0.1	948
	Pillar	Tool	v8.0.0	v8.0.12	508
	DataFrame Library	v1.0	v2.0	225

Table 2 .

 2 Association rules mined from the commit history

	Project	Min sup. Min conf. Assoc. rules Transforming
	Pharo	5	0.4	377	152
	Moose Core	2	0.1	88	40
	Famix	4	0.4	149	60
	Pillar	2	0.1	49	16
	DataFrame	5	0.1	22	7

Table 3 .

 3 Number of recommended deprecations accepted by developers

	Project	Recommended Accepted Transforming
	Pharo	113	61	56
	Moose Core	33	1	1
	Famix	87	68	28
	Pillar	1	0	0
	DataFrame	11	4	4

Table 4 .

 4 Number of missing rules accepted by developers

	Project	Missing Recommended Accepted
	Pharo	189	6	2
	Moose Core	2	0	0
	Famix	27	2	2
	Pillar	0	0	0
	DataFrame	0	0	0

Pharo is a dynamically-typed object oriented programming language and IDE: https: //pharo.org/

Pharo is a programming language and an IDE written entirely in itself. This can be a source of confusion. In this section, we discuss the analysis of how transforming deprecations, introduced into Pharo (an open-source project with over 150 contributors), were used by its developers to deprecate methods in other parts of the same project. In other words, we study how Pharo developers use the rewriting functionality of Pharo to deprecate methods in Pharo.

https://anonymous.4open.science/r/VisibilityDeductor-EF86

https://anonymous.4open.science/r/DepMiner-0D5B

Pharo is an open-source project written in Pharo programming language (see footnote in Section 3), https://github.com/pharo-project/pharo

https://github.com/moosetechnology/Moose

https://github.com/moosetechnology/Famix

https://github.com/pillar-markup/pillar

https://github.com/PolyMathOrg/DataFrame

Acknowledgements

We want to thank all developers who participated in our experiment and helped us evaluate the recommendations, particularly Guillermo Polito, Pablo Tesone, Marcus Denker, and Benoît Verhaeghe. We are also grateful to the Arolla software company for financing this research.

! This work was financed by the Arolla software company.


Table S3 : The formulas of the different generalized linear mixed models constructed during the analysis of bud's survival and the associated value of AICc's weights, related to the analysis of bud survival.

First, there is the results for the random effect selection and then for the fixed effects selection. Table S4 : The formulas of the different linear mixed models and generalized linear mixed models constructed during the first sexual reproduction analysis and the analysis of the number of eggs produced, and the associated value of AICc's weight, related to the analysis of the timing of sexual reproduction.

First, there is the results for the random effect selection and then for the fixed effects selection.

Data formatting

The dataframe all (corresponding to asexual_reproduction_indivudual_following.csv) contains the detail of asexual reproduction's following for each individual. data_sum (corresponding to asexual_reproduction_summary.csv) is the summary for each variable per individual of the asexual reproduction's following. surv (corresponding to asexual_reproduction_survival.csv) contains the survival's data. transmission (corresponding to asexual_reproduction_tumoral_transmission.csv) gather the data about the tumoral's transmission during the budding.

Analysis of the age at the first asexual reproduction

We start by isolating all the data concerning the first reproductive episode, then a Wilcoxon's and a Kolmorogov-Smirnov test are applied on the number of day past before the first reproduction between the heathly and tumorous hydras. The standardized distributions of the first day of reproduction are a significantly different in both groups.

Graphical representation

Budding rate analysis

We start by testing if the random effects of the birthday of the individual and/or the individual are useful to explain the variable. Given that the b4 model do not converge, it has been excluded. The weights of AICc suggest that the first model with the random effect of the individual best fitted the data than the other ones.

Then, we selected now the fixed effects to include. The fixed effect of the interaction between the Status and the Phase seems to be the ones allowing the better explanation of the variable.

The final model take into account the fixed of the interaction between the Status and the Phase, and the random effect of the individual. Final result Graphical representation

Number of bud per reproductive episode analysis

We start by creating a column describing qualitatively the type of reproductive episode observed.

all$bud_type<-with(all,ifelse(Nb_simultanous_buds<2,"simple","multiple")) all$bud_type<-as.factor(all$bud_type)

The random effect is selected. The third and fourth model do not converge, they have been excluded. The second model seems to be the best.

The fixed effects are selected.

sim2_1<-glmmTMB(bud_type~Phase*Status+(1|ID_parent),REML = FALSE,family=binomial(logit),data =all) sim2_2<-glmmTMB(bud_type~Phase+Status+(1|ID_parent),REML = FALSE,family=binomial(logit),data =all) sim2_3<-glmmTMB(bud_type~Phase+(1|ID_parent),REML = FALSE,family=binomial(logit),data =all) sim2_4<-glmmTMB(bud_type~Status+(1|ID_parent),REML = FALSE,family=binomial(logit),data =all) sim2_5<-glmmTMB(bud_type~1+(1|ID_parent),REML = FALSE,family=binomial(logit),data =all) ## model weights ## [1] 0.158 0.332 0.000 0.510 0.000

The fourth model has the better AICc.

The final model take into account the fixed effect of the Status and the random effect of the individual.

sim2_4<-glmmTMB(bud_type~Status+(1|ID_parent),REML = TRUE,family=binomial(logit),data =all)

Finally, model's residuals are checked.

The model selected fit well the data.

Final result

Type of reproductive episode (simple/multiple) Status

Predictors Odds Ratiosstd. Error p

Tumorous status 0.37 0.07

7.363e-08

Observations 751

Graphical representation

Analysis of the gap between two reproductive episode

The random effect is selected. The fourth model do not converge then have been excluded. The first model seems to be the best.

The fixed effect is selected. The second model has the better AICc.

The final model take into account the fixed effect of the Status and the Phase, and the random effect of the individual.

del1_2<-glmmTMB(Delta~Status+Phase+(1|ID_parent),REML = TRUE,family = poisson(log),data=all)

Finally, model's residuals are checked.

The overdispersion and the outlier tests are correct.

Final result

Delta ~ Status + Phase

Predictors Incidence Rate Ratiosstd. Error p

Tumor status 0.81 0.04

3.392e-06

Passage of the 5th week 1.25 0.05

5.208e-08

Graphical representation

Survival time analysis

We start by calculating the survival time.

surv$survival_Time<-with(surv,ifelse(is.na(Death_date),as.numeric(surv$Last_check_date-surv$P arent_birthday),as.numeric(surv$Death_date-surv$Parent_birthday)))

Then we traced the graph of the instantaneous risk over the time.

The instantaneous risk is not linear across the time so we will used a Weibull distribution to model this risk.

survie<-Surv(as.numeric(surv$survival_Time),surv$Censored) modele<-survreg(survie~surv$Status,dist="weibull")

Final result

Survival time ~ Status

Predictors Estimatesstd. Error p

Tumor status 0.39 0.07 6.862e-08

Log(scale) 0.33 0.05

5.883e-15

Observations 62

Graphical representation

Analysis of bud's survival First, the random effect is selected. The first and the fourth model are equal, so we decided to keeps the nested random effect because at the same survey date different batch.

Then, we select the fixed effect. The twelfth model has the better AICc.

The final model take into account the fixed of the Production_phase, and the nested random effect of the Survey_date and the Complet_batch.

Final result

Analysis of tumor transmission

We begin with isolating, inside the surviving buds, those which are from a tumor strain.

transmission_alive<-subset(transmission,Survival=="Alive") transmission_alive$Bud_status<-as.factor(transmission_alive$Bud_status) tum<-subset(transmission_alive, Status=="Tumor")

Afterward, we select the random effect needed. The last model does not converged, so it has been excluded. The second and the third model have identical AICc so we decided to selected the random effect of the Complet_batch because it has more level than the Survey_date.

devtum41<-glmmTMB(Bud_status~Production_phase*Bud_type+(1|Complet_batch),REML=FALSE, family = binomial(logit),data=tum) devtum42<-glmmTMB(Bud_status~Production_phase+Bud_type+(1|Complet_batch),REML=FALSE, family = binomial(logit),data=tum) devtum43<-glmmTMB(Bud_status~Production_phase+(1|Complet_batch),REML=FALSE, family = binomial (logit),data=tum) devtum44<-glmmTMB(Bud_status~Bud_type+(1|Complet_batch),REML=FALSE, family = binomial(logit), data=tum) devtum45<-glmmTMB(Bud_status~1+(1|Complet_batch),REML=FALSE, family = binomial(logit),data=tu m)

The last model has the better AICc.

The final model take into account no fixed effect, and the random effect of the Complet_batch. The weights of AICc suggest that the model with the random effect fit more to the data than the other one.

Then we select the fixed effects needed. The fixed effect of the interaction between the Status and the Phase seems to be the one allowing the better explanation of the variable.

The final model take into account the fixed of the interaction between the Status and the Phase, and the random effect of the date. Finally, model's residuals are checked to see if the model fit and explain really the data.

Lexical

  Status : the strain from which the individual comes from (tumorous or healthy) ID_parent : the name of each individual Parent_birthday : the date of birth of the individual Survey_date : the date when the survey has been done ID_reproductive_episode : the number attributed to each reproductive episode Delta : the number of day between two reproductive episode Nb_simultanous_buds : the number of bud produce by each individual at each reproductive episode Cum_nb_buds : the cumulative sum of the number of buds for each individual Time : the number of day between the birth and the survey date Week : the number of week past since the birthday of the individual Phase : the age of the individual when it has been placed in the sexual reproduction conditions Nb_buds : the number of bud produce by each individual for each phase Delay : the delay (in days) of each reproductive phase for each individual (considering that the first phase start at the production of the first bud) Mean_simult_buds : the mean number of bud produce by each individual at a reproductive episode per each phase Mean_Delta : the mean number of day between two reproductive episode per each phase Stand_nb_bud : the mean number of bud produce by each individual for each phase on 100 days (calculated by correcting the total number of bud per phase by its delay and multiplied by 100) Time : the number of day between the birth and the survey date Week : the number of week past since the birthday of the individual Death_date : the date of each individual death Spont_Tum_date : the date of apparition of spontaneous tumors on control individuals Spont_tum_event : the observation of spontaneous tumor on control hydra during the experiment Censored : the observation the individual death during the experiment Last_check_date : the date when the last survival measurement had take place Production_phase : the parent's age-class when the bud was produced Batch : the batch number of the parent Bud_type : the type (simple or multiple) of budding episod which from the bud is originated Bud_birthday : the day when the bud was detach Bud_status : the status of the buds one month after their birth Complet_batch : the name of batch composed by the status, the phase and the batch number Survival : the individual's condition (alive or dead)

  b1<-glmmTMB(Stand_nb_bud~Phase*Status+(1|ID_parent),REML = TRUE, family=poisson(log),data = d ata_sum) b2<-glmmTMB(Stand_nb_bud~Phase*Status+(1|Parent_birthday),REML = TRUE, family=poisson(log),da ta = data_sum) b3<-glmmTMB(Stand_nb_bud~Phase*Status+(1|ID_parent)+(1|Parent_birthday),REML = TRUE, family=p oisson(log),data = data_sum) b4<-glmmTMB(Stand_nb_bud~Phase*Status,REML = TRUE, family=poisson(log),data = data_sum)

  b1_1<-glmmTMB(Stand_nb_bud~Phase*Status+(1|ID_parent),REML = FALSE,family=poisson(log),data = data_sum) b1_2<-glmmTMB(Stand_nb_bud~Phase+Status+(1|ID_parent),REML = FALSE,family=poisson(log),data = data_sum) b1_3<-glmmTMB(Stand_nb_bud~Phase+(1|ID_parent),REML = FALSE,family=poisson(log),data = data_s um) b1_4<-glmmTMB(Stand_nb_bud~Status+(1|ID_parent),REML = FALSE,family=poisson(log),data = data_ sum) b1_5<-glmmTMB(Stand_nb_bud~1+(1|ID_parent),REML = FALSE,family=poisson(log),data = data_sum) ## model weights ## [1] 0.994 0.006 0.000 0.000 0.000

  b1_1<-glmmTMB(Stand_nb_bud~Phase*Status+(1|ID_parent),REML = TRUE,family=poisson(log),data = data_sum)Finally, model's residuals are checked to see if the model is equilibrated.Overdispersion, Kolmogorov-Smirnov and outlier test end all correct.

  sim1<-glmmTMB(bud_type~Phase*Status+(1|ID_parent),REML = TRUE, family=binomial(logit),data = all) sim2<-glmmTMB(bud_type~Phase*Status+(1|Parent_birthday),REML = TRUE, family=binomial(logit),d ata = all) sim3<-glmmTMB(bud_type~Phase*Status+(1|ID_parent)+(1|Parent_birthday),REML = TRUE, family=bin omial(logit),data = all) sim4<-glmmTMB(bud_type~Phase*Status,REML = TRUE, family=binomial(logit),data = all) ## model weights ## [1] 0.489 0.511

  del1<-glmmTMB(Delta~Status*Phase+(1|ID_parent),REML = TRUE, family = poisson(log),data=all) del2<-glmmTMB(Delta~Status*Phase+(1|Parent_birthday),REML = TRUE, family = poisson(log),data= all) del3<-glmmTMB(Delta~Status*Phase+(1|ID_parent)+(1|Parent_birthday),REML = TRUE, family = pois son(log),data=all) del4<-glmmTMB(Delta~Status*Phase,REML = TRUE, family = poisson(log),data=all)

  del1_1<-glmmTMB(Delta~Status*Phase+(1|ID_parent),REML = FALSE, family = poisson(log),data=al l) del1_2<-glmmTMB(Delta~Status+Phase+(1|ID_parent),REML = FALSE,family = poisson(log),data=all) del1_3<-glmmTMB(Delta~Status+(1|ID_parent),REML = FALSE,family = poisson(log),data=all) del1_4<-glmmTMB(Delta~Phase+(1|ID_parent),REML = FALSE,family = poisson(log),data=all) del1_5<-glmmTMB(Delta~1+(1|ID_parent),REML = FALSE,family = poisson(log),data=all)

  surv1<-glmmTMB(Survival~Status*Production_phase*Bud_type+(1|Complet_batch)+(1|Survey_date),RE ML=TRUE, family = binomial(logit),data=transmission) surv2<-glmmTMB(Survival~Status*Production_phase*Bud_type+(1|Complet_batch),REML=TRUE, family = binomial(logit),data=transmission) surv3<-glmmTMB(Survival~Status*Production_phase*Bud_type+(1|Survey_date),REML=TRUE, family = binomial(logit),data=transmission) surv4<-glmmTMB(Survival~Status*Production_phase*Bud_type+(1|Survey_date/Complet_batch),REML=T RUE, family = binomial(logit),data=transmission) surv5<-glmmTMB(Survival~Status*Production_phase*Bud_type,REML=TRUE, family = binomial(logit)

  surv4_12<-glmmTMB(Survival~Production_phase+(1|Survey_date/Complet_batch),REML=TRUE, family = binomial(logit),data=transmission) The modele's residuals are checked. ## Object of Class DHARMa with simulated residuals based on 250 simulations with refit = FALS E . See ?DHARMa::simulateResiduals for help. ## ## Scaled residual values: 0.4767229 0.5598669 0.1379513 0.388332 0.02551519 0.02524903 0.852 6626 0.67773 0.8209898 0.6766341 0.6859425 0.3158154 0.5510649 0.4938672 0.1657359 0.6996305 0.317379 0.6103911 0.2221154 0.5752245 ...

  devtum10<-glmmTMB(Bud_status~Production_phase+Bud_type+(1|Complet_batch)+(1|Survey_date),REML =TRUE, family = binomial(logit),data=tum) devtum20<-glmmTMB(Bud_status~Production_phase+Bud_type+(1|Complet_batch),REML=TRUE, family = binomial(logit),data=tum) devtum30<-glmmTMB(Bud_status~Production_phase+Bud_type+(1|Survey_date),REML=TRUE, family = bi nomial(logit),data=tum) devtum40<-glmmTMB(Bud_status~Production_phase+Bud_type+(1|Survey_date/Complet_batch),REML=TRU E, family = binomial(logit),data=tum) devtum50<-glmmTMB(Bud_status~Production_phase+Bud_type,REML=TRUE, family = binomial(logit),da ta=tum) ## model weights ## [1] 0.125 0.375 0.375 0.125

  devtum45<-glmmTMB(Bud_status~1+(1|Complet_batch),REML=TRUE, family = binomial(logit),data=tu m) The model's residual are checked. . codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 f1<-lm(First_reproduction_age~Status*Phase,data=sexual_reproduction) f2<-lmer(First_reproduction_age~Status*Phase+(1|Cold_placement_date),REML = TRUE,

  f2_1<-lmer(First_reproduction_age~Status*Phase+(1|Cold_placement_date),REML = FALSE,data=sexu al_reproduction) f2_2<-lmer(First_reproduction_age~Status+Phase+(1|Cold_placement_date),REML = FALSE,data=sexu al_reproduction) f2_3<-lmer(First_reproduction_age~Status+(1|Cold_placement_date),REML = FALSE,data=sexual_rep roduction) f2_4<-lmer(First_reproduction_age~Phase+(1|Cold_placement_date),REML = FALSE,data=sexual_repr oduction) f2_5<-lmer(First_reproduction_age~1+(1|Cold_placement_date),REML = FALSE,data=sexual_reproduc tion) ## model weights ## [1] 0.734 0.258 0.000 0.008 0.000

  f2_1<-lmer(First_reproduction_age~Status*Phase+(1|Cold_placement_date),REML = TRUE,data=sexua l_reproduction)

  Theses tests are not significant, we standardized the variable by dividing by the maximal number of days before the first reproduction for each status. Then we reapply the same tests.

	##
	## Wilcoxon rank sum test with continuity correction
	##
	## data: first_repro[first_repro$Status == "Control", "Time"] and first_repro[first_repro$St
	atus == "Tumor", "Time"]
	## W = 525, p-value = 0.1543
	## alternative hypothesis: true location shift is not equal to 0
	ks.test(first_repro[first_repro$Status=="Control","Time"],first_repro[first_repro$Status=="Tu
	mor","Time"])
	##
	## Two-sample Kolmogorov-Smirnov test
	##
	## data: first_repro[first_repro$Status == "Control", "Time"] and first_repro[first_repro$St
	atus == "Tumor", "Time"]
	## D = 0.22222, p-value = 0.3364
	## alternative hypothesis: two-sided
	ks.test(nctrl,ntum)
	##
	## Two-sample Kolmogorov-Smirnov test
	##
	## data: nctrl and ntum
	## D = 0.44444, p-value = 0.001632
	## alternative hypothesis: two-sided
	wilcox.test(nctrl,ntum)
	##
	## Wilcoxon rank sum test with continuity correction
	##
	## data: nctrl and ntum
	## W = 377.5, p-value = 0.00215

first_repro<-as.data.frame(all[all$ID_reproductive_episode=="1",] ) wilcox.test(first_repro[first_repro$Status=="Control","Time"],first_repro[first_repro$Status= ="Tumor","Time"]) fr_ctrl<-subset(first_repro,Status=="Control") fr_tum<-subset(first_repro,Status=="Tumor") nctrl<-(fr_ctrl$Time/max(fr_ctrl$Time)) ntum<-(fr_tum$Time/max(fr_tum$Time)) ## alternative hypothesis: true location shift is not equal to 0

Code S2 : Code of the analysis of the traits related to sexual reproduction related to STAR Methods. Tumors alter life-history traits in the freshwater cnidarian, Hydra oligactis Justine BOUTRY and Sophie TISSOT R Packages loading library(readr) library(lme4) library(MASS) library(DHARMa) library(MuMIn) library(glmmTMB) library(ggplot2) library(sjPlot) library(ade4) library(RVAideMemoire) library(survival) library(car) library(survminer) library(factoextra) library(ggridges)

Data formatting

The dataframe sexual_reproduction (corresponding to sexual_reproduction.csv) contains the detail of sexual reproduction's following for each individual. The variables Cold_survival_time, Eggs_number and First_reproduction_age are kept in numerical form. The other variables are formatted in the factor form. The model with the random effect of the date has the lower AICc.

Then, it's the fixed effect which is tested. The effect of the interaction is kept.

The final model take into account the fixed of the interaction between the Status and the Phase, and the random effect of the date.

Finally, model's residuals are checked.

The model selected fit well the data.

Final result