From a75053db999b64c89d500141e19f713a939800fd Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:48:15 +0000 Subject: [PATCH 01/85] Setting up GitHub Classroom Feedback From a7a155cc963642840e378cbec74a8937341d687f Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Wed, 22 Mar 2023 02:09:51 +0300 Subject: [PATCH 02/85] Add gitignore file --- .gitignore | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5df3cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Compiled class file +*.class + +# Log file +*.log + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + From 53c5dce9689516e206cc762d170e73233c561bc1 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Wed, 22 Mar 2023 02:11:52 +0300 Subject: [PATCH 03/85] Add GNU GPL license --- LICENSE | 675 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..53d1f3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + From f6808fcd2f20d6388cb02682759d80dd73085180 Mon Sep 17 00:00:00 2001 From: vacman Date: Fri, 24 Mar 2023 23:34:50 +0300 Subject: [PATCH 04/85] feat(gitignor): ignored gradle files --- .gitignore | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.gitignore b/.gitignore index d5df3cf..56e2609 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,24 @@ *.tar.gz *.rar +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath From f0f938b8f83d01beaf787afe8f74a03e81363155 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Fri, 24 Mar 2023 20:50:48 +0300 Subject: [PATCH 05/85] feat(gitignor): ignored MacOS files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 56e2609..d335887 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,7 @@ gradle-app.setting .project # JDT-specific (Eclipse Java Development Tools) .classpath + +# MacOS +*.DS_Store +.AppleDouble \ No newline at end of file From e3b0c5739e764c8496f01ccbea211efeb167b399 Mon Sep 17 00:00:00 2001 From: vacman Date: Sat, 25 Mar 2023 00:46:50 +0300 Subject: [PATCH 06/85] add: gradle init files --- .gitattributes | 9 + .gitignore | 4 +- .idea/.gitignore | 8 + .idea/compiler.xml | 6 + .idea/gradle.xml | 18 ++ .idea/jarRepositories.xml | 20 ++ .idea/kotlinc.xml | 6 + .idea/misc.xml | 8 + .idea/vcs.xml | 6 + app/build.gradle.kts | 41 ++++ app/src/main/kotlin/trees/App.kt | 15 ++ app/src/test/kotlin/trees/AppTest.kt | 14 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 244 +++++++++++++++++++++++ gradlew.bat | 92 +++++++++ settings.gradle.kts | 11 + 17 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 app/build.gradle.kts create mode 100644 app/src/main/kotlin/trees/App.kt create mode 100644 app/src/test/kotlin/trees/AppTest.kt create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..097f9f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore index d335887..919b932 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,6 @@ gradle-app.setting # MacOS *.DS_Store -.AppleDouble \ No newline at end of file +.AppleDouble +# Ignore Gradle build output directory +build diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b73660a --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..d894eea --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..0fc3113 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..4ce98bc --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..1a7c8ba --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id("org.jetbrains.kotlin.jvm") version "1.8.10" + + // Apply the application plugin to add support for building a CLI application in Java. + application +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use the Kotlin JUnit 5 integration. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + + // Use the JUnit 5 integration. + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") + + // This dependency is used by the application. + implementation("com.google.guava:guava:31.1-jre") +} + +application { + // Define the main class for the application. + mainClass.set("trees.AppKt") +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt new file mode 100644 index 0000000..bf957e4 --- /dev/null +++ b/app/src/main/kotlin/trees/App.kt @@ -0,0 +1,15 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package trees + +class App { + val greeting: String + get() { + return "Hello World!" + } +} + +fun main() { + println(App().greeting) +} diff --git a/app/src/test/kotlin/trees/AppTest.kt b/app/src/test/kotlin/trees/AppTest.kt new file mode 100644 index 0000000..93fb359 --- /dev/null +++ b/app/src/test/kotlin/trees/AppTest.kt @@ -0,0 +1,14 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package trees + +import kotlin.test.Test +import kotlin.test.assertNotNull + +class AppTest { + @Test fun appHasAGreeting() { + val classUnderTest = App() + assertNotNull(classUnderTest.greeting, "app should have a greeting") + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..42defcc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..79a61d4 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..81a0d01 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/8.0/userguide/multi_project_builds.html + */ + +rootProject.name = "trees-9" +include("app") From 055cec82e44018e96219a73d293c3d59b503da48 Mon Sep 17 00:00:00 2001 From: vacman Date: Mon, 27 Mar 2023 14:56:03 +0300 Subject: [PATCH 07/85] feat(gitignore): ignored IDEA directory deleted .idea files --- .gitignore | 2 ++ .idea/.gitignore | 8 -------- .idea/compiler.xml | 6 ------ .idea/gradle.xml | 18 ------------------ .idea/jarRepositories.xml | 20 -------------------- .idea/kotlinc.xml | 6 ------ .idea/misc.xml | 8 -------- .idea/vcs.xml | 6 ------ 8 files changed, 2 insertions(+), 72 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/kotlinc.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 919b932..fe3d8af 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ gradle-app.setting .AppleDouble # Ignore Gradle build output directory build +# Ignore idea workflow files +.idea/* diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b73660a..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index d894eea..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392f..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 0fc3113..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 4ce98bc..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From a0c768c0a14d27138b67cb39719696e9611c2bda Mon Sep 17 00:00:00 2001 From: vacman Date: Mon, 27 Mar 2023 15:27:36 +0300 Subject: [PATCH 08/85] add: init files for trees created abstract node, tree and bst pattern's --- app/src/main/kotlin/trees/App.kt | 12 +----------- app/src/main/kotlin/trees/abstract_node.kt | 18 ++++++++++++++++++ app/src/main/kotlin/trees/abstract_tree.kt | 16 ++++++++++++++++ app/src/main/kotlin/trees/bs_tree.kt | 11 +++++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 app/src/main/kotlin/trees/abstract_node.kt create mode 100644 app/src/main/kotlin/trees/abstract_tree.kt create mode 100644 app/src/main/kotlin/trees/bs_tree.kt diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index bf957e4..12eb4ff 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,15 +1,5 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ package trees -class App { - val greeting: String - get() { - return "Hello World!" - } -} - fun main() { - println(App().greeting) + println("Work started") } diff --git a/app/src/main/kotlin/trees/abstract_node.kt b/app/src/main/kotlin/trees/abstract_node.kt new file mode 100644 index 0000000..36a14b6 --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_node.kt @@ -0,0 +1,18 @@ +package trees + +class Node, Value>( + val key: Key, + var value: Value, + var parent: Node? = null, + var left: Node? = null, + var right: Node? = null + +) : Comparable { + override fun compareTo(other: Key): Int { + return key.compareTo(other) + } + + fun equalKey(other: Key): Boolean { + return this.compareTo(other) == 0 + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/abstract_tree.kt b/app/src/main/kotlin/trees/abstract_tree.kt new file mode 100644 index 0000000..196a7ea --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_tree.kt @@ -0,0 +1,16 @@ +package trees + +abstract class Tree, Value> { + private var rootNode: Node? = null + + abstract fun addNode() + + abstract fun removeNode() + + fun find(value: Int) { + TODO("not implemented yet") +// if (this == null) +// return null +// if this.Key + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/bs_tree.kt b/app/src/main/kotlin/trees/bs_tree.kt new file mode 100644 index 0000000..6f55598 --- /dev/null +++ b/app/src/main/kotlin/trees/bs_tree.kt @@ -0,0 +1,11 @@ +package trees + +class BSTree, Value> : Tree() { + override fun addNode() { + TODO("Not yet implemented") + } + + override fun removeNode() { + TODO("Not yet implemented") + } +} \ No newline at end of file From bbd2864a83c2924e238c7e0515521f39beaa8ad2 Mon Sep 17 00:00:00 2001 From: vacman Date: Mon, 27 Mar 2023 15:32:17 +0300 Subject: [PATCH 09/85] fix: pulled gitignore changes --- .gitignore | 2 ++ .idea/.gitignore | 8 -------- .idea/compiler.xml | 6 ------ .idea/gradle.xml | 18 ------------------ .idea/jarRepositories.xml | 20 -------------------- .idea/kotlinc.xml | 6 ------ .idea/misc.xml | 8 -------- .idea/vcs.xml | 6 ------ 8 files changed, 2 insertions(+), 72 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/kotlinc.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 919b932..71ee182 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ gradle-app.setting .AppleDouble # Ignore Gradle build output directory build +# Ignore idea workflow files +.idea/* \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b73660a..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index d894eea..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392f..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 0fc3113..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 4ce98bc..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From cb35bbcf5e0571e6ea3a98893d5e0e14f18e15bb Mon Sep 17 00:00:00 2001 From: raf-nr Date: Tue, 28 Mar 2023 02:22:16 +0300 Subject: [PATCH 10/85] add: mergeable config --- .github/mergeable.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/mergeable.yml diff --git a/.github/mergeable.yml b/.github/mergeable.yml new file mode 100644 index 0000000..5ef2f5e --- /dev/null +++ b/.github/mergeable.yml @@ -0,0 +1,20 @@ +version: 2 +mergeable: + - when: pull_request.*, pull_request_review.* + name: 'Check decoration' + validate: + - do: title + must_exclude: + regex: ^\[WIP\] + - do: description + no_empty: + enabled: true + message: Description matter and should not be empty. Provide detail with **what** was changed, **why** it was changed, and **how** it was changed. + - do: approvals + min: + count: 1 + limit: + users: + - vacmannnn + - gladiuswq + - raf-nr \ No newline at end of file From 1f3df0bf914faa54b676701ab913fd6da64e774c Mon Sep 17 00:00:00 2001 From: vacman Date: Tue, 28 Mar 2023 22:06:56 +0300 Subject: [PATCH 11/85] feat: find function and init of root node in bst removed parent in node, changed type of rootNode --- app/src/main/kotlin/trees/abstract_node.kt | 3 +-- app/src/main/kotlin/trees/abstract_tree.kt | 19 ++++++++++--------- app/src/main/kotlin/trees/bs_tree.kt | 9 ++++++++- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/main/kotlin/trees/abstract_node.kt b/app/src/main/kotlin/trees/abstract_node.kt index 36a14b6..ba60f13 100644 --- a/app/src/main/kotlin/trees/abstract_node.kt +++ b/app/src/main/kotlin/trees/abstract_node.kt @@ -1,9 +1,8 @@ package trees class Node, Value>( - val key: Key, + var key: Key, var value: Value, - var parent: Node? = null, var left: Node? = null, var right: Node? = null diff --git a/app/src/main/kotlin/trees/abstract_tree.kt b/app/src/main/kotlin/trees/abstract_tree.kt index 196a7ea..e24dbb5 100644 --- a/app/src/main/kotlin/trees/abstract_tree.kt +++ b/app/src/main/kotlin/trees/abstract_tree.kt @@ -1,16 +1,17 @@ package trees -abstract class Tree, Value> { - private var rootNode: Node? = null - +abstract class Tree, Value> ( + protected var rootNode: Node? = null +) { abstract fun addNode() abstract fun removeNode() - - fun find(value: Int) { - TODO("not implemented yet") -// if (this == null) -// return null -// if this.Key + fun find(value: Key): Node? { + return rootNode?.let { trueFind(value, it) } + } + private fun trueFind(value: Key, node: Node): Node? = when { + (node > value) -> node.left?.let { trueFind(value, it) } + (node < value) -> node.right?.let { trueFind(value, it) } + else -> node } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/bs_tree.kt b/app/src/main/kotlin/trees/bs_tree.kt index 6f55598..79a66c6 100644 --- a/app/src/main/kotlin/trees/bs_tree.kt +++ b/app/src/main/kotlin/trees/bs_tree.kt @@ -1,6 +1,13 @@ package trees -class BSTree, Value> : Tree() { +class BSTree, Value>(key: Key, value: Value) : Tree() { + init { + insert(key, value) + } + private fun insert(key: Key, value: Value) { + if (rootNode == null) + rootNode = Node(key, value) + } override fun addNode() { TODO("Not yet implemented") } From 130b3eaa58f756d51e97590f100d4f32504dcc0f Mon Sep 17 00:00:00 2001 From: raf-nr Date: Tue, 28 Mar 2023 23:11:38 +0300 Subject: [PATCH 12/85] add: KVPairs class for a key-value pair --- app/src/main/kotlin/trees/KVPairs.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 app/src/main/kotlin/trees/KVPairs.kt diff --git a/app/src/main/kotlin/trees/KVPairs.kt b/app/src/main/kotlin/trees/KVPairs.kt new file mode 100644 index 0000000..55037a5 --- /dev/null +++ b/app/src/main/kotlin/trees/KVPairs.kt @@ -0,0 +1,5 @@ +class KVPairs, V>(val key: K, val value: V?) : Comparable> { + override fun compareTo(anotherPair: KVPairs): Int { + return key.compareTo(anotherPair.key) + } +} \ No newline at end of file From 8eefcfe05532d6ccabce47e24deeb254ec091649 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Tue, 28 Mar 2023 23:17:30 +0300 Subject: [PATCH 13/85] add: init files for trees --- app/src/main/kotlin/trees/BinaryTree.kt | 9 +++++++++ app/src/main/kotlin/trees/Node.kt | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 app/src/main/kotlin/trees/BinaryTree.kt create mode 100644 app/src/main/kotlin/trees/Node.kt diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt new file mode 100644 index 0000000..17bbe29 --- /dev/null +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -0,0 +1,9 @@ +abstract class BinaryTree, Node_type: Node> { + private var root: Node_type? = null + + fun find(node: Node): Boolean { + TODO("Not yet implemented") + } + abstract fun add(node: Node) + abstract fun remove(node: Node) +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/Node.kt b/app/src/main/kotlin/trees/Node.kt new file mode 100644 index 0000000..8892e5f --- /dev/null +++ b/app/src/main/kotlin/trees/Node.kt @@ -0,0 +1,4 @@ +abstract class Node, V>(var keyValue: KVPairs) : Comparable> { + var left: Node? = null + var right: Node? = null +} \ No newline at end of file From 765069efbf394e3dfc141e6206688f775c1159a1 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Thu, 30 Mar 2023 02:37:43 +0300 Subject: [PATCH 14/85] add: new init files with implementation changed abstract tree to binary tree, implemented find function and add in bst --- app/src/main/kotlin/trees/BinarySearchTree.kt | 23 +++++++++++++++++++ app/src/main/kotlin/trees/BinaryTree.kt | 22 +++++++++++++----- app/src/main/kotlin/trees/Node.kt | 11 +++++---- app/src/main/kotlin/trees/abstract_node.kt | 17 -------------- app/src/main/kotlin/trees/abstract_tree.kt | 17 -------------- app/src/main/kotlin/trees/bs_tree.kt | 18 --------------- 6 files changed, 46 insertions(+), 62 deletions(-) create mode 100644 app/src/main/kotlin/trees/BinarySearchTree.kt delete mode 100644 app/src/main/kotlin/trees/abstract_node.kt delete mode 100644 app/src/main/kotlin/trees/abstract_tree.kt delete mode 100644 app/src/main/kotlin/trees/bs_tree.kt diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt new file mode 100644 index 0000000..31c0ade --- /dev/null +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -0,0 +1,23 @@ +package trees + +import KVPairs +import Node + +class BinarySearchTree, V, KV: KVPairs, NODE_TYPE: Node>: BinaryTree() { + override fun add(start_node: NODE_TYPE?, node: NODE_TYPE): NODE_TYPE { + if (root === start_node && start_node == null) { + root = node + return node + } + if (start_node == null) { + return node + } + + if (start_node.keyValue < node.keyValue) { + start_node.right = add(start_node.right, node) + } else { + start_node.left = add(start_node.left, node) + } + return start_node + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 17bbe29..65d2a2b 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -1,9 +1,19 @@ -abstract class BinaryTree, Node_type: Node> { - private var root: Node_type? = null +package trees - fun find(node: Node): Boolean { - TODO("Not yet implemented") +import KVPairs +import Node + +abstract class BinaryTree, V, T: KVPairs, NODE_TYPE: Node> { + var root: NODE_TYPE? = null + + private fun recursive_find(node: NODE_TYPE, searchedNode: NODE_TYPE): NODE_TYPE? = when { + node.keyValue > searchedNode.keyValue -> node.left?.let { recursive_find(it, searchedNode) } + node.keyValue < searchedNode.keyValue -> node.right?.let { recursive_find(it, searchedNode) } + else -> node + } + fun find(searchedNode: NODE_TYPE): Boolean { + val a = root?.let { recursive_find(it, searchedNode) } + return a != null } - abstract fun add(node: Node) - abstract fun remove(node: Node) + abstract fun add(start_node: NODE_TYPE?, new_node: NODE_TYPE): NODE_TYPE } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/Node.kt b/app/src/main/kotlin/trees/Node.kt index 8892e5f..bbcd1e1 100644 --- a/app/src/main/kotlin/trees/Node.kt +++ b/app/src/main/kotlin/trees/Node.kt @@ -1,4 +1,7 @@ -abstract class Node, V>(var keyValue: KVPairs) : Comparable> { - var left: Node? = null - var right: Node? = null -} \ No newline at end of file +abstract class Node, NODE_TYPE: Node> { + abstract var keyValue: T + var left: NODE_TYPE? = null + var right: NODE_TYPE? = null +} + +class BSTNode>(override var keyValue: T): Node>() \ No newline at end of file diff --git a/app/src/main/kotlin/trees/abstract_node.kt b/app/src/main/kotlin/trees/abstract_node.kt deleted file mode 100644 index ba60f13..0000000 --- a/app/src/main/kotlin/trees/abstract_node.kt +++ /dev/null @@ -1,17 +0,0 @@ -package trees - -class Node, Value>( - var key: Key, - var value: Value, - var left: Node? = null, - var right: Node? = null - -) : Comparable { - override fun compareTo(other: Key): Int { - return key.compareTo(other) - } - - fun equalKey(other: Key): Boolean { - return this.compareTo(other) == 0 - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/abstract_tree.kt b/app/src/main/kotlin/trees/abstract_tree.kt deleted file mode 100644 index e24dbb5..0000000 --- a/app/src/main/kotlin/trees/abstract_tree.kt +++ /dev/null @@ -1,17 +0,0 @@ -package trees - -abstract class Tree, Value> ( - protected var rootNode: Node? = null -) { - abstract fun addNode() - - abstract fun removeNode() - fun find(value: Key): Node? { - return rootNode?.let { trueFind(value, it) } - } - private fun trueFind(value: Key, node: Node): Node? = when { - (node > value) -> node.left?.let { trueFind(value, it) } - (node < value) -> node.right?.let { trueFind(value, it) } - else -> node - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/bs_tree.kt b/app/src/main/kotlin/trees/bs_tree.kt deleted file mode 100644 index 79a66c6..0000000 --- a/app/src/main/kotlin/trees/bs_tree.kt +++ /dev/null @@ -1,18 +0,0 @@ -package trees - -class BSTree, Value>(key: Key, value: Value) : Tree() { - init { - insert(key, value) - } - private fun insert(key: Key, value: Value) { - if (rootNode == null) - rootNode = Node(key, value) - } - override fun addNode() { - TODO("Not yet implemented") - } - - override fun removeNode() { - TODO("Not yet implemented") - } -} \ No newline at end of file From c2d86e61d99ba61e169f5f9f2f3fd366c98cf2c0 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Thu, 30 Mar 2023 19:30:38 +0300 Subject: [PATCH 15/85] feat: implement remove in BST, changed add func --- app/src/main/kotlin/trees/App.kt | 23 ++++- app/src/main/kotlin/trees/BinarySearchTree.kt | 98 +++++++++++++++++-- app/src/main/kotlin/trees/BinaryTree.kt | 9 +- app/src/main/kotlin/trees/Node.kt | 4 +- 4 files changed, 119 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 12eb4ff..f35d6e9 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,5 +1,26 @@ package trees +import BSTNode +import KVPairs + fun main() { - println("Work started") +// var tree = BinaryTree, Node>() + val newpair = KVPairs(56, "удачи") + val secondpair = KVPairs(86, "уди") + val fourhtpair = KVPairs(34, "уди") + val thirspair = KVPairs(89, "ачи") + val newNode = BSTNode(newpair) + val secondNode = BSTNode(secondpair) + var fourthNode = BSTNode(fourhtpair) + val thirdNode = BSTNode(thirspair) + val newTree: BinarySearchTree, BSTNode>> = BinarySearchTree() +// newNode.right = secondNode +// secondNode.right = thirdNode +// newTree.root = newNode + newTree.add(newNode) + newTree.add(secondNode) + newTree.add(thirdNode) + println(newTree.find(thirdNode)) + TODO("СДЕЛАТЬ ОБЪВЛЕНИЕ ДЕРЕВА КРАСИВЕЕ И ПРОЩЕ, ТАК ЖЕ ПОДУМАТЬ НАД КОНСТРУКТОРОМ В НОДЕ") } + diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 31c0ade..9c7fe69 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -3,21 +3,101 @@ package trees import KVPairs import Node -class BinarySearchTree, V, KV: KVPairs, NODE_TYPE: Node>: BinaryTree() { - override fun add(start_node: NODE_TYPE?, node: NODE_TYPE): NODE_TYPE { - if (root === start_node && start_node == null) { +class BinarySearchTree, V, KV : KVPairs, NODE_TYPE : Node> : + BinaryTree() { + override fun add(node: NODE_TYPE) { + recursive_add(root, node) + } + + private fun recursive_add(current: NODE_TYPE?, node: NODE_TYPE): NODE_TYPE { + if (root == null) { root = node return node } - if (start_node == null) { + if (current == null) return node - } - if (start_node.keyValue < node.keyValue) { - start_node.right = add(start_node.right, node) + if (current.keyValue < node.keyValue) { + current.right = recursive_add(current.right, node) } else { - start_node.left = add(start_node.left, node) + current.left = recursive_add(current.left, node) } - return start_node + return current + } + + override fun delete(node: NODE_TYPE) { + root?.let { recursive_delete(it, node) } + } + + private fun recursive_delete(current: NODE_TYPE, node: NODE_TYPE) { + when { + node.keyValue > current.keyValue -> scan(node, current.right, current) + node.keyValue < current.keyValue -> scan(node, current.left, current) + else -> removeNode(current, null) + } + } + + private fun scan(node: NODE_TYPE, current: NODE_TYPE?, parent: NODE_TYPE) { + if (current == null) { + println("value ${node.keyValue} not exist in the tree") + return + } + when { + node.keyValue > current.keyValue -> scan(node, current.right, current) + node.keyValue < current.keyValue -> scan(node, current.left, current) + else -> removeNode(current, parent) + } + } + + private fun removeNode(node: NODE_TYPE, parent: NODE_TYPE?) { + node.left?.let { leftChild -> + run { + node.right?.let { + removeTwoChildNode(node) + } ?: removeSingleChildNode(node, leftChild) + } + } ?: run { + node.right?.let { rightChild -> removeSingleChildNode(node, rightChild) } ?: removeNoChildNode(node, parent) + } + } + + private fun removeNoChildNode(node: NODE_TYPE, parent: NODE_TYPE?) { + parent?.let { p -> + if (node == p.left) { + p.left = null + } else if (node == p.right) { + p.right = null + } + } ?: throw IllegalStateException( + "Can not remove the root node without child nodes" + ) + } + + private fun removeTwoChildNode(node: NODE_TYPE) { + val leftChild = node.left + if (leftChild != null) { + leftChild.right?.let { + val maxParent = findParentOfMaxChild(leftChild) + maxParent.right?.let { + node.keyValue = it.keyValue + maxParent.right = null + } ?: throw IllegalStateException("Node with max child must have the right child!") + + } ?: run { + node.keyValue = leftChild.keyValue + node.left = leftChild.left + } + } + } + + private fun findParentOfMaxChild(n: NODE_TYPE): NODE_TYPE { + return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } + ?: throw IllegalArgumentException("Right child must be non-null") + } + + private fun removeSingleChildNode(parent: NODE_TYPE, child: NODE_TYPE) { + parent.keyValue = child.keyValue + parent.left = child.left + parent.right = child.right } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 65d2a2b..499f526 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -3,17 +3,20 @@ package trees import KVPairs import Node -abstract class BinaryTree, V, T: KVPairs, NODE_TYPE: Node> { - var root: NODE_TYPE? = null +abstract class BinaryTree, V, T : KVPairs, NODE_TYPE : Node> { + protected var root: NODE_TYPE? = null private fun recursive_find(node: NODE_TYPE, searchedNode: NODE_TYPE): NODE_TYPE? = when { node.keyValue > searchedNode.keyValue -> node.left?.let { recursive_find(it, searchedNode) } node.keyValue < searchedNode.keyValue -> node.right?.let { recursive_find(it, searchedNode) } else -> node } + fun find(searchedNode: NODE_TYPE): Boolean { val a = root?.let { recursive_find(it, searchedNode) } return a != null } - abstract fun add(start_node: NODE_TYPE?, new_node: NODE_TYPE): NODE_TYPE + + abstract fun add(node: NODE_TYPE) + abstract fun delete(node: NODE_TYPE) } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/Node.kt b/app/src/main/kotlin/trees/Node.kt index bbcd1e1..2bca629 100644 --- a/app/src/main/kotlin/trees/Node.kt +++ b/app/src/main/kotlin/trees/Node.kt @@ -1,7 +1,7 @@ -abstract class Node, NODE_TYPE: Node> { +abstract class Node, NODE_TYPE : Node> { abstract var keyValue: T var left: NODE_TYPE? = null var right: NODE_TYPE? = null } -class BSTNode>(override var keyValue: T): Node>() \ No newline at end of file +class BSTNode>(override var keyValue: T) : Node>() \ No newline at end of file From 28c1fab9c48fac28d07d5a2d63ce45bba843d012 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Sun, 2 Apr 2023 15:44:31 +0300 Subject: [PATCH 16/85] add: created files for remaining trees --- app/src/main/kotlin/trees/AVLTree.kt | 18 ++++++++++++++++++ .../main/kotlin/trees/AbstractBalanceTree.kt | 10 ++++++++++ app/src/main/kotlin/trees/RBTree.kt | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 app/src/main/kotlin/trees/AVLTree.kt create mode 100644 app/src/main/kotlin/trees/AbstractBalanceTree.kt create mode 100644 app/src/main/kotlin/trees/RBTree.kt diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt new file mode 100644 index 0000000..0965052 --- /dev/null +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -0,0 +1,18 @@ +package trees + +import KVPairs + +class AVLTree, V, KV : KVPairs, NODE_TYPE : Node> : + AbstractBalanceTree() { + override fun balance() { + TODO("Not yet implemented") + } + + override fun add(node: NODE_TYPE) { + TODO("Not yet implemented") + } + + override fun delete(node: NODE_TYPE) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt new file mode 100644 index 0000000..4005369 --- /dev/null +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -0,0 +1,10 @@ +package trees + +import KVPairs + + +abstract class AbstractBalanceTree, V, KV : KVPairs, NODE_TYPE : Node> : + BinaryTree() { + abstract fun balance(); + + } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt new file mode 100644 index 0000000..be73bb4 --- /dev/null +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -0,0 +1,18 @@ +package trees + +import KVPairs + +class RBTree, V, KV : KVPairs, NODE_TYPE : Node> : + AbstractBalanceTree() { + override fun balance() { + TODO("Not yet implemented") + } + + override fun add(node: NODE_TYPE) { + TODO("Not yet implemented") + } + + override fun delete(node: NODE_TYPE) { + TODO("Not yet implemented") + } +} \ No newline at end of file From d16815f4e3952d090f112290c8da052b8baa0e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=84=D0=B8=D0=BA=20=D0=9D=D1=83=D1=80=D0=BC?= =?UTF-8?q?=D1=83=D1=85=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= <113212617+raf-nr@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:21:53 +0300 Subject: [PATCH 17/85] feat: delete 'node' file deleting node.kt to split into separate files --- app/src/main/kotlin/trees/Node.kt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 app/src/main/kotlin/trees/Node.kt diff --git a/app/src/main/kotlin/trees/Node.kt b/app/src/main/kotlin/trees/Node.kt deleted file mode 100644 index 2bca629..0000000 --- a/app/src/main/kotlin/trees/Node.kt +++ /dev/null @@ -1,7 +0,0 @@ -abstract class Node, NODE_TYPE : Node> { - abstract var keyValue: T - var left: NODE_TYPE? = null - var right: NODE_TYPE? = null -} - -class BSTNode>(override var keyValue: T) : Node>() \ No newline at end of file From dc2329a714d61435ce8c3a466d186e61a7b09bc6 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 16:36:11 +0300 Subject: [PATCH 18/85] feat: splitting a remote 'Node' file into separate files --- app/src/main/kotlin/trees/AbstractNode.kt | 5 +++++ app/src/main/kotlin/trees/BSTNode.kt | 1 + 2 files changed, 6 insertions(+) create mode 100644 app/src/main/kotlin/trees/AbstractNode.kt create mode 100644 app/src/main/kotlin/trees/BSTNode.kt diff --git a/app/src/main/kotlin/trees/AbstractNode.kt b/app/src/main/kotlin/trees/AbstractNode.kt new file mode 100644 index 0000000..215e1cd --- /dev/null +++ b/app/src/main/kotlin/trees/AbstractNode.kt @@ -0,0 +1,5 @@ +abstract class Node, NODE_TYPE: Node> { + abstract var keyValue: T + var left: NODE_TYPE? = null + var right: NODE_TYPE? = null +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/BSTNode.kt b/app/src/main/kotlin/trees/BSTNode.kt new file mode 100644 index 0000000..f6fb03a --- /dev/null +++ b/app/src/main/kotlin/trees/BSTNode.kt @@ -0,0 +1 @@ +class BSTNode, NODE_TYPE: BSTNode>(override var keyValue: T): Node() \ No newline at end of file From a55d8e4660ab549dac20fe7f7c1e83fe258ab45e Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 16:48:49 +0300 Subject: [PATCH 19/85] add: AVL and RB nodes --- app/src/main/kotlin/trees/AVLNode.kt | 3 +++ app/src/main/kotlin/trees/RBNode.kt | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 app/src/main/kotlin/trees/AVLNode.kt create mode 100644 app/src/main/kotlin/trees/RBNode.kt diff --git a/app/src/main/kotlin/trees/AVLNode.kt b/app/src/main/kotlin/trees/AVLNode.kt new file mode 100644 index 0000000..5259f74 --- /dev/null +++ b/app/src/main/kotlin/trees/AVLNode.kt @@ -0,0 +1,3 @@ +class AVLNode, NODE_TYPE: AVLNode>(override var keyValue: T): Node(){ + var height: Int? = null +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/RBNode.kt b/app/src/main/kotlin/trees/RBNode.kt new file mode 100644 index 0000000..13765b4 --- /dev/null +++ b/app/src/main/kotlin/trees/RBNode.kt @@ -0,0 +1,9 @@ +class RBNode, NODE_TYPE: RBNode>(override var keyValue: T): Node(){ + + var color = Color.RED + + enum class Color { + RED, + BLACK + } +} \ No newline at end of file From 1e89f709934b7ee50b6a8af45e7b711a248273b2 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 16:56:58 +0300 Subject: [PATCH 20/85] feat: placing node classes in a separate directory --- app/src/main/kotlin/trees/AVLTree.kt | 1 + app/src/main/kotlin/trees/AbstractBalanceTree.kt | 2 +- app/src/main/kotlin/trees/BinarySearchTree.kt | 2 +- app/src/main/kotlin/trees/BinaryTree.kt | 2 +- app/src/main/kotlin/trees/RBTree.kt | 1 + app/src/main/kotlin/trees/{ => nodes}/AVLNode.kt | 2 ++ app/src/main/kotlin/trees/{ => nodes}/AbstractNode.kt | 2 ++ app/src/main/kotlin/trees/{ => nodes}/BSTNode.kt | 2 ++ app/src/main/kotlin/trees/{ => nodes}/RBNode.kt | 2 ++ 9 files changed, 13 insertions(+), 3 deletions(-) rename app/src/main/kotlin/trees/{ => nodes}/AVLNode.kt (87%) rename app/src/main/kotlin/trees/{ => nodes}/AbstractNode.kt (88%) rename app/src/main/kotlin/trees/{ => nodes}/BSTNode.kt (84%) rename app/src/main/kotlin/trees/{ => nodes}/RBNode.kt (90%) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 0965052..7e6d7d3 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -1,6 +1,7 @@ package trees import KVPairs +import trees.nodes.AVLNode class AVLTree, V, KV : KVPairs, NODE_TYPE : Node> : AbstractBalanceTree() { diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt index 4005369..eec539b 100644 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -1,7 +1,7 @@ package trees import KVPairs - +import trees.nodes.AbstractNode abstract class AbstractBalanceTree, V, KV : KVPairs, NODE_TYPE : Node> : BinaryTree() { diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 9c7fe69..540a11a 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -1,7 +1,7 @@ package trees import KVPairs -import Node +import trees.nodes.BSTNode class BinarySearchTree, V, KV : KVPairs, NODE_TYPE : Node> : BinaryTree() { diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 499f526..72e68b5 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -1,7 +1,7 @@ package trees import KVPairs -import Node +import trees.nodes.AbstractNode abstract class BinaryTree, V, T : KVPairs, NODE_TYPE : Node> { protected var root: NODE_TYPE? = null diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index be73bb4..097dff5 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,6 +1,7 @@ package trees import KVPairs +import trees.nodes.RBNode class RBTree, V, KV : KVPairs, NODE_TYPE : Node> : AbstractBalanceTree() { diff --git a/app/src/main/kotlin/trees/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt similarity index 87% rename from app/src/main/kotlin/trees/AVLNode.kt rename to app/src/main/kotlin/trees/nodes/AVLNode.kt index 5259f74..d95f487 100644 --- a/app/src/main/kotlin/trees/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,3 +1,5 @@ +package trees.nodes + class AVLNode, NODE_TYPE: AVLNode>(override var keyValue: T): Node(){ var height: Int? = null } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/AbstractNode.kt b/app/src/main/kotlin/trees/nodes/AbstractNode.kt similarity index 88% rename from app/src/main/kotlin/trees/AbstractNode.kt rename to app/src/main/kotlin/trees/nodes/AbstractNode.kt index 215e1cd..035ff2e 100644 --- a/app/src/main/kotlin/trees/AbstractNode.kt +++ b/app/src/main/kotlin/trees/nodes/AbstractNode.kt @@ -1,3 +1,5 @@ + +package trees.nodes abstract class Node, NODE_TYPE: Node> { abstract var keyValue: T var left: NODE_TYPE? = null diff --git a/app/src/main/kotlin/trees/BSTNode.kt b/app/src/main/kotlin/trees/nodes/BSTNode.kt similarity index 84% rename from app/src/main/kotlin/trees/BSTNode.kt rename to app/src/main/kotlin/trees/nodes/BSTNode.kt index f6fb03a..0002ddf 100644 --- a/app/src/main/kotlin/trees/BSTNode.kt +++ b/app/src/main/kotlin/trees/nodes/BSTNode.kt @@ -1 +1,3 @@ +package trees.nodes + class BSTNode, NODE_TYPE: BSTNode>(override var keyValue: T): Node() \ No newline at end of file diff --git a/app/src/main/kotlin/trees/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt similarity index 90% rename from app/src/main/kotlin/trees/RBNode.kt rename to app/src/main/kotlin/trees/nodes/RBNode.kt index 13765b4..76a4e3f 100644 --- a/app/src/main/kotlin/trees/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,3 +1,5 @@ +package trees.nodes + class RBNode, NODE_TYPE: RBNode>(override var keyValue: T): Node(){ var color = Color.RED From 0f703c504a031f52e61ec54441247ee66d4bbf26 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 17:07:10 +0300 Subject: [PATCH 21/85] feat: transferring the KVPairs class to a file with the AbstractNode class --- app/src/main/kotlin/trees/AVLTree.kt | 1 - app/src/main/kotlin/trees/AbstractBalanceTree.kt | 1 - app/src/main/kotlin/trees/BinaryTree.kt | 1 - app/src/main/kotlin/trees/KVPairs.kt | 5 ----- app/src/main/kotlin/trees/RBTree.kt | 1 - app/src/main/kotlin/trees/nodes/AbstractNode.kt | 6 ++++++ 6 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 app/src/main/kotlin/trees/KVPairs.kt diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 7e6d7d3..da69e26 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -1,6 +1,5 @@ package trees -import KVPairs import trees.nodes.AVLNode class AVLTree, V, KV : KVPairs, NODE_TYPE : Node> : diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt index eec539b..2839019 100644 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -1,6 +1,5 @@ package trees -import KVPairs import trees.nodes.AbstractNode abstract class AbstractBalanceTree, V, KV : KVPairs, NODE_TYPE : Node> : diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 72e68b5..4b1699d 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -1,6 +1,5 @@ package trees -import KVPairs import trees.nodes.AbstractNode abstract class BinaryTree, V, T : KVPairs, NODE_TYPE : Node> { diff --git a/app/src/main/kotlin/trees/KVPairs.kt b/app/src/main/kotlin/trees/KVPairs.kt deleted file mode 100644 index 55037a5..0000000 --- a/app/src/main/kotlin/trees/KVPairs.kt +++ /dev/null @@ -1,5 +0,0 @@ -class KVPairs, V>(val key: K, val value: V?) : Comparable> { - override fun compareTo(anotherPair: KVPairs): Int { - return key.compareTo(anotherPair.key) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 097dff5..c5924a4 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,6 +1,5 @@ package trees -import KVPairs import trees.nodes.RBNode class RBTree, V, KV : KVPairs, NODE_TYPE : Node> : diff --git a/app/src/main/kotlin/trees/nodes/AbstractNode.kt b/app/src/main/kotlin/trees/nodes/AbstractNode.kt index 035ff2e..b674dfd 100644 --- a/app/src/main/kotlin/trees/nodes/AbstractNode.kt +++ b/app/src/main/kotlin/trees/nodes/AbstractNode.kt @@ -4,4 +4,10 @@ abstract class Node, NODE_TYPE: Node> { abstract var keyValue: T var left: NODE_TYPE? = null var right: NODE_TYPE? = null +} + +class KVPairs, V>(val key: K, val value: V?) : Comparable> { + override fun compareTo(anotherPair: KVPairs): Int { + return key.compareTo(anotherPair.key) + } } \ No newline at end of file From 1cbe12c5a917c67d24c806a682375ea30af657af Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 17:16:41 +0300 Subject: [PATCH 22/85] fix: removed unnecessary generics, added new ones --- app/src/main/kotlin/trees/AVLTree.kt | 3 +-- app/src/main/kotlin/trees/AbstractBalanceTree.kt | 6 ++---- app/src/main/kotlin/trees/BinarySearchTree.kt | 8 +++----- app/src/main/kotlin/trees/BinaryTree.kt | 2 +- app/src/main/kotlin/trees/RBTree.kt | 3 +-- app/src/main/kotlin/trees/nodes/AbstractNode.kt | 1 - 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index da69e26..c09f4ad 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -2,8 +2,7 @@ package trees import trees.nodes.AVLNode -class AVLTree, V, KV : KVPairs, NODE_TYPE : Node> : - AbstractBalanceTree() { +class AVLTree, NODE_TYPE : Node> : AbstractBalanceTree() { override fun balance() { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt index 2839019..1c7b2e9 100644 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -2,8 +2,6 @@ package trees import trees.nodes.AbstractNode -abstract class AbstractBalanceTree, V, KV : KVPairs, NODE_TYPE : Node> : - BinaryTree() { +abstract class AbstractBalanceTree, NODE_TYPE : Node> : BinaryTree() { abstract fun balance(); - - } \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 540a11a..4baf00a 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -1,10 +1,8 @@ package trees -import KVPairs import trees.nodes.BSTNode -class BinarySearchTree, V, KV : KVPairs, NODE_TYPE : Node> : - BinaryTree() { +class BinarySearchTree, NODE_TYPE : Node> : BinaryTree() { override fun add(node: NODE_TYPE) { recursive_add(root, node) } @@ -14,9 +12,9 @@ class BinarySearchTree, V, KV : KVPairs, NODE_TYPE : Nod root = node return node } - if (current == null) + if (current == null) { return node - + } if (current.keyValue < node.keyValue) { current.right = recursive_add(current.right, node) } else { diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 4b1699d..b925e71 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -2,7 +2,7 @@ package trees import trees.nodes.AbstractNode -abstract class BinaryTree, V, T : KVPairs, NODE_TYPE : Node> { +abstract class BinaryTree, NODE_TYPE : Node> { protected var root: NODE_TYPE? = null private fun recursive_find(node: NODE_TYPE, searchedNode: NODE_TYPE): NODE_TYPE? = when { diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index c5924a4..7fab49e 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -2,8 +2,7 @@ package trees import trees.nodes.RBNode -class RBTree, V, KV : KVPairs, NODE_TYPE : Node> : - AbstractBalanceTree() { +class RBTree, NODE_TYPE : Node> : AbstractBalanceTree, NODE_TYPE>() { override fun balance() { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/nodes/AbstractNode.kt b/app/src/main/kotlin/trees/nodes/AbstractNode.kt index b674dfd..25d9ff6 100644 --- a/app/src/main/kotlin/trees/nodes/AbstractNode.kt +++ b/app/src/main/kotlin/trees/nodes/AbstractNode.kt @@ -1,4 +1,3 @@ - package trees.nodes abstract class Node, NODE_TYPE: Node> { abstract var keyValue: T From 4932a8f6812f39ffd2d233db63ac4f2c8575ecab Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Sun, 2 Apr 2023 17:32:51 +0300 Subject: [PATCH 23/85] fix: changed import directives, return of find function small codestyle changes --- app/src/main/kotlin/trees/AVLTree.kt | 4 ++-- app/src/main/kotlin/trees/AbstractBalanceTree.kt | 7 ++++--- app/src/main/kotlin/trees/BinarySearchTree.kt | 4 ++-- app/src/main/kotlin/trees/BinaryTree.kt | 6 +++--- app/src/main/kotlin/trees/RBTree.kt | 4 ++-- app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- app/src/main/kotlin/trees/nodes/BSTNode.kt | 2 +- app/src/main/kotlin/trees/nodes/RBNode.kt | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index c09f4ad..403b286 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.AVLNode +import trees.nodes.Node -class AVLTree, NODE_TYPE : Node> : AbstractBalanceTree() { +class AVLTree, NODE_TYPE : Node> : AbstractBalanceTree() { override fun balance() { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt index 1c7b2e9..306141b 100644 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -1,7 +1,8 @@ package trees -import trees.nodes.AbstractNode +import trees.nodes.Node -abstract class AbstractBalanceTree, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance(); + +abstract class AbstractBalanceTree, NODE_TYPE : Node> : BinaryTree() { + abstract fun balance() } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 4baf00a..34d42b2 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.BSTNode +import trees.nodes.Node -class BinarySearchTree, NODE_TYPE : Node> : BinaryTree() { +class BinarySearchTree, NODE_TYPE : Node> : BinaryTree() { override fun add(node: NODE_TYPE) { recursive_add(root, node) } diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index b925e71..050e45c 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.AbstractNode +import trees.nodes.Node -abstract class BinaryTree, NODE_TYPE : Node> { +abstract class BinaryTree, NODE_TYPE : Node> { protected var root: NODE_TYPE? = null private fun recursive_find(node: NODE_TYPE, searchedNode: NODE_TYPE): NODE_TYPE? = when { @@ -13,7 +13,7 @@ abstract class BinaryTree, NODE_TYPE : Node> { fun find(searchedNode: NODE_TYPE): Boolean { val a = root?.let { recursive_find(it, searchedNode) } - return a != null + return a?.keyValue != searchedNode.keyValue } abstract fun add(node: NODE_TYPE) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 7fab49e..91dc95d 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.RBNode +import trees.nodes.Node -class RBTree, NODE_TYPE : Node> : AbstractBalanceTree, NODE_TYPE>() { +class RBTree, NODE_TYPE : Node> : AbstractBalanceTree() { override fun balance() { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index d95f487..a129d1e 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes -class AVLNode, NODE_TYPE: AVLNode>(override var keyValue: T): Node(){ +class AVLNode, NODE_TYPE : AVLNode>(override var keyValue: T) : Node() { var height: Int? = null } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/BSTNode.kt b/app/src/main/kotlin/trees/nodes/BSTNode.kt index 0002ddf..41e9ab4 100644 --- a/app/src/main/kotlin/trees/nodes/BSTNode.kt +++ b/app/src/main/kotlin/trees/nodes/BSTNode.kt @@ -1,3 +1,3 @@ package trees.nodes -class BSTNode, NODE_TYPE: BSTNode>(override var keyValue: T): Node() \ No newline at end of file +class BSTNode, NODE_TYPE : BSTNode>(override var keyValue: T) : Node() \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index 76a4e3f..a2a3f0e 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,6 +1,6 @@ package trees.nodes -class RBNode, NODE_TYPE: RBNode>(override var keyValue: T): Node(){ +class RBNode, NODE_TYPE : RBNode>(override var keyValue: T) : Node() { var color = Color.RED From cca9c2d410969e0f48e809061db928bb64d3aa38 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 2 Apr 2023 17:57:24 +0300 Subject: [PATCH 24/85] fix: node types in tree declaration, cleared 'app' file --- app/src/main/kotlin/trees/AVLTree.kt | 4 ++-- .../main/kotlin/trees/AbstractBalanceTree.kt | 1 - app/src/main/kotlin/trees/App.kt | 23 +++---------------- app/src/main/kotlin/trees/BinarySearchTree.kt | 4 ++-- app/src/main/kotlin/trees/RBTree.kt | 4 ++-- 5 files changed, 9 insertions(+), 27 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 403b286..97026b0 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.Node +import trees.nodes.AVLNode -class AVLTree, NODE_TYPE : Node> : AbstractBalanceTree() { +class AVLTree, NODE_TYPE : AVLNode> : AbstractBalanceTree() { override fun balance() { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt index 306141b..f8236bf 100644 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ b/app/src/main/kotlin/trees/AbstractBalanceTree.kt @@ -2,7 +2,6 @@ package trees import trees.nodes.Node - abstract class AbstractBalanceTree, NODE_TYPE : Node> : BinaryTree() { abstract fun balance() } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index f35d6e9..1838c36 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,26 +1,9 @@ package trees -import BSTNode -import KVPairs +import trees.nodes.KVPairs +import trees.nodes.BSTNode fun main() { -// var tree = BinaryTree, Node>() - val newpair = KVPairs(56, "удачи") - val secondpair = KVPairs(86, "уди") - val fourhtpair = KVPairs(34, "уди") - val thirspair = KVPairs(89, "ачи") - val newNode = BSTNode(newpair) - val secondNode = BSTNode(secondpair) - var fourthNode = BSTNode(fourhtpair) - val thirdNode = BSTNode(thirspair) - val newTree: BinarySearchTree, BSTNode>> = BinarySearchTree() -// newNode.right = secondNode -// secondNode.right = thirdNode -// newTree.root = newNode - newTree.add(newNode) - newTree.add(secondNode) - newTree.add(thirdNode) - println(newTree.find(thirdNode)) - TODO("СДЕЛАТЬ ОБЪВЛЕНИЕ ДЕРЕВА КРАСИВЕЕ И ПРОЩЕ, ТАК ЖЕ ПОДУМАТЬ НАД КОНСТРУКТОРОМ В НОДЕ") + println("Hello world") } diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 34d42b2..9930b36 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.Node +import trees.nodes.BSTNode -class BinarySearchTree, NODE_TYPE : Node> : BinaryTree() { +class BinarySearchTree, NODE_TYPE : BSTNode> : BinaryTree() { override fun add(node: NODE_TYPE) { recursive_add(root, node) } diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 91dc95d..140cd04 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,8 +1,8 @@ package trees -import trees.nodes.Node +import trees.nodes.RBNode -class RBTree, NODE_TYPE : Node> : AbstractBalanceTree() { +class RBTree, NODE_TYPE : RBNode> : AbstractBalanceTree() { override fun balance() { TODO("Not yet implemented") } From 3bdba1a8aff16c739482ba7e30bd802bd9c49158 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 7 Apr 2023 21:09:52 +0300 Subject: [PATCH 25/85] add: CI file that tests project build while PR --- .github/workflows/github-CI.yml | 22 ++++++++++++++++++++++ app/src/main/kotlin/trees/App.kt | 3 --- app/src/test/kotlin/trees/AppTest.kt | 12 ------------ 3 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/github-CI.yml diff --git a/.github/workflows/github-CI.yml b/.github/workflows/github-CI.yml new file mode 100644 index 0000000..9bb1d83 --- /dev/null +++ b/.github/workflows/github-CI.yml @@ -0,0 +1,22 @@ +name: Run Kotlin code build + +on: + pull_request: + branches: + - main + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + - name: Run build with Gradle Wrapper + run: ./gradlew build + - uses: actions/upload-artifact@v3 + with: + name: Package + path: build/libs \ No newline at end of file diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 1838c36..bffb7e4 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,8 +1,5 @@ package trees -import trees.nodes.KVPairs -import trees.nodes.BSTNode - fun main() { println("Hello world") } diff --git a/app/src/test/kotlin/trees/AppTest.kt b/app/src/test/kotlin/trees/AppTest.kt index 93fb359..90b56e3 100644 --- a/app/src/test/kotlin/trees/AppTest.kt +++ b/app/src/test/kotlin/trees/AppTest.kt @@ -1,14 +1,2 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ package trees -import kotlin.test.Test -import kotlin.test.assertNotNull - -class AppTest { - @Test fun appHasAGreeting() { - val classUnderTest = App() - assertNotNull(classUnderTest.greeting, "app should have a greeting") - } -} From 5cb24ee1977fd7752e60afe1dabb9472921429af Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Sat, 8 Apr 2023 00:26:13 +0300 Subject: [PATCH 26/85] add: README.md --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ad4b97 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Trees by team 9 +### Implemented: +- Binary search tree +- Abstract class for red-black and AVL tree + +### Future: +- AVL and RB tree +- Auto-tests +- Data bases for trees +- UI support + +#### Created by: gladiuswq, raf-nr, vacmannnn + + + + + From b2ee631bc837037134e216285aa7dc4a99b03134 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 9 Apr 2023 00:38:05 +0300 Subject: [PATCH 27/85] fix: name of parameters, generics in nodes --- app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- app/src/main/kotlin/trees/nodes/AbstractNode.kt | 4 ++-- app/src/main/kotlin/trees/nodes/BSTNode.kt | 2 +- app/src/main/kotlin/trees/nodes/RBNode.kt | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index a129d1e..e2cfc80 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes -class AVLNode, NODE_TYPE : AVLNode>(override var keyValue: T) : Node() { +class AVLNode>(override var keyValue: T) : Node>() { var height: Int? = null } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/AbstractNode.kt b/app/src/main/kotlin/trees/nodes/AbstractNode.kt index 25d9ff6..85bedb4 100644 --- a/app/src/main/kotlin/trees/nodes/AbstractNode.kt +++ b/app/src/main/kotlin/trees/nodes/AbstractNode.kt @@ -6,7 +6,7 @@ abstract class Node, NODE_TYPE: Node> { } class KVPairs, V>(val key: K, val value: V?) : Comparable> { - override fun compareTo(anotherPair: KVPairs): Int { - return key.compareTo(anotherPair.key) + override fun compareTo(other: KVPairs): Int { + return key.compareTo(other.key) } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/BSTNode.kt b/app/src/main/kotlin/trees/nodes/BSTNode.kt index 41e9ab4..ee6ed8e 100644 --- a/app/src/main/kotlin/trees/nodes/BSTNode.kt +++ b/app/src/main/kotlin/trees/nodes/BSTNode.kt @@ -1,3 +1,3 @@ package trees.nodes -class BSTNode, NODE_TYPE : BSTNode>(override var keyValue: T) : Node() \ No newline at end of file +class BSTNode>(override var keyValue: T) : Node>() \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index a2a3f0e..106adb0 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,6 +1,5 @@ package trees.nodes - -class RBNode, NODE_TYPE : RBNode>(override var keyValue: T) : Node() { +class RBNode>(override var keyValue: T) : Node>() { var color = Color.RED From bf28099de919b1c11e660fc6b9b7daf0678fbb75 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 9 Apr 2023 00:50:08 +0300 Subject: [PATCH 28/85] fix: typo with comparison operator --- app/src/main/kotlin/trees/BinaryTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt index 050e45c..b39f299 100644 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/BinaryTree.kt @@ -13,7 +13,7 @@ abstract class BinaryTree, NODE_TYPE : Node> { fun find(searchedNode: NODE_TYPE): Boolean { val a = root?.let { recursive_find(it, searchedNode) } - return a?.keyValue != searchedNode.keyValue + return a?.keyValue == searchedNode.keyValue } abstract fun add(node: NODE_TYPE) From 00b883786359cee572b1a8d8a4c49347145f1ddb Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 9 Apr 2023 00:56:26 +0300 Subject: [PATCH 29/85] fix: class declaration, node types --- app/src/main/kotlin/trees/BinarySearchTree.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BinarySearchTree.kt index 9930b36..44a0730 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BinarySearchTree.kt @@ -2,12 +2,12 @@ package trees import trees.nodes.BSTNode -class BinarySearchTree, NODE_TYPE : BSTNode> : BinaryTree() { - override fun add(node: NODE_TYPE) { +class BinarySearchTree> : BinaryTree>() { + override fun add(node: BSTNode) { recursive_add(root, node) } - private fun recursive_add(current: NODE_TYPE?, node: NODE_TYPE): NODE_TYPE { + private fun recursive_add(current: BSTNode?, node: BSTNode): BSTNode { if (root == null) { root = node return node @@ -23,11 +23,11 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B return current } - override fun delete(node: NODE_TYPE) { + override fun delete(node: BSTNode) { root?.let { recursive_delete(it, node) } } - private fun recursive_delete(current: NODE_TYPE, node: NODE_TYPE) { + private fun recursive_delete(current: BSTNode, node: BSTNode) { when { node.keyValue > current.keyValue -> scan(node, current.right, current) node.keyValue < current.keyValue -> scan(node, current.left, current) @@ -35,7 +35,7 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B } } - private fun scan(node: NODE_TYPE, current: NODE_TYPE?, parent: NODE_TYPE) { + private fun scan(node: BSTNode, current: BSTNode?, parent: BSTNode) { if (current == null) { println("value ${node.keyValue} not exist in the tree") return @@ -47,7 +47,7 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B } } - private fun removeNode(node: NODE_TYPE, parent: NODE_TYPE?) { + private fun removeNode(node: BSTNode, parent: BSTNode?) { node.left?.let { leftChild -> run { node.right?.let { @@ -59,7 +59,7 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B } } - private fun removeNoChildNode(node: NODE_TYPE, parent: NODE_TYPE?) { + private fun removeNoChildNode(node: BSTNode, parent: BSTNode?) { parent?.let { p -> if (node == p.left) { p.left = null @@ -71,7 +71,7 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B ) } - private fun removeTwoChildNode(node: NODE_TYPE) { + private fun removeTwoChildNode(node: BSTNode) { val leftChild = node.left if (leftChild != null) { leftChild.right?.let { @@ -88,12 +88,12 @@ class BinarySearchTree, NODE_TYPE : BSTNode> : B } } - private fun findParentOfMaxChild(n: NODE_TYPE): NODE_TYPE { + private fun findParentOfMaxChild(n: BSTNode): BSTNode { return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } ?: throw IllegalArgumentException("Right child must be non-null") } - private fun removeSingleChildNode(parent: NODE_TYPE, child: NODE_TYPE) { + private fun removeSingleChildNode(parent: BSTNode, child: BSTNode) { parent.keyValue = child.keyValue parent.left = child.left parent.right = child.right From b9811efb3e85c3b740ae4b9e5b6dd1ead0450622 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Sun, 9 Apr 2023 23:20:02 +0300 Subject: [PATCH 30/85] fix: change structure, names and add print method --- app/src/main/kotlin/trees/AVLTree.kt | 9 ++-- .../main/kotlin/trees/AbstractBalanceTree.kt | 7 --- app/src/main/kotlin/trees/App.kt | 11 +++-- .../trees/{BinarySearchTree.kt => BSTree.kt} | 46 +++++++++---------- app/src/main/kotlin/trees/BinaryTree.kt | 21 --------- app/src/main/kotlin/trees/RBTree.kt | 11 +++-- .../trees/abstract_trees/BalanceTree.kt | 7 +++ .../kotlin/trees/abstract_trees/BinaryTree.kt | 39 ++++++++++++++++ app/src/main/kotlin/trees/nodes/AVLNode.kt | 4 +- .../main/kotlin/trees/nodes/AbstractNode.kt | 12 ----- app/src/main/kotlin/trees/nodes/BSNode.kt | 3 ++ app/src/main/kotlin/trees/nodes/BSTNode.kt | 3 -- app/src/main/kotlin/trees/nodes/Node.kt | 5 ++ app/src/main/kotlin/trees/nodes/RBNode.kt | 6 +-- 14 files changed, 100 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/kotlin/trees/AbstractBalanceTree.kt rename app/src/main/kotlin/trees/{BinarySearchTree.kt => BSTree.kt} (59%) delete mode 100644 app/src/main/kotlin/trees/BinaryTree.kt create mode 100644 app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt create mode 100644 app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt delete mode 100644 app/src/main/kotlin/trees/nodes/AbstractNode.kt create mode 100644 app/src/main/kotlin/trees/nodes/BSNode.kt delete mode 100644 app/src/main/kotlin/trees/nodes/BSTNode.kt create mode 100644 app/src/main/kotlin/trees/nodes/Node.kt diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 97026b0..a8235ad 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -1,17 +1,18 @@ package trees +import trees.abstract_trees.BalanceTree import trees.nodes.AVLNode -class AVLTree, NODE_TYPE : AVLNode> : AbstractBalanceTree() { +class AVLTree, V> : BalanceTree>() { override fun balance() { TODO("Not yet implemented") } - override fun add(node: NODE_TYPE) { + override fun add(node: AVLNode) { TODO("Not yet implemented") } - override fun delete(node: NODE_TYPE) { + override fun remove(node: AVLNode) { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/AbstractBalanceTree.kt b/app/src/main/kotlin/trees/AbstractBalanceTree.kt deleted file mode 100644 index f8236bf..0000000 --- a/app/src/main/kotlin/trees/AbstractBalanceTree.kt +++ /dev/null @@ -1,7 +0,0 @@ -package trees - -import trees.nodes.Node - -abstract class AbstractBalanceTree, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance() -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 1838c36..5b57c09 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,9 +1,14 @@ package trees -import trees.nodes.KVPairs -import trees.nodes.BSTNode +import trees.nodes.BSNode fun main() { - println("Hello world") + val t = BSTree() + t.add(BSNode(5, "a")) + t.add(BSNode(7, "b")) + t.add(BSNode(3, "c")) + t.add(BSNode(4, "d")) + t.add(BSNode(2, "e")) + t.print() } diff --git a/app/src/main/kotlin/trees/BinarySearchTree.kt b/app/src/main/kotlin/trees/BSTree.kt similarity index 59% rename from app/src/main/kotlin/trees/BinarySearchTree.kt rename to app/src/main/kotlin/trees/BSTree.kt index 44a0730..b99a81b 100644 --- a/app/src/main/kotlin/trees/BinarySearchTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -1,13 +1,13 @@ package trees -import trees.nodes.BSTNode +import trees.abstract_trees.BinaryTree +import trees.nodes.BSNode -class BinarySearchTree> : BinaryTree>() { - override fun add(node: BSTNode) { +class BSTree, V> : BinaryTree>() { + override fun add(node: BSNode) { recursive_add(root, node) } - - private fun recursive_add(current: BSTNode?, node: BSTNode): BSTNode { + private fun recursive_add(current: BSNode?, node: BSNode): BSNode { if (root == null) { root = node return node @@ -15,7 +15,7 @@ class BinarySearchTree> : BinaryTree>() { if (current == null) { return node } - if (current.keyValue < node.keyValue) { + if (current.key < node.key) { current.right = recursive_add(current.right, node) } else { current.left = recursive_add(current.left, node) @@ -23,31 +23,31 @@ class BinarySearchTree> : BinaryTree>() { return current } - override fun delete(node: BSTNode) { + override fun remove(node: BSNode) { root?.let { recursive_delete(it, node) } } - private fun recursive_delete(current: BSTNode, node: BSTNode) { + private fun recursive_delete(current: BSNode, node: BSNode) { when { - node.keyValue > current.keyValue -> scan(node, current.right, current) - node.keyValue < current.keyValue -> scan(node, current.left, current) + node.key > current.key -> scan(node, current.right, current) + node.key < current.key -> scan(node, current.left, current) else -> removeNode(current, null) } } - private fun scan(node: BSTNode, current: BSTNode?, parent: BSTNode) { + private fun scan(node: BSNode, current: BSNode?, parent: BSNode) { if (current == null) { - println("value ${node.keyValue} not exist in the tree") + println("value ${node.key} not exist in the tree") return } when { - node.keyValue > current.keyValue -> scan(node, current.right, current) - node.keyValue < current.keyValue -> scan(node, current.left, current) + node.key > current.key -> scan(node, current.right, current) + node.key < current.key -> scan(node, current.left, current) else -> removeNode(current, parent) } } - private fun removeNode(node: BSTNode, parent: BSTNode?) { + private fun removeNode(node: BSNode, parent: BSNode?) { node.left?.let { leftChild -> run { node.right?.let { @@ -59,7 +59,7 @@ class BinarySearchTree> : BinaryTree>() { } } - private fun removeNoChildNode(node: BSTNode, parent: BSTNode?) { + private fun removeNoChildNode(node: BSNode, parent: BSNode?) { parent?.let { p -> if (node == p.left) { p.left = null @@ -71,31 +71,31 @@ class BinarySearchTree> : BinaryTree>() { ) } - private fun removeTwoChildNode(node: BSTNode) { + private fun removeTwoChildNode(node: BSNode) { val leftChild = node.left if (leftChild != null) { leftChild.right?.let { val maxParent = findParentOfMaxChild(leftChild) maxParent.right?.let { - node.keyValue = it.keyValue + node.key = it.key maxParent.right = null } ?: throw IllegalStateException("Node with max child must have the right child!") } ?: run { - node.keyValue = leftChild.keyValue + node.key = leftChild.key node.left = leftChild.left } } } - private fun findParentOfMaxChild(n: BSTNode): BSTNode { + private fun findParentOfMaxChild(n: BSNode): BSNode { return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } ?: throw IllegalArgumentException("Right child must be non-null") } - private fun removeSingleChildNode(parent: BSTNode, child: BSTNode) { - parent.keyValue = child.keyValue + private fun removeSingleChildNode(parent: BSNode, child: BSNode) { + parent.key = child.key parent.left = child.left parent.right = child.right } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/BinaryTree.kt b/app/src/main/kotlin/trees/BinaryTree.kt deleted file mode 100644 index b39f299..0000000 --- a/app/src/main/kotlin/trees/BinaryTree.kt +++ /dev/null @@ -1,21 +0,0 @@ -package trees - -import trees.nodes.Node - -abstract class BinaryTree, NODE_TYPE : Node> { - protected var root: NODE_TYPE? = null - - private fun recursive_find(node: NODE_TYPE, searchedNode: NODE_TYPE): NODE_TYPE? = when { - node.keyValue > searchedNode.keyValue -> node.left?.let { recursive_find(it, searchedNode) } - node.keyValue < searchedNode.keyValue -> node.right?.let { recursive_find(it, searchedNode) } - else -> node - } - - fun find(searchedNode: NODE_TYPE): Boolean { - val a = root?.let { recursive_find(it, searchedNode) } - return a?.keyValue == searchedNode.keyValue - } - - abstract fun add(node: NODE_TYPE) - abstract fun delete(node: NODE_TYPE) -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 140cd04..9be98ee 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,17 +1,18 @@ package trees +import trees.abstract_trees.BalanceTree import trees.nodes.RBNode -class RBTree, NODE_TYPE : RBNode> : AbstractBalanceTree() { - override fun balance() { +class RBTree, V> : BalanceTree>() { + override fun add(node: RBNode) { TODO("Not yet implemented") } - override fun add(node: NODE_TYPE) { + override fun remove(node: RBNode) { TODO("Not yet implemented") } - override fun delete(node: NODE_TYPE) { + override fun balance() { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt new file mode 100644 index 0000000..b581c34 --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -0,0 +1,7 @@ +package trees.abstract_trees + +import trees.nodes.Node + +abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { + abstract fun balance() +} diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt new file mode 100644 index 0000000..ce4dced --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -0,0 +1,39 @@ +package trees.abstract_trees + +import trees.nodes.Node + +abstract class BinaryTree, V, NODE_TYPE: Node> { + protected var root: NODE_TYPE? = null + + fun find(node: NODE_TYPE): NODE_TYPE? { + var current = root + while (current != null && node != current) { + current = when { + current.key < node.key -> current.right + current.key > node.key -> current.left + else -> node + } + } + return current + } + + private fun recursivePrint(prefix: String, node: NODE_TYPE?, isLeft: Boolean) { + if (node == null) { + return + } + println("$prefix└──${node.key}") + var newPrefix = "$prefix " + if (isLeft) { + newPrefix = "$prefix│ " + } + recursivePrint(newPrefix, node.left, true) + recursivePrint(newPrefix, node.right, false) + } + + fun print() { + recursivePrint("", root, false) + } + + abstract fun add(node: NODE_TYPE) + abstract fun remove(node: NODE_TYPE) +} diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index e2cfc80..338895d 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes -class AVLNode>(override var keyValue: T) : Node>() { +class AVLNode, V>(key: K, value: V) : Node>(key, value) { var height: Int? = null -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/nodes/AbstractNode.kt b/app/src/main/kotlin/trees/nodes/AbstractNode.kt deleted file mode 100644 index 85bedb4..0000000 --- a/app/src/main/kotlin/trees/nodes/AbstractNode.kt +++ /dev/null @@ -1,12 +0,0 @@ -package trees.nodes -abstract class Node, NODE_TYPE: Node> { - abstract var keyValue: T - var left: NODE_TYPE? = null - var right: NODE_TYPE? = null -} - -class KVPairs, V>(val key: K, val value: V?) : Comparable> { - override fun compareTo(other: KVPairs): Int { - return key.compareTo(other.key) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/BSNode.kt b/app/src/main/kotlin/trees/nodes/BSNode.kt new file mode 100644 index 0000000..e16735c --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/BSNode.kt @@ -0,0 +1,3 @@ +package trees.nodes + +class BSNode, V>(key: K, value: V) : Node>(key, value) diff --git a/app/src/main/kotlin/trees/nodes/BSTNode.kt b/app/src/main/kotlin/trees/nodes/BSTNode.kt deleted file mode 100644 index ee6ed8e..0000000 --- a/app/src/main/kotlin/trees/nodes/BSTNode.kt +++ /dev/null @@ -1,3 +0,0 @@ -package trees.nodes - -class BSTNode>(override var keyValue: T) : Node>() \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/Node.kt b/app/src/main/kotlin/trees/nodes/Node.kt new file mode 100644 index 0000000..dd15928 --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/Node.kt @@ -0,0 +1,5 @@ +package trees.nodes +abstract class Node, V, NODE_TYPE: Node>(var key: K, var value: V) { + var left: NODE_TYPE? = null + var right: NODE_TYPE? = null +} diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index 106adb0..c205ca1 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,10 +1,8 @@ package trees.nodes -class RBNode>(override var keyValue: T) : Node>() { - +class RBNode, V>(key: K, value: V) : Node>(key, value) { var color = Color.RED - enum class Color { RED, BLACK } -} \ No newline at end of file +} From c9a84bed9b2c85c077ff31da06962611b30ae71b Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Mon, 10 Apr 2023 22:27:18 +0300 Subject: [PATCH 31/85] add: init files for trees test --- app/build.gradle.kts | 3 +++ app/src/main/kotlin/trees/App.kt | 14 -------------- app/src/test/kotlin/trees/AVLTreeTest.kt | 20 ++++++++++++++++++++ app/src/test/kotlin/trees/AppTest.kt | 2 -- app/src/test/kotlin/trees/BSTreeTest.kt | 16 ++++++++++++++++ app/src/test/kotlin/trees/RBTreeTest.kt | 20 ++++++++++++++++++++ 6 files changed, 59 insertions(+), 16 deletions(-) delete mode 100644 app/src/main/kotlin/trees/App.kt create mode 100644 app/src/test/kotlin/trees/AVLTreeTest.kt delete mode 100644 app/src/test/kotlin/trees/AppTest.kt create mode 100644 app/src/test/kotlin/trees/BSTreeTest.kt create mode 100644 app/src/test/kotlin/trees/RBTreeTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1a7c8ba..aab3c0d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,6 +28,9 @@ dependencies { // This dependency is used by the application. implementation("com.google.guava:guava:31.1-jre") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") } application { diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt deleted file mode 100644 index 5b57c09..0000000 --- a/app/src/main/kotlin/trees/App.kt +++ /dev/null @@ -1,14 +0,0 @@ -package trees - -import trees.nodes.BSNode - -fun main() { - val t = BSTree() - t.add(BSNode(5, "a")) - t.add(BSNode(7, "b")) - t.add(BSNode(3, "c")) - t.add(BSNode(4, "d")) - t.add(BSNode(2, "e")) - t.print() -} - diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt new file mode 100644 index 0000000..d286abc --- /dev/null +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -0,0 +1,20 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class AVLTreeTest { + + @Test + fun balance() { + } + + @Test + fun add() { + } + + @Test + fun remove() { + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/AppTest.kt b/app/src/test/kotlin/trees/AppTest.kt deleted file mode 100644 index 90b56e3..0000000 --- a/app/src/test/kotlin/trees/AppTest.kt +++ /dev/null @@ -1,2 +0,0 @@ -package trees - diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt new file mode 100644 index 0000000..64d3ad9 --- /dev/null +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -0,0 +1,16 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class BSTreeTest { + + @Test + fun add() { + } + + @Test + fun remove() { + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt new file mode 100644 index 0000000..bf61705 --- /dev/null +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -0,0 +1,20 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class RBTreeTest { + + @Test + fun add() { + } + + @Test + fun remove() { + } + + @Test + fun balance() { + } +} \ No newline at end of file From 7db522b6ec8370f2c44de11ab88b6adab9c3c2ef Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:21:48 +0300 Subject: [PATCH 32/85] Implement BSTree, git workflow, readme, license (#14) Implemented bstree, find and print functions for all trees, pattern for RBTree, AVLTree CI that checks gradle build while pull requests, mergeable that checks not empty names in PR Short readme, GNU GPL V3 license --- .gitattributes | 9 + .github/mergeable.yml | 20 + .github/workflows/github-CI.yml | 22 + .gitignore | 44 ++ LICENSE | 675 ++++++++++++++++++ README.md | 17 + app/build.gradle.kts | 44 ++ app/src/main/kotlin/trees/AVLTree.kt | 18 + app/src/main/kotlin/trees/BSTree.kt | 101 +++ app/src/main/kotlin/trees/RBTree.kt | 18 + .../trees/abstract_trees/BalanceTree.kt | 7 + .../kotlin/trees/abstract_trees/BinaryTree.kt | 39 + app/src/main/kotlin/trees/nodes/AVLNode.kt | 5 + app/src/main/kotlin/trees/nodes/BSNode.kt | 3 + app/src/main/kotlin/trees/nodes/Node.kt | 5 + app/src/main/kotlin/trees/nodes/RBNode.kt | 8 + app/src/test/kotlin/trees/AVLTreeTest.kt | 20 + app/src/test/kotlin/trees/BSTreeTest.kt | 16 + app/src/test/kotlin/trees/RBTreeTest.kt | 20 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 244 +++++++ gradlew.bat | 92 +++ settings.gradle.kts | 11 + 24 files changed, 1444 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/mergeable.yml create mode 100644 .github/workflows/github-CI.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/build.gradle.kts create mode 100644 app/src/main/kotlin/trees/AVLTree.kt create mode 100644 app/src/main/kotlin/trees/BSTree.kt create mode 100644 app/src/main/kotlin/trees/RBTree.kt create mode 100644 app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt create mode 100644 app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt create mode 100644 app/src/main/kotlin/trees/nodes/AVLNode.kt create mode 100644 app/src/main/kotlin/trees/nodes/BSNode.kt create mode 100644 app/src/main/kotlin/trees/nodes/Node.kt create mode 100644 app/src/main/kotlin/trees/nodes/RBNode.kt create mode 100644 app/src/test/kotlin/trees/AVLTreeTest.kt create mode 100644 app/src/test/kotlin/trees/BSTreeTest.kt create mode 100644 app/src/test/kotlin/trees/RBTreeTest.kt create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..097f9f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + diff --git a/.github/mergeable.yml b/.github/mergeable.yml new file mode 100644 index 0000000..5ef2f5e --- /dev/null +++ b/.github/mergeable.yml @@ -0,0 +1,20 @@ +version: 2 +mergeable: + - when: pull_request.*, pull_request_review.* + name: 'Check decoration' + validate: + - do: title + must_exclude: + regex: ^\[WIP\] + - do: description + no_empty: + enabled: true + message: Description matter and should not be empty. Provide detail with **what** was changed, **why** it was changed, and **how** it was changed. + - do: approvals + min: + count: 1 + limit: + users: + - vacmannnn + - gladiuswq + - raf-nr \ No newline at end of file diff --git a/.github/workflows/github-CI.yml b/.github/workflows/github-CI.yml new file mode 100644 index 0000000..9bb1d83 --- /dev/null +++ b/.github/workflows/github-CI.yml @@ -0,0 +1,22 @@ +name: Run Kotlin code build + +on: + pull_request: + branches: + - main + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + - name: Run build with Gradle Wrapper + run: ./gradlew build + - uses: actions/upload-artifact@v3 + with: + name: Package + path: build/libs \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe3d8af --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Compiled class file +*.class + +# Log file +*.log + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# MacOS +*.DS_Store +.AppleDouble +# Ignore Gradle build output directory +build +# Ignore idea workflow files +.idea/* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..53d1f3d --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ad4b97 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Trees by team 9 +### Implemented: +- Binary search tree +- Abstract class for red-black and AVL tree + +### Future: +- AVL and RB tree +- Auto-tests +- Data bases for trees +- UI support + +#### Created by: gladiuswq, raf-nr, vacmannnn + + + + + diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..aab3c0d --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id("org.jetbrains.kotlin.jvm") version "1.8.10" + + // Apply the application plugin to add support for building a CLI application in Java. + application +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use the Kotlin JUnit 5 integration. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + + // Use the JUnit 5 integration. + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") + + // This dependency is used by the application. + implementation("com.google.guava:guava:31.1-jre") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") +} + +application { + // Define the main class for the application. + mainClass.set("trees.AppKt") +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt new file mode 100644 index 0000000..a8235ad --- /dev/null +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -0,0 +1,18 @@ +package trees + +import trees.abstract_trees.BalanceTree +import trees.nodes.AVLNode + +class AVLTree, V> : BalanceTree>() { + override fun balance() { + TODO("Not yet implemented") + } + + override fun add(node: AVLNode) { + TODO("Not yet implemented") + } + + override fun remove(node: AVLNode) { + TODO("Not yet implemented") + } +} diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt new file mode 100644 index 0000000..b99a81b --- /dev/null +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -0,0 +1,101 @@ +package trees + +import trees.abstract_trees.BinaryTree +import trees.nodes.BSNode + +class BSTree, V> : BinaryTree>() { + override fun add(node: BSNode) { + recursive_add(root, node) + } + private fun recursive_add(current: BSNode?, node: BSNode): BSNode { + if (root == null) { + root = node + return node + } + if (current == null) { + return node + } + if (current.key < node.key) { + current.right = recursive_add(current.right, node) + } else { + current.left = recursive_add(current.left, node) + } + return current + } + + override fun remove(node: BSNode) { + root?.let { recursive_delete(it, node) } + } + + private fun recursive_delete(current: BSNode, node: BSNode) { + when { + node.key > current.key -> scan(node, current.right, current) + node.key < current.key -> scan(node, current.left, current) + else -> removeNode(current, null) + } + } + + private fun scan(node: BSNode, current: BSNode?, parent: BSNode) { + if (current == null) { + println("value ${node.key} not exist in the tree") + return + } + when { + node.key > current.key -> scan(node, current.right, current) + node.key < current.key -> scan(node, current.left, current) + else -> removeNode(current, parent) + } + } + + private fun removeNode(node: BSNode, parent: BSNode?) { + node.left?.let { leftChild -> + run { + node.right?.let { + removeTwoChildNode(node) + } ?: removeSingleChildNode(node, leftChild) + } + } ?: run { + node.right?.let { rightChild -> removeSingleChildNode(node, rightChild) } ?: removeNoChildNode(node, parent) + } + } + + private fun removeNoChildNode(node: BSNode, parent: BSNode?) { + parent?.let { p -> + if (node == p.left) { + p.left = null + } else if (node == p.right) { + p.right = null + } + } ?: throw IllegalStateException( + "Can not remove the root node without child nodes" + ) + } + + private fun removeTwoChildNode(node: BSNode) { + val leftChild = node.left + if (leftChild != null) { + leftChild.right?.let { + val maxParent = findParentOfMaxChild(leftChild) + maxParent.right?.let { + node.key = it.key + maxParent.right = null + } ?: throw IllegalStateException("Node with max child must have the right child!") + + } ?: run { + node.key = leftChild.key + node.left = leftChild.left + } + } + } + + private fun findParentOfMaxChild(n: BSNode): BSNode { + return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } + ?: throw IllegalArgumentException("Right child must be non-null") + } + + private fun removeSingleChildNode(parent: BSNode, child: BSNode) { + parent.key = child.key + parent.left = child.left + parent.right = child.right + } +} diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt new file mode 100644 index 0000000..9be98ee --- /dev/null +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -0,0 +1,18 @@ +package trees + +import trees.abstract_trees.BalanceTree +import trees.nodes.RBNode + +class RBTree, V> : BalanceTree>() { + override fun add(node: RBNode) { + TODO("Not yet implemented") + } + + override fun remove(node: RBNode) { + TODO("Not yet implemented") + } + + override fun balance() { + TODO("Not yet implemented") + } +} diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt new file mode 100644 index 0000000..b581c34 --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -0,0 +1,7 @@ +package trees.abstract_trees + +import trees.nodes.Node + +abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { + abstract fun balance() +} diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt new file mode 100644 index 0000000..ce4dced --- /dev/null +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -0,0 +1,39 @@ +package trees.abstract_trees + +import trees.nodes.Node + +abstract class BinaryTree, V, NODE_TYPE: Node> { + protected var root: NODE_TYPE? = null + + fun find(node: NODE_TYPE): NODE_TYPE? { + var current = root + while (current != null && node != current) { + current = when { + current.key < node.key -> current.right + current.key > node.key -> current.left + else -> node + } + } + return current + } + + private fun recursivePrint(prefix: String, node: NODE_TYPE?, isLeft: Boolean) { + if (node == null) { + return + } + println("$prefix└──${node.key}") + var newPrefix = "$prefix " + if (isLeft) { + newPrefix = "$prefix│ " + } + recursivePrint(newPrefix, node.left, true) + recursivePrint(newPrefix, node.right, false) + } + + fun print() { + recursivePrint("", root, false) + } + + abstract fun add(node: NODE_TYPE) + abstract fun remove(node: NODE_TYPE) +} diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt new file mode 100644 index 0000000..338895d --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -0,0 +1,5 @@ +package trees.nodes + +class AVLNode, V>(key: K, value: V) : Node>(key, value) { + var height: Int? = null +} diff --git a/app/src/main/kotlin/trees/nodes/BSNode.kt b/app/src/main/kotlin/trees/nodes/BSNode.kt new file mode 100644 index 0000000..e16735c --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/BSNode.kt @@ -0,0 +1,3 @@ +package trees.nodes + +class BSNode, V>(key: K, value: V) : Node>(key, value) diff --git a/app/src/main/kotlin/trees/nodes/Node.kt b/app/src/main/kotlin/trees/nodes/Node.kt new file mode 100644 index 0000000..dd15928 --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/Node.kt @@ -0,0 +1,5 @@ +package trees.nodes +abstract class Node, V, NODE_TYPE: Node>(var key: K, var value: V) { + var left: NODE_TYPE? = null + var right: NODE_TYPE? = null +} diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt new file mode 100644 index 0000000..c205ca1 --- /dev/null +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -0,0 +1,8 @@ +package trees.nodes +class RBNode, V>(key: K, value: V) : Node>(key, value) { + var color = Color.RED + enum class Color { + RED, + BLACK + } +} diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt new file mode 100644 index 0000000..d286abc --- /dev/null +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -0,0 +1,20 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class AVLTreeTest { + + @Test + fun balance() { + } + + @Test + fun add() { + } + + @Test + fun remove() { + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt new file mode 100644 index 0000000..64d3ad9 --- /dev/null +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -0,0 +1,16 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class BSTreeTest { + + @Test + fun add() { + } + + @Test + fun remove() { + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt new file mode 100644 index 0000000..bf61705 --- /dev/null +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -0,0 +1,20 @@ +package trees + +import org.junit.jupiter.api.Test + +import org.junit.jupiter.api.Assertions.* + +class RBTreeTest { + + @Test + fun add() { + } + + @Test + fun remove() { + } + + @Test + fun balance() { + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..42defcc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..79a61d4 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..81a0d01 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/8.0/userguide/multi_project_builds.html + */ + +rootProject.name = "trees-9" +include("app") From d6712f1a77bc77004b48f5075b7f75b0d06359b6 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Tue, 11 Apr 2023 23:29:11 +0300 Subject: [PATCH 33/85] fix: remove unnecessary recursive add function --- app/src/main/kotlin/trees/BSTree.kt | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index b99a81b..cfad53d 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -5,22 +5,25 @@ import trees.nodes.BSNode class BSTree, V> : BinaryTree>() { override fun add(node: BSNode) { - recursive_add(root, node) - } - private fun recursive_add(current: BSNode?, node: BSNode): BSNode { if (root == null) { root = node - return node + return } - if (current == null) { - return node + var current = root + var parent = current + while (current != null) { + parent = current + current = when { + current.key < node.key -> current.right + current.key > node.key -> current.left + else -> throw IllegalArgumentException("Node with key ${node.key} is already in the tree") + } } - if (current.key < node.key) { - current.right = recursive_add(current.right, node) + if (parent!!.key < node.key) { + parent.right = node } else { - current.left = recursive_add(current.left, node) + parent.left = node } - return current } override fun remove(node: BSNode) { From 682ee755e28109d2d1e460adfa2cc9fd5b3e2a80 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Wed, 12 Apr 2023 16:36:00 +0300 Subject: [PATCH 34/85] add: raw add and remove methods --- app/src/main/kotlin/trees/AVLTree.kt | 2 +- app/src/main/kotlin/trees/RBTree.kt | 332 +++++++++++++++++- .../trees/abstract_trees/BalanceTree.kt | 2 +- app/src/main/kotlin/trees/nodes/RBNode.kt | 2 + 4 files changed, 333 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index a8235ad..66f66d7 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -4,7 +4,7 @@ import trees.abstract_trees.BalanceTree import trees.nodes.AVLNode class AVLTree, V> : BalanceTree>() { - override fun balance() { + override fun balance(node: AVLNode) { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 9be98ee..fa38da3 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,18 +1,344 @@ package trees +import org.checkerframework.checker.units.qual.K import trees.abstract_trees.BalanceTree import trees.nodes.RBNode + class RBTree, V> : BalanceTree>() { override fun add(node: RBNode) { - TODO("Not yet implemented") + if (root == null) { + root = node + return + } + var current = root + var parent = current + while (current != null) { + parent = current + current = when { + current.key < node.key -> current.right + current.key > node.key -> current.left + else -> throw IllegalArgumentException("Node with key ${node.key} is already in the tree") + } + } + if (parent!!.key < node.key) { + parent.right = node + } else { + parent.left = node + } + node.parent = parent + + balance(node) + } + override fun balance(node: RBNode) { + var parent = node.parent; + + // Case 1: Parent is null, we've reached the root, the end of the recursion + if (parent == null) { + // Uncomment the following line if you want to enforce black roots (rule 2): + // node.color = BLACK; + return; + } + + // Parent is black --> nothing to do + if (parent.color == RBNode.Color.BLACK) { + return; + } + + // From here on, parent is red + var grandparent = parent.parent; + + // Case 2: + // Not having a grandparent means that parent is the root. If we enforce black roots + // (rule 2), grandparent will never be null, and the following if-then block can be + // removed. + if (grandparent == null) { + // As this method is only called on red nodes (either + // recursively on red grandparents), all we have to do is to recolor the root black. + parent.color = RBNode.Color.BLACK; + return; + } + + // Get the uncle (maybe null/nil, in which case its color is BLACK) + var uncle = getUncle(parent); + + // Case 3: Uncle is red -> recolor parent, grandparent and uncle + if (uncle != null && uncle.color == RBNode.Color.RED) { + parent.color = RBNode.Color.BLACK; + grandparent.color = RBNode.Color.RED; + uncle.color = RBNode.Color.BLACK; + + // Call recursively for grandparent, which is now red. + // It might be root or have a red parent, in which case we need to fix more... + balance(grandparent); + } + + // Note on performance: + // It would be faster to do the uncle color check within the following code. This way + // we would avoid checking the grandparent-parent direction twice (once in getUncle() + // and once in the following else-if). But for better understanding of the code, + // I left the uncle color check as a separate step. + + // Parent is left child of grandparent + else if (parent == grandparent.left) { + // Case 4a: Uncle is black and node is left->right "inner child" of its grandparent + if (node == parent.right) { + rotateLeft(parent); + + // Let "parent" point to the new root node of the rotated sub-tree. + // It will be recolored in the next step, which we're going to fall-through to. + parent = node; + } + + // Case 5a: Uncle is black and node is left->left "outer child" of its grandparent + rotateRight(grandparent); + + // Recolor original parent and grandparent + parent.color = RBNode.Color.BLACK; + grandparent.color = RBNode.Color.RED; + } + + // Parent is right child of grandparent + else { + // Case 4b: Uncle is black and node is right->left "inner child" of its grandparent + if (node == parent.left) { + rotateRight(parent); + + // Let "parent" point to the new root node of the rotated sub-tree. + // It will be recolored in the next step, which we're going to fall-through to. + parent = node; + } + + // Case 5b: Uncle is black and node is right->right "outer child" of its grandparent + rotateLeft(grandparent); + + // Recolor original parent and grandparent + parent.color = RBNode.Color.BLACK; + grandparent.color = RBNode.Color.RED; + } + } + + private fun getUncle(parent: RBNode): RBNode? { + val grandparent = parent.parent + return if (grandparent?.left == parent) { + grandparent.right + } else if (grandparent?.right == parent) { + grandparent.left + } else { + throw IllegalStateException("Parent is not a child of its grandparent") + } } override fun remove(node: RBNode) { TODO("Not yet implemented") } - override fun balance() { - TODO("Not yet implemented") + private fun replaceParentsChild(parent: RBNode?, oldChild: RBNode?, newChild: RBNode?) { + if (parent == null) { + root = newChild + } else if (parent.left == oldChild) { + parent.left = newChild + } else if (parent.right == oldChild) { + parent.right = newChild + } else { + throw IllegalStateException("Node is not a child if its parent") + } + + if (newChild != null) { + newChild.parent = parent + } + } + private fun rotateRight(node: RBNode) { + val parent: RBNode? = node.parent + val leftChild: RBNode? = node.left + node.left = leftChild?.right + if (leftChild?.right != null) { + leftChild.right!!.parent = node + } + leftChild!!.right = node + node.parent = leftChild + + replaceParentsChild(parent, node, leftChild) + } + + private fun rotateLeft(node: RBNode) { + val parent: RBNode? = node.parent + val rightChild: RBNode? = node.right + node.right = rightChild?.left + if (rightChild?.left != null) { + rightChild.left!!.parent = node + } + rightChild!!.left = node + node.parent = rightChild + + replaceParentsChild(parent, node, rightChild) + } + + fun deleteNode(insertNode: RBNode) { + var node = root + + // Find the node to be deleted + while (node != null && node.key != insertNode.key) { + // Traverse the tree to the left or right depending on the key + node = if (insertNode.key < node.key) { + node.left + } else { + node.right + } + } + + // Node not found? + if (node == null) { + return + } + + // At this point, "node" is the node to be deleted + + // In this variable, we'll store the node at which we're going to start to fix the R-B + // properties after deleting a node. + val movedUpNode: RBNode + val deletedNodeColor: RBNode.Color + + // Node has zero or one child + if (node.left == null || node.right == null) { + movedUpNode = deleteNodeWithZeroOrOneChild(node) + deletedNodeColor = node.color + } else { + // Find minimum node of right subtree ("inorder successor" of current node) + val inOrderSuccessor: RBNode = findMinimum(node.right) + + // Copy inorder successor's data to current node (keep its color!) + node.key = inOrderSuccessor.key + + // Delete inorder successor just as we would delete a node with 0 or 1 child + movedUpNode = deleteNodeWithZeroOrOneChild(inOrderSuccessor) + deletedNodeColor = inOrderSuccessor.color + } + if (deletedNodeColor == RBNode.Color.BLACK) { + fixRedBlackPropertiesAfterDelete(movedUpNode) + + // Remove the temporary NIL node + if (movedUpNode.javaClass == NILNode()) { + replaceParentsChild(movedUpNode.parent, movedUpNode, null) + } + } + } + private fun deleteNodeWithZeroOrOneChild(node: RBNode): RBNode? { + // Node has ONLY a left child --> replace by its left child + return if (node.left != null) { + replaceParentsChild(node.parent, node, node.left) + node.left // moved-up node + } else if (node.right != null) { + replaceParentsChild(node.parent, node, node.right) + node.right // moved-up node + } else { + val newChild: RBNode? = if (node.color == RBNode.Color.BLACK) NilNode() else null + replaceParentsChild(node.parent, node, newChild) + newChild + } + } + private fun findMinimum(node: RBNode): RBNode? { + var current: RBNode? = node + while (current?.left != null) { + current = current.left + } + return current + } + + private fun fixRedBlackPropertiesAfterDelete(node: RBNode) { + // Case 1: Examined node is root, end of recursion + if (node == root) { + // Uncomment the following line if you want to enforce black roots (rule 2): + // node.color = BLACK; + return + } + var sibling: RBNode = getSibling(node) + + // Case 2: Red sibling + if (sibling.color == RBNode.Color.RED) { + handleRedSibling(node, sibling) + sibling = getSibling(node) // Get new sibling for fall-through to cases 3-6 + } + + // Cases 3+4: Black sibling with two black children + if (sibling.left!!.color == RBNode.Color.BLACK && sibling.right!!.color == RBNode.Color.BLACK) { + sibling.color = RBNode.Color.RED + + // Case 3: Black sibling with two black children + red parent + if (node.parent!!.color == RBNode.Color.RED) { + node.parent!!.color = RBNode.Color.BLACK + } else { + fixRedBlackPropertiesAfterDelete(node.parent!!) + } + } else { + handleBlackSiblingWithAtLeastOneRedChild(node, sibling) + } + } + private fun handleRedSibling(node: RBNode, sibling: RBNode) { + // Recolor... + sibling.color = RBNode.Color.BLACK + node.parent!!.color = RBNode.Color.RED + + // ... and rotate + if (node == node.parent!!.left) { + rotateLeft(node.parent!!) + } else { + rotateRight(node.parent!!) + } + } + private fun handleBlackSiblingWithAtLeastOneRedChild(node: RBNode, sibling: RBNode) { + var sibling: RBNode = sibling + val nodeIsLeftChild = node == node.parent.left + + // Case 5: Black sibling with at least one red child + "outer nephew" is black + // --> Recolor sibling and its child, and rotate around sibling + if (nodeIsLeftChild && isBlack(sibling.right)) { + sibling.left.color = BLACK + sibling.color = RED + rotateRight(sibling) + sibling = node.parent.right + } else if (!nodeIsLeftChild && isBlack(sibling.left)) { + sibling.right.color = BLACK + sibling.color = RED + rotateLeft(sibling) + sibling = node.parent.left + } + + // Fall-through to case 6... + + // Case 6: Black sibling with at least one red child + "outer nephew" is red + // --> Recolor sibling + parent + sibling's child, and rotate around parent + sibling.color = node.parent.color + node.parent.color = BLACK + if (nodeIsLeftChild) { + sibling.right.color = BLACK + rotateLeft(node.parent) + } else { + sibling.left.color = BLACK + rotateRight(node.parent) + } } + + private fun getSibling(node: Node): Node? { + val parent: Node = node.parent + return if (node === parent.left) { + parent.right + } else if (node === parent.right) { + parent.left + } else { + throw java.lang.IllegalStateException("Parent is not a child of its grandparent") + } + } + + private fun isBlack(node: Node?): Boolean { + return node == null || node.color === BLACK + } + + + private class NilNode private constructor() : Node(0) { + init { + this.color = BLACK + } + } + } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index b581c34..90106a4 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance() + abstract fun balance(node: NODE_TYPE) } diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index c205ca1..49cee36 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,5 +1,7 @@ package trees.nodes + class RBNode, V>(key: K, value: V) : Node>(key, value) { + var parent: RBNode? = null var color = Color.RED enum class Color { RED, From 5ce9c4f3c5f6a78d35ad28fee7a957f6e959fbf4 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Thu, 13 Apr 2023 01:23:59 +0300 Subject: [PATCH 35/85] fix: find search by key --- app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index ce4dced..c9e0ba3 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -5,13 +5,13 @@ import trees.nodes.Node abstract class BinaryTree, V, NODE_TYPE: Node> { protected var root: NODE_TYPE? = null - fun find(node: NODE_TYPE): NODE_TYPE? { + fun find(key: K): NODE_TYPE? { var current = root - while (current != null && node != current) { + while (current != null && key != current.key) { current = when { - current.key < node.key -> current.right - current.key > node.key -> current.left - else -> node + current.key < key -> current.right + current.key > key -> current.left + else -> current } } return current From f72e3a225be9a2e1ee0fc1ddf86beed4d88cbc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=84=D0=B8=D0=BA=20=D0=9D=D1=83=D1=80=D0=BC?= =?UTF-8?q?=D1=83=D1=85=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= <113212617+raf-nr@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:04:52 +0300 Subject: [PATCH 36/85] implement AVL-tree Implemented function of adding and removing avl-nodes, their balance --- app/src/main/kotlin/trees/AVLTree.kt | 94 ++++++++++++++++++- app/src/main/kotlin/trees/RBTree.kt | 2 +- .../trees/abstract_trees/BalanceTree.kt | 2 +- .../kotlin/trees/abstract_trees/BinaryTree.kt | 8 ++ app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- 5 files changed, 100 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index a8235ad..c664cca 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -2,17 +2,101 @@ package trees import trees.abstract_trees.BalanceTree import trees.nodes.AVLNode +import kotlin.math.max class AVLTree, V> : BalanceTree>() { - override fun balance() { - TODO("Not yet implemented") - } override fun add(node: AVLNode) { - TODO("Not yet implemented") + root = recursive_add(root, node) + updateHeight(root) + } + + private fun recursive_add(currentNode: AVLNode?, node: AVLNode): AVLNode { + if (currentNode == null) return node + if (currentNode.key < node.key) { + currentNode.right = recursive_add(currentNode.right, node) + } + if (currentNode.key > node.key) { + currentNode.left = recursive_add(currentNode.left, node) + } + return balance(currentNode) } override fun remove(node: AVLNode) { - TODO("Not yet implemented") + root = recursive_remove(root, node) + updateHeight(root) + } + + private fun recursive_remove(currentNode: AVLNode?, node: AVLNode): AVLNode? { + if (currentNode == null) return null + if (currentNode.key < node.key) { + currentNode.right = recursive_remove(currentNode.right, node) + } else if (currentNode.key > node.key) { + currentNode.left = recursive_remove(currentNode.left, node) + } else { + if (currentNode.left == null || currentNode.right == null) + return currentNode.left ?: currentNode.right + val minRightSubtree = getMinNode(currentNode.right!!) + currentNode.key = minRightSubtree.key + currentNode.value = minRightSubtree.value + currentNode.right = recursive_remove(currentNode.right, minRightSubtree) + } + return balance(currentNode) + } + + override fun balance(node: AVLNode): AVLNode { + updateHeight(node) + when(getBalanceFactor(node)) { + -2 -> { + if (getBalanceFactor(node.left) == 1) { + node.left = node.left?.let { rotateLeft(it) } + updateHeight(node.left) + } + val balancedNode = rotateRight(node) + updateHeight(balancedNode) + return balancedNode + } + 2 -> { + if (getBalanceFactor(node.right) == -1) { + node.right = node.right?.let { rotateRight(it) } + updateHeight(node.right) + } + val balancedNode = rotateLeft(node) + updateHeight(balancedNode) + return balancedNode + } + } + return node + } + + private fun getHeight(node: AVLNode?): Int { + return node?.height ?: -1 + } + + private fun getBalanceFactor(node: AVLNode?): Int { + if (node == null) return 0 + return getHeight(node.right) - getHeight(node.left) + } + + private fun updateHeight(node: AVLNode?) { + node?.let {it.height = max(getHeight(it.left), getHeight(it.right)) + 1} + } + + private fun rotateRight(node: AVLNode): AVLNode { + val leftChild = node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val rightGrandChild = leftChild.right + leftChild.right = node + node.left = rightGrandChild + updateHeight(leftChild.right) + return leftChild + } + + private fun rotateLeft(node: AVLNode): AVLNode { + val rightChild = node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val leftGrandChild = rightChild.left + rightChild.left = node + node.right = leftGrandChild + updateHeight(rightChild.left) + return rightChild } } diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 9be98ee..8586412 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -12,7 +12,7 @@ class RBTree, V> : BalanceTree>() { TODO("Not yet implemented") } - override fun balance() { + override fun balance(node: RBNode): RBNode? { TODO("Not yet implemented") } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index b581c34..5b5d789 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance() + abstract fun balance(node: NODE_TYPE): NODE_TYPE? } diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index c9e0ba3..127a860 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -34,6 +34,14 @@ abstract class BinaryTree, V, NODE_TYPE: Node> recursivePrint("", root, false) } + protected fun getMinNode(node: NODE_TYPE): NODE_TYPE { + var minimal = node + while (true) { + minimal = minimal.left ?: break + } + return minimal + } + abstract fun add(node: NODE_TYPE) abstract fun remove(node: NODE_TYPE) } diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index 338895d..05b9864 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes class AVLNode, V>(key: K, value: V) : Node>(key, value) { - var height: Int? = null + var height: Int? = 0 } From d99f143289d98abb161bd0b4921cc3e8f0337fba Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Mon, 17 Apr 2023 20:32:51 +0300 Subject: [PATCH 37/85] feat: function to validate BSTree small codestyle changes, remove delete by key --- app/src/main/kotlin/trees/AVLTree.kt | 34 +++++++++------- app/src/main/kotlin/trees/BSTree.kt | 39 +++++++++++++------ app/src/main/kotlin/trees/RBTree.kt | 2 +- .../kotlin/trees/abstract_trees/BinaryTree.kt | 4 +- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index c664cca..f58c2cd 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -4,49 +4,50 @@ import trees.abstract_trees.BalanceTree import trees.nodes.AVLNode import kotlin.math.max -class AVLTree, V> : BalanceTree>() { +class AVLTree, V> : BalanceTree>() { override fun add(node: AVLNode) { - root = recursive_add(root, node) + root = recursiveAdd(root, node) updateHeight(root) } - private fun recursive_add(currentNode: AVLNode?, node: AVLNode): AVLNode { + private fun recursiveAdd(currentNode: AVLNode?, node: AVLNode): AVLNode { if (currentNode == null) return node if (currentNode.key < node.key) { - currentNode.right = recursive_add(currentNode.right, node) + currentNode.right = recursiveAdd(currentNode.right, node) } if (currentNode.key > node.key) { - currentNode.left = recursive_add(currentNode.left, node) + currentNode.left = recursiveAdd(currentNode.left, node) } return balance(currentNode) } - override fun remove(node: AVLNode) { - root = recursive_remove(root, node) + override fun remove(key: K) { + val node = find(key) + root = node?.let { recursiveRemove(root, it) } updateHeight(root) } - private fun recursive_remove(currentNode: AVLNode?, node: AVLNode): AVLNode? { + private fun recursiveRemove(currentNode: AVLNode?, node: AVLNode): AVLNode? { if (currentNode == null) return null if (currentNode.key < node.key) { - currentNode.right = recursive_remove(currentNode.right, node) + currentNode.right = recursiveRemove(currentNode.right, node) } else if (currentNode.key > node.key) { - currentNode.left = recursive_remove(currentNode.left, node) + currentNode.left = recursiveRemove(currentNode.left, node) } else { if (currentNode.left == null || currentNode.right == null) return currentNode.left ?: currentNode.right val minRightSubtree = getMinNode(currentNode.right!!) currentNode.key = minRightSubtree.key currentNode.value = minRightSubtree.value - currentNode.right = recursive_remove(currentNode.right, minRightSubtree) + currentNode.right = recursiveRemove(currentNode.right, minRightSubtree) } return balance(currentNode) } override fun balance(node: AVLNode): AVLNode { updateHeight(node) - when(getBalanceFactor(node)) { + when (getBalanceFactor(node)) { -2 -> { if (getBalanceFactor(node.left) == 1) { node.left = node.left?.let { rotateLeft(it) } @@ -56,6 +57,7 @@ class AVLTree, V> : BalanceTree>() { updateHeight(balancedNode) return balancedNode } + 2 -> { if (getBalanceFactor(node.right) == -1) { node.right = node.right?.let { rotateRight(it) } @@ -79,11 +81,12 @@ class AVLTree, V> : BalanceTree>() { } private fun updateHeight(node: AVLNode?) { - node?.let {it.height = max(getHeight(it.left), getHeight(it.right)) + 1} + node?.let { it.height = max(getHeight(it.left), getHeight(it.right)) + 1 } } private fun rotateRight(node: AVLNode): AVLNode { - val leftChild = node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val leftChild = + node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") val rightGrandChild = leftChild.right leftChild.right = node node.left = rightGrandChild @@ -92,7 +95,8 @@ class AVLTree, V> : BalanceTree>() { } private fun rotateLeft(node: AVLNode): AVLNode { - val rightChild = node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val rightChild = + node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") val leftGrandChild = rightChild.left rightChild.left = node node.right = leftGrandChild diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index cfad53d..dc3d9e5 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -4,6 +4,20 @@ import trees.abstract_trees.BinaryTree import trees.nodes.BSNode class BSTree, V> : BinaryTree>() { + fun isBstOk(node: BSNode? = root): Boolean { + if (node == null) { + return true + } + // `compare to` returns not null value + if (node.left != null && node.left!!.key > node.key) { + return false + } + if (node.right != null && node.right!!.key < node.key) { + return false + } + return !(!isBstOk(node.right) || !isBstOk(node.left)) + } + override fun add(node: BSNode) { if (root == null) { root = node @@ -26,15 +40,14 @@ class BSTree, V> : BinaryTree>() { } } - override fun remove(node: BSNode) { - root?.let { recursive_delete(it, node) } - } - - private fun recursive_delete(current: BSNode, node: BSNode) { - when { - node.key > current.key -> scan(node, current.right, current) - node.key < current.key -> scan(node, current.left, current) - else -> removeNode(current, null) + override fun remove(key: K) { + val node = find(key) + root?.let { + node?.let { itNode -> + if (itNode.key > it.key) scan(itNode, it.right, it) + if (itNode.key < it.key) scan(itNode, it.left, it) + else removeNode(it, null) + } } } @@ -69,9 +82,11 @@ class BSTree, V> : BinaryTree>() { } else if (node == p.right) { p.right = null } - } ?: throw IllegalStateException( - "Can not remove the root node without child nodes" - ) + } ?: rootRemove() + } + + private fun rootRemove() { + root = null } private fun removeTwoChildNode(node: BSNode) { diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 8586412..fbf167e 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -8,7 +8,7 @@ class RBTree, V> : BalanceTree>() { TODO("Not yet implemented") } - override fun remove(node: RBNode) { + override fun remove(key: K) { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index 127a860..d72e1eb 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -2,7 +2,7 @@ package trees.abstract_trees import trees.nodes.Node -abstract class BinaryTree, V, NODE_TYPE: Node> { +abstract class BinaryTree, V, NODE_TYPE : Node> { protected var root: NODE_TYPE? = null fun find(key: K): NODE_TYPE? { @@ -43,5 +43,5 @@ abstract class BinaryTree, V, NODE_TYPE: Node> } abstract fun add(node: NODE_TYPE) - abstract fun remove(node: NODE_TYPE) + abstract fun remove(key: K) } From e14b6a896617b93db5ef2a04351802a3c957210b Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Mon, 17 Apr 2023 22:29:22 +0300 Subject: [PATCH 38/85] add: basic red black tree implementation --- app/src/main/kotlin/trees/RBTree.kt | 274 +++++++++------------- app/src/main/kotlin/trees/nodes/RBNode.kt | 3 + 2 files changed, 108 insertions(+), 169 deletions(-) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index fa38da3..2636869 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -1,9 +1,8 @@ package trees -import org.checkerframework.checker.units.qual.K import trees.abstract_trees.BalanceTree import trees.nodes.RBNode - +import trees.nodes.RBNode.Color class RBTree, V> : BalanceTree>() { override fun add(node: RBNode) { @@ -28,93 +27,88 @@ class RBTree, V> : BalanceTree>() { } node.parent = parent - balance(node) + balanceAfterInsert(node) } - override fun balance(node: RBNode) { - var parent = node.parent; - // Case 1: Parent is null, we've reached the root, the end of the recursion - if (parent == null) { - // Uncomment the following line if you want to enforce black roots (rule 2): - // node.color = BLACK; - return; + override fun remove(node: RBNode) { + var current = root + while (current != null && current.key != node.key) { + current = if (node.key < current.key) { + current.left + } else { + current.right + } } - - // Parent is black --> nothing to do - if (parent.color == RBNode.Color.BLACK) { - return; + if (current == null) { + throw IllegalStateException("There is no such node with key ${node.key} in tree") } - // From here on, parent is red - var grandparent = parent.parent; + val movedUpNode: RBNode? + val deletedNodeColor: Color - // Case 2: - // Not having a grandparent means that parent is the root. If we enforce black roots - // (rule 2), grandparent will never be null, and the following if-then block can be - // removed. - if (grandparent == null) { - // As this method is only called on red nodes (either - // recursively on red grandparents), all we have to do is to recolor the root black. - parent.color = RBNode.Color.BLACK; - return; + // Node has zero or one child + if (current.left == null || current.right == null) { + movedUpNode = deleteNodeWithZeroOrOneChild(current) + deletedNodeColor = current.color + } else { + // Find minimum node of right subtree ("inorder successor" of current node) + val inOrderSuccessor: RBNode = findMinimum(current.right!!) + + // Copy inorder successor's data to current node (keep its color!) + current.key = inOrderSuccessor.key + + // Delete inorder successor just as we would delete a node with 0 or 1 child + movedUpNode = deleteNodeWithZeroOrOneChild(inOrderSuccessor)!! + deletedNodeColor = inOrderSuccessor.color } + if (deletedNodeColor == Color.BLACK) { + balanceAfterDelete(movedUpNode!!) - // Get the uncle (maybe null/nil, in which case its color is BLACK) - var uncle = getUncle(parent); + // Remove the temporary NIL node + if (movedUpNode.isNil) { + replaceParentsChild(movedUpNode.parent, movedUpNode, null) + } + } + } - // Case 3: Uncle is red -> recolor parent, grandparent and uncle - if (uncle != null && uncle.color == RBNode.Color.RED) { - parent.color = RBNode.Color.BLACK; - grandparent.color = RBNode.Color.RED; - uncle.color = RBNode.Color.BLACK; + override fun balance(node: RBNode) { + TODO("Not yet implemented") + } - // Call recursively for grandparent, which is now red. - // It might be root or have a red parent, in which case we need to fix more... - balance(grandparent); + private fun balanceAfterInsert(node: RBNode) { + var parent = node.parent ?: return; + if (parent.color == Color.BLACK) { + return } - // Note on performance: - // It would be faster to do the uncle color check within the following code. This way - // we would avoid checking the grandparent-parent direction twice (once in getUncle() - // and once in the following else-if). But for better understanding of the code, - // I left the uncle color check as a separate step. + val grandparent = parent.parent; + if (grandparent == null) { + parent.color = Color.BLACK; + return + } - // Parent is left child of grandparent - else if (parent == grandparent.left) { - // Case 4a: Uncle is black and node is left->right "inner child" of its grandparent + val uncle = getUncle(parent); + if (uncle != null && uncle.color == Color.RED) { + parent.color = Color.BLACK; + grandparent.color = Color.RED; + uncle.color = Color.BLACK; + balanceAfterInsert(grandparent); + } else if (parent == grandparent.left) { if (node == parent.right) { rotateLeft(parent); - - // Let "parent" point to the new root node of the rotated sub-tree. - // It will be recolored in the next step, which we're going to fall-through to. parent = node; } - - // Case 5a: Uncle is black and node is left->left "outer child" of its grandparent rotateRight(grandparent); - - // Recolor original parent and grandparent - parent.color = RBNode.Color.BLACK; - grandparent.color = RBNode.Color.RED; - } - - // Parent is right child of grandparent - else { - // Case 4b: Uncle is black and node is right->left "inner child" of its grandparent + parent.color = Color.BLACK; + grandparent.color = Color.RED; + } else { if (node == parent.left) { rotateRight(parent); - - // Let "parent" point to the new root node of the rotated sub-tree. - // It will be recolored in the next step, which we're going to fall-through to. parent = node; } - - // Case 5b: Uncle is black and node is right->right "outer child" of its grandparent rotateLeft(grandparent); - - // Recolor original parent and grandparent - parent.color = RBNode.Color.BLACK; - grandparent.color = RBNode.Color.RED; + parent.color = Color.BLACK; + grandparent.color = Color.RED; } } @@ -129,10 +123,6 @@ class RBTree, V> : BalanceTree>() { } } - override fun remove(node: RBNode) { - TODO("Not yet implemented") - } - private fun replaceParentsChild(parent: RBNode?, oldChild: RBNode?, newChild: RBNode?) { if (parent == null) { root = newChild @@ -148,6 +138,7 @@ class RBTree, V> : BalanceTree>() { newChild.parent = parent } } + private fun rotateRight(node: RBNode) { val parent: RBNode? = node.parent val leftChild: RBNode? = node.left @@ -174,112 +165,66 @@ class RBTree, V> : BalanceTree>() { replaceParentsChild(parent, node, rightChild) } - fun deleteNode(insertNode: RBNode) { - var node = root - - // Find the node to be deleted - while (node != null && node.key != insertNode.key) { - // Traverse the tree to the left or right depending on the key - node = if (insertNode.key < node.key) { - node.left - } else { - node.right - } - } - - // Node not found? - if (node == null) { - return - } - - // At this point, "node" is the node to be deleted - - // In this variable, we'll store the node at which we're going to start to fix the R-B - // properties after deleting a node. - val movedUpNode: RBNode - val deletedNodeColor: RBNode.Color - - // Node has zero or one child - if (node.left == null || node.right == null) { - movedUpNode = deleteNodeWithZeroOrOneChild(node) - deletedNodeColor = node.color - } else { - // Find minimum node of right subtree ("inorder successor" of current node) - val inOrderSuccessor: RBNode = findMinimum(node.right) - - // Copy inorder successor's data to current node (keep its color!) - node.key = inOrderSuccessor.key - // Delete inorder successor just as we would delete a node with 0 or 1 child - movedUpNode = deleteNodeWithZeroOrOneChild(inOrderSuccessor) - deletedNodeColor = inOrderSuccessor.color - } - if (deletedNodeColor == RBNode.Color.BLACK) { - fixRedBlackPropertiesAfterDelete(movedUpNode) - - // Remove the temporary NIL node - if (movedUpNode.javaClass == NILNode()) { - replaceParentsChild(movedUpNode.parent, movedUpNode, null) - } - } - } private fun deleteNodeWithZeroOrOneChild(node: RBNode): RBNode? { - // Node has ONLY a left child --> replace by its left child return if (node.left != null) { replaceParentsChild(node.parent, node, node.left) - node.left // moved-up node + node.left } else if (node.right != null) { replaceParentsChild(node.parent, node, node.right) - node.right // moved-up node + node.right } else { - val newChild: RBNode? = if (node.color == RBNode.Color.BLACK) NilNode() else null + var newChild: RBNode? = node + newChild!!.isNil = true + if (node.color == Color.RED) { + newChild = null + } replaceParentsChild(node.parent, node, newChild) newChild } } - private fun findMinimum(node: RBNode): RBNode? { - var current: RBNode? = node - while (current?.left != null) { - current = current.left + + private fun findMinimum(node: RBNode): RBNode { + var current = node + while (current.left != null) { + current = current.left!! } return current } - private fun fixRedBlackPropertiesAfterDelete(node: RBNode) { + private fun balanceAfterDelete(node: RBNode) { // Case 1: Examined node is root, end of recursion if (node == root) { // Uncomment the following line if you want to enforce black roots (rule 2): // node.color = BLACK; return } - var sibling: RBNode = getSibling(node) + var sibling = getSibling(node) // Case 2: Red sibling - if (sibling.color == RBNode.Color.RED) { + if (sibling?.color == Color.RED) { handleRedSibling(node, sibling) - sibling = getSibling(node) // Get new sibling for fall-through to cases 3-6 + sibling = getSibling(node)!! // Get new sibling for fall-through to cases 3-6 } // Cases 3+4: Black sibling with two black children - if (sibling.left!!.color == RBNode.Color.BLACK && sibling.right!!.color == RBNode.Color.BLACK) { - sibling.color = RBNode.Color.RED + if (isBlack(sibling?.left) && isBlack(sibling?.right)) { + sibling?.color = Color.RED // Case 3: Black sibling with two black children + red parent - if (node.parent!!.color == RBNode.Color.RED) { - node.parent!!.color = RBNode.Color.BLACK + if (node.parent!!.color == Color.RED) { + node.parent!!.color = Color.BLACK } else { - fixRedBlackPropertiesAfterDelete(node.parent!!) + balanceAfterDelete(node.parent!!) } } else { - handleBlackSiblingWithAtLeastOneRedChild(node, sibling) + handleBlackSiblingWithAtLeastOneRedChild(node, sibling!!) } } private fun handleRedSibling(node: RBNode, sibling: RBNode) { - // Recolor... - sibling.color = RBNode.Color.BLACK - node.parent!!.color = RBNode.Color.RED + sibling.color = Color.BLACK + node.parent!!.color = Color.RED - // ... and rotate if (node == node.parent!!.left) { rotateLeft(node.parent!!) } else { @@ -287,58 +232,49 @@ class RBTree, V> : BalanceTree>() { } } private fun handleBlackSiblingWithAtLeastOneRedChild(node: RBNode, sibling: RBNode) { - var sibling: RBNode = sibling - val nodeIsLeftChild = node == node.parent.left + val nodeIsLeftChild = node == node.parent!!.left // Case 5: Black sibling with at least one red child + "outer nephew" is black // --> Recolor sibling and its child, and rotate around sibling if (nodeIsLeftChild && isBlack(sibling.right)) { - sibling.left.color = BLACK - sibling.color = RED + sibling.left!!.color = Color.BLACK + sibling.color = Color.RED rotateRight(sibling) - sibling = node.parent.right + sibling.parent!!.right = node.parent!!.right } else if (!nodeIsLeftChild && isBlack(sibling.left)) { - sibling.right.color = BLACK - sibling.color = RED + sibling.right!!.color = Color.BLACK + sibling.color = Color.RED rotateLeft(sibling) - sibling = node.parent.left + sibling.parent!!.left = node.parent!!.left } // Fall-through to case 6... // Case 6: Black sibling with at least one red child + "outer nephew" is red // --> Recolor sibling + parent + sibling's child, and rotate around parent - sibling.color = node.parent.color - node.parent.color = BLACK + sibling.color = node.parent!!.color + node.parent!!.color = Color.BLACK if (nodeIsLeftChild) { - sibling.right.color = BLACK - rotateLeft(node.parent) + sibling.right!!.color = Color.BLACK + rotateLeft(node.parent!!) } else { - sibling.left.color = BLACK - rotateRight(node.parent) + sibling.left!!.color = Color.BLACK + rotateRight(node.parent!!) } } - private fun getSibling(node: Node): Node? { - val parent: Node = node.parent - return if (node === parent.left) { + private fun getSibling(node: RBNode): RBNode? { + val parent = node.parent + return if (node == parent?.left) { parent.right - } else if (node === parent.right) { + } else if (node == parent?.right) { parent.left } else { - throw java.lang.IllegalStateException("Parent is not a child of its grandparent") + throw IllegalStateException("Parent is not a child of its grandparent") } } - private fun isBlack(node: Node?): Boolean { - return node == null || node.color === BLACK + private fun isBlack(node: RBNode?): Boolean { + return node == null || node.color == Color.BLACK } - - - private class NilNode private constructor() : Node(0) { - init { - this.color = BLACK - } - } - } diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index 49cee36..91ea97c 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -7,4 +7,7 @@ class RBNode, V>(key: K, value: V) : Node>(k RED, BLACK } + + var isNil: Boolean = false + } From 75bc5c820f90ee6af5923da6178ef7640788a077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=84=D0=B8=D0=BA=20=D0=9D=D1=83=D1=80=D0=BC?= =?UTF-8?q?=D1=83=D1=85=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= <113212617+raf-nr@users.noreply.github.com> Date: Mon, 17 Apr 2023 23:42:36 +0300 Subject: [PATCH 39/85] Correction of functions for adding and deleting nodes Changed the add and delete functions in the binary search tree. In the function of adding nodes in all trees, the key and value are now passed, instead of the nodes themselves. When adding nodes, with a key that already exists, its value is replaced with the new one. --- app/src/main/kotlin/trees/AVLTree.kt | 7 +- app/src/main/kotlin/trees/BSTree.kt | 117 +++++------------- app/src/main/kotlin/trees/RBTree.kt | 2 +- .../kotlin/trees/abstract_trees/BinaryTree.kt | 2 +- 4 files changed, 36 insertions(+), 92 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index f58c2cd..5e8d800 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -6,8 +6,8 @@ import kotlin.math.max class AVLTree, V> : BalanceTree>() { - override fun add(node: AVLNode) { - root = recursiveAdd(root, node) + override fun add(key: K, value: V) { + root = recursiveAdd(root, AVLNode(key, value)) updateHeight(root) } @@ -19,6 +19,9 @@ class AVLTree, V> : BalanceTree>() { if (currentNode.key > node.key) { currentNode.left = recursiveAdd(currentNode.left, node) } + if (currentNode.key == node.key) { + return node + } return balance(currentNode) } diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index dc3d9e5..826f941 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -18,102 +18,43 @@ class BSTree, V> : BinaryTree>() { return !(!isBstOk(node.right) || !isBstOk(node.left)) } - override fun add(node: BSNode) { - if (root == null) { - root = node - return - } - var current = root - var parent = current - while (current != null) { - parent = current - current = when { - current.key < node.key -> current.right - current.key > node.key -> current.left - else -> throw IllegalArgumentException("Node with key ${node.key} is already in the tree") - } - } - if (parent!!.key < node.key) { - parent.right = node - } else { - parent.left = node - } + override fun add(key: K, value: V) { + root = recursiveAdd(root, BSNode(key, value)) } - override fun remove(key: K) { - val node = find(key) - root?.let { - node?.let { itNode -> - if (itNode.key > it.key) scan(itNode, it.right, it) - if (itNode.key < it.key) scan(itNode, it.left, it) - else removeNode(it, null) - } + private fun recursiveAdd(currentNode: BSNode?, node: BSNode): BSNode { + if (currentNode == null) return node + if (currentNode.key < node.key) { + currentNode.right = recursiveAdd(currentNode.right, node) } - } - - private fun scan(node: BSNode, current: BSNode?, parent: BSNode) { - if (current == null) { - println("value ${node.key} not exist in the tree") - return - } - when { - node.key > current.key -> scan(node, current.right, current) - node.key < current.key -> scan(node, current.left, current) - else -> removeNode(current, parent) - } - } - - private fun removeNode(node: BSNode, parent: BSNode?) { - node.left?.let { leftChild -> - run { - node.right?.let { - removeTwoChildNode(node) - } ?: removeSingleChildNode(node, leftChild) - } - } ?: run { - node.right?.let { rightChild -> removeSingleChildNode(node, rightChild) } ?: removeNoChildNode(node, parent) + if (currentNode.key > node.key) { + currentNode.left = recursiveAdd(currentNode.left, node) } - } - - private fun removeNoChildNode(node: BSNode, parent: BSNode?) { - parent?.let { p -> - if (node == p.left) { - p.left = null - } else if (node == p.right) { - p.right = null - } - } ?: rootRemove() - } - - private fun rootRemove() { - root = null - } - - private fun removeTwoChildNode(node: BSNode) { - val leftChild = node.left - if (leftChild != null) { - leftChild.right?.let { - val maxParent = findParentOfMaxChild(leftChild) - maxParent.right?.let { - node.key = it.key - maxParent.right = null - } ?: throw IllegalStateException("Node with max child must have the right child!") - - } ?: run { - node.key = leftChild.key - node.left = leftChild.left - } + if (currentNode.key == node.key) { + return node } + return currentNode } - private fun findParentOfMaxChild(n: BSNode): BSNode { - return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } - ?: throw IllegalArgumentException("Right child must be non-null") + override fun remove(key: K) { + val node = find(key) + root = node?.let { recursiveRemove(root, it) } } - private fun removeSingleChildNode(parent: BSNode, child: BSNode) { - parent.key = child.key - parent.left = child.left - parent.right = child.right + private fun recursiveRemove(currentNode: BSNode?, node: BSNode): BSNode? { + if (currentNode == null) return null + if (currentNode.key < node.key) { + currentNode.right = recursiveRemove(currentNode.right, node) + } else if (currentNode.key > node.key) { + currentNode.left = recursiveRemove(currentNode.left, node) + } else { + if (currentNode.left == null || currentNode.right == null) + return currentNode.left ?: currentNode.right + val minRightSubtree = getMinNode(currentNode.right!!) + currentNode.key = minRightSubtree.key + currentNode.value = minRightSubtree.value + currentNode.right = recursiveRemove(currentNode.right, minRightSubtree) + } + return currentNode } } diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index fbf167e..60fa647 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -4,7 +4,7 @@ import trees.abstract_trees.BalanceTree import trees.nodes.RBNode class RBTree, V> : BalanceTree>() { - override fun add(node: RBNode) { + override fun add(key: K, value: V) { TODO("Not yet implemented") } diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index d72e1eb..6b0a532 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -42,6 +42,6 @@ abstract class BinaryTree, V, NODE_TYPE : Node Date: Tue, 18 Apr 2023 01:48:30 +0300 Subject: [PATCH 40/85] add: tests for binary search tree tests for functions find, remove and add, checker to validate tree --- app/src/main/kotlin/trees/BSTree.kt | 13 ------- app/src/main/kotlin/trees/RBTree.kt | 1 - .../kotlin/trees/abstract_trees/BinaryTree.kt | 2 +- app/src/test/kotlin/trees/BSTreeTest.kt | 34 +++++++++++++++++-- .../test/kotlin/treesValidChecker/checkers.kt | 19 +++++++++++ 5 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 app/src/test/kotlin/treesValidChecker/checkers.kt diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index 826f941..74da37c 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -4,19 +4,6 @@ import trees.abstract_trees.BinaryTree import trees.nodes.BSNode class BSTree, V> : BinaryTree>() { - fun isBstOk(node: BSNode? = root): Boolean { - if (node == null) { - return true - } - // `compare to` returns not null value - if (node.left != null && node.left!!.key > node.key) { - return false - } - if (node.right != null && node.right!!.key < node.key) { - return false - } - return !(!isBstOk(node.right) || !isBstOk(node.left)) - } override fun add(key: K, value: V) { root = recursiveAdd(root, BSNode(key, value)) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 2bf2fe8..e1dc5f0 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -240,7 +240,6 @@ class RBTree, V> : BalanceTree>() { rotateRight(node.parent!!) } } - private fun getSibling(node: RBNode): RBNode? { val parent = node.parent return when (node) { diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index 56cd911..186ec86 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -4,7 +4,7 @@ import trees.nodes.Node import trees.nodes.RBNode abstract class BinaryTree, V, NODE_TYPE : Node> { - protected var root: NODE_TYPE? = null + internal var root: NODE_TYPE? = null fun find(key: K): NODE_TYPE? { var current = root diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt index 64d3ad9..cf2e4c3 100644 --- a/app/src/test/kotlin/trees/BSTreeTest.kt +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -1,16 +1,44 @@ package trees import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isBstOk class BSTreeTest { + private val tree = BSTree() + + @Test + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) + } + + @Test + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) + } @Test - fun add() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isBstOk()) + } } @Test - fun remove() { + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isBstOk()) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt new file mode 100644 index 0000000..e2c33f3 --- /dev/null +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -0,0 +1,19 @@ +package treesValidChecker + +import trees.BSTree +import trees.nodes.BSNode + +val BStree = BSTree() + +fun isBstOk(node: BSNode? = BStree.root): Boolean { + if (node == null) { + return true + } + if (node.left != null && node.left!!.key > node.key) { + return false + } + if (node.right != null && node.right!!.key < node.key) { + return false + } + return !(!isBstOk(node.right) || !isBstOk(node.left)) +} \ No newline at end of file From 554fda986698d5ae8a3c7b96820d8f2efcde537a Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 18 Apr 2023 02:13:58 +0300 Subject: [PATCH 41/85] feat: find, add, remove tests for AVL --- app/src/main/kotlin/trees/AVLTree.kt | 2 +- app/src/test/kotlin/trees/AVLTreeTest.kt | 32 ++++++++++++++++--- .../test/kotlin/treesValidChecker/checkers.kt | 14 ++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 15b43ea..8910e12 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -74,7 +74,7 @@ class AVLTree, V> : BalanceTree>() { return node } - private fun getHeight(node: AVLNode?): Int { + internal fun getHeight(node: AVLNode?): Int { return node?.height ?: -1 } diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt index d286abc..afed47f 100644 --- a/app/src/test/kotlin/trees/AVLTreeTest.kt +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -1,20 +1,44 @@ package trees import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isAVLOk class AVLTreeTest { + private val tree = AVLTree() + + @Test + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) + } @Test - fun balance() { + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) } @Test - fun add() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isAVLOk()) + } } @Test - fun remove() { + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isAVLOk()) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt index e2c33f3..e58194d 100644 --- a/app/src/test/kotlin/treesValidChecker/checkers.kt +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -1,7 +1,10 @@ package treesValidChecker +import trees.AVLTree import trees.BSTree +import trees.nodes.AVLNode import trees.nodes.BSNode +import kotlin.math.abs val BStree = BSTree() @@ -16,4 +19,15 @@ fun isBstOk(node: BSNode? = BStree.root): Boolean { return false } return !(!isBstOk(node.right) || !isBstOk(node.left)) +} + +val AVLtree = AVLTree() + +fun isAVLOk(node: AVLNode? = AVLtree.root): Boolean { + if (node == null) { + return true + } + val lh = AVLtree.getHeight(node.left) + val rh = AVLtree.getHeight(node.right) + return abs(lh-rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) } \ No newline at end of file From 677d6e45c1e9567bcd21b1299a9b16fed720d404 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Tue, 18 Apr 2023 16:52:35 +0300 Subject: [PATCH 42/85] fix: null pointer error --- app/src/main/kotlin/trees/RBTree.kt | 53 ++++++++++++------- .../kotlin/trees/abstract_trees/BinaryTree.kt | 1 - 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index e1dc5f0..8878b11 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -5,14 +5,18 @@ import trees.nodes.RBNode import trees.nodes.RBNode.Color class RBTree, V> : BalanceTree>() { + override fun add(key: K, value: V) { val node = RBNode(key, value) + if (root == null) { root = node return } + var current = root var parent = current + while (current != null) { parent = current current = when { @@ -21,18 +25,20 @@ class RBTree, V> : BalanceTree>() { else -> throw IllegalArgumentException("Node with key ${node.key} is already in the tree") } } + if (parent!!.key < node.key) { parent.right = node } else { parent.left = node } - node.parent = parent + node.parent = parent balance(node, false) } override fun remove(key: K) { var current = root + while (current != null && current.key != key) { current = if (key < current.key) { current.left @@ -40,8 +46,9 @@ class RBTree, V> : BalanceTree>() { current.right } } + if (current == null) { - throw IllegalStateException("There is no such node with key $key in tree") + throw IllegalStateException("There is no such node with key $key in the tree") } val movedUpNode: RBNode? @@ -53,8 +60,8 @@ class RBTree, V> : BalanceTree>() { } else { val inOrderSuccessor: RBNode = getMinNode(current.right!!) current.key = inOrderSuccessor.key - movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) deletedNodeColor = inOrderSuccessor.color + movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) } if (deletedNodeColor == Color.BLACK) { @@ -93,18 +100,22 @@ class RBTree, V> : BalanceTree>() { uncle.color = Color.BLACK balanceAfterAdd(grandparent) } else if (parent == grandparent.left) { + if (node == parent.right) { rotateLeft(parent) parent = node } + rotateRight(grandparent) parent.color = Color.BLACK grandparent.color = Color.RED } else { + if (node == parent.left) { rotateRight(parent) parent = node } + rotateLeft(grandparent) parent.color = Color.BLACK grandparent.color = Color.RED @@ -119,10 +130,10 @@ class RBTree, V> : BalanceTree>() { var sibling = getSibling(node) if (sibling?.color == Color.RED) { handleRedSibling(node, sibling) - sibling = getSibling(node)!! + sibling = getSibling(node) } - if (isBlack(sibling?.left) && isBlack(sibling?.right)) { + if (isBlackOrNull(sibling?.left) && isBlackOrNull(sibling?.right)) { sibling?.color = Color.RED if (node.parent!!.color == Color.RED) { node.parent!!.color = Color.BLACK @@ -141,7 +152,7 @@ class RBTree, V> : BalanceTree>() { } else if (grandparent?.right == parent) { grandparent.left } else { - throw IllegalStateException("Parent is not a child of its grandparent") + throw IllegalStateException("Node with key ${parent.key} is not a child of its grandparent") } } @@ -153,7 +164,7 @@ class RBTree, V> : BalanceTree>() { } else if (parent.right == oldChild) { parent.right = newChild } else { - throw IllegalStateException("Node is not a child if its parent") + throw IllegalStateException("Node with key ${parent.key} is not a parent if its child") } if (newChild != null) { @@ -165,9 +176,11 @@ class RBTree, V> : BalanceTree>() { val parent: RBNode? = node.parent val leftChild: RBNode? = node.left node.left = leftChild?.right + if (leftChild?.right != null) { leftChild.right!!.parent = node } + leftChild!!.right = node node.parent = leftChild @@ -178,9 +191,11 @@ class RBTree, V> : BalanceTree>() { val parent: RBNode? = node.parent val rightChild: RBNode? = node.right node.right = rightChild?.left + if (rightChild?.left != null) { rightChild.left!!.parent = node } + rightChild!!.left = node node.parent = rightChild @@ -209,7 +224,6 @@ class RBTree, V> : BalanceTree>() { private fun handleRedSibling(node: RBNode, sibling: RBNode) { sibling.color = Color.BLACK node.parent!!.color = Color.RED - if (node == node.parent!!.left) { rotateLeft(node.parent!!) } else { @@ -217,39 +231,42 @@ class RBTree, V> : BalanceTree>() { } } private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { - val nodeIsLeftChild = node == node.parent!!.left - if (nodeIsLeftChild && isBlack(sibling.right)) { + var mutableSibling: RBNode = sibling + val nodeIsLeftChild = (node == node.parent!!.left) + if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + sibling.left!!.color = Color.BLACK sibling.color = Color.RED rotateRight(sibling) - sibling.parent!!.right = node.parent!!.right - } else if (!nodeIsLeftChild && isBlack(sibling.left)) { + mutableSibling = node.parent!!.right!! + } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { sibling.right!!.color = Color.BLACK sibling.color = Color.RED rotateLeft(sibling) - sibling.parent!!.left = node.parent!!.left + mutableSibling = node.parent!!.left!! } - sibling.color = node.parent!!.color + mutableSibling.color = node.parent!!.color node.parent!!.color = Color.BLACK if (nodeIsLeftChild) { - sibling.right!!.color = Color.BLACK + mutableSibling.right!!.color = Color.BLACK rotateLeft(node.parent!!) } else { - sibling.left!!.color = Color.BLACK + mutableSibling.left!!.color = Color.BLACK rotateRight(node.parent!!) } } + private fun getSibling(node: RBNode): RBNode? { val parent = node.parent return when (node) { parent?.left -> parent.right parent?.right -> parent.left - else -> throw IllegalStateException("Parent is not a child of its grandparent") + else -> throw IllegalStateException("Node with key ${node.key} is not a child of its parent") } } - private fun isBlack(node: RBNode?): Boolean { + private fun isBlackOrNull(node: RBNode?): Boolean { return node == null || node.color == Color.BLACK } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index 186ec86..986ba3c 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -1,7 +1,6 @@ package trees.abstract_trees import trees.nodes.Node -import trees.nodes.RBNode abstract class BinaryTree, V, NODE_TYPE : Node> { internal var root: NODE_TYPE? = null From e9e2ef66b9307ef8c13999dc92ff0c121ba67031 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Tue, 18 Apr 2023 16:56:40 +0300 Subject: [PATCH 43/85] fix: correct typo --- app/src/main/kotlin/trees/RBTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 8878b11..983691f 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -152,7 +152,7 @@ class RBTree, V> : BalanceTree>() { } else if (grandparent?.right == parent) { grandparent.left } else { - throw IllegalStateException("Node with key ${parent.key} is not a child of its grandparent") + throw IllegalStateException("Node with key ${parent.key} is not a child of its parent") } } From 616195690984d59cad09e7325ad24f40dd216a0a Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 18 Apr 2023 19:53:28 +0300 Subject: [PATCH 44/85] add: functions to work with flat file database Function to write whole tree in file, insert nodes to tree from file , check if file with DB exist File path is in trees/DB/BST --- .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt new file mode 100644 index 0000000..5ba4571 --- /dev/null +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -0,0 +1,33 @@ +package trees.dataBases.BST + +import trees.BSTree +import trees.nodes.BSNode +import java.io.File + +const val fileName = "./app/src/main/kotlin/trees/dataBases/BST/flatFile" +val file = File(fileName) + +fun checkIfFileExist(file: File): Boolean { + return file.exists() +} + +fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { + val stack = mutableListOf(node?.key) + var stringToWrite: String + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { tree.find(it) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + stringToWrite = "${current?.key}${current?.value} 0 0\n" + file.appendText(stringToWrite) + } +} + +fun insertAllNodesToTree(tree: BSTree, file: File) { + file.forEachLine { + val inputs = it.split(" ").toTypedArray() + tree.add(inputs[0].toInt(),inputs[1]) + } +} \ No newline at end of file From fc60c3d6bdfb73de83916eb16b7c9c267c14bda9 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 18 Apr 2023 19:54:53 +0300 Subject: [PATCH 45/85] feat: find, add, remove tests for RB --- app/src/main/kotlin/trees/RBTree.kt | 3 +- app/src/test/kotlin/trees/RBTreeTest.kt | 39 +++++++++++++++++-- .../test/kotlin/treesValidChecker/checkers.kt | 37 ++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 983691f..f9f8cb6 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -230,6 +230,7 @@ class RBTree, V> : BalanceTree>() { rotateRight(node.parent!!) } } + private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { var mutableSibling: RBNode = sibling val nodeIsLeftChild = (node == node.parent!!.left) @@ -269,4 +270,4 @@ class RBTree, V> : BalanceTree>() { private fun isBlackOrNull(node: RBNode?): Boolean { return node == null || node.color == Color.BLACK } -} +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt index bf61705..b6b578a 100644 --- a/app/src/test/kotlin/trees/RBTreeTest.kt +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -3,18 +3,51 @@ package trees import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isRbOk +import java.lang.IllegalStateException class RBTreeTest { + private val tree = RBTree() @Test - fun add() { + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) } @Test - fun remove() { + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) } @Test - fun balance() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isRbOk()) + } + } + + @Test + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isRbOk()) + } + } + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100,0) + tree.remove(101) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt index e58194d..96199aa 100644 --- a/app/src/test/kotlin/treesValidChecker/checkers.kt +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -2,8 +2,10 @@ package treesValidChecker import trees.AVLTree import trees.BSTree +import trees.RBTree import trees.nodes.AVLNode import trees.nodes.BSNode +import trees.nodes.RBNode import kotlin.math.abs val BStree = BSTree() @@ -30,4 +32,39 @@ fun isAVLOk(node: AVLNode? = AVLtree.root): Boolean { val lh = AVLtree.getHeight(node.left) val rh = AVLtree.getHeight(node.right) return abs(lh-rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) +} + +val RBtree = RBTree() + +fun isRbOk(node: RBNode? = RBtree.root): Boolean { + if (node == null) { + return true + } + val blackLeft = numOfBlack(node.left) + val blackRight = numOfBlack(node.right) + return !(blackLeft != blackRight || blackLeft == -1 || !noTwoRedInARow()) +} +fun numOfBlack(node: RBNode? = RBtree.root): Int { + if (node == null) + return 0 + val blackLeft = numOfBlack(node.left) + val blackRight = numOfBlack(node.right) + if (blackRight != blackLeft || blackLeft == -1) { + return -1 + } + if (node.color == RBNode.Color.BLACK) { + return 1 + blackLeft + } + return blackLeft +} + +fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { + if (node == null) + return true + if (node.color == RBNode.Color.BLACK) { + return (noTwoRedInARow(node.left) && noTwoRedInARow(node.right)) + } + if (row == 1) + return false + return (noTwoRedInARow(node.left,1) && noTwoRedInARow(node.right,1)) } \ No newline at end of file From 5a293d2805fbf8df930f322eda5b31e4e5d77459 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 18 Apr 2023 20:00:28 +0300 Subject: [PATCH 46/85] feat(readme): written current changes --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9ad4b97..44dbd8c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Trees by team 9 ### Implemented: -- Binary search tree -- Abstract class for red-black and AVL tree +- Binary search, red-black, AVL tree +- Tests for all types of implemented trees +- Flat-file database for BST support ### Future: -- AVL and RB tree -- Auto-tests -- Data bases for trees +- Data bases for AVL and RB tree - UI support #### Created by: gladiuswq, raf-nr, vacmannnn From 5020e74524cfaced582348fa762e3f1de19ec99a Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 18 Apr 2023 20:21:33 +0300 Subject: [PATCH 47/85] Implement RB, AVL trees, tests and flat tipe database --- README.md | 9 +- app/src/main/kotlin/trees/AVLTree.kt | 105 ++++++- app/src/main/kotlin/trees/BSTree.kt | 112 ++------ app/src/main/kotlin/trees/RBTree.kt | 267 +++++++++++++++++- .../trees/abstract_trees/BalanceTree.kt | 2 +- .../kotlin/trees/abstract_trees/BinaryTree.kt | 26 +- .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 33 +++ app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- app/src/main/kotlin/trees/nodes/RBNode.kt | 5 +- app/src/test/kotlin/trees/AVLTreeTest.kt | 32 ++- app/src/test/kotlin/trees/BSTreeTest.kt | 34 ++- app/src/test/kotlin/trees/RBTreeTest.kt | 39 ++- .../test/kotlin/treesValidChecker/checkers.kt | 70 +++++ 13 files changed, 613 insertions(+), 123 deletions(-) create mode 100644 app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt create mode 100644 app/src/test/kotlin/treesValidChecker/checkers.kt diff --git a/README.md b/README.md index 9ad4b97..44dbd8c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Trees by team 9 ### Implemented: -- Binary search tree -- Abstract class for red-black and AVL tree +- Binary search, red-black, AVL tree +- Tests for all types of implemented trees +- Flat-file database for BST support ### Future: -- AVL and RB tree -- Auto-tests -- Data bases for trees +- Data bases for AVL and RB tree - UI support #### Created by: gladiuswq, raf-nr, vacmannnn diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index a8235ad..8910e12 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -2,17 +2,108 @@ package trees import trees.abstract_trees.BalanceTree import trees.nodes.AVLNode +import kotlin.math.max -class AVLTree, V> : BalanceTree>() { - override fun balance() { - TODO("Not yet implemented") +class AVLTree, V> : BalanceTree>() { + + override fun add(key: K, value: V) { + root = recursiveAdd(root, AVLNode(key, value)) + updateHeight(root) + } + + private fun recursiveAdd(currentNode: AVLNode?, node: AVLNode): AVLNode { + if (currentNode == null) return node + if (currentNode.key < node.key) { + currentNode.right = recursiveAdd(currentNode.right, node) + } + if (currentNode.key > node.key) { + currentNode.left = recursiveAdd(currentNode.left, node) + } + if (currentNode.key == node.key) { + return node + } + return balance(currentNode) + } + + override fun remove(key: K) { + val node = find(key) + root = node?.let { recursiveRemove(root, it) } + updateHeight(root) + } + + private fun recursiveRemove(currentNode: AVLNode?, node: AVLNode): AVLNode? { + if (currentNode == null) return null + if (currentNode.key < node.key) { + currentNode.right = recursiveRemove(currentNode.right, node) + } else if (currentNode.key > node.key) { + currentNode.left = recursiveRemove(currentNode.left, node) + } else { + if (currentNode.left == null || currentNode.right == null) + return currentNode.left ?: currentNode.right + val minRightSubtree = getMinNode(currentNode.right!!) + currentNode.key = minRightSubtree.key + currentNode.value = minRightSubtree.value + currentNode.right = recursiveRemove(currentNode.right, minRightSubtree) + } + return balance(currentNode) + } + + override fun balance(node: AVLNode, afterRemove: Boolean?): AVLNode { + updateHeight(node) + when (getBalanceFactor(node)) { + -2 -> { + if (getBalanceFactor(node.left) == 1) { + node.left = node.left?.let { rotateLeft(it) } + updateHeight(node.left) + } + val balancedNode = rotateRight(node) + updateHeight(balancedNode) + return balancedNode + } + + 2 -> { + if (getBalanceFactor(node.right) == -1) { + node.right = node.right?.let { rotateRight(it) } + updateHeight(node.right) + } + val balancedNode = rotateLeft(node) + updateHeight(balancedNode) + return balancedNode + } + } + return node + } + + internal fun getHeight(node: AVLNode?): Int { + return node?.height ?: -1 + } + + private fun getBalanceFactor(node: AVLNode?): Int { + if (node == null) return 0 + return getHeight(node.right) - getHeight(node.left) + } + + private fun updateHeight(node: AVLNode?) { + node?.let { it.height = max(getHeight(it.left), getHeight(it.right)) + 1 } } - override fun add(node: AVLNode) { - TODO("Not yet implemented") + private fun rotateRight(node: AVLNode): AVLNode { + val leftChild = + node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val rightGrandChild = leftChild.right + leftChild.right = node + node.left = rightGrandChild + updateHeight(leftChild.right) + return leftChild } - override fun remove(node: AVLNode) { - TODO("Not yet implemented") + private fun rotateLeft(node: AVLNode): AVLNode { + val rightChild = + node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + val leftGrandChild = rightChild.left + rightChild.left = node + node.right = leftGrandChild + updateHeight(rightChild.left) + return rightChild } } diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index b99a81b..74da37c 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -4,98 +4,44 @@ import trees.abstract_trees.BinaryTree import trees.nodes.BSNode class BSTree, V> : BinaryTree>() { - override fun add(node: BSNode) { - recursive_add(root, node) - } - private fun recursive_add(current: BSNode?, node: BSNode): BSNode { - if (root == null) { - root = node - return node - } - if (current == null) { - return node - } - if (current.key < node.key) { - current.right = recursive_add(current.right, node) - } else { - current.left = recursive_add(current.left, node) - } - return current - } - override fun remove(node: BSNode) { - root?.let { recursive_delete(it, node) } - } - - private fun recursive_delete(current: BSNode, node: BSNode) { - when { - node.key > current.key -> scan(node, current.right, current) - node.key < current.key -> scan(node, current.left, current) - else -> removeNode(current, null) - } + override fun add(key: K, value: V) { + root = recursiveAdd(root, BSNode(key, value)) } - private fun scan(node: BSNode, current: BSNode?, parent: BSNode) { - if (current == null) { - println("value ${node.key} not exist in the tree") - return + private fun recursiveAdd(currentNode: BSNode?, node: BSNode): BSNode { + if (currentNode == null) return node + if (currentNode.key < node.key) { + currentNode.right = recursiveAdd(currentNode.right, node) } - when { - node.key > current.key -> scan(node, current.right, current) - node.key < current.key -> scan(node, current.left, current) - else -> removeNode(current, parent) + if (currentNode.key > node.key) { + currentNode.left = recursiveAdd(currentNode.left, node) } - } - - private fun removeNode(node: BSNode, parent: BSNode?) { - node.left?.let { leftChild -> - run { - node.right?.let { - removeTwoChildNode(node) - } ?: removeSingleChildNode(node, leftChild) - } - } ?: run { - node.right?.let { rightChild -> removeSingleChildNode(node, rightChild) } ?: removeNoChildNode(node, parent) - } - } - - private fun removeNoChildNode(node: BSNode, parent: BSNode?) { - parent?.let { p -> - if (node == p.left) { - p.left = null - } else if (node == p.right) { - p.right = null - } - } ?: throw IllegalStateException( - "Can not remove the root node without child nodes" - ) - } - - private fun removeTwoChildNode(node: BSNode) { - val leftChild = node.left - if (leftChild != null) { - leftChild.right?.let { - val maxParent = findParentOfMaxChild(leftChild) - maxParent.right?.let { - node.key = it.key - maxParent.right = null - } ?: throw IllegalStateException("Node with max child must have the right child!") - - } ?: run { - node.key = leftChild.key - node.left = leftChild.left - } + if (currentNode.key == node.key) { + return node } + return currentNode } - private fun findParentOfMaxChild(n: BSNode): BSNode { - return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n } - ?: throw IllegalArgumentException("Right child must be non-null") + override fun remove(key: K) { + val node = find(key) + root = node?.let { recursiveRemove(root, it) } } - private fun removeSingleChildNode(parent: BSNode, child: BSNode) { - parent.key = child.key - parent.left = child.left - parent.right = child.right + private fun recursiveRemove(currentNode: BSNode?, node: BSNode): BSNode? { + if (currentNode == null) return null + if (currentNode.key < node.key) { + currentNode.right = recursiveRemove(currentNode.right, node) + } else if (currentNode.key > node.key) { + currentNode.left = recursiveRemove(currentNode.left, node) + } else { + if (currentNode.left == null || currentNode.right == null) + return currentNode.left ?: currentNode.right + val minRightSubtree = getMinNode(currentNode.right!!) + currentNode.key = minRightSubtree.key + currentNode.value = minRightSubtree.value + currentNode.right = recursiveRemove(currentNode.right, minRightSubtree) + } + return currentNode } } diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 9be98ee..00fb52d 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -2,17 +2,272 @@ package trees import trees.abstract_trees.BalanceTree import trees.nodes.RBNode +import trees.nodes.RBNode.Color class RBTree, V> : BalanceTree>() { - override fun add(node: RBNode) { - TODO("Not yet implemented") + + override fun add(key: K, value: V) { + val node = RBNode(key, value) + + if (root == null) { + root = node + return + } + + var current = root + var parent = current + + while (current != null) { + parent = current + current = when { + current.key < node.key -> current.right + current.key > node.key -> current.left + else -> throw IllegalArgumentException("Node with key ${node.key} is already in the tree") + } + } + + if (parent!!.key < node.key) { + parent.right = node + } else { + parent.left = node + } + + node.parent = parent + balance(node, false) + } + + override fun remove(key: K) { + var current = root + + while (current != null && current.key != key) { + current = if (key < current.key) { + current.left + } else { + current.right + } + } + + if (current == null) { + throw IllegalStateException("There is no such node with key $key in the tree") + } + + val movedUpNode: RBNode? + val deletedNodeColor: Color + + if (current.left == null || current.right == null) { + movedUpNode = removeNodeWithZeroOrOneChild(current) + deletedNodeColor = current.color + } else { + val inOrderSuccessor: RBNode = getMinNode(current.right!!) + current.key = inOrderSuccessor.key + deletedNodeColor = inOrderSuccessor.color + movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) + } + + if (deletedNodeColor == Color.BLACK) { + balance(movedUpNode!!, true) + if (movedUpNode.isTemp) { + replaceParentsChild(movedUpNode.parent, movedUpNode, null) + } + } + } + + override fun balance(node: RBNode, afterRemove: Boolean?): RBNode? { + if (afterRemove!!) { + balanceAfterRemove(node) + } else { + balanceAfterAdd(node) + } + return null + } + + private fun balanceAfterAdd(node: RBNode) { + var parent = node.parent ?: return + if (parent.color == Color.BLACK) { + return + } + + val grandparent = parent.parent + if (grandparent == null) { + parent.color = Color.BLACK + return + } + + val uncle = getUncle(parent) + if (uncle != null && uncle.color == Color.RED) { + parent.color = Color.BLACK + grandparent.color = Color.RED + uncle.color = Color.BLACK + balanceAfterAdd(grandparent) + } else if (parent == grandparent.left) { + + if (node == parent.right) { + rotateLeft(parent) + parent = node + } + + rotateRight(grandparent) + parent.color = Color.BLACK + grandparent.color = Color.RED + } else { + + if (node == parent.left) { + rotateRight(parent) + parent = node + } + + rotateLeft(grandparent) + parent.color = Color.BLACK + grandparent.color = Color.RED + } + } + + private fun balanceAfterRemove(node: RBNode) { + if (node == root) { + return + } + + var sibling = getSibling(node) + if (sibling?.color == Color.RED) { + handleRedSibling(node, sibling) + sibling = getSibling(node) + } + + if (isBlackOrNull(sibling?.left) && isBlackOrNull(sibling?.right)) { + sibling?.color = Color.RED + if (node.parent!!.color == Color.RED) { + node.parent!!.color = Color.BLACK + } else { + balanceAfterRemove(node.parent!!) + } + } else { + handleBlackRedSibling(node, sibling!!) + } + } + + private fun getUncle(parent: RBNode): RBNode? { + val grandparent = parent.parent + return if (grandparent?.left == parent) { + grandparent.right + } else if (grandparent?.right == parent) { + grandparent.left + } else { + throw IllegalStateException("Node with key ${parent.key} is not a child of its parent") + } + } + + private fun replaceParentsChild(parent: RBNode?, oldChild: RBNode?, newChild: RBNode?) { + if (parent == null) { + root = newChild + } else if (parent.left == oldChild) { + parent.left = newChild + } else if (parent.right == oldChild) { + parent.right = newChild + } else { + throw IllegalStateException("Node with key ${parent.key} is not a parent if its child") + } + + if (newChild != null) { + newChild.parent = parent + } + } + + private fun rotateRight(node: RBNode) { + val parent: RBNode? = node.parent + val leftChild: RBNode? = node.left + node.left = leftChild?.right + + if (leftChild?.right != null) { + leftChild.right!!.parent = node + } + + leftChild!!.right = node + node.parent = leftChild + + replaceParentsChild(parent, node, leftChild) + } + + private fun rotateLeft(node: RBNode) { + val parent: RBNode? = node.parent + val rightChild: RBNode? = node.right + node.right = rightChild?.left + + if (rightChild?.left != null) { + rightChild.left!!.parent = node + } + + rightChild!!.left = node + node.parent = rightChild + + replaceParentsChild(parent, node, rightChild) + } + + + private fun removeNodeWithZeroOrOneChild(node: RBNode): RBNode? { + return if (node.left != null) { + replaceParentsChild(node.parent, node, node.left) + node.left + } else if (node.right != null) { + replaceParentsChild(node.parent, node, node.right) + node.right + } else { + var newChild: RBNode? = node + newChild!!.isTemp = true + if (node.color == Color.RED) { + newChild = null + } + replaceParentsChild(node.parent, node, newChild) + newChild + } + } + + private fun handleRedSibling(node: RBNode, sibling: RBNode) { + sibling.color = Color.BLACK + node.parent!!.color = Color.RED + if (node == node.parent!!.left) { + rotateLeft(node.parent!!) + } else { + rotateRight(node.parent!!) + } + } + + private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { + var mutableSibling: RBNode = sibling + val nodeIsLeftChild = (node == node.parent!!.left) + if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + + sibling.left!!.color = Color.BLACK + sibling.color = Color.RED + rotateRight(sibling) + mutableSibling = node.parent!!.right!! + } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { + sibling.right!!.color = Color.BLACK + sibling.color = Color.RED + rotateLeft(sibling) + mutableSibling = node.parent!!.left!! + } + + mutableSibling.color = node.parent!!.color + node.parent!!.color = Color.BLACK + if (nodeIsLeftChild) { + mutableSibling.right!!.color = Color.BLACK + rotateLeft(node.parent!!) + } else { + mutableSibling.left!!.color = Color.BLACK + rotateRight(node.parent!!) + } } - override fun remove(node: RBNode) { - TODO("Not yet implemented") + private fun getSibling(node: RBNode): RBNode? { + val parent = node.parent + return when (node) { + parent?.left -> parent.right + parent?.right -> parent.left + else -> throw IllegalStateException("Node with key ${node.key} is not a child of its parent") + } } - override fun balance() { - TODO("Not yet implemented") + private fun isBlackOrNull(node: RBNode?): Boolean { + return node == null || node.color == Color.BLACK } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index b581c34..92d3eb8 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance() + abstract fun balance(node: NODE_TYPE, afterRemove: Boolean? = null): NODE_TYPE? } diff --git a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt index ce4dced..986ba3c 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BinaryTree.kt @@ -2,16 +2,16 @@ package trees.abstract_trees import trees.nodes.Node -abstract class BinaryTree, V, NODE_TYPE: Node> { - protected var root: NODE_TYPE? = null +abstract class BinaryTree, V, NODE_TYPE : Node> { + internal var root: NODE_TYPE? = null - fun find(node: NODE_TYPE): NODE_TYPE? { + fun find(key: K): NODE_TYPE? { var current = root - while (current != null && node != current) { + while (current != null && key != current.key) { current = when { - current.key < node.key -> current.right - current.key > node.key -> current.left - else -> node + current.key < key -> current.right + current.key > key -> current.left + else -> current } } return current @@ -34,6 +34,14 @@ abstract class BinaryTree, V, NODE_TYPE: Node> recursivePrint("", root, false) } - abstract fun add(node: NODE_TYPE) - abstract fun remove(node: NODE_TYPE) + protected fun getMinNode(node: NODE_TYPE): NODE_TYPE { + var minimal = node + while (true) { + minimal = minimal.left ?: break + } + return minimal + } + + abstract fun add(key: K, value: V) + abstract fun remove(key: K) } diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt new file mode 100644 index 0000000..5ba4571 --- /dev/null +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -0,0 +1,33 @@ +package trees.dataBases.BST + +import trees.BSTree +import trees.nodes.BSNode +import java.io.File + +const val fileName = "./app/src/main/kotlin/trees/dataBases/BST/flatFile" +val file = File(fileName) + +fun checkIfFileExist(file: File): Boolean { + return file.exists() +} + +fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { + val stack = mutableListOf(node?.key) + var stringToWrite: String + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { tree.find(it) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + stringToWrite = "${current?.key}${current?.value} 0 0\n" + file.appendText(stringToWrite) + } +} + +fun insertAllNodesToTree(tree: BSTree, file: File) { + file.forEachLine { + val inputs = it.split(" ").toTypedArray() + tree.add(inputs[0].toInt(),inputs[1]) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index 338895d..05b9864 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes class AVLNode, V>(key: K, value: V) : Node>(key, value) { - var height: Int? = null + var height: Int? = 0 } diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index c205ca1..6f9dbc5 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,8 +1,11 @@ package trees.nodes + class RBNode, V>(key: K, value: V) : Node>(key, value) { - var color = Color.RED + var parent: RBNode? = null + var color: Color = Color.RED enum class Color { RED, BLACK } + var isTemp: Boolean = false } diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt index d286abc..afed47f 100644 --- a/app/src/test/kotlin/trees/AVLTreeTest.kt +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -1,20 +1,44 @@ package trees import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isAVLOk class AVLTreeTest { + private val tree = AVLTree() + + @Test + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) + } @Test - fun balance() { + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) } @Test - fun add() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isAVLOk()) + } } @Test - fun remove() { + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isAVLOk()) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt index 64d3ad9..cf2e4c3 100644 --- a/app/src/test/kotlin/trees/BSTreeTest.kt +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -1,16 +1,44 @@ package trees import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isBstOk class BSTreeTest { + private val tree = BSTree() + + @Test + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) + } + + @Test + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) + } @Test - fun add() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isBstOk()) + } } @Test - fun remove() { + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isBstOk()) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt index bf61705..b6b578a 100644 --- a/app/src/test/kotlin/trees/RBTreeTest.kt +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -3,18 +3,51 @@ package trees import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import treesValidChecker.isRbOk +import java.lang.IllegalStateException class RBTreeTest { + private val tree = RBTree() @Test - fun add() { + fun `find existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNotNull(tree.find(408)) } @Test - fun remove() { + fun `find not existing node`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + assertNull(tree.find(10001)) } @Test - fun balance() { + fun `add 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + assertTrue(isRbOk()) + } + } + + @Test + fun `remove 10000 elements`() { + for (i in (0..10000).shuffled()) { + tree.add(i, 0) + } + for (i in (0..10000).shuffled()) { + tree.remove(i) + assertTrue(isRbOk()) + } + } + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100,0) + tree.remove(101) + } } } \ No newline at end of file diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt new file mode 100644 index 0000000..96199aa --- /dev/null +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -0,0 +1,70 @@ +package treesValidChecker + +import trees.AVLTree +import trees.BSTree +import trees.RBTree +import trees.nodes.AVLNode +import trees.nodes.BSNode +import trees.nodes.RBNode +import kotlin.math.abs + +val BStree = BSTree() + +fun isBstOk(node: BSNode? = BStree.root): Boolean { + if (node == null) { + return true + } + if (node.left != null && node.left!!.key > node.key) { + return false + } + if (node.right != null && node.right!!.key < node.key) { + return false + } + return !(!isBstOk(node.right) || !isBstOk(node.left)) +} + +val AVLtree = AVLTree() + +fun isAVLOk(node: AVLNode? = AVLtree.root): Boolean { + if (node == null) { + return true + } + val lh = AVLtree.getHeight(node.left) + val rh = AVLtree.getHeight(node.right) + return abs(lh-rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) +} + +val RBtree = RBTree() + +fun isRbOk(node: RBNode? = RBtree.root): Boolean { + if (node == null) { + return true + } + val blackLeft = numOfBlack(node.left) + val blackRight = numOfBlack(node.right) + return !(blackLeft != blackRight || blackLeft == -1 || !noTwoRedInARow()) +} +fun numOfBlack(node: RBNode? = RBtree.root): Int { + if (node == null) + return 0 + val blackLeft = numOfBlack(node.left) + val blackRight = numOfBlack(node.right) + if (blackRight != blackLeft || blackLeft == -1) { + return -1 + } + if (node.color == RBNode.Color.BLACK) { + return 1 + blackLeft + } + return blackLeft +} + +fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { + if (node == null) + return true + if (node.color == RBNode.Color.BLACK) { + return (noTwoRedInARow(node.left) && noTwoRedInARow(node.right)) + } + if (row == 1) + return false + return (noTwoRedInARow(node.left,1) && noTwoRedInARow(node.right,1)) +} \ No newline at end of file From dd75fb3ab161d123d31b6509b762da1ff40d278b Mon Sep 17 00:00:00 2001 From: Maxim Chizhov <109301708+gladiuswq@users.noreply.github.com> Date: Wed, 19 Apr 2023 01:51:37 +0300 Subject: [PATCH 48/85] fix: simplify code and improve safety --- app/src/main/kotlin/trees/AVLTree.kt | 2 +- app/src/main/kotlin/trees/RBTree.kt | 125 +++++++++++------- .../trees/abstract_trees/BalanceTree.kt | 2 +- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 8910e12..b392f12 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -48,7 +48,7 @@ class AVLTree, V> : BalanceTree>() { return balance(currentNode) } - override fun balance(node: AVLNode, afterRemove: Boolean?): AVLNode { + override fun balance(node: AVLNode, afterRemove: Boolean): AVLNode { updateHeight(node) when (getBalanceFactor(node)) { -2 -> { diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 00fb52d..81cf8d9 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -26,7 +26,11 @@ class RBTree, V> : BalanceTree>() { } } - if (parent!!.key < node.key) { + if (parent == null) { + throw IllegalStateException("Only root parent can be null") + } + + if (parent.key < node.key) { parent.right = node } else { parent.left = node @@ -54,26 +58,32 @@ class RBTree, V> : BalanceTree>() { val movedUpNode: RBNode? val deletedNodeColor: Color - if (current.left == null || current.right == null) { + val leftSibling = current.left + val rightSibling = current.right + + if (leftSibling == null || rightSibling == null) { movedUpNode = removeNodeWithZeroOrOneChild(current) deletedNodeColor = current.color } else { - val inOrderSuccessor: RBNode = getMinNode(current.right!!) + val inOrderSuccessor = getMinNode(rightSibling) current.key = inOrderSuccessor.key deletedNodeColor = inOrderSuccessor.color movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) } if (deletedNodeColor == Color.BLACK) { - balance(movedUpNode!!, true) + if (movedUpNode == null) { + throw IllegalStateException("Incorrect tree structure") + } + balance(movedUpNode, true) if (movedUpNode.isTemp) { replaceParentsChild(movedUpNode.parent, movedUpNode, null) } } } - override fun balance(node: RBNode, afterRemove: Boolean?): RBNode? { - if (afterRemove!!) { + override fun balance(node: RBNode, afterRemove: Boolean): RBNode? { + if (afterRemove) { balanceAfterRemove(node) } else { balanceAfterAdd(node) @@ -82,8 +92,8 @@ class RBTree, V> : BalanceTree>() { } private fun balanceAfterAdd(node: RBNode) { - var parent = node.parent ?: return - if (parent.color == Color.BLACK) { + var parent = node.parent + if (parent == null || parent.color == Color.BLACK) { return } @@ -127,21 +137,26 @@ class RBTree, V> : BalanceTree>() { return } - var sibling = getSibling(node) - if (sibling?.color == Color.RED) { + val parent = node.parent + var sibling = getSibling(node) ?: throw IllegalStateException("Can't balance node with null sibling") + + if (sibling.color == Color.RED) { handleRedSibling(node, sibling) - sibling = getSibling(node) + sibling = getSibling(node) ?: throw IllegalStateException("Incorrect node relations") } - if (isBlackOrNull(sibling?.left) && isBlackOrNull(sibling?.right)) { - sibling?.color = Color.RED - if (node.parent!!.color == Color.RED) { - node.parent!!.color = Color.BLACK + if (isBlackOrNull(sibling.left) && isBlackOrNull(sibling.right)) { + sibling.color = Color.RED + if (parent == null) { + throw IllegalStateException("Only root can have null parent") + } + if (parent.color == Color.RED) { + parent.color = Color.BLACK } else { - balanceAfterRemove(node.parent!!) + balanceAfterRemove(parent) } } else { - handleBlackRedSibling(node, sibling!!) + handleBlackRedSibling(node, sibling) } } @@ -173,36 +188,39 @@ class RBTree, V> : BalanceTree>() { } private fun rotateRight(node: RBNode) { - val parent: RBNode? = node.parent - val leftChild: RBNode? = node.left - node.left = leftChild?.right + val parent = node.parent + val leftChild = node.left ?: throw IllegalStateException("Left child of node with key ${node.key} can't be null") + val cousin = leftChild.right + + node.left = cousin - if (leftChild?.right != null) { - leftChild.right!!.parent = node + if (cousin != null) { + cousin.parent = node } - leftChild!!.right = node + leftChild.right = node node.parent = leftChild replaceParentsChild(parent, node, leftChild) } private fun rotateLeft(node: RBNode) { - val parent: RBNode? = node.parent - val rightChild: RBNode? = node.right - node.right = rightChild?.left + val parent = node.parent + val rightChild = node.right ?: throw IllegalStateException("Right child of node with key ${node.key} can't be null") + val cousin = rightChild.left + + node.right = cousin - if (rightChild?.left != null) { - rightChild.left!!.parent = node + if (cousin != null) { + cousin.parent = node } - rightChild!!.left = node + rightChild.left = node node.parent = rightChild replaceParentsChild(parent, node, rightChild) } - private fun removeNodeWithZeroOrOneChild(node: RBNode): RBNode? { return if (node.left != null) { replaceParentsChild(node.parent, node, node.left) @@ -211,8 +229,8 @@ class RBTree, V> : BalanceTree>() { replaceParentsChild(node.parent, node, node.right) node.right } else { + node.isTemp = true var newChild: RBNode? = node - newChild!!.isTemp = true if (node.color == Color.RED) { newChild = null } @@ -222,39 +240,48 @@ class RBTree, V> : BalanceTree>() { } private fun handleRedSibling(node: RBNode, sibling: RBNode) { + val parent = node.parent ?: throw IllegalStateException("Can't handle node with null parent") sibling.color = Color.BLACK - node.parent!!.color = Color.RED - if (node == node.parent!!.left) { - rotateLeft(node.parent!!) + parent.color = Color.RED + if (node == parent.left) { + rotateLeft(parent) } else { - rotateRight(node.parent!!) + rotateRight(parent) } } - private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { - var mutableSibling: RBNode = sibling - val nodeIsLeftChild = (node == node.parent!!.left) - if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + private fun handleBlackRedSibling(node: RBNode?, sibling: RBNode?) { + val parent = node?.parent + if (node == null || sibling == null || parent == null) { + throw IllegalStateException("Can't handle null nodes") + } - sibling.left!!.color = Color.BLACK + var mutableSibling = sibling + val nodeIsLeftChild = (node == parent.left) + if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + val tempNode = sibling.left ?: throw IllegalStateException("Left child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateRight(sibling) - mutableSibling = node.parent!!.right!! + mutableSibling = parent.right ?: throw IllegalStateException("Right child of node with key ${parent.key} can't be null") } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { - sibling.right!!.color = Color.BLACK + val tempNode = sibling.right ?: throw IllegalStateException("Right child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateLeft(sibling) - mutableSibling = node.parent!!.left!! + mutableSibling = parent.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") } - mutableSibling.color = node.parent!!.color - node.parent!!.color = Color.BLACK + mutableSibling.color = parent.color + parent.color = Color.BLACK if (nodeIsLeftChild) { - mutableSibling.right!!.color = Color.BLACK - rotateLeft(node.parent!!) + val tempNode = mutableSibling.right ?: throw IllegalStateException("Right child of node with key ${mutableSibling.key} can't be null") + tempNode.color = Color.BLACK + rotateLeft(parent) } else { - mutableSibling.left!!.color = Color.BLACK - rotateRight(node.parent!!) + val tempNode = mutableSibling.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") + tempNode.color = Color.BLACK + rotateRight(parent) } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index 92d3eb8..c3795db 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance(node: NODE_TYPE, afterRemove: Boolean? = null): NODE_TYPE? + abstract fun balance(node: NODE_TYPE, afterRemove: Boolean = false): NODE_TYPE? } From bd1eba42872cc35be7ebddde731afe4ba0f4e8a6 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Wed, 19 Apr 2023 03:13:25 +0300 Subject: [PATCH 49/85] add: test with removing a non-existent node --- app/src/test/kotlin/trees/AVLTreeTest.kt | 9 +++++++++ app/src/test/kotlin/trees/BSTreeTest.kt | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt index afed47f..22f0797 100644 --- a/app/src/test/kotlin/trees/AVLTreeTest.kt +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -3,6 +3,7 @@ package trees import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import treesValidChecker.isAVLOk +import java.lang.IllegalStateException class AVLTreeTest { private val tree = AVLTree() @@ -41,4 +42,12 @@ class AVLTreeTest { assertTrue(isAVLOk()) } } + + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100,0) + tree.remove(101) + } + } } \ No newline at end of file diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt index cf2e4c3..a39a9ca 100644 --- a/app/src/test/kotlin/trees/BSTreeTest.kt +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -3,6 +3,7 @@ package trees import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import treesValidChecker.isBstOk +import java.lang.IllegalStateException class BSTreeTest { private val tree = BSTree() @@ -41,4 +42,12 @@ class BSTreeTest { assertTrue(isBstOk()) } } + + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100,0) + tree.remove(101) + } + } } \ No newline at end of file From 1fcea32814fd19b519c21d18bf6ffb47b70c23a1 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Wed, 19 Apr 2023 03:39:41 +0300 Subject: [PATCH 50/85] fix: remove in BST and AVL throw exception if node not exist --- app/src/main/kotlin/trees/AVLTree.kt | 4 ++-- app/src/main/kotlin/trees/BSTree.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 8910e12..1eba09d 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -26,8 +26,8 @@ class AVLTree, V> : BalanceTree>() { } override fun remove(key: K) { - val node = find(key) - root = node?.let { recursiveRemove(root, it) } + val node = find(key) ?: throw IllegalStateException("There is no such node with key $key in the tree") + root = recursiveRemove(root, node) updateHeight(root) } diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index 74da37c..41a4284 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -24,8 +24,8 @@ class BSTree, V> : BinaryTree>() { } override fun remove(key: K) { - val node = find(key) - root = node?.let { recursiveRemove(root, it) } + val node = find(key) ?: throw IllegalStateException("There is no such node with key $key in the tree") + root = recursiveRemove(root, node) } private fun recursiveRemove(currentNode: BSNode?, node: BSNode): BSNode? { From b8d62809470f5f87d3eef257644737c46108baff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=84=D0=B8=D0=BA=20=D0=9D=D1=83=D1=80=D0=BC?= =?UTF-8?q?=D1=83=D1=85=D0=B0=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= <113212617+raf-nr@users.noreply.github.com> Date: Wed, 19 Apr 2023 03:53:19 +0300 Subject: [PATCH 51/85] add: functions for saving the AVL-tree in the database --- app/build.gradle.kts | 6 ++ .../kotlin/trees/dataBases/AVL/SQLiteDB.kt | 93 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index aab3c0d..33e103d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,12 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + + implementation("org.xerial:sqlite-jdbc:3.14.2") + + // Logging + implementation("io.github.microutils:kotlin-logging-jvm:2.0.6") + implementation("org.slf4j:slf4j-simple:1.7.29") } application { diff --git a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt new file mode 100644 index 0000000..93f7592 --- /dev/null +++ b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt @@ -0,0 +1,93 @@ +package main.kotlin.trees.dataBases.AVL + +import mu.KotlinLogging +import java.io.Closeable +import java.sql.* +import trees.nodes.AVLNode +import trees.AVLTree + +private val logger = KotlinLogging.logger { } +private const val DB_DRIVER = "jdbc:sqlite" + +class SQLiteDB(private val path: String): Closeable { + private val connection = DriverManager.getConnection("$DB_DRIVER:$path") + ?: throw SQLException("Cannot connect to database") + private val addNodeStatement by lazy { connection.prepareStatement("INSERT INTO nodes (key, value, x, y) VALUES (?, ?, ?, ?);") } + + init { + logger.info { "Connected to database: $path" } + } + + fun open() { + connection.createStatement().also {statement -> + try { + statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text, x DOUBLE NOT NULL, y DOUBLE NOT NULL);") + logger.info { "Tables created or already exists" } + } catch (problem: Exception) { + println(problem) + logger.error(problem) {"Cannot create table in database"} + } finally { + statement.close() + } + } + } + + fun addNode(node: AVLNode) { + try { + addNodeStatement.setInt(1, node.key) + addNodeStatement.setString(2, node.value) + addNodeStatement.setDouble(3, 0.0) + addNodeStatement.setDouble(4, 0.0) + addNodeStatement.execute() + } catch (problem: Exception) { + logger.error(problem) { "Cannot add node: ${node.key}" } + } + } + + fun writeAllNodesToDB(node: AVLNode?, tree: AVLTree) { + val stack = mutableListOf(node?.key) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { tree.find(it) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + current?.let{addNode((it))} + } + } + + fun selectNodes(): MutableList> { + val nodesList = mutableListOf>() + try { + val query = "SELECT key, value, x, y FROM nodes" + val rs = connection.createStatement().executeQuery(query) + while (rs.next()) { + val key = rs.getInt("key") + val value = rs.getString("value") + nodesList.add(AVLNode(key, value)) + } + } catch (problem: Exception) { + logger.error(problem) {"Failed to select"} + } + return nodesList + } + + fun delete() { + connection.createStatement().also { statement -> + try { + statement.execute("DROP TABLE IF EXISTS nodes") + logger.info { "Table deleted" } + } catch (problem: Exception) { + println(problem) + logger.error(problem) { "Cannot delete table in database" } + } + } + } + + override fun close() { + addNodeStatement.close() + connection.close() + logger.info { "Connection closed" } + } + +} \ No newline at end of file From b54c9cd90d0ed8006458ec2e3ace55fd18fac5ca Mon Sep 17 00:00:00 2001 From: Maxim Chizhov <109301708+gladiuswq@users.noreply.github.com> Date: Wed, 19 Apr 2023 03:54:17 +0300 Subject: [PATCH 52/85] fix: improve code safety and readability --- app/src/main/kotlin/trees/AVLTree.kt | 2 +- app/src/main/kotlin/trees/RBTree.kt | 125 +++++++++++------- .../trees/abstract_trees/BalanceTree.kt | 2 +- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 1eba09d..a012fd7 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -48,7 +48,7 @@ class AVLTree, V> : BalanceTree>() { return balance(currentNode) } - override fun balance(node: AVLNode, afterRemove: Boolean?): AVLNode { + override fun balance(node: AVLNode, afterRemove: Boolean): AVLNode { updateHeight(node) when (getBalanceFactor(node)) { -2 -> { diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 00fb52d..81cf8d9 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -26,7 +26,11 @@ class RBTree, V> : BalanceTree>() { } } - if (parent!!.key < node.key) { + if (parent == null) { + throw IllegalStateException("Only root parent can be null") + } + + if (parent.key < node.key) { parent.right = node } else { parent.left = node @@ -54,26 +58,32 @@ class RBTree, V> : BalanceTree>() { val movedUpNode: RBNode? val deletedNodeColor: Color - if (current.left == null || current.right == null) { + val leftSibling = current.left + val rightSibling = current.right + + if (leftSibling == null || rightSibling == null) { movedUpNode = removeNodeWithZeroOrOneChild(current) deletedNodeColor = current.color } else { - val inOrderSuccessor: RBNode = getMinNode(current.right!!) + val inOrderSuccessor = getMinNode(rightSibling) current.key = inOrderSuccessor.key deletedNodeColor = inOrderSuccessor.color movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) } if (deletedNodeColor == Color.BLACK) { - balance(movedUpNode!!, true) + if (movedUpNode == null) { + throw IllegalStateException("Incorrect tree structure") + } + balance(movedUpNode, true) if (movedUpNode.isTemp) { replaceParentsChild(movedUpNode.parent, movedUpNode, null) } } } - override fun balance(node: RBNode, afterRemove: Boolean?): RBNode? { - if (afterRemove!!) { + override fun balance(node: RBNode, afterRemove: Boolean): RBNode? { + if (afterRemove) { balanceAfterRemove(node) } else { balanceAfterAdd(node) @@ -82,8 +92,8 @@ class RBTree, V> : BalanceTree>() { } private fun balanceAfterAdd(node: RBNode) { - var parent = node.parent ?: return - if (parent.color == Color.BLACK) { + var parent = node.parent + if (parent == null || parent.color == Color.BLACK) { return } @@ -127,21 +137,26 @@ class RBTree, V> : BalanceTree>() { return } - var sibling = getSibling(node) - if (sibling?.color == Color.RED) { + val parent = node.parent + var sibling = getSibling(node) ?: throw IllegalStateException("Can't balance node with null sibling") + + if (sibling.color == Color.RED) { handleRedSibling(node, sibling) - sibling = getSibling(node) + sibling = getSibling(node) ?: throw IllegalStateException("Incorrect node relations") } - if (isBlackOrNull(sibling?.left) && isBlackOrNull(sibling?.right)) { - sibling?.color = Color.RED - if (node.parent!!.color == Color.RED) { - node.parent!!.color = Color.BLACK + if (isBlackOrNull(sibling.left) && isBlackOrNull(sibling.right)) { + sibling.color = Color.RED + if (parent == null) { + throw IllegalStateException("Only root can have null parent") + } + if (parent.color == Color.RED) { + parent.color = Color.BLACK } else { - balanceAfterRemove(node.parent!!) + balanceAfterRemove(parent) } } else { - handleBlackRedSibling(node, sibling!!) + handleBlackRedSibling(node, sibling) } } @@ -173,36 +188,39 @@ class RBTree, V> : BalanceTree>() { } private fun rotateRight(node: RBNode) { - val parent: RBNode? = node.parent - val leftChild: RBNode? = node.left - node.left = leftChild?.right + val parent = node.parent + val leftChild = node.left ?: throw IllegalStateException("Left child of node with key ${node.key} can't be null") + val cousin = leftChild.right + + node.left = cousin - if (leftChild?.right != null) { - leftChild.right!!.parent = node + if (cousin != null) { + cousin.parent = node } - leftChild!!.right = node + leftChild.right = node node.parent = leftChild replaceParentsChild(parent, node, leftChild) } private fun rotateLeft(node: RBNode) { - val parent: RBNode? = node.parent - val rightChild: RBNode? = node.right - node.right = rightChild?.left + val parent = node.parent + val rightChild = node.right ?: throw IllegalStateException("Right child of node with key ${node.key} can't be null") + val cousin = rightChild.left + + node.right = cousin - if (rightChild?.left != null) { - rightChild.left!!.parent = node + if (cousin != null) { + cousin.parent = node } - rightChild!!.left = node + rightChild.left = node node.parent = rightChild replaceParentsChild(parent, node, rightChild) } - private fun removeNodeWithZeroOrOneChild(node: RBNode): RBNode? { return if (node.left != null) { replaceParentsChild(node.parent, node, node.left) @@ -211,8 +229,8 @@ class RBTree, V> : BalanceTree>() { replaceParentsChild(node.parent, node, node.right) node.right } else { + node.isTemp = true var newChild: RBNode? = node - newChild!!.isTemp = true if (node.color == Color.RED) { newChild = null } @@ -222,39 +240,48 @@ class RBTree, V> : BalanceTree>() { } private fun handleRedSibling(node: RBNode, sibling: RBNode) { + val parent = node.parent ?: throw IllegalStateException("Can't handle node with null parent") sibling.color = Color.BLACK - node.parent!!.color = Color.RED - if (node == node.parent!!.left) { - rotateLeft(node.parent!!) + parent.color = Color.RED + if (node == parent.left) { + rotateLeft(parent) } else { - rotateRight(node.parent!!) + rotateRight(parent) } } - private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { - var mutableSibling: RBNode = sibling - val nodeIsLeftChild = (node == node.parent!!.left) - if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + private fun handleBlackRedSibling(node: RBNode?, sibling: RBNode?) { + val parent = node?.parent + if (node == null || sibling == null || parent == null) { + throw IllegalStateException("Can't handle null nodes") + } - sibling.left!!.color = Color.BLACK + var mutableSibling = sibling + val nodeIsLeftChild = (node == parent.left) + if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + val tempNode = sibling.left ?: throw IllegalStateException("Left child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateRight(sibling) - mutableSibling = node.parent!!.right!! + mutableSibling = parent.right ?: throw IllegalStateException("Right child of node with key ${parent.key} can't be null") } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { - sibling.right!!.color = Color.BLACK + val tempNode = sibling.right ?: throw IllegalStateException("Right child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateLeft(sibling) - mutableSibling = node.parent!!.left!! + mutableSibling = parent.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") } - mutableSibling.color = node.parent!!.color - node.parent!!.color = Color.BLACK + mutableSibling.color = parent.color + parent.color = Color.BLACK if (nodeIsLeftChild) { - mutableSibling.right!!.color = Color.BLACK - rotateLeft(node.parent!!) + val tempNode = mutableSibling.right ?: throw IllegalStateException("Right child of node with key ${mutableSibling.key} can't be null") + tempNode.color = Color.BLACK + rotateLeft(parent) } else { - mutableSibling.left!!.color = Color.BLACK - rotateRight(node.parent!!) + val tempNode = mutableSibling.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") + tempNode.color = Color.BLACK + rotateRight(parent) } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index 92d3eb8..c3795db 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance(node: NODE_TYPE, afterRemove: Boolean? = null): NODE_TYPE? + abstract fun balance(node: NODE_TYPE, afterRemove: Boolean = false): NODE_TYPE? } From e83df4b27ae30d7e552eee9d7f2e3ba26842b52e Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Wed, 19 Apr 2023 05:54:54 +0300 Subject: [PATCH 53/85] fix: make proper formatting --- app/src/main/kotlin/trees/AVLTree.kt | 4 ++-- app/src/main/kotlin/trees/RBTree.kt | 24 ++++++++++++------- .../kotlin/trees/dataBases/AVL/SQLiteDB.kt | 16 ++++++------- .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 4 ++-- app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- app/src/main/kotlin/trees/nodes/BSNode.kt | 2 +- app/src/main/kotlin/trees/nodes/Node.kt | 3 ++- app/src/main/kotlin/trees/nodes/RBNode.kt | 4 +++- 8 files changed, 35 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index a012fd7..e32e61f 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -89,7 +89,7 @@ class AVLTree, V> : BalanceTree>() { private fun rotateRight(node: AVLNode): AVLNode { val leftChild = - node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right") val rightGrandChild = leftChild.right leftChild.right = node node.left = rightGrandChild @@ -99,7 +99,7 @@ class AVLTree, V> : BalanceTree>() { private fun rotateLeft(node: AVLNode): AVLNode { val rightChild = - node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right") val leftGrandChild = rightChild.left rightChild.left = node node.right = leftGrandChild diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 81cf8d9..2619901 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -189,7 +189,8 @@ class RBTree, V> : BalanceTree>() { private fun rotateRight(node: RBNode) { val parent = node.parent - val leftChild = node.left ?: throw IllegalStateException("Left child of node with key ${node.key} can't be null") + val leftChild = + node.left ?: throw IllegalStateException("Left child of node with key ${node.key} can't be null") val cousin = leftChild.right node.left = cousin @@ -206,7 +207,8 @@ class RBTree, V> : BalanceTree>() { private fun rotateLeft(node: RBNode) { val parent = node.parent - val rightChild = node.right ?: throw IllegalStateException("Right child of node with key ${node.key} can't be null") + val rightChild = + node.right ?: throw IllegalStateException("Right child of node with key ${node.key} can't be null") val cousin = rightChild.left node.right = cousin @@ -259,27 +261,33 @@ class RBTree, V> : BalanceTree>() { var mutableSibling = sibling val nodeIsLeftChild = (node == parent.left) if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { - val tempNode = sibling.left ?: throw IllegalStateException("Left child of node with key ${sibling.key} can't be null") + val tempNode = + sibling.left ?: throw IllegalStateException("Left child of node with key ${sibling.key} can't be null") tempNode.color = Color.BLACK sibling.color = Color.RED rotateRight(sibling) - mutableSibling = parent.right ?: throw IllegalStateException("Right child of node with key ${parent.key} can't be null") + mutableSibling = + parent.right ?: throw IllegalStateException("Right child of node with key ${parent.key} can't be null") } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { - val tempNode = sibling.right ?: throw IllegalStateException("Right child of node with key ${sibling.key} can't be null") + val tempNode = sibling.right + ?: throw IllegalStateException("Right child of node with key ${sibling.key} can't be null") tempNode.color = Color.BLACK sibling.color = Color.RED rotateLeft(sibling) - mutableSibling = parent.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") + mutableSibling = + parent.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") } mutableSibling.color = parent.color parent.color = Color.BLACK if (nodeIsLeftChild) { - val tempNode = mutableSibling.right ?: throw IllegalStateException("Right child of node with key ${mutableSibling.key} can't be null") + val tempNode = mutableSibling.right + ?: throw IllegalStateException("Right child of node with key ${mutableSibling.key} can't be null") tempNode.color = Color.BLACK rotateLeft(parent) } else { - val tempNode = mutableSibling.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") + val tempNode = mutableSibling.left + ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") tempNode.color = Color.BLACK rotateRight(parent) } diff --git a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt index 93f7592..a3d492f 100644 --- a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt +++ b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt @@ -9,7 +9,7 @@ import trees.AVLTree private val logger = KotlinLogging.logger { } private const val DB_DRIVER = "jdbc:sqlite" -class SQLiteDB(private val path: String): Closeable { +class SQLiteDB(private val path: String) : Closeable { private val connection = DriverManager.getConnection("$DB_DRIVER:$path") ?: throw SQLException("Cannot connect to database") private val addNodeStatement by lazy { connection.prepareStatement("INSERT INTO nodes (key, value, x, y) VALUES (?, ?, ?, ?);") } @@ -19,13 +19,13 @@ class SQLiteDB(private val path: String): Closeable { } fun open() { - connection.createStatement().also {statement -> + connection.createStatement().also { statement -> try { - statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text, x DOUBLE NOT NULL, y DOUBLE NOT NULL);") + statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text, x DOUBLE NOT NULL, y DOUBLE NOT NULL);") logger.info { "Tables created or already exists" } } catch (problem: Exception) { println(problem) - logger.error(problem) {"Cannot create table in database"} + logger.error(problem) { "Cannot create table in database" } } finally { statement.close() } @@ -40,7 +40,7 @@ class SQLiteDB(private val path: String): Closeable { addNodeStatement.setDouble(4, 0.0) addNodeStatement.execute() } catch (problem: Exception) { - logger.error(problem) { "Cannot add node: ${node.key}" } + logger.error(problem) { "Cannot add node with key ${node.key}" } } } @@ -52,7 +52,7 @@ class SQLiteDB(private val path: String): Closeable { current.left?.key.apply(stack::add) if (current?.right != null) current.right?.key.apply(stack::add) - current?.let{addNode((it))} + current?.let { addNode((it)) } } } @@ -67,7 +67,7 @@ class SQLiteDB(private val path: String): Closeable { nodesList.add(AVLNode(key, value)) } } catch (problem: Exception) { - logger.error(problem) {"Failed to select"} + logger.error(problem) { "Failed to select" } } return nodesList } @@ -90,4 +90,4 @@ class SQLiteDB(private val path: String): Closeable { logger.info { "Connection closed" } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 5ba4571..4f2dab6 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -28,6 +28,6 @@ fun writeAllNodesToFile(node: BSNode?, tree: BSTree, f fun insertAllNodesToTree(tree: BSTree, file: File) { file.forEachLine { val inputs = it.split(" ").toTypedArray() - tree.add(inputs[0].toInt(),inputs[1]) + tree.add(inputs[0].toInt(), inputs[1]) } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index 05b9864..7f26b41 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes -class AVLNode, V>(key: K, value: V) : Node>(key, value) { +class AVLNode, V>(key: K, value: V) : Node>(key, value) { var height: Int? = 0 } diff --git a/app/src/main/kotlin/trees/nodes/BSNode.kt b/app/src/main/kotlin/trees/nodes/BSNode.kt index e16735c..6543dad 100644 --- a/app/src/main/kotlin/trees/nodes/BSNode.kt +++ b/app/src/main/kotlin/trees/nodes/BSNode.kt @@ -1,3 +1,3 @@ package trees.nodes -class BSNode, V>(key: K, value: V) : Node>(key, value) +class BSNode, V>(key: K, value: V) : Node>(key, value) diff --git a/app/src/main/kotlin/trees/nodes/Node.kt b/app/src/main/kotlin/trees/nodes/Node.kt index dd15928..2ad34c6 100644 --- a/app/src/main/kotlin/trees/nodes/Node.kt +++ b/app/src/main/kotlin/trees/nodes/Node.kt @@ -1,5 +1,6 @@ package trees.nodes -abstract class Node, V, NODE_TYPE: Node>(var key: K, var value: V) { + +abstract class Node, V, NODE_TYPE : Node>(var key: K, var value: V) { var left: NODE_TYPE? = null var right: NODE_TYPE? = null } diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index 6f9dbc5..4d3c275 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,11 +1,13 @@ package trees.nodes -class RBNode, V>(key: K, value: V) : Node>(key, value) { +class RBNode, V>(key: K, value: V) : Node>(key, value) { var parent: RBNode? = null var color: Color = Color.RED + enum class Color { RED, BLACK } + var isTemp: Boolean = false } From 990b27bc0aa0a016a21f97885897cf92956dde29 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:55:05 +0300 Subject: [PATCH 54/85] feat: readme decoration, remove in flatFileDB Removed unnecessary imports, added spaces --- README.md | 62 ++++++++++++++++--- .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 15 +++++ app/src/test/kotlin/trees/AVLTreeTest.kt | 5 +- app/src/test/kotlin/trees/BSTreeTest.kt | 5 +- app/src/test/kotlin/trees/RBTreeTest.kt | 7 +-- .../test/kotlin/treesValidChecker/checkers.kt | 9 +-- 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 44dbd8c..dffaf4b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,62 @@ # Trees by team 9 -### Implemented: -- Binary search, red-black, AVL tree -- Tests for all types of implemented trees -- Flat-file database for BST support +A project created to study the mechanics of AVL, Red-Black and BST trees, unit tests, databases and graphic user interface -### Future: -- Data bases for AVL and RB tree -- UI support +## Navigation + +- [Download and build](#download-and-build) +- [Trees](#trees) + - [Unit tests](#unit-tests) + - [Databases](#databases) + - [Future](#future) +- [Authors](#authors) + +## Download and build: +Possible way to clone project by SSH key in command line: + + git clone git@github.com:spbu-coding-2022/trees-9.git + +You may build it by [gradle](https://gradle.org/): + + ./gradlew build + +*Aware : build method depends on operating system* +## Trees +Implemented 3 basic tree: +- [Binary search](https://en.wikipedia.org/wiki/Binary_search_tree) tree +- [Red-Black](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) tree +- [AVL](https://en.wikipedia.org/wiki/AVL_tree) tree -#### Created by: gladiuswq, raf-nr, vacmannnn +Each tree support add, remove, find and print functions. +Node consist of **comparable** key and value +*Attention: undefined behavior when adding more than 100000 values* +## Unit tests (by [JUnit 5](https://junit.org/junit5/)) +Tests aimed at checking different cases: +- Find existing and not existing nodes +- Add up to 10000 nodes +- Remove existing and not existing nodes + +For all trees, the corresponding validator is implemented + +## Databases +Binary search tree mapped to flat file database, AVL mapped to sqlite + +Capability: +- Insert all nodes to tree +- Write all nodes to DB +- Delete itself + +*It's okey if*: relationships of nodes changed when the application is restarted +### Future: +- Neo4j database to store red-black tree (in progress) +- GUI support +- CONTRIBUTING.md + +## Authors +- gladiuswq - [github](https://github.com/gladiuswq), [contact](https://t.me/gladiuswq) +- raf-nr - [github](https://github.com/raf-nr), [contact](https://t.me/nrrafik) +- vacmannnn - [github](https://github.com/vacmannnn), [contact](https://t.me/vacmannnn) diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 4f2dab6..82a0e26 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -11,6 +11,21 @@ fun checkIfFileExist(file: File): Boolean { return file.exists() } +fun removeTree(file: File): Boolean { + if (checkIfFileExist(file)) { + var result = file.delete() + if (result) { + result = file.createNewFile() + if (result) { + return result + } + throw Exception("Fail with file create") + } + throw Exception("Fail with file remove") + } + throw Exception("File with tree not exist") +} + fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { val stack = mutableListOf(node?.key) var stringToWrite: String diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt index 22f0797..1e2df08 100644 --- a/app/src/test/kotlin/trees/AVLTreeTest.kt +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -1,9 +1,8 @@ package trees -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isAVLOk -import java.lang.IllegalStateException class AVLTreeTest { private val tree = AVLTree() @@ -46,7 +45,7 @@ class AVLTreeTest { @Test fun `remove a node that is not in tree`() { assertThrows(IllegalStateException::class.java) { - tree.add(100,0) + tree.add(100, 0) tree.remove(101) } } diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt index a39a9ca..75ce0ba 100644 --- a/app/src/test/kotlin/trees/BSTreeTest.kt +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -1,9 +1,8 @@ package trees -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isBstOk -import java.lang.IllegalStateException class BSTreeTest { private val tree = BSTree() @@ -46,7 +45,7 @@ class BSTreeTest { @Test fun `remove a node that is not in tree`() { assertThrows(IllegalStateException::class.java) { - tree.add(100,0) + tree.add(100, 0) tree.remove(101) } } diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt index b6b578a..623168d 100644 --- a/app/src/test/kotlin/trees/RBTreeTest.kt +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -1,10 +1,8 @@ package trees -import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isRbOk -import java.lang.IllegalStateException class RBTreeTest { private val tree = RBTree() @@ -43,10 +41,11 @@ class RBTreeTest { assertTrue(isRbOk()) } } + @Test fun `remove a node that is not in tree`() { assertThrows(IllegalStateException::class.java) { - tree.add(100,0) + tree.add(100, 0) tree.remove(101) } } diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt index 96199aa..fe53d1d 100644 --- a/app/src/test/kotlin/treesValidChecker/checkers.kt +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -31,7 +31,7 @@ fun isAVLOk(node: AVLNode? = AVLtree.root): Boolean { } val lh = AVLtree.getHeight(node.left) val rh = AVLtree.getHeight(node.right) - return abs(lh-rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) + return abs(lh - rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) } val RBtree = RBTree() @@ -44,7 +44,8 @@ fun isRbOk(node: RBNode? = RBtree.root): Boolean { val blackRight = numOfBlack(node.right) return !(blackLeft != blackRight || blackLeft == -1 || !noTwoRedInARow()) } -fun numOfBlack(node: RBNode? = RBtree.root): Int { + +fun numOfBlack(node: RBNode? = RBtree.root): Int { if (node == null) return 0 val blackLeft = numOfBlack(node.left) @@ -58,7 +59,7 @@ fun numOfBlack(node: RBNode? = RBtree.root): Int { return blackLeft } -fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { +fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { if (node == null) return true if (node.color == RBNode.Color.BLACK) { @@ -66,5 +67,5 @@ fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean } if (row == 1) return false - return (noTwoRedInARow(node.left,1) && noTwoRedInARow(node.right,1)) + return (noTwoRedInARow(node.left, 1) && noTwoRedInARow(node.right, 1)) } \ No newline at end of file From a82736f84f4c54e631f4f8a6d5108e351c783fa7 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Wed, 19 Apr 2023 09:13:04 +0300 Subject: [PATCH 55/85] Implement sql, git decoration, remove in flatFileDB Code style changes (blank spaces, names), tests for remove --- README.md | 62 ++++++-- app/build.gradle.kts | 6 + app/src/main/kotlin/trees/AVLTree.kt | 11 +- app/src/main/kotlin/trees/BSTree.kt | 4 +- app/src/main/kotlin/trees/RBTree.kt | 135 +++++++++++------- .../trees/abstract_trees/BalanceTree.kt | 2 +- .../kotlin/trees/dataBases/AVL/SQLiteDB.kt | 93 ++++++++++++ .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 19 ++- app/src/main/kotlin/trees/nodes/AVLNode.kt | 2 +- app/src/main/kotlin/trees/nodes/BSNode.kt | 2 +- app/src/main/kotlin/trees/nodes/Node.kt | 3 +- app/src/main/kotlin/trees/nodes/RBNode.kt | 4 +- app/src/test/kotlin/trees/AVLTreeTest.kt | 10 +- app/src/test/kotlin/trees/BSTreeTest.kt | 10 +- app/src/test/kotlin/trees/RBTreeTest.kt | 7 +- .../test/kotlin/treesValidChecker/checkers.kt | 11 +- 16 files changed, 299 insertions(+), 82 deletions(-) create mode 100644 app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt diff --git a/README.md b/README.md index 44dbd8c..dffaf4b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,62 @@ # Trees by team 9 -### Implemented: -- Binary search, red-black, AVL tree -- Tests for all types of implemented trees -- Flat-file database for BST support +A project created to study the mechanics of AVL, Red-Black and BST trees, unit tests, databases and graphic user interface -### Future: -- Data bases for AVL and RB tree -- UI support +## Navigation + +- [Download and build](#download-and-build) +- [Trees](#trees) + - [Unit tests](#unit-tests) + - [Databases](#databases) + - [Future](#future) +- [Authors](#authors) + +## Download and build: +Possible way to clone project by SSH key in command line: + + git clone git@github.com:spbu-coding-2022/trees-9.git + +You may build it by [gradle](https://gradle.org/): + + ./gradlew build + +*Aware : build method depends on operating system* +## Trees +Implemented 3 basic tree: +- [Binary search](https://en.wikipedia.org/wiki/Binary_search_tree) tree +- [Red-Black](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) tree +- [AVL](https://en.wikipedia.org/wiki/AVL_tree) tree -#### Created by: gladiuswq, raf-nr, vacmannnn +Each tree support add, remove, find and print functions. +Node consist of **comparable** key and value +*Attention: undefined behavior when adding more than 100000 values* +## Unit tests (by [JUnit 5](https://junit.org/junit5/)) +Tests aimed at checking different cases: +- Find existing and not existing nodes +- Add up to 10000 nodes +- Remove existing and not existing nodes + +For all trees, the corresponding validator is implemented + +## Databases +Binary search tree mapped to flat file database, AVL mapped to sqlite + +Capability: +- Insert all nodes to tree +- Write all nodes to DB +- Delete itself + +*It's okey if*: relationships of nodes changed when the application is restarted +### Future: +- Neo4j database to store red-black tree (in progress) +- GUI support +- CONTRIBUTING.md + +## Authors +- gladiuswq - [github](https://github.com/gladiuswq), [contact](https://t.me/gladiuswq) +- raf-nr - [github](https://github.com/raf-nr), [contact](https://t.me/nrrafik) +- vacmannnn - [github](https://github.com/vacmannnn), [contact](https://t.me/vacmannnn) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index aab3c0d..33e103d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,12 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + + implementation("org.xerial:sqlite-jdbc:3.14.2") + + // Logging + implementation("io.github.microutils:kotlin-logging-jvm:2.0.6") + implementation("org.slf4j:slf4j-simple:1.7.29") } application { diff --git a/app/src/main/kotlin/trees/AVLTree.kt b/app/src/main/kotlin/trees/AVLTree.kt index 8910e12..aa52937 100644 --- a/app/src/main/kotlin/trees/AVLTree.kt +++ b/app/src/main/kotlin/trees/AVLTree.kt @@ -26,8 +26,8 @@ class AVLTree, V> : BalanceTree>() { } override fun remove(key: K) { - val node = find(key) - root = node?.let { recursiveRemove(root, it) } + val node = find(key) ?: throw IllegalStateException("There is no such node with key $key in the tree") + root = recursiveRemove(root, node) updateHeight(root) } @@ -48,7 +48,8 @@ class AVLTree, V> : BalanceTree>() { return balance(currentNode) } - override fun balance(node: AVLNode, afterRemove: Boolean?): AVLNode { + + override fun balance(node: AVLNode, afterRemove: Boolean): AVLNode { updateHeight(node) when (getBalanceFactor(node)) { -2 -> { @@ -89,7 +90,7 @@ class AVLTree, V> : BalanceTree>() { private fun rotateRight(node: AVLNode): AVLNode { val leftChild = - node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + node.left ?: throw IllegalArgumentException("When turning right, the node must have a child on the right") val rightGrandChild = leftChild.right leftChild.right = node node.left = rightGrandChild @@ -99,7 +100,7 @@ class AVLTree, V> : BalanceTree>() { private fun rotateLeft(node: AVLNode): AVLNode { val rightChild = - node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right.") + node.right ?: throw IllegalArgumentException("When turning right, the node must have a child on the right") val leftGrandChild = rightChild.left rightChild.left = node node.right = leftGrandChild diff --git a/app/src/main/kotlin/trees/BSTree.kt b/app/src/main/kotlin/trees/BSTree.kt index 74da37c..41a4284 100644 --- a/app/src/main/kotlin/trees/BSTree.kt +++ b/app/src/main/kotlin/trees/BSTree.kt @@ -24,8 +24,8 @@ class BSTree, V> : BinaryTree>() { } override fun remove(key: K) { - val node = find(key) - root = node?.let { recursiveRemove(root, it) } + val node = find(key) ?: throw IllegalStateException("There is no such node with key $key in the tree") + root = recursiveRemove(root, node) } private fun recursiveRemove(currentNode: BSNode?, node: BSNode): BSNode? { diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 00fb52d..9d95351 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -26,7 +26,11 @@ class RBTree, V> : BalanceTree>() { } } - if (parent!!.key < node.key) { + if (parent == null) { + throw IllegalStateException("Only root parent can be null") + } + + if (parent.key < node.key) { parent.right = node } else { parent.left = node @@ -54,26 +58,32 @@ class RBTree, V> : BalanceTree>() { val movedUpNode: RBNode? val deletedNodeColor: Color - if (current.left == null || current.right == null) { + val leftSibling = current.left + val rightSibling = current.right + + if (leftSibling == null || rightSibling == null) { movedUpNode = removeNodeWithZeroOrOneChild(current) deletedNodeColor = current.color } else { - val inOrderSuccessor: RBNode = getMinNode(current.right!!) + val inOrderSuccessor = getMinNode(rightSibling) current.key = inOrderSuccessor.key deletedNodeColor = inOrderSuccessor.color movedUpNode = removeNodeWithZeroOrOneChild(inOrderSuccessor) } if (deletedNodeColor == Color.BLACK) { - balance(movedUpNode!!, true) + if (movedUpNode == null) { + throw IllegalStateException("Incorrect tree structure") + } + balance(movedUpNode, true) if (movedUpNode.isTemp) { replaceParentsChild(movedUpNode.parent, movedUpNode, null) } } } - override fun balance(node: RBNode, afterRemove: Boolean?): RBNode? { - if (afterRemove!!) { + override fun balance(node: RBNode, afterRemove: Boolean): RBNode? { + if (afterRemove) { balanceAfterRemove(node) } else { balanceAfterAdd(node) @@ -82,8 +92,8 @@ class RBTree, V> : BalanceTree>() { } private fun balanceAfterAdd(node: RBNode) { - var parent = node.parent ?: return - if (parent.color == Color.BLACK) { + var parent = node.parent + if (parent == null || parent.color == Color.BLACK) { return } @@ -127,21 +137,26 @@ class RBTree, V> : BalanceTree>() { return } - var sibling = getSibling(node) - if (sibling?.color == Color.RED) { + val parent = node.parent + var sibling = getSibling(node) ?: throw IllegalStateException("Can't balance node with null sibling") + + if (sibling.color == Color.RED) { handleRedSibling(node, sibling) - sibling = getSibling(node) + sibling = getSibling(node) ?: throw IllegalStateException("Incorrect node relations") } - if (isBlackOrNull(sibling?.left) && isBlackOrNull(sibling?.right)) { - sibling?.color = Color.RED - if (node.parent!!.color == Color.RED) { - node.parent!!.color = Color.BLACK + if (isBlackOrNull(sibling.left) && isBlackOrNull(sibling.right)) { + sibling.color = Color.RED + if (parent == null) { + throw IllegalStateException("Only root can have null parent") + } + if (parent.color == Color.RED) { + parent.color = Color.BLACK } else { - balanceAfterRemove(node.parent!!) + balanceAfterRemove(parent) } } else { - handleBlackRedSibling(node, sibling!!) + handleBlackRedSibling(node, sibling) } } @@ -173,36 +188,42 @@ class RBTree, V> : BalanceTree>() { } private fun rotateRight(node: RBNode) { - val parent: RBNode? = node.parent - val leftChild: RBNode? = node.left - node.left = leftChild?.right + val parent = node.parent + val leftChild = + node.left ?: throw IllegalStateException("Left child of node with key ${node.key} can't be null") + val cousin = leftChild.right + + node.left = cousin - if (leftChild?.right != null) { - leftChild.right!!.parent = node + if (cousin != null) { + cousin.parent = node } - leftChild!!.right = node + leftChild.right = node node.parent = leftChild replaceParentsChild(parent, node, leftChild) } private fun rotateLeft(node: RBNode) { - val parent: RBNode? = node.parent - val rightChild: RBNode? = node.right - node.right = rightChild?.left + val parent = node.parent + val rightChild = + node.right ?: throw IllegalStateException("Right child of node with key ${node.key} can't be null") + val cousin = rightChild.left - if (rightChild?.left != null) { - rightChild.left!!.parent = node + node.right = cousin + + if (cousin != null) { + cousin.parent = node } - rightChild!!.left = node + rightChild.left = node + node.parent = rightChild replaceParentsChild(parent, node, rightChild) } - private fun removeNodeWithZeroOrOneChild(node: RBNode): RBNode? { return if (node.left != null) { replaceParentsChild(node.parent, node, node.left) @@ -211,8 +232,9 @@ class RBTree, V> : BalanceTree>() { replaceParentsChild(node.parent, node, node.right) node.right } else { + node.isTemp = true var newChild: RBNode? = node - newChild!!.isTemp = true + if (node.color == Color.RED) { newChild = null } @@ -222,39 +244,54 @@ class RBTree, V> : BalanceTree>() { } private fun handleRedSibling(node: RBNode, sibling: RBNode) { + val parent = node.parent ?: throw IllegalStateException("Can't handle node with null parent") sibling.color = Color.BLACK - node.parent!!.color = Color.RED - if (node == node.parent!!.left) { - rotateLeft(node.parent!!) + parent.color = Color.RED + if (node == parent.left) { + rotateLeft(parent) } else { - rotateRight(node.parent!!) + rotateRight(parent) } } - private fun handleBlackRedSibling(node: RBNode, sibling: RBNode) { - var mutableSibling: RBNode = sibling - val nodeIsLeftChild = (node == node.parent!!.left) - if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + private fun handleBlackRedSibling(node: RBNode?, sibling: RBNode?) { + val parent = node?.parent + if (node == null || sibling == null || parent == null) { + throw IllegalStateException("Can't handle null nodes") + } - sibling.left!!.color = Color.BLACK + var mutableSibling = sibling + val nodeIsLeftChild = (node == parent.left) + if (nodeIsLeftChild && isBlackOrNull(sibling.right)) { + val tempNode = + sibling.left ?: throw IllegalStateException("Left child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateRight(sibling) - mutableSibling = node.parent!!.right!! + mutableSibling = + parent.right ?: throw IllegalStateException("Right child of node with key ${parent.key} can't be null") } else if (!nodeIsLeftChild && isBlackOrNull(sibling.left)) { - sibling.right!!.color = Color.BLACK + val tempNode = sibling.right + ?: throw IllegalStateException("Right child of node with key ${sibling.key} can't be null") + tempNode.color = Color.BLACK sibling.color = Color.RED rotateLeft(sibling) - mutableSibling = node.parent!!.left!! + mutableSibling = + parent.left ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") } - mutableSibling.color = node.parent!!.color - node.parent!!.color = Color.BLACK + mutableSibling.color = parent.color + parent.color = Color.BLACK if (nodeIsLeftChild) { - mutableSibling.right!!.color = Color.BLACK - rotateLeft(node.parent!!) + val tempNode = mutableSibling.right + ?: throw IllegalStateException("Right child of node with key ${mutableSibling.key} can't be null") + tempNode.color = Color.BLACK + rotateLeft(parent) } else { - mutableSibling.left!!.color = Color.BLACK - rotateRight(node.parent!!) + val tempNode = mutableSibling.left + ?: throw IllegalStateException("Left child of node with key ${parent.key} can't be null") + tempNode.color = Color.BLACK + rotateRight(parent) } } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index 92d3eb8..c3795db 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance(node: NODE_TYPE, afterRemove: Boolean? = null): NODE_TYPE? + abstract fun balance(node: NODE_TYPE, afterRemove: Boolean = false): NODE_TYPE? } diff --git a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt new file mode 100644 index 0000000..a3d492f --- /dev/null +++ b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt @@ -0,0 +1,93 @@ +package main.kotlin.trees.dataBases.AVL + +import mu.KotlinLogging +import java.io.Closeable +import java.sql.* +import trees.nodes.AVLNode +import trees.AVLTree + +private val logger = KotlinLogging.logger { } +private const val DB_DRIVER = "jdbc:sqlite" + +class SQLiteDB(private val path: String) : Closeable { + private val connection = DriverManager.getConnection("$DB_DRIVER:$path") + ?: throw SQLException("Cannot connect to database") + private val addNodeStatement by lazy { connection.prepareStatement("INSERT INTO nodes (key, value, x, y) VALUES (?, ?, ?, ?);") } + + init { + logger.info { "Connected to database: $path" } + } + + fun open() { + connection.createStatement().also { statement -> + try { + statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text, x DOUBLE NOT NULL, y DOUBLE NOT NULL);") + logger.info { "Tables created or already exists" } + } catch (problem: Exception) { + println(problem) + logger.error(problem) { "Cannot create table in database" } + } finally { + statement.close() + } + } + } + + fun addNode(node: AVLNode) { + try { + addNodeStatement.setInt(1, node.key) + addNodeStatement.setString(2, node.value) + addNodeStatement.setDouble(3, 0.0) + addNodeStatement.setDouble(4, 0.0) + addNodeStatement.execute() + } catch (problem: Exception) { + logger.error(problem) { "Cannot add node with key ${node.key}" } + } + } + + fun writeAllNodesToDB(node: AVLNode?, tree: AVLTree) { + val stack = mutableListOf(node?.key) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { tree.find(it) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + current?.let { addNode((it)) } + } + } + + fun selectNodes(): MutableList> { + val nodesList = mutableListOf>() + try { + val query = "SELECT key, value, x, y FROM nodes" + val rs = connection.createStatement().executeQuery(query) + while (rs.next()) { + val key = rs.getInt("key") + val value = rs.getString("value") + nodesList.add(AVLNode(key, value)) + } + } catch (problem: Exception) { + logger.error(problem) { "Failed to select" } + } + return nodesList + } + + fun delete() { + connection.createStatement().also { statement -> + try { + statement.execute("DROP TABLE IF EXISTS nodes") + logger.info { "Table deleted" } + } catch (problem: Exception) { + println(problem) + logger.error(problem) { "Cannot delete table in database" } + } + } + } + + override fun close() { + addNodeStatement.close() + connection.close() + logger.info { "Connection closed" } + } + +} diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 5ba4571..82a0e26 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -11,6 +11,21 @@ fun checkIfFileExist(file: File): Boolean { return file.exists() } +fun removeTree(file: File): Boolean { + if (checkIfFileExist(file)) { + var result = file.delete() + if (result) { + result = file.createNewFile() + if (result) { + return result + } + throw Exception("Fail with file create") + } + throw Exception("Fail with file remove") + } + throw Exception("File with tree not exist") +} + fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { val stack = mutableListOf(node?.key) var stringToWrite: String @@ -28,6 +43,6 @@ fun writeAllNodesToFile(node: BSNode?, tree: BSTree, f fun insertAllNodesToTree(tree: BSTree, file: File) { file.forEachLine { val inputs = it.split(" ").toTypedArray() - tree.add(inputs[0].toInt(),inputs[1]) + tree.add(inputs[0].toInt(), inputs[1]) } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/nodes/AVLNode.kt b/app/src/main/kotlin/trees/nodes/AVLNode.kt index 05b9864..7f26b41 100644 --- a/app/src/main/kotlin/trees/nodes/AVLNode.kt +++ b/app/src/main/kotlin/trees/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package trees.nodes -class AVLNode, V>(key: K, value: V) : Node>(key, value) { +class AVLNode, V>(key: K, value: V) : Node>(key, value) { var height: Int? = 0 } diff --git a/app/src/main/kotlin/trees/nodes/BSNode.kt b/app/src/main/kotlin/trees/nodes/BSNode.kt index e16735c..6543dad 100644 --- a/app/src/main/kotlin/trees/nodes/BSNode.kt +++ b/app/src/main/kotlin/trees/nodes/BSNode.kt @@ -1,3 +1,3 @@ package trees.nodes -class BSNode, V>(key: K, value: V) : Node>(key, value) +class BSNode, V>(key: K, value: V) : Node>(key, value) diff --git a/app/src/main/kotlin/trees/nodes/Node.kt b/app/src/main/kotlin/trees/nodes/Node.kt index dd15928..2ad34c6 100644 --- a/app/src/main/kotlin/trees/nodes/Node.kt +++ b/app/src/main/kotlin/trees/nodes/Node.kt @@ -1,5 +1,6 @@ package trees.nodes -abstract class Node, V, NODE_TYPE: Node>(var key: K, var value: V) { + +abstract class Node, V, NODE_TYPE : Node>(var key: K, var value: V) { var left: NODE_TYPE? = null var right: NODE_TYPE? = null } diff --git a/app/src/main/kotlin/trees/nodes/RBNode.kt b/app/src/main/kotlin/trees/nodes/RBNode.kt index 6f9dbc5..9669b95 100644 --- a/app/src/main/kotlin/trees/nodes/RBNode.kt +++ b/app/src/main/kotlin/trees/nodes/RBNode.kt @@ -1,11 +1,13 @@ package trees.nodes -class RBNode, V>(key: K, value: V) : Node>(key, value) { +class RBNode, V>(key: K, value: V) : Node>(key, value) { var parent: RBNode? = null var color: Color = Color.RED + enum class Color { RED, BLACK } + var isTemp: Boolean = false } diff --git a/app/src/test/kotlin/trees/AVLTreeTest.kt b/app/src/test/kotlin/trees/AVLTreeTest.kt index afed47f..1e2df08 100644 --- a/app/src/test/kotlin/trees/AVLTreeTest.kt +++ b/app/src/test/kotlin/trees/AVLTreeTest.kt @@ -1,7 +1,7 @@ package trees -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isAVLOk class AVLTreeTest { @@ -41,4 +41,12 @@ class AVLTreeTest { assertTrue(isAVLOk()) } } + + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100, 0) + tree.remove(101) + } + } } \ No newline at end of file diff --git a/app/src/test/kotlin/trees/BSTreeTest.kt b/app/src/test/kotlin/trees/BSTreeTest.kt index cf2e4c3..75ce0ba 100644 --- a/app/src/test/kotlin/trees/BSTreeTest.kt +++ b/app/src/test/kotlin/trees/BSTreeTest.kt @@ -1,7 +1,7 @@ package trees -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isBstOk class BSTreeTest { @@ -41,4 +41,12 @@ class BSTreeTest { assertTrue(isBstOk()) } } + + @Test + fun `remove a node that is not in tree`() { + assertThrows(IllegalStateException::class.java) { + tree.add(100, 0) + tree.remove(101) + } + } } \ No newline at end of file diff --git a/app/src/test/kotlin/trees/RBTreeTest.kt b/app/src/test/kotlin/trees/RBTreeTest.kt index b6b578a..623168d 100644 --- a/app/src/test/kotlin/trees/RBTreeTest.kt +++ b/app/src/test/kotlin/trees/RBTreeTest.kt @@ -1,10 +1,8 @@ package trees -import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test import treesValidChecker.isRbOk -import java.lang.IllegalStateException class RBTreeTest { private val tree = RBTree() @@ -43,10 +41,11 @@ class RBTreeTest { assertTrue(isRbOk()) } } + @Test fun `remove a node that is not in tree`() { assertThrows(IllegalStateException::class.java) { - tree.add(100,0) + tree.add(100, 0) tree.remove(101) } } diff --git a/app/src/test/kotlin/treesValidChecker/checkers.kt b/app/src/test/kotlin/treesValidChecker/checkers.kt index 96199aa..a9a7705 100644 --- a/app/src/test/kotlin/treesValidChecker/checkers.kt +++ b/app/src/test/kotlin/treesValidChecker/checkers.kt @@ -31,7 +31,7 @@ fun isAVLOk(node: AVLNode? = AVLtree.root): Boolean { } val lh = AVLtree.getHeight(node.left) val rh = AVLtree.getHeight(node.right) - return abs(lh-rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) + return abs(lh - rh) <= 1 && isAVLOk(node.left) && isAVLOk(node.right) } val RBtree = RBTree() @@ -44,7 +44,8 @@ fun isRbOk(node: RBNode? = RBtree.root): Boolean { val blackRight = numOfBlack(node.right) return !(blackLeft != blackRight || blackLeft == -1 || !noTwoRedInARow()) } -fun numOfBlack(node: RBNode? = RBtree.root): Int { + +fun numOfBlack(node: RBNode? = RBtree.root): Int { if (node == null) return 0 val blackLeft = numOfBlack(node.left) @@ -58,7 +59,7 @@ fun numOfBlack(node: RBNode? = RBtree.root): Int { return blackLeft } -fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { +fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean { if (node == null) return true if (node.color == RBNode.Color.BLACK) { @@ -66,5 +67,5 @@ fun noTwoRedInARow(node: RBNode? = RBtree.root, row: Int = 0): Boolean } if (row == 1) return false - return (noTwoRedInARow(node.left,1) && noTwoRedInARow(node.right,1)) -} \ No newline at end of file + return (noTwoRedInARow(node.left, 1) && noTwoRedInARow(node.right, 1)) +} From 03b2979becbe43435ec9e08cc8e1f3991e09f47a Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 23 Apr 2023 16:28:41 +0300 Subject: [PATCH 56/85] add: balance tests for BS and AVL trees --- .../kotlin/trees/balance/AVLBalanceTest.kt | 103 +++++++++++++++ .../kotlin/trees/balance/BSBalanceTest.kt | 125 ++++++++++++++++++ .../kotlin/trees/balance/RBBalanceTest.kt | 4 + 3 files changed, 232 insertions(+) create mode 100644 app/src/test/kotlin/trees/balance/AVLBalanceTest.kt create mode 100644 app/src/test/kotlin/trees/balance/BSBalanceTest.kt create mode 100644 app/src/test/kotlin/trees/balance/RBBalanceTest.kt diff --git a/app/src/test/kotlin/trees/balance/AVLBalanceTest.kt b/app/src/test/kotlin/trees/balance/AVLBalanceTest.kt new file mode 100644 index 0000000..d743e8c --- /dev/null +++ b/app/src/test/kotlin/trees/balance/AVLBalanceTest.kt @@ -0,0 +1,103 @@ +package trees.balance + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import trees.AVLTree + +class AVLBalanceTest { + + private val tree = AVLTree() + + @Test + fun `test method add in AVLTree (1)`() { + tree.add(10, "hello") + tree.add(8, "hello") + tree.add(6, "hello") + /* + + 10 + / 8 + 8 ---------> / \ + / 6 10 + 6 + + */ + + Assertions.assertEquals(8, tree.root?.key) + Assertions.assertEquals(6, tree.root?.left?.key) + Assertions.assertEquals(10, tree.root?.right?.key) + } + + @Test + fun `test method add in AVLTree (2)`() { + tree.add(8, "hello") + tree.add(6, "hello") + tree.add(10, "hello") + tree.add(11, "hello") + tree.add(12, "hello") + /* + 8 + / \ 8 + 6 10 / \ + \ ---------> 6 11 + 11 / \ + \ 10 12 + 12 + */ + + Assertions.assertEquals(8, tree.root?.key) + Assertions.assertEquals(6, tree.root?.left?.key) + Assertions.assertEquals(11, tree.root?.right?.key) + Assertions.assertEquals(10, tree.root?.right?.left?.key) + Assertions.assertEquals(12, tree.root?.right?.right?.key) + } + + @Test + fun `test method remove in AVLTree(remove without balance)`() { + tree.add(8, "hello") + tree.add(6, "hello") + tree.add(10, "hello") + tree.add(11, "hello") + tree.add(12, "hello") + + tree.remove(12) + /* + 8 8 + / \ / \ + 6 11 ---------> 6 11 + / \ / + 10 12 10 + + */ + + Assertions.assertEquals(8, tree.root?.key) + Assertions.assertEquals(6, tree.root?.left?.key) + Assertions.assertEquals(11, tree.root?.right?.key) + Assertions.assertEquals(10, tree.root?.right?.left?.key) + Assertions.assertEquals(null, tree.root?.right?.right?.key) + } + + @Test + fun `test method remove in AVLTree(remove with balance)`() { + tree.add(8, "hello") + tree.add(6, "hello") + tree.add(10, "hello") + tree.add(11, "hello") + tree.add(12, "hello") + + tree.remove(6) + /* + 8 11 + / \ / \ + 6 11 ---------> 8 12 + / \ \ + 10 12 10 + + */ + + Assertions.assertEquals(11, tree.root?.key) + Assertions.assertEquals(8, tree.root?.left?.key) + Assertions.assertEquals(12, tree.root?.right?.key) + Assertions.assertEquals(10, tree.root?.left?.right?.key) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/balance/BSBalanceTest.kt b/app/src/test/kotlin/trees/balance/BSBalanceTest.kt new file mode 100644 index 0000000..502b008 --- /dev/null +++ b/app/src/test/kotlin/trees/balance/BSBalanceTest.kt @@ -0,0 +1,125 @@ +package trees.balance + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import trees.BSTree + +class BSBalanceTest { + private val tree = BSTree() + + @Test + fun `test method add in BSTree (1)`() { + tree.add(10, "hello") + tree.add(5, "hello") + tree.add(7, "hello") + tree.add(13, "hello") + tree.add(11, "hello") + tree.add(14, "hello") + tree.add(12, "hello") + tree.add(4, "hello") + + /* + 10 + / \ + 5 13 + / \ / \ + 6 7 11 14 + \ + 12 + */ + + assertEquals(10, tree.root?.key) + assertEquals(5, tree.root?.left?.key) + assertEquals(13, tree.root?.right?.key) + assertEquals(4, tree.root?.left?.left?.key) + assertEquals(7, tree.root?.left?.right?.key) + assertEquals(14, tree.root?.right?.right?.key) + assertEquals(11, tree.root?.right?.left?.key) + assertEquals(12, tree.root?.right?.left?.right?.key) + } + + @Test + fun `test method add in BSTree (2)`() { + tree.add(10, "hello") + tree.add(9, "hello") + tree.add(8, "hello") + tree.add(7, "hello") + + /* + 10 + / + 9 + / + 8 + / + 7 + */ + + assertEquals(10, tree.root?.key) + assertEquals(9, tree.root?.left?.key) + assertEquals(null, tree.root?.right?.key) + assertEquals(8, tree.root?.left?.left?.key) + assertEquals(7, tree.root?.left?.left?.left?.key) + } + + @Test + fun `test method remove in BSTree(remove leaf)`() { + tree.add(10, "hello") + tree.add(5, "hello") + tree.add(7, "hello") + + tree.remove(7) + /* + 10 + / 10 + 5 --------> / + \ 5 + 7 + */ + + assertEquals(10, tree.root?.key) + assertEquals(5, tree.root?.left?.key) + assertEquals(null, tree.root?.left?.right?.key) + assertEquals(null, tree.root?.left?.left?.key) + } + + @Test + fun `test method remove in BSTree(remove node with one child)`() { + tree.add(10, "hello") + tree.add(5, "hello") + tree.add(7, "hello") + + tree.remove(5) + /* + 10 + / 10 + 5 --------> / + \ 7 + 7 + */ + + assertEquals(10, tree.root?.key) + assertEquals(7, tree.root?.left?.key) + assertEquals(null, tree.root?.right?.key) + assertEquals(null, tree.root?.left?.right?.key) + assertEquals(null, tree.root?.left?.left?.key) + } + + @Test + fun `test method remove in BSTree(remove node with two child)`() { + tree.add(10, "hello") + tree.add(5, "hello") + tree.add(12, "hello") + + tree.remove(10) + /* + 10 12 + / \ --------> / + 5 12 5 + */ + + assertEquals(12, tree.root?.key) + assertEquals(5, tree.root?.left?.key) + assertEquals(null, tree.root?.right?.key) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/trees/balance/RBBalanceTest.kt b/app/src/test/kotlin/trees/balance/RBBalanceTest.kt new file mode 100644 index 0000000..01c726d --- /dev/null +++ b/app/src/test/kotlin/trees/balance/RBBalanceTest.kt @@ -0,0 +1,4 @@ +package trees.balance + +class RBBalanceTest { +} \ No newline at end of file From 8f5de1c050932888617035dde945b6965703736c Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Sun, 23 Apr 2023 16:36:38 +0300 Subject: [PATCH 57/85] feat: CI runs on 3 platforms with jacoco test coverage --- .github/workflows/github-CI.yml | 44 ++++++++++------ app/build.gradle.kts | 92 ++++++++++++++++++++++++++++----- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/.github/workflows/github-CI.yml b/.github/workflows/github-CI.yml index 9bb1d83..e49559d 100644 --- a/.github/workflows/github-CI.yml +++ b/.github/workflows/github-CI.yml @@ -1,22 +1,34 @@ -name: Run Kotlin code build +name: Run Kotlin project build with code and test coverage -on: - pull_request: - branches: - - main - - develop +on: [push] jobs: - build: - runs-on: ubuntu-latest - + run: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 - - name: Run build with Gradle Wrapper + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: zulu + java-version: '17' + cache: gradle + + - name: Run gradle build run: ./gradlew build - - uses: actions/upload-artifact@v3 + + - if: matrix.os == 'ubuntu-latest' # Container action is only supported on Linux + name: Run Test Coverage + run: ./gradlew jacocoTestReport + + - if: matrix.os == 'ubuntu-latest' + name: Jacoco Code Coverage Report + uses: cicirello/jacoco-badge-generator@v2.8.0 with: - name: Package - path: build/libs \ No newline at end of file + generate-branches-badge: true + jacoco-csv-file: app/build/jacoco/report.csv \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 33e103d..9333fdc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,15 +1,7 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Kotlin application project to get you started. - * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle - * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html - */ - plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. id("org.jetbrains.kotlin.jvm") version "1.8.10" - + jacoco // Apply the application plugin to add support for building a CLI application in Java. application } @@ -39,12 +31,88 @@ dependencies { implementation("org.slf4j:slf4j-simple:1.7.29") } +tasks.test { + finalizedBy(tasks.jacocoTestReport) + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + } + val failedTests = mutableListOf() + val skippedTests = mutableListOf() + + addTestListener (object: TestListener { + override fun beforeSuite(suite: TestDescriptor?) { } + override fun beforeTest(testDescriptor: TestDescriptor?) { } + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + when (result.resultType) { + TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) + TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor) + else -> {} + } + } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + if (suite.parent == null) { // root suite + logger.lifecycle("####################################################################################") + logger.lifecycle("Test result: ${result.resultType}") + logger.lifecycle( + "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeed, " + + "${result.skippedTestCount} skipped, " + + "${result.failedTestCount} failed." + ) + } + } + }) + +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) + reports { + xml.required.set(true) + xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml")) + csv.required.set(true) + csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) + html.required.set(true) + html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) + } +} + +tasks.jacocoTestCoverageVerification { + classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + } }) + dependsOn(tasks.jacocoTestReport) + violationRules { + rule { + element = "CLASS" + limit { + counter = "BRANCH" + minimum = 0.5.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "LINE" + minimum = 0.6.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "METHOD" + minimum = 0.9.toBigDecimal() + } + } + } +} + application { - // Define the main class for the application. mainClass.set("trees.AppKt") } tasks.named("test") { - // Use JUnit Platform for unit tests. useJUnitPlatform() -} +} \ No newline at end of file From cf3d84b09cd9e29ef58eac342ba20168d5c15569 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Sun, 23 Apr 2023 16:50:21 +0300 Subject: [PATCH 58/85] CI runs on 3 platforms with jacoco test coverage Added jacoco functions to gradle build, distribution of build is zulu --- .github/workflows/github-CI.yml | 44 ++++++++++------ app/build.gradle.kts | 92 ++++++++++++++++++++++++++++----- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/.github/workflows/github-CI.yml b/.github/workflows/github-CI.yml index 9bb1d83..e49559d 100644 --- a/.github/workflows/github-CI.yml +++ b/.github/workflows/github-CI.yml @@ -1,22 +1,34 @@ -name: Run Kotlin code build +name: Run Kotlin project build with code and test coverage -on: - pull_request: - branches: - - main - - develop +on: [push] jobs: - build: - runs-on: ubuntu-latest - + run: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 - - name: Run build with Gradle Wrapper + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: zulu + java-version: '17' + cache: gradle + + - name: Run gradle build run: ./gradlew build - - uses: actions/upload-artifact@v3 + + - if: matrix.os == 'ubuntu-latest' # Container action is only supported on Linux + name: Run Test Coverage + run: ./gradlew jacocoTestReport + + - if: matrix.os == 'ubuntu-latest' + name: Jacoco Code Coverage Report + uses: cicirello/jacoco-badge-generator@v2.8.0 with: - name: Package - path: build/libs \ No newline at end of file + generate-branches-badge: true + jacoco-csv-file: app/build/jacoco/report.csv \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 33e103d..9333fdc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,15 +1,7 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Kotlin application project to get you started. - * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle - * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html - */ - plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. id("org.jetbrains.kotlin.jvm") version "1.8.10" - + jacoco // Apply the application plugin to add support for building a CLI application in Java. application } @@ -39,12 +31,88 @@ dependencies { implementation("org.slf4j:slf4j-simple:1.7.29") } +tasks.test { + finalizedBy(tasks.jacocoTestReport) + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + } + val failedTests = mutableListOf() + val skippedTests = mutableListOf() + + addTestListener (object: TestListener { + override fun beforeSuite(suite: TestDescriptor?) { } + override fun beforeTest(testDescriptor: TestDescriptor?) { } + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + when (result.resultType) { + TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) + TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor) + else -> {} + } + } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + if (suite.parent == null) { // root suite + logger.lifecycle("####################################################################################") + logger.lifecycle("Test result: ${result.resultType}") + logger.lifecycle( + "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeed, " + + "${result.skippedTestCount} skipped, " + + "${result.failedTestCount} failed." + ) + } + } + }) + +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) + reports { + xml.required.set(true) + xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml")) + csv.required.set(true) + csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) + html.required.set(true) + html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) + } +} + +tasks.jacocoTestCoverageVerification { + classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + } }) + dependsOn(tasks.jacocoTestReport) + violationRules { + rule { + element = "CLASS" + limit { + counter = "BRANCH" + minimum = 0.5.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "LINE" + minimum = 0.6.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "METHOD" + minimum = 0.9.toBigDecimal() + } + } + } +} + application { - // Define the main class for the application. mainClass.set("trees.AppKt") } tasks.named("test") { - // Use JUnit Platform for unit tests. useJUnitPlatform() -} +} \ No newline at end of file From a44d643f693a9a94c2608aa3175c0b6f70341234 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Sun, 23 Apr 2023 17:03:07 +0300 Subject: [PATCH 59/85] feat: using csv library to work with write and read functions --- app/build.gradle.kts | 1 + .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 36 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9333fdc..710ef86 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") // This dependency is used by the application. + implementation ("org.apache.commons:commons-csv:1.5") implementation("com.google.guava:guava:31.1-jre") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 82a0e26..052bb5a 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -1,5 +1,8 @@ package trees.dataBases.BST +import org.apache.commons.csv.CSVFormat +import org.apache.commons.csv.CSVParser +import org.apache.commons.csv.CSVPrinter import trees.BSTree import trees.nodes.BSNode import java.io.File @@ -11,7 +14,7 @@ fun checkIfFileExist(file: File): Boolean { return file.exists() } -fun removeTree(file: File): Boolean { +fun removeFile(file: File): Boolean { if (checkIfFileExist(file)) { var result = file.delete() if (result) { @@ -28,21 +31,28 @@ fun removeTree(file: File): Boolean { fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { val stack = mutableListOf(node?.key) - var stringToWrite: String - while (stack.isNotEmpty()) { - val current = stack.removeLast()?.let { tree.find(it) } - if (current?.left != null) - current.left?.key.apply(stack::add) - if (current?.right != null) - current.right?.key.apply(stack::add) - stringToWrite = "${current?.key}${current?.value} 0 0\n" - file.appendText(stringToWrite) + file.bufferedWriter().use { + val csvPrinter = CSVPrinter(it, CSVFormat.DEFAULT) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let {current -> tree.find(current) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + csvPrinter.printRecord("${current?.key}","${current?.value}","0","0") + } } } fun insertAllNodesToTree(tree: BSTree, file: File) { - file.forEachLine { - val inputs = it.split(" ").toTypedArray() - tree.add(inputs[0].toInt(), inputs[1]) + file.bufferedReader ().use { + val csvParser = CSVParser(it, CSVFormat.DEFAULT) + for(csvRecord in csvParser) { + val nodeKey = csvRecord.get(0) + val nodeValue = csvRecord.get(1) + val nodeX = csvRecord.get(2) + val nodeY = csvRecord.get(3) + tree.add(nodeKey.toInt(), nodeValue) + } } } From 9025887561decc4e57d4ddcf1e8982e449d81285 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Mon, 24 Apr 2023 18:55:16 +0300 Subject: [PATCH 60/85] add: raw compose app --- app/build.gradle.kts | 4 ++ app/src/main/kotlin/trees/App.kt | 43 ++++++++++++++++++++++ app/src/main/kotlin/trees/gui/additions.kt | 0 settings.gradle.kts | 7 ++++ 4 files changed, 54 insertions(+) create mode 100644 app/src/main/kotlin/trees/App.kt create mode 100644 app/src/main/kotlin/trees/gui/additions.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 710ef86..b2b42ce 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,11 +4,14 @@ plugins { jacoco // Apply the application plugin to add support for building a CLI application in Java. application + id("org.jetbrains.compose") version "1.4.0" } repositories { // Use Maven Central for resolving dependencies. mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + google() } dependencies { @@ -30,6 +33,7 @@ dependencies { // Logging implementation("io.github.microutils:kotlin-logging-jvm:2.0.6") implementation("org.slf4j:slf4j-simple:1.7.29") + implementation(compose.desktop.currentOs) } tasks.test { diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt new file mode 100644 index 0000000..7b94426 --- /dev/null +++ b/app/src/main/kotlin/trees/App.kt @@ -0,0 +1,43 @@ +package trees + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState + + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "Compose for Desktop", + state = rememberWindowState(width = 300.dp, height = 300.dp) + ) { + val count = remember { mutableStateOf(0) } + MaterialTheme { + Column(Modifier.fillMaxSize(), Arrangement.spacedBy(5.dp)) { + Button(modifier = Modifier.align(Alignment.CenterHorizontally), + onClick = { + count.value++ + }) { + Text(if (count.value == 0) "Hello World" else "Clicked ${count.value}!") + } + Button(modifier = Modifier.align(Alignment.CenterHorizontally), + onClick = { + count.value = 0 + }) { + Text("Reset") + } + } + } + } +} diff --git a/app/src/main/kotlin/trees/gui/additions.kt b/app/src/main/kotlin/trees/gui/additions.kt new file mode 100644 index 0000000..e69de29 diff --git a/settings.gradle.kts b/settings.gradle.kts index 81a0d01..df42d93 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,3 +9,10 @@ rootProject.name = "trees-9" include("app") + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } +} From b2367447f4083dccd1dcb8e6a8a791c32835ea92 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Tue, 25 Apr 2023 05:58:52 +0300 Subject: [PATCH 61/85] feat: home screen implementation --- app/src/main/kotlin/trees/App.kt | 71 ++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 7b94426..b17f083 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,43 +1,72 @@ package trees +import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme import androidx.compose.material.Text -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState +import kotlin.system.exitProcess +@Preview +@Composable +fun HomeScreen() { + Column(Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally,) { + Button( + onClick = { + TODO("TreeScreen") + }) { + Text(text = "Binary search tree") + } + Button( + onClick = { + TODO("TreeScreen") + }) { + Text(text = "AVL tree") + } + Button( + onClick = { + TODO("TreeScreen") + }) { + Text(text = "Red-black tree") + } + Button( + onClick = { + exitProcess(0) + }) { + Text(text = "Exit") + } + } +} fun main() = application { Window( onCloseRequest = ::exitApplication, - title = "Compose for Desktop", + title = "Trees by ", state = rememberWindowState(width = 300.dp, height = 300.dp) ) { - val count = remember { mutableStateOf(0) } - MaterialTheme { - Column(Modifier.fillMaxSize(), Arrangement.spacedBy(5.dp)) { - Button(modifier = Modifier.align(Alignment.CenterHorizontally), - onClick = { - count.value++ - }) { - Text(if (count.value == 0) "Hello World" else "Clicked ${count.value}!") - } - Button(modifier = Modifier.align(Alignment.CenterHorizontally), - onClick = { - count.value = 0 - }) { - Text("Reset") - } - } - } + HomeScreen() } } + +//val bst = insertAllNodesToTree() +// var x = 1 +// val stack = mutableListOf(bst.root?.key) +// while (stack.isNotEmpty()) { +// val current = stack.removeLast()?.let { current -> bst.find(current) } +// if (current?.left != null) +// current.left?.key.apply(stack::add) +// if (current?.right != null) +// current.right?.key.apply(stack::add) +// Text("Node $x - ${current?.key}, ${current?.value}\n") +// x++ +// } \ No newline at end of file From 3ac89e69dbbce877fc9be9dad56d33b380ebab25 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Tue, 25 Apr 2023 06:15:40 +0300 Subject: [PATCH 62/85] feat: HomeScreen buttons are the same size --- app/src/main/kotlin/trees/App.kt | 40 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index b17f083..7b10a91 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -4,12 +4,15 @@ import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState @@ -18,6 +21,8 @@ import kotlin.system.exitProcess @Preview @Composable fun HomeScreen() { + val fontsize = 22 + val buttonWidth = 225 Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally,) { @@ -25,25 +30,37 @@ fun HomeScreen() { onClick = { TODO("TreeScreen") }) { - Text(text = "Binary search tree") + Text(text = "Binary search tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center) } Button( onClick = { TODO("TreeScreen") }) { - Text(text = "AVL tree") + Text(text = "AVL tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center) } Button( onClick = { TODO("TreeScreen") }) { - Text(text = "Red-black tree") + Text(text = "Red-black tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center) } Button( onClick = { exitProcess(0) }) { - Text(text = "Exit") + Text(text = "Exit", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center) } } } @@ -56,17 +73,4 @@ fun main() = application { ) { HomeScreen() } -} - -//val bst = insertAllNodesToTree() -// var x = 1 -// val stack = mutableListOf(bst.root?.key) -// while (stack.isNotEmpty()) { -// val current = stack.removeLast()?.let { current -> bst.find(current) } -// if (current?.left != null) -// current.left?.key.apply(stack::add) -// if (current?.right != null) -// current.right?.key.apply(stack::add) -// Text("Node $x - ${current?.key}, ${current?.value}\n") -// x++ -// } \ No newline at end of file +} \ No newline at end of file From f7d9ce24486749304018ac00a1fa4c53bb54c758 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Wed, 26 Apr 2023 23:43:52 +0300 Subject: [PATCH 63/85] add: node view model for displaying them --- app/src/main/kotlin/trees/gui/Node.kt | 41 +++++++++++++++++++ .../main/kotlin/trees/gui/model/NodeView.kt | 20 +++++++++ 2 files changed, 61 insertions(+) create mode 100644 app/src/main/kotlin/trees/gui/Node.kt create mode 100644 app/src/main/kotlin/trees/gui/model/NodeView.kt diff --git a/app/src/main/kotlin/trees/gui/Node.kt b/app/src/main/kotlin/trees/gui/Node.kt new file mode 100644 index 0000000..162a5f9 --- /dev/null +++ b/app/src/main/kotlin/trees/gui/Node.kt @@ -0,0 +1,41 @@ +package trees.gui + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.dp + +@Composable +fun Node( + key: Int, + value: String?, + x: Double, + y: Double +) { + val x = remember { mutableStateOf(x.dp) } + val y = remember { mutableStateOf(y.dp) } + Box(modifier = Modifier.offset((x.value), (y.value))) { + Box(modifier = Modifier + .requiredSize(54.dp) + .clip(CircleShape) + .background(Color.Red) + .pointerInput(x, y) { + detectDragGestures { change, dragAmount -> + change.consume() + x.value += dragAmount.x.toDp() + y.value += dragAmount.y.toDp() + } + }, + contentAlignment = Alignment.Center) { + Text("$key") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/model/NodeView.kt b/app/src/main/kotlin/trees/gui/model/NodeView.kt new file mode 100644 index 0000000..2a3b09b --- /dev/null +++ b/app/src/main/kotlin/trees/gui/model/NodeView.kt @@ -0,0 +1,20 @@ +package trees.gui.model + +import trees.nodes.Node + +class NodeView>(parNode: NT) { + var node = parNode + val x = 0.0 + val y = 0.0 + var left: NodeView? = null + var right: NodeView? = null + + init { + node.left?.let { + left = NodeView(it) + } + node.right?.let { + right = NodeView(it) + } + } +} \ No newline at end of file From 469d95e1c2249f0278367635d53f8a509cfc3030 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 28 Apr 2023 05:33:48 +0300 Subject: [PATCH 64/85] feat(flatFilesDB): write and insert functions works only with key and value --- .../main/kotlin/trees/dataBases/BST/flatFilesDB.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 052bb5a..4a439c9 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -8,13 +8,13 @@ import trees.nodes.BSNode import java.io.File const val fileName = "./app/src/main/kotlin/trees/dataBases/BST/flatFile" -val file = File(fileName) +val curFile = File(fileName) fun checkIfFileExist(file: File): Boolean { return file.exists() } -fun removeFile(file: File): Boolean { +fun removeFile(file: File = curFile): Boolean { if (checkIfFileExist(file)) { var result = file.delete() if (result) { @@ -29,7 +29,7 @@ fun removeFile(file: File): Boolean { throw Exception("File with tree not exist") } -fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File) { +fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File = File(fileName)) { val stack = mutableListOf(node?.key) file.bufferedWriter().use { val csvPrinter = CSVPrinter(it, CSVFormat.DEFAULT) @@ -39,20 +39,20 @@ fun writeAllNodesToFile(node: BSNode?, tree: BSTree, f current.left?.key.apply(stack::add) if (current?.right != null) current.right?.key.apply(stack::add) - csvPrinter.printRecord("${current?.key}","${current?.value}","0","0") + csvPrinter.printRecord("${current?.key}","${current?.value}") } } } -fun insertAllNodesToTree(tree: BSTree, file: File) { +fun insertAllNodesToTree(file: File = curFile): BSTree { + val tree = BSTree() file.bufferedReader ().use { val csvParser = CSVParser(it, CSVFormat.DEFAULT) for(csvRecord in csvParser) { val nodeKey = csvRecord.get(0) val nodeValue = csvRecord.get(1) - val nodeX = csvRecord.get(2) - val nodeY = csvRecord.get(3) tree.add(nodeKey.toInt(), nodeValue) } } + return tree } From a49c989476362e97c232c9e9bc58fae66599f7e1 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 28 Apr 2023 05:37:47 +0300 Subject: [PATCH 65/85] feat: nodes in BSTScreen can be printed, implemented functions to work with coordinates added compose3 material, deleted unnecessary file --- app/build.gradle.kts | 3 +- app/src/main/kotlin/trees/App.kt | 155 ++++++++++++++++++++++---- app/src/main/kotlin/trees/gui/Node.kt | 41 ------- 3 files changed, 138 insertions(+), 61 deletions(-) delete mode 100644 app/src/main/kotlin/trees/gui/Node.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b2b42ce..6355c7e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,11 +29,12 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") implementation("org.xerial:sqlite-jdbc:3.14.2") + implementation(compose.material3) + implementation(compose.desktop.currentOs) // Logging implementation("io.github.microutils:kotlin-logging-jvm:2.0.6") implementation("org.slf4j:slf4j-simple:1.7.29") - implementation(compose.desktop.currentOs) } tasks.test { diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 7b10a91..51d8730 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,21 +1,31 @@ package trees import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.width -import androidx.compose.material.Button -import androidx.compose.material.Text +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState +import trees.dataBases.BST.insertAllNodesToTree +import trees.dataBases.BST.removeFile +import trees.dataBases.BST.writeAllNodesToFile +import trees.nodes.BSNode +import kotlin.math.roundToInt import kotlin.system.exitProcess @Preview @@ -23,44 +33,150 @@ import kotlin.system.exitProcess fun HomeScreen() { val fontsize = 22 val buttonWidth = 225 - Column(Modifier.fillMaxSize(), + Column( + Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally,) { + horizontalAlignment = Alignment.CenterHorizontally, + ) { Button( onClick = { TODO("TreeScreen") +// BSTScreen() }) { - Text(text = "Binary search tree", + Text( + text = "Binary search tree", modifier = Modifier.width(buttonWidth.dp), fontSize = fontsize.sp, - textAlign = TextAlign.Center) + textAlign = TextAlign.Center + ) } Button( onClick = { TODO("TreeScreen") }) { - Text(text = "AVL tree", + Text( + text = "AVL tree", modifier = Modifier.width(buttonWidth.dp), fontSize = fontsize.sp, - textAlign = TextAlign.Center) + textAlign = TextAlign.Center + ) } Button( onClick = { TODO("TreeScreen") }) { - Text(text = "Red-black tree", + Text( + text = "Red-black tree", modifier = Modifier.width(buttonWidth.dp), fontSize = fontsize.sp, - textAlign = TextAlign.Center) + textAlign = TextAlign.Center + ) } Button( onClick = { exitProcess(0) }) { - Text(text = "Exit", + Text( + text = "Exit", modifier = Modifier.width(buttonWidth.dp), fontSize = fontsize.sp, - textAlign = TextAlign.Center) + textAlign = TextAlign.Center + ) + } + } +} + +fun settingValue(value: String, x: Float, y: Float, isNotFirst: Boolean): String { + val roundedX = (x * 1000).roundToInt() / 1000.0f + val roundedY = (y * 1000).roundToInt() / 1000.0f + if (isNotFirst) { + val pointsLenSize = roundedX.toString().length + roundedY.toString().length + 2 + return roundedX.toString() + ';' + roundedY.toString() + ';' + value.slice(pointsLenSize until value.length) + } else { + return "$roundedX;$roundedY;$value" + } +} + +@Composable +fun BSTScreen() { + val tree = insertAllNodesToTree() + val node = tree.root + val stack = mutableListOf(node?.key) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { current -> tree.find(current) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + current?.let { printNode(it) } + } + + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + removeFile() + writeAllNodesToFile(tree.root, tree) + }) { + Text( + text = "Save tree", + modifier = Modifier.width(250.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + removeFile() + }) { + Text( + text = "Remove BD file", + modifier = Modifier.width(250.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } +} + +@Composable +fun printNode(node: BSNode) { + var p = 0 + var p1 = 0 + for (i in 0 until node.value.length) { + if (node.value[i] == ';') { + p = i + break + } + } + for (i in p + 1 until node.value.length) { + if (node.value[i] == ';') { + p1 = i + break + } + } + val x = remember { mutableStateOf((node.value.slice(0 until p).toFloat()).dp) } + val y = remember { mutableStateOf((node.value.slice(p + 1 until p1).toFloat()).dp) } + Box(modifier = Modifier.offset((x.value), (y.value))) { + Box( + modifier = Modifier + .requiredSize(54.dp) + .clip(CircleShape) + .background(Color.Red) + .pointerInput(x, y) { + detectDragGestures { change, dragAmount -> + change.consume() + x.value += dragAmount.x.toDp() + y.value += dragAmount.y.toDp() + node.value = settingValue(node.value, x.value.value, y.value.value, true) + } + }, + contentAlignment = Alignment.Center + ) { + Text("${node.key}") } } } @@ -69,8 +185,9 @@ fun main() = application { Window( onCloseRequest = ::exitApplication, title = "Trees by ", - state = rememberWindowState(width = 300.dp, height = 300.dp) + state = rememberWindowState(width = 1200.dp, height = 900.dp) ) { - HomeScreen() + HomeScreen() +// BSTScreen() } -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/Node.kt b/app/src/main/kotlin/trees/gui/Node.kt deleted file mode 100644 index 162a5f9..0000000 --- a/app/src/main/kotlin/trees/gui/Node.kt +++ /dev/null @@ -1,41 +0,0 @@ -package trees.gui - -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.unit.dp - -@Composable -fun Node( - key: Int, - value: String?, - x: Double, - y: Double -) { - val x = remember { mutableStateOf(x.dp) } - val y = remember { mutableStateOf(y.dp) } - Box(modifier = Modifier.offset((x.value), (y.value))) { - Box(modifier = Modifier - .requiredSize(54.dp) - .clip(CircleShape) - .background(Color.Red) - .pointerInput(x, y) { - detectDragGestures { change, dragAmount -> - change.consume() - x.value += dragAmount.x.toDp() - y.value += dragAmount.y.toDp() - } - }, - contentAlignment = Alignment.Center) { - Text("$key") - } - } -} \ No newline at end of file From bd364e0c43417355f536b8f7bb389449e564ed47 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 28 Apr 2023 05:50:43 +0300 Subject: [PATCH 66/85] add: folder refactoring - different files to screens --- app/src/main/kotlin/trees/App.kt | 179 +----------------- app/src/main/kotlin/trees/gui/additions.kt | 0 .../main/kotlin/trees/gui/nodeFunctions.kt | 70 +++++++ .../kotlin/trees/gui/screens/BSTScreen.kt | 63 ++++++ .../kotlin/trees/gui/screens/homeScreen.kt | 72 +++++++ 5 files changed, 207 insertions(+), 177 deletions(-) delete mode 100644 app/src/main/kotlin/trees/gui/additions.kt create mode 100644 app/src/main/kotlin/trees/gui/nodeFunctions.kt create mode 100644 app/src/main/kotlin/trees/gui/screens/BSTScreen.kt create mode 100644 app/src/main/kotlin/trees/gui/screens/homeScreen.kt diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 51d8730..51ab270 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,185 +1,10 @@ package trees -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState -import trees.dataBases.BST.insertAllNodesToTree -import trees.dataBases.BST.removeFile -import trees.dataBases.BST.writeAllNodesToFile -import trees.nodes.BSNode -import kotlin.math.roundToInt -import kotlin.system.exitProcess - -@Preview -@Composable -fun HomeScreen() { - val fontsize = 22 - val buttonWidth = 225 - Column( - Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button( - onClick = { - TODO("TreeScreen") -// BSTScreen() - }) { - Text( - text = "Binary search tree", - modifier = Modifier.width(buttonWidth.dp), - fontSize = fontsize.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - TODO("TreeScreen") - }) { - Text( - text = "AVL tree", - modifier = Modifier.width(buttonWidth.dp), - fontSize = fontsize.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - TODO("TreeScreen") - }) { - Text( - text = "Red-black tree", - modifier = Modifier.width(buttonWidth.dp), - fontSize = fontsize.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - exitProcess(0) - }) { - Text( - text = "Exit", - modifier = Modifier.width(buttonWidth.dp), - fontSize = fontsize.sp, - textAlign = TextAlign.Center - ) - } - } -} - -fun settingValue(value: String, x: Float, y: Float, isNotFirst: Boolean): String { - val roundedX = (x * 1000).roundToInt() / 1000.0f - val roundedY = (y * 1000).roundToInt() / 1000.0f - if (isNotFirst) { - val pointsLenSize = roundedX.toString().length + roundedY.toString().length + 2 - return roundedX.toString() + ';' + roundedY.toString() + ';' + value.slice(pointsLenSize until value.length) - } else { - return "$roundedX;$roundedY;$value" - } -} - -@Composable -fun BSTScreen() { - val tree = insertAllNodesToTree() - val node = tree.root - val stack = mutableListOf(node?.key) - while (stack.isNotEmpty()) { - val current = stack.removeLast()?.let { current -> tree.find(current) } - if (current?.left != null) - current.left?.key.apply(stack::add) - if (current?.right != null) - current.right?.key.apply(stack::add) - current?.let { printNode(it) } - } - - Column( - Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button( - onClick = { - removeFile() - writeAllNodesToFile(tree.root, tree) - }) { - Text( - text = "Save tree", - modifier = Modifier.width(250.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - removeFile() - }) { - Text( - text = "Remove BD file", - modifier = Modifier.width(250.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - } -} - -@Composable -fun printNode(node: BSNode) { - var p = 0 - var p1 = 0 - for (i in 0 until node.value.length) { - if (node.value[i] == ';') { - p = i - break - } - } - for (i in p + 1 until node.value.length) { - if (node.value[i] == ';') { - p1 = i - break - } - } - val x = remember { mutableStateOf((node.value.slice(0 until p).toFloat()).dp) } - val y = remember { mutableStateOf((node.value.slice(p + 1 until p1).toFloat()).dp) } - Box(modifier = Modifier.offset((x.value), (y.value))) { - Box( - modifier = Modifier - .requiredSize(54.dp) - .clip(CircleShape) - .background(Color.Red) - .pointerInput(x, y) { - detectDragGestures { change, dragAmount -> - change.consume() - x.value += dragAmount.x.toDp() - y.value += dragAmount.y.toDp() - node.value = settingValue(node.value, x.value.value, y.value.value, true) - } - }, - contentAlignment = Alignment.Center - ) { - Text("${node.key}") - } - } -} +import trees.gui.screens.homeScreen fun main() = application { Window( @@ -187,7 +12,7 @@ fun main() = application { title = "Trees by ", state = rememberWindowState(width = 1200.dp, height = 900.dp) ) { - HomeScreen() + homeScreen() // BSTScreen() } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/additions.kt b/app/src/main/kotlin/trees/gui/additions.kt deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt new file mode 100644 index 0000000..857e67c --- /dev/null +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -0,0 +1,70 @@ +package trees.gui + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.dp +import trees.nodes.BSNode +import kotlin.math.roundToInt + +fun settingValue(value: String, x: Float, y: Float, isNotFirst: Boolean): String { + val roundedX = (x * 1000).roundToInt() / 1000.0f + val roundedY = (y * 1000).roundToInt() / 1000.0f + if (isNotFirst) { + val pointsLenSize = roundedX.toString().length + roundedY.toString().length + 2 + return roundedX.toString() + ';' + roundedY.toString() + ';' + value.slice(pointsLenSize until value.length) + } else { + return "$roundedX;$roundedY;$value" + } +} + +@Composable +fun printNode(node: BSNode) { + var p = 0 + var p1 = 0 + for (i in 0 until node.value.length) { + if (node.value[i] == ';') { + p = i + break + } + } + for (i in p + 1 until node.value.length) { + if (node.value[i] == ';') { + p1 = i + break + } + } + val x = remember { mutableStateOf((node.value.slice(0 until p).toFloat()).dp) } + val y = remember { mutableStateOf((node.value.slice(p + 1 until p1).toFloat()).dp) } + Box(modifier = Modifier.offset((x.value), (y.value))) { + Box( + modifier = Modifier + .requiredSize(54.dp) + .clip(CircleShape) + .background(Color.Red) + .pointerInput(x, y) { + detectDragGestures { change, dragAmount -> + change.consume() + x.value += dragAmount.x.toDp() + y.value += dragAmount.y.toDp() + node.value = settingValue(node.value, x.value.value, y.value.value, true) + } + }, + contentAlignment = Alignment.Center + ) { + Text("${node.key}") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt new file mode 100644 index 0000000..23d39bd --- /dev/null +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -0,0 +1,63 @@ +package trees.gui.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import trees.dataBases.BST.insertAllNodesToTree +import trees.dataBases.BST.removeFile +import trees.dataBases.BST.writeAllNodesToFile +import trees.gui.printNode + +@Composable +fun BSTScreen() { + val tree = insertAllNodesToTree() + val node = tree.root + val stack = mutableListOf(node?.key) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { current -> tree.find(current) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + current?.let { printNode(it) } + } + + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + removeFile() + writeAllNodesToFile(tree.root, tree) + }) { + Text( + text = "Save tree", + modifier = Modifier.width(250.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + removeFile() + }) { + Text( + text = "Remove BD file", + modifier = Modifier.width(250.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } +} diff --git a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt new file mode 100644 index 0000000..ebf6d67 --- /dev/null +++ b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt @@ -0,0 +1,72 @@ +package trees.gui.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlin.system.exitProcess + +@Composable +fun homeScreen() { + val fontsize = 22 + val buttonWidth = 225 + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + TODO("TreeScreen") +// BSTScreen() + }) { + Text( + text = "Binary search tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + TODO("TreeScreen") + }) { + Text( + text = "AVL tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + TODO("TreeScreen") + }) { + Text( + text = "Red-black tree", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + exitProcess(0) + }) { + Text( + text = "Exit", + modifier = Modifier.width(buttonWidth.dp), + fontSize = fontsize.sp, + textAlign = TextAlign.Center + ) + } + } +} \ No newline at end of file From 892320d30b77243aa96e318281339a2aec34c080 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 28 Apr 2023 15:13:09 +0300 Subject: [PATCH 67/85] feat: implemented navigation between homeScreen and BSTScreen --- app/src/main/kotlin/trees/App.kt | 16 ++++++++++++++-- .../main/kotlin/trees/gui/navigation/Screens.kt | 6 ++++++ .../main/kotlin/trees/gui/screens/homeScreen.kt | 8 ++------ 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 app/src/main/kotlin/trees/gui/navigation/Screens.kt diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 51ab270..b0e25a2 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -1,9 +1,15 @@ package trees +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState +import trees.gui.navigation.Screen +import trees.gui.screens.BSTScreen import trees.gui.screens.homeScreen fun main() = application { @@ -12,7 +18,13 @@ fun main() = application { title = "Trees by ", state = rememberWindowState(width = 1200.dp, height = 900.dp) ) { - homeScreen() -// BSTScreen() + var screenState by remember { mutableStateOf(Screen.homeScreen) } + when (val screen = screenState) { + is Screen.homeScreen -> + homeScreen(toBST = { screenState = Screen.BSTScreen }) + + is Screen.BSTScreen -> + BSTScreen() + } } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/navigation/Screens.kt b/app/src/main/kotlin/trees/gui/navigation/Screens.kt new file mode 100644 index 0000000..13f5483 --- /dev/null +++ b/app/src/main/kotlin/trees/gui/navigation/Screens.kt @@ -0,0 +1,6 @@ +package trees.gui.navigation + +sealed class Screen { + object homeScreen : Screen() + object BSTScreen : Screen() +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt index ebf6d67..31bf422 100644 --- a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.unit.sp import kotlin.system.exitProcess @Composable -fun homeScreen() { +fun homeScreen(toBST: () -> Unit) { val fontsize = 22 val buttonWidth = 225 Column( @@ -23,11 +23,7 @@ fun homeScreen() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - Button( - onClick = { - TODO("TreeScreen") -// BSTScreen() - }) { + Button(onClick = toBST) { Text( text = "Binary search tree", modifier = Modifier.width(buttonWidth.dp), From e38d03ad6fab2fe771e0d88a8d1c1cfd7f744c24 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Fri, 28 Apr 2023 18:38:39 +0300 Subject: [PATCH 68/85] feat(BSTScreen): back to menu buttom --- app/src/main/kotlin/trees/App.kt | 2 +- .../kotlin/trees/gui/screens/BSTScreen.kt | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index b0e25a2..2a692fa 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -24,7 +24,7 @@ fun main() = application { homeScreen(toBST = { screenState = Screen.BSTScreen }) is Screen.BSTScreen -> - BSTScreen() + BSTScreen(toMenu = { screenState = Screen.homeScreen}) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 23d39bd..a1f2242 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -18,7 +18,7 @@ import trees.dataBases.BST.writeAllNodesToFile import trees.gui.printNode @Composable -fun BSTScreen() { +fun BSTScreen(toMenu: () -> Unit) { val tree = insertAllNodesToTree() val node = tree.root val stack = mutableListOf(node?.key) @@ -43,7 +43,7 @@ fun BSTScreen() { }) { Text( text = "Save tree", - modifier = Modifier.width(250.dp), + modifier = Modifier.width(150.dp), fontSize = 16.sp, textAlign = TextAlign.Center ) @@ -53,8 +53,19 @@ fun BSTScreen() { removeFile() }) { Text( - text = "Remove BD file", - modifier = Modifier.width(250.dp), + text = "Clear DB file", + modifier = Modifier.width(150.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(150.dp), fontSize = 16.sp, textAlign = TextAlign.Center ) From da005f26f7abe5abe7d989ee13f8c534ecd64487 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 2 May 2023 08:05:12 +0300 Subject: [PATCH 69/85] fix: use find function, privacy change, return of balance --- app/src/main/kotlin/trees/RBTree.kt | 20 ++++--------------- .../trees/abstract_trees/BalanceTree.kt | 2 +- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/app/src/main/kotlin/trees/RBTree.kt b/app/src/main/kotlin/trees/RBTree.kt index 9d95351..e85738f 100644 --- a/app/src/main/kotlin/trees/RBTree.kt +++ b/app/src/main/kotlin/trees/RBTree.kt @@ -41,19 +41,7 @@ class RBTree, V> : BalanceTree>() { } override fun remove(key: K) { - var current = root - - while (current != null && current.key != key) { - current = if (key < current.key) { - current.left - } else { - current.right - } - } - - if (current == null) { - throw IllegalStateException("There is no such node with key $key in the tree") - } + val current = find(key) ?: throw IllegalStateException("There is no such node with key $key in the tree") val movedUpNode: RBNode? val deletedNodeColor: Color @@ -82,13 +70,13 @@ class RBTree, V> : BalanceTree>() { } } - override fun balance(node: RBNode, afterRemove: Boolean): RBNode? { + override fun balance(node: RBNode, afterRemove: Boolean): RBNode { if (afterRemove) { balanceAfterRemove(node) } else { balanceAfterAdd(node) } - return null + return node } private fun balanceAfterAdd(node: RBNode) { @@ -234,7 +222,7 @@ class RBTree, V> : BalanceTree>() { } else { node.isTemp = true var newChild: RBNode? = node - + if (node.color == Color.RED) { newChild = null } diff --git a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt index c3795db..9e01fc0 100644 --- a/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt +++ b/app/src/main/kotlin/trees/abstract_trees/BalanceTree.kt @@ -3,5 +3,5 @@ package trees.abstract_trees import trees.nodes.Node abstract class BalanceTree, V, NODE_TYPE : Node> : BinaryTree() { - abstract fun balance(node: NODE_TYPE, afterRemove: Boolean = false): NODE_TYPE? + protected abstract fun balance(node: NODE_TYPE, afterRemove: Boolean = false): NODE_TYPE } From 5f01f20fbc7b83c07e6b44a509d5fe6c23a38aca Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 2 May 2023 18:21:30 +0300 Subject: [PATCH 70/85] delete: RBBalanceTest --- app/src/test/kotlin/trees/balance/RBBalanceTest.kt | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 app/src/test/kotlin/trees/balance/RBBalanceTest.kt diff --git a/app/src/test/kotlin/trees/balance/RBBalanceTest.kt b/app/src/test/kotlin/trees/balance/RBBalanceTest.kt deleted file mode 100644 index 01c726d..0000000 --- a/app/src/test/kotlin/trees/balance/RBBalanceTest.kt +++ /dev/null @@ -1,4 +0,0 @@ -package trees.balance - -class RBBalanceTest { -} \ No newline at end of file From d231b6e0345fb294bbd51a9da6783a0a0ecde744 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Tue, 2 May 2023 18:25:58 +0300 Subject: [PATCH 71/85] add: Neo4j database implementation Implemented import, export, open and close functions to work with neo4j --- app/build.gradle.kts | 5 +- .../trees/dataBases/RBT/Neo4jRepository.kt | 100 ++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 app/src/main/kotlin/trees/dataBases/RBT/Neo4jRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6355c7e..0c61ab2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. id("org.jetbrains.kotlin.jvm") version "1.8.10" jacoco // Apply the application plugin to add support for building a CLI application in Java. @@ -8,22 +7,20 @@ plugins { } repositories { - // Use Maven Central for resolving dependencies. mavenCentral() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") google() } dependencies { - // Use the Kotlin JUnit 5 integration. testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") - // Use the JUnit 5 integration. testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") // This dependency is used by the application. implementation ("org.apache.commons:commons-csv:1.5") implementation("com.google.guava:guava:31.1-jre") + implementation("org.neo4j.driver", "neo4j-java-driver", "5.7.0") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") diff --git a/app/src/main/kotlin/trees/dataBases/RBT/Neo4jRepository.kt b/app/src/main/kotlin/trees/dataBases/RBT/Neo4jRepository.kt new file mode 100644 index 0000000..cb7faac --- /dev/null +++ b/app/src/main/kotlin/trees/dataBases/RBT/Neo4jRepository.kt @@ -0,0 +1,100 @@ +package trees.dataBases.RBT + +import org.neo4j.driver.* +import org.neo4j.driver.exceptions.SessionExpiredException +import org.neo4j.driver.exceptions.value.Uncoercible +import trees.nodes.RBNode +import java.io.Closeable +import java.io.IOException + +class Neo4jRepository: Closeable { + private var driver: Driver? = null + + fun exportRBTree(root: RBNode) { + val session = driver?.session() ?: throw IOException("Driver is not open") + session.executeWrite { tx -> + cleanDataBase(tx) + tx.run(genExportRBNodes(root)) + } + session.close() + } + + + fun importRBTree(): RBNode? { + val session = driver?.session() ?: throw IOException("Driver is not open") + val res: RBNode? = session.executeRead { tx -> + importRBNodes(tx) + } + session.close() + return res + } + + private fun cleanDataBase(tx: TransactionContext) { + tx.run("MATCH (n: RBNode) DETACH DELETE n") + } + + private fun genExportRBNodes(root: RBNode): String { + val sb = StringBuilder() + traverseExportRBNode(sb, root) + return sb.toString() + } + + private fun traverseExportRBNode( + sb: StringBuilder, + nodeView: RBNode, + ) { + sb.append( + "CREATE (:RBNode {key : \"${nodeView.key}\", " + + "value: \"${nodeView.value}\"" + ) + } + + private fun importRBNodes(tx: TransactionContext): RBNode? { + val nodeAndKeysRecords = tx.run( + "MATCH (p: RBNode)" + + "RETURN p.key AS key, p.value AS value" + ) + return parseRBNodes(nodeAndKeysRecords) + } + + private class NodeAndKeys( + val nv: RBNode, + ) + + private fun parseRBNodes(nodeAndKeysRecords: Result): RBNode? { + val key2nk = mutableMapOf() + for (nkRecord in nodeAndKeysRecords) { + try { + val key = nkRecord["key"].asInt() + val value = nkRecord["value"].asString() + val nv = RBNode(key, value) + key2nk[key] = NodeAndKeys(nv) + } catch (ex: Uncoercible) { + throw IOException("Invalid nodes label in the database", ex) + } + } + val nks = key2nk.values.toTypedArray() + if (nks.isEmpty()) { // if nodeAndKeysRecords was empty + return null + } + if (key2nk.values.size != 1) { + throw IOException("Found ${key2nk.values.size} nodes without parents in database, expected only 1 node") + } + val root = key2nk.values.first().nv + return root + } + + fun open(uri: String, username: String, password: String) { + try { + driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password)) + } catch (ex: IllegalArgumentException) { + throw IOException("Wrong URI", ex) + } catch (ex: SessionExpiredException) { + throw IOException("Session failed, try restarting the app", ex) + } + } + + override fun close() { + driver?.close() + } +} \ No newline at end of file From 9fc654ea0e8f5e3032d33f3705dd184e41f02149 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Sun, 30 Apr 2023 11:18:09 +0300 Subject: [PATCH 72/85] add: menu of functions for actions on trees, implementation of the remaining windows --- app/src/main/kotlin/trees/App.kt | 10 +- .../kotlin/trees/gui/navigation/Screens.kt | 2 + .../kotlin/trees/gui/screens/AVLScreen.kt | 111 ++++++++++++++ .../kotlin/trees/gui/screens/BSTScreen.kt | 143 ++++++++++++------ .../main/kotlin/trees/gui/screens/RBScreen.kt | 116 ++++++++++++++ .../kotlin/trees/gui/screens/homeScreen.kt | 16 +- 6 files changed, 344 insertions(+), 54 deletions(-) create mode 100644 app/src/main/kotlin/trees/gui/screens/AVLScreen.kt create mode 100644 app/src/main/kotlin/trees/gui/screens/RBScreen.kt diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 2a692fa..a74e9ff 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -10,6 +10,8 @@ import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import trees.gui.navigation.Screen import trees.gui.screens.BSTScreen +import trees.gui.screens.AVLScreen +import trees.gui.screens.RBScreen import trees.gui.screens.homeScreen fun main() = application { @@ -21,10 +23,16 @@ fun main() = application { var screenState by remember { mutableStateOf(Screen.homeScreen) } when (val screen = screenState) { is Screen.homeScreen -> - homeScreen(toBST = { screenState = Screen.BSTScreen }) + homeScreen(toBST = { screenState = Screen.BSTScreen }, toAVL = { screenState = Screen.AVLScreen }, toRB = { screenState = Screen.RBScreen }) is Screen.BSTScreen -> BSTScreen(toMenu = { screenState = Screen.homeScreen}) + + is Screen.AVLScreen -> + AVLScreen(toMenu = { screenState = Screen.homeScreen}) + + is Screen.RBScreen -> + RBScreen(toMenu = { screenState = Screen.homeScreen}) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/navigation/Screens.kt b/app/src/main/kotlin/trees/gui/navigation/Screens.kt index 13f5483..f3448e7 100644 --- a/app/src/main/kotlin/trees/gui/navigation/Screens.kt +++ b/app/src/main/kotlin/trees/gui/navigation/Screens.kt @@ -3,4 +3,6 @@ package trees.gui.navigation sealed class Screen { object homeScreen : Screen() object BSTScreen : Screen() + object AVLScreen : Screen() + object RBScreen : Screen() } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt new file mode 100644 index 0000000..4509cfe --- /dev/null +++ b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt @@ -0,0 +1,111 @@ +package trees.gui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import trees.BSTree +import trees.dataBases.BST.insertAllNodesToTree +import trees.dataBases.BST.removeFile +import trees.dataBases.BST.writeAllNodesToFile +import trees.gui.printNode +import trees.nodes.BSNode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.modifier.modifierLocalMapOf + +@Composable +fun AVLScreen(toMenu: () -> Unit) { + Box() { + Column( + Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + Column( + Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + }, + ) { + Text( + text = "Insert node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Remove node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Find node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + Column( + Modifier.width(200.dp).height(180.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + }) { + Text( + text = "Save tree", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Clear DB file", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index a1f2242..a0cddce 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -1,25 +1,36 @@ package trees.gui.screens -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Button import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import trees.BSTree import trees.dataBases.BST.insertAllNodesToTree import trees.dataBases.BST.removeFile import trees.dataBases.BST.writeAllNodesToFile import trees.gui.printNode +import trees.nodes.BSNode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.modifier.modifierLocalMapOf @Composable fun BSTScreen(toMenu: () -> Unit) { val tree = insertAllNodesToTree() +// val tree = BSTree() +// tree.root = BSNode(123, "717.875;217.375;.25;dsfsafds") +// tree.root?.left = BSNode(75, "633.625;481.875;875;kkdks") +// tree.root?.right = BSNode(3459, "222.388;486.055;18;1asdf") val node = tree.root val stack = mutableListOf(node?.key) while (stack.isNotEmpty()) { @@ -30,45 +41,89 @@ fun BSTScreen(toMenu: () -> Unit) { current.right?.key.apply(stack::add) current?.let { printNode(it) } } - - Column( - Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button( - onClick = { - removeFile() - writeAllNodesToFile(tree.root, tree) - }) { - Text( - text = "Save tree", - modifier = Modifier.width(150.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - removeFile() - }) { - Text( - text = "Clear DB file", - modifier = Modifier.width(150.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - toMenu() - }) { - Text( - text = "Back to menu", - modifier = Modifier.width(150.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) + Box() { + Column( + Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + Column( + Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + }, + ) { + Text( + text = "Insert node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Remove node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Find node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + Column( + Modifier.width(200.dp).height(180.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + removeFile() + writeAllNodesToFile(tree.root, tree) + }) { + Text( + text = "Save tree", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + removeFile() + }) { + Text( + text = "Clear DB file", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } } } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt new file mode 100644 index 0000000..54d65c6 --- /dev/null +++ b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt @@ -0,0 +1,116 @@ +package trees.gui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import trees.BSTree +import trees.dataBases.BST.insertAllNodesToTree +import trees.dataBases.BST.removeFile +import trees.dataBases.BST.writeAllNodesToFile +import trees.gui.printNode +import trees.nodes.BSNode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.modifier.modifierLocalMapOf + +@Composable +fun RBScreen(toMenu: () -> Unit) { + Box() { + Column( + Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + Column( + Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + }, + ) { + Text( + text = "Insert node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Remove node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Find node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + Column( + Modifier.width(200.dp).height(180.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + }) { + Text( + text = "Save tree", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + }) { + Text( + text = "Clear DB file", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + } + Column( + Modifier.offset() + ) { + + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt index 31bf422..cbc5829 100644 --- a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt @@ -15,7 +15,11 @@ import androidx.compose.ui.unit.sp import kotlin.system.exitProcess @Composable -fun homeScreen(toBST: () -> Unit) { +fun homeScreen( + toBST: () -> Unit, + toAVL: () -> Unit, + toRB: () -> Unit +) { val fontsize = 22 val buttonWidth = 225 Column( @@ -31,10 +35,7 @@ fun homeScreen(toBST: () -> Unit) { textAlign = TextAlign.Center ) } - Button( - onClick = { - TODO("TreeScreen") - }) { + Button(onClick = toAVL) { Text( text = "AVL tree", modifier = Modifier.width(buttonWidth.dp), @@ -42,10 +43,7 @@ fun homeScreen(toBST: () -> Unit) { textAlign = TextAlign.Center ) } - Button( - onClick = { - TODO("TreeScreen") - }) { + Button(onClick = toRB) { Text( text = "Red-black tree", modifier = Modifier.width(buttonWidth.dp), From 65bc6778d94a1dfdaa5d5ce3f897b98fe4e91bac Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Mon, 1 May 2023 19:24:58 +0300 Subject: [PATCH 73/85] add: text fields --- .../kotlin/trees/gui/screens/AVLScreen.kt | 21 ++++++++++++++++-- .../kotlin/trees/gui/screens/BSTScreen.kt | 22 +++++++++++++++++-- .../main/kotlin/trees/gui/screens/RBScreen.kt | 21 ++++++++++++++++-- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt index 4509cfe..d127143 100644 --- a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt @@ -4,10 +4,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -24,6 +26,7 @@ import trees.nodes.BSNode import androidx.compose.ui.graphics.Color import androidx.compose.ui.modifier.modifierLocalMapOf +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AVLScreen(toMenu: () -> Unit) { Box() { @@ -33,10 +36,24 @@ fun AVLScreen(toMenu: () -> Unit) { horizontalAlignment = Alignment.Start, ) { Column( - Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { + var key by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(vertical = 15.dp).clip(RoundedCornerShape(25.dp)), + value = key, + onValueChange = { key = it }, + label = { Text("Enter key") } + ) + var value by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(bottom = 15.dp).clip(RoundedCornerShape(25.dp)), + value = value, + onValueChange = { value = it }, + label = { Text("Enter value") } + ) Button( onClick = { }, diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index a0cddce..8da3f61 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -4,10 +4,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -22,8 +24,10 @@ import trees.dataBases.BST.writeAllNodesToFile import trees.gui.printNode import trees.nodes.BSNode import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.layout import androidx.compose.ui.modifier.modifierLocalMapOf +@OptIn(ExperimentalMaterial3Api::class) @Composable fun BSTScreen(toMenu: () -> Unit) { val tree = insertAllNodesToTree() @@ -48,10 +52,24 @@ fun BSTScreen(toMenu: () -> Unit) { horizontalAlignment = Alignment.Start, ) { Column( - Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { + var key by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(vertical = 15.dp).clip(RoundedCornerShape(25.dp)), + value = key, + onValueChange = { key = it }, + label = { Text("Enter key") } + ) + var value by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(bottom = 15.dp).clip(RoundedCornerShape(25.dp)), + value = value, + onValueChange = { value = it }, + label = { Text("Enter value") } + ) Button( onClick = { }, diff --git a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt index 54d65c6..baa0cbb 100644 --- a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt @@ -4,10 +4,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -24,6 +26,7 @@ import trees.nodes.BSNode import androidx.compose.ui.graphics.Color import androidx.compose.ui.modifier.modifierLocalMapOf +@OptIn(ExperimentalMaterial3Api::class) @Composable fun RBScreen(toMenu: () -> Unit) { Box() { @@ -33,10 +36,24 @@ fun RBScreen(toMenu: () -> Unit) { horizontalAlignment = Alignment.Start, ) { Column( - Modifier.width(200.dp).height(180.dp).offset(0.dp, 0.dp), + Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { + var key by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(vertical = 15.dp).clip(RoundedCornerShape(25.dp)), + value = key, + onValueChange = { key = it }, + label = { Text("Enter key") } + ) + var value by remember { mutableStateOf("") } + TextField( + modifier = Modifier.width(180.dp).padding(bottom = 15.dp).clip(RoundedCornerShape(25.dp)), + value = value, + onValueChange = { value = it }, + label = { Text("Enter value") } + ) Button( onClick = { }, From 46c33e8713c1eb12182bfeb4d6d19f28ee1391ab Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Tue, 2 May 2023 08:04:02 +0300 Subject: [PATCH 74/85] feat(BSTScreen): implemented add, remove, find functions --- app/src/main/kotlin/trees/App.kt | 15 +- .../main/kotlin/trees/gui/nodeFunctions.kt | 110 ++++++- .../kotlin/trees/gui/screens/BSTScreen.kt | 289 +++++++++++------- 3 files changed, 290 insertions(+), 124 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index a74e9ff..e687c94 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -9,8 +9,8 @@ import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import trees.gui.navigation.Screen -import trees.gui.screens.BSTScreen import trees.gui.screens.AVLScreen +import trees.gui.screens.BSTScreen import trees.gui.screens.RBScreen import trees.gui.screens.homeScreen @@ -18,21 +18,24 @@ fun main() = application { Window( onCloseRequest = ::exitApplication, title = "Trees by ", - state = rememberWindowState(width = 1200.dp, height = 900.dp) + state = rememberWindowState(width = 1920.dp, height = 1080.dp) ) { var screenState by remember { mutableStateOf(Screen.homeScreen) } when (val screen = screenState) { is Screen.homeScreen -> - homeScreen(toBST = { screenState = Screen.BSTScreen }, toAVL = { screenState = Screen.AVLScreen }, toRB = { screenState = Screen.RBScreen }) + homeScreen( + toBST = { screenState = Screen.BSTScreen }, + toAVL = { screenState = Screen.AVLScreen }, + toRB = { screenState = Screen.RBScreen }) is Screen.BSTScreen -> - BSTScreen(toMenu = { screenState = Screen.homeScreen}) + BSTScreen(toMenu = { screenState = Screen.homeScreen }, toBST = { screenState = Screen.BSTScreen }) is Screen.AVLScreen -> - AVLScreen(toMenu = { screenState = Screen.homeScreen}) + AVLScreen(toMenu = { screenState = Screen.homeScreen }) is Screen.RBScreen -> - RBScreen(toMenu = { screenState = Screen.homeScreen}) + RBScreen(toMenu = { screenState = Screen.homeScreen }) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt index 857e67c..c06963d 100644 --- a/app/src/main/kotlin/trees/gui/nodeFunctions.kt +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -5,62 +5,146 @@ import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp import trees.nodes.BSNode import kotlin.math.roundToInt -fun settingValue(value: String, x: Float, y: Float, isNotFirst: Boolean): String { +fun getParent(key: Int, rootNode: BSNode): BSNode { + var current: BSNode = rootNode + while (current.left?.key != key && current.right?.key != key) { + println("${current.key} $key ${current.right?.key} ${current.left?.key}") + current = when { + current.key < key -> current.right!! + current.key > key -> current.left!! + else -> return current + } + } + return current +} + +fun findBracketPoint(value: String): Int { + var count = 0 + for (i in value.indices) { + if (value[i] == ';') { + count++ + } + if (count == 2) { + return i + } + } + return 0 +} + +@Composable +fun printLine(start: BSNode, end: BSNode) { + val coorSt = getCoordinate(start.value) + val x0 = coorSt.x.dp + val y0 = coorSt.y.dp + val coorEn = getCoordinate(end.value) + val x1 = coorEn.x.dp + val y1 = coorEn.y.dp + Box(modifier = Modifier.offset(x0 / 2 + 13.dp, y0 / 2 + 10.dp)) { + Box(modifier = Modifier.offset(x0 / 2 + 13.dp, y0 / 2 + 10.dp)) { + Box( + modifier = Modifier.size(x1 - x0, y1 - y0) + .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 1f) } + ) + } + } +} + +class Coordinate( + var x: Float, + var y: Float +) + +fun getCoordinate(value: String): Coordinate { + var pointerToBracket1 = 0 + var pointerToBracket2 = 0 + for (i in value.indices) { + if (value[i] == ';') { + pointerToBracket1 = i + break + } + } + for (i in pointerToBracket1 + 1 until value.length) { + if (value[i] == ';') { + pointerToBracket2 = i + break + } + } + val x = value.slice(0 until pointerToBracket1).toFloat() + val y = value.slice(pointerToBracket1 + 1 until pointerToBracket2).toFloat() + return Coordinate(x, y) +} + +fun settingValue(value: String, x: Float, y: Float, pointerToValue: Int, isNotFirst: Boolean): String { val roundedX = (x * 1000).roundToInt() / 1000.0f val roundedY = (y * 1000).roundToInt() / 1000.0f if (isNotFirst) { - val pointsLenSize = roundedX.toString().length + roundedY.toString().length + 2 - return roundedX.toString() + ';' + roundedY.toString() + ';' + value.slice(pointsLenSize until value.length) + return roundedX.toString() + ';' + roundedY.toString() + ';' + value.slice(pointerToValue + 1 until value.length) } else { return "$roundedX;$roundedY;$value" } } +@OptIn(ExperimentalComposeUiApi::class) @Composable -fun printNode(node: BSNode) { - var p = 0 +fun printNode(node: BSNode, difColour: Boolean = false) { var p1 = 0 - for (i in 0 until node.value.length) { + var p2 = 0 + for (i in node.value.indices) { if (node.value[i] == ';') { - p = i + p1 = i break } } - for (i in p + 1 until node.value.length) { + for (i in p1 + 1 until node.value.length) { if (node.value[i] == ';') { - p1 = i + p2 = i break } } - val x = remember { mutableStateOf((node.value.slice(0 until p).toFloat()).dp) } - val y = remember { mutableStateOf((node.value.slice(p + 1 until p1).toFloat()).dp) } + val coordinate = getCoordinate(node.value) + val x = remember(node.key) { mutableStateOf((coordinate.x).dp) } + val y = remember(node.key) { mutableStateOf((coordinate.y).dp) } + val valueToPrint = node.value.slice(p2 + 1 until node.value.length) + val color = if (difColour) { + Color.Green + } else { + Color.Red + } Box(modifier = Modifier.offset((x.value), (y.value))) { Box( modifier = Modifier .requiredSize(54.dp) .clip(CircleShape) - .background(Color.Red) + .background(color) .pointerInput(x, y) { detectDragGestures { change, dragAmount -> change.consume() x.value += dragAmount.x.toDp() y.value += dragAmount.y.toDp() - node.value = settingValue(node.value, x.value.value, y.value.value, true) } + } + .onPointerEvent(eventType = PointerEventType.Release) { + node.value = settingValue(node.value, x.value.value, y.value.value, p2, true) }, contentAlignment = Alignment.Center ) { diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 8da3f61..3f08931 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -1,19 +1,17 @@ package trees.gui.screens +import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -21,125 +19,206 @@ import trees.BSTree import trees.dataBases.BST.insertAllNodesToTree import trees.dataBases.BST.removeFile import trees.dataBases.BST.writeAllNodesToFile -import trees.gui.printNode +import trees.gui.* import trees.nodes.BSNode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.layout -import androidx.compose.ui.modifier.modifierLocalMapOf + +var treeInit = false +var findMode = false +var enteredValue: String = "" +var enteredKey: String = "" @OptIn(ExperimentalMaterial3Api::class) +@Preview @Composable -fun BSTScreen(toMenu: () -> Unit) { - val tree = insertAllNodesToTree() -// val tree = BSTree() -// tree.root = BSNode(123, "717.875;217.375;.25;dsfsafds") -// tree.root?.left = BSNode(75, "633.625;481.875;875;kkdks") -// tree.root?.right = BSNode(3459, "222.388;486.055;18;1asdf") - val node = tree.root +fun BSTScreen(toMenu: () -> Unit, toBST: () -> Unit) { + val tree = remember { mutableStateOf(BSTree()) } + var screenReload by remember { mutableStateOf(false) } + if (screenReload) { + screenReload = false + } + if (!treeInit) { + tree.value = insertAllNodesToTree() + treeInit = !treeInit + } + val node = tree.value.root val stack = mutableListOf(node?.key) while (stack.isNotEmpty()) { - val current = stack.removeLast()?.let { current -> tree.find(current) } + val current = stack.removeLast()?.let { current -> tree.value.find(current) } if (current?.left != null) current.left?.key.apply(stack::add) if (current?.right != null) current.right?.key.apply(stack::add) - current?.let { printNode(it) } + current?.let { + if (findMode && (enteredKey.toIntOrNull() != null) && it.key == enteredKey.toInt() && (tree.value.find( + enteredKey.toInt() + ) != null) + ) { + printNode(it, true) + } else { + printNode(it) + } + val parent = tree.value.root?.let { it1 -> getParent(it.key, it1) } + parent?.let { it1 -> + if (tree.value.root != it) { + printLine(it1, it) + } + } + } } - Box() { + Box { Column( Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start, ) { - Column( - Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + Row( + verticalAlignment = Alignment.Top ) { - var key by remember { mutableStateOf("") } - TextField( - modifier = Modifier.width(180.dp).padding(vertical = 15.dp).clip(RoundedCornerShape(25.dp)), - value = key, - onValueChange = { key = it }, - label = { Text("Enter key") } - ) - var value by remember { mutableStateOf("") } - TextField( - modifier = Modifier.width(180.dp).padding(bottom = 15.dp).clip(RoundedCornerShape(25.dp)), - value = value, - onValueChange = { value = it }, - label = { Text("Enter value") } - ) - Button( - onClick = { - }, + Column( + verticalArrangement = Arrangement.SpaceBetween ) { - Text( - text = "Insert node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - }) { - Text( - text = "Remove node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - }) { - Text( - text = "Find node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - } - Column( - Modifier.width(200.dp).height(180.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button( - onClick = { - removeFile() - writeAllNodesToFile(tree.root, tree) - }) { - Text( - text = "Save tree", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - removeFile() - }) { - Text( - text = "Clear DB file", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) + Column( + Modifier.width(200.dp).height(150.dp).offset(0.dp, 0.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + findMode = false + var coordinate = Coordinate(0f,0f) + var parent = BSNode(0,"") + var value = settingValue(enteredValue, coordinate.x+50.0f, coordinate.y+.0f, 0, false) + var curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + if (tree.value.root?.key != enteredKey.toInt()) { + parent = getParent(enteredKey.toInt(), tree.value.root!!) + coordinate = getCoordinate(parent.value) + println("${coordinate.x} ${coordinate.y}") + } + val point = findBracketPoint(value) + val offset = 85f + value = if (parent.left?.key == enteredKey.toInt()) { + settingValue(value, coordinate.x-offset, coordinate.y+offset, point, true) + } else { + settingValue(value, coordinate.x+offset, coordinate.y+offset, point, true) + } + curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + screenReload = true + }, + ) { + Text( + text = "Insert node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + if (findMode) { + findMode = false + } + tree.value.remove(enteredKey.toInt()) + screenReload = true + }) { + Text( + text = "Remove node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + findMode = !findMode + screenReload = true + }) { + Text( + text = "Find mode", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + Column( + Modifier.width(200.dp).height(150.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + removeFile() + writeAllNodesToFile(tree.value.root, tree.value) + }) { + Text( + text = "Save tree", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + removeFile() + }) { + Text( + text = "Clear DB file", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + treeInit = !treeInit + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } } - Button( - onClick = { - toMenu() - }) { - Text( - text = "Back to menu", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) + Column() { + Column( + Modifier.width(200.dp).height(260.dp).offset(25.dp, 10.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + val fieldsWidth = 140 + var key by remember { mutableStateOf("") } + OutlinedTextField( + modifier = Modifier + .width(fieldsWidth.dp) + .height(65.dp) + .padding(vertical = 5.dp), + value = key, + onValueChange = { + key = it + enteredKey = it + }, + label = { Text("Enter key", textAlign = TextAlign.Center) } + ) + var value by remember { mutableStateOf("") } + OutlinedTextField( + modifier = Modifier + .width(fieldsWidth.dp) + .height(65.dp) + .padding(vertical = 5.dp), + value = value, + onValueChange = { + value = it + enteredValue = it + }, + label = { Text("Enter value", textAlign = TextAlign.Center) } + ) + } + Text("all shit") } } } From 2371a67b69f71183f925447c334681f454ad31c6 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Tue, 2 May 2023 18:34:37 +0300 Subject: [PATCH 75/85] fix: removed unnecessary bst screen parameter --- app/src/main/kotlin/trees/App.kt | 2 +- app/src/main/kotlin/trees/gui/screens/BSTScreen.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index e687c94..071fa14 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -29,7 +29,7 @@ fun main() = application { toRB = { screenState = Screen.RBScreen }) is Screen.BSTScreen -> - BSTScreen(toMenu = { screenState = Screen.homeScreen }, toBST = { screenState = Screen.BSTScreen }) + BSTScreen(toMenu = { screenState = Screen.homeScreen }) is Screen.AVLScreen -> AVLScreen(toMenu = { screenState = Screen.homeScreen }) diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 3f08931..8b19cbd 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -30,7 +30,7 @@ var enteredKey: String = "" @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -fun BSTScreen(toMenu: () -> Unit, toBST: () -> Unit) { +fun BSTScreen(toMenu: () -> Unit) { val tree = remember { mutableStateOf(BSTree()) } var screenReload by remember { mutableStateOf(false) } if (screenReload) { From 3fedc21f31f5511ee412b41034cfe52706fe4c41 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Tue, 2 May 2023 18:46:48 +0300 Subject: [PATCH 76/85] fix: beautify code --- app/src/main/kotlin/trees/App.kt | 2 +- app/src/main/kotlin/trees/gui/model/NodeView.kt | 4 ++-- .../main/kotlin/trees/gui/navigation/Screens.kt | 2 +- app/src/main/kotlin/trees/gui/nodeFunctions.kt | 2 +- app/src/main/kotlin/trees/gui/screens/AVLScreen.kt | 2 +- app/src/main/kotlin/trees/gui/screens/BSTScreen.kt | 14 +++++++------- app/src/main/kotlin/trees/gui/screens/RBScreen.kt | 2 +- .../main/kotlin/trees/gui/screens/homeScreen.kt | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 071fa14..445049a 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -38,4 +38,4 @@ fun main() = application { RBScreen(toMenu = { screenState = Screen.homeScreen }) } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/model/NodeView.kt b/app/src/main/kotlin/trees/gui/model/NodeView.kt index 2a3b09b..c70c7ce 100644 --- a/app/src/main/kotlin/trees/gui/model/NodeView.kt +++ b/app/src/main/kotlin/trees/gui/model/NodeView.kt @@ -2,7 +2,7 @@ package trees.gui.model import trees.nodes.Node -class NodeView>(parNode: NT) { +class NodeView>(parNode: NT) { var node = parNode val x = 0.0 val y = 0.0 @@ -17,4 +17,4 @@ class NodeView>(parNode: NT) { right = NodeView(it) } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/navigation/Screens.kt b/app/src/main/kotlin/trees/gui/navigation/Screens.kt index f3448e7..9146814 100644 --- a/app/src/main/kotlin/trees/gui/navigation/Screens.kt +++ b/app/src/main/kotlin/trees/gui/navigation/Screens.kt @@ -5,4 +5,4 @@ sealed class Screen { object BSTScreen : Screen() object AVLScreen : Screen() object RBScreen : Screen() -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt index c06963d..1469cb9 100644 --- a/app/src/main/kotlin/trees/gui/nodeFunctions.kt +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -151,4 +151,4 @@ fun printNode(node: BSNode, difColour: Boolean = false) { Text("${node.key}") } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt index d127143..43468fe 100644 --- a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt @@ -125,4 +125,4 @@ fun AVLScreen(toMenu: () -> Unit) { } } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 8b19cbd..8b2af99 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -85,9 +85,10 @@ fun BSTScreen(toMenu: () -> Unit) { Button( onClick = { findMode = false - var coordinate = Coordinate(0f,0f) - var parent = BSNode(0,"") - var value = settingValue(enteredValue, coordinate.x+50.0f, coordinate.y+.0f, 0, false) + var coordinate = Coordinate(0f, 0f) + var parent = BSNode(0, "") + var value = + settingValue(enteredValue, coordinate.x + 50.0f, coordinate.y + .0f, 0, false) var curNode = BSNode(enteredKey.toInt(), value) tree.value.add(curNode.key, curNode.value) if (tree.value.root?.key != enteredKey.toInt()) { @@ -98,9 +99,9 @@ fun BSTScreen(toMenu: () -> Unit) { val point = findBracketPoint(value) val offset = 85f value = if (parent.left?.key == enteredKey.toInt()) { - settingValue(value, coordinate.x-offset, coordinate.y+offset, point, true) + settingValue(value, coordinate.x - offset, coordinate.y + offset, point, true) } else { - settingValue(value, coordinate.x+offset, coordinate.y+offset, point, true) + settingValue(value, coordinate.x + offset, coordinate.y + offset, point, true) } curNode = BSNode(enteredKey.toInt(), value) tree.value.add(curNode.key, curNode.value) @@ -218,9 +219,8 @@ fun BSTScreen(toMenu: () -> Unit) { label = { Text("Enter value", textAlign = TextAlign.Center) } ) } - Text("all shit") } } } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt index baa0cbb..6fff47c 100644 --- a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt @@ -130,4 +130,4 @@ fun RBScreen(toMenu: () -> Unit) { } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt index cbc5829..f00421a 100644 --- a/app/src/main/kotlin/trees/gui/screens/homeScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/homeScreen.kt @@ -63,4 +63,4 @@ fun homeScreen( ) } } -} \ No newline at end of file +} From 9905453878741dd011c4f2e46250097bdea5c622 Mon Sep 17 00:00:00 2001 From: Rafik Nurmuhametov <113212617+raf-nr@users.noreply.github.com> Date: Tue, 2 May 2023 19:33:51 +0300 Subject: [PATCH 77/85] fix(sql): deleting unnoticed outputs, fixed bugs with statement closing Added closing statement, removed unnecessary columns, fixed bugs with table deletion --- app/build.gradle.kts | 2 +- .../main/kotlin/trees/dataBases/AVL/SQLiteDB.kt | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0c61ab2..796a886 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") - implementation("org.xerial:sqlite-jdbc:3.14.2") + implementation("org.xerial:sqlite-jdbc:3.41.2.1") implementation(compose.material3) implementation(compose.desktop.currentOs) diff --git a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt index a3d492f..83efcd7 100644 --- a/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt +++ b/app/src/main/kotlin/trees/dataBases/AVL/SQLiteDB.kt @@ -12,7 +12,7 @@ private const val DB_DRIVER = "jdbc:sqlite" class SQLiteDB(private val path: String) : Closeable { private val connection = DriverManager.getConnection("$DB_DRIVER:$path") ?: throw SQLException("Cannot connect to database") - private val addNodeStatement by lazy { connection.prepareStatement("INSERT INTO nodes (key, value, x, y) VALUES (?, ?, ?, ?);") } + private val addNodeStatement by lazy { connection.prepareStatement("INSERT INTO nodes(key, value) VALUES (?, ?);") } init { logger.info { "Connected to database: $path" } @@ -21,10 +21,9 @@ class SQLiteDB(private val path: String) : Closeable { fun open() { connection.createStatement().also { statement -> try { - statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text, x DOUBLE NOT NULL, y DOUBLE NOT NULL);") + statement.execute("CREATE TABLE IF NOT EXISTS nodes(key INTEGER NOT NULL PRIMARY KEY, value text);") logger.info { "Tables created or already exists" } } catch (problem: Exception) { - println(problem) logger.error(problem) { "Cannot create table in database" } } finally { statement.close() @@ -36,8 +35,6 @@ class SQLiteDB(private val path: String) : Closeable { try { addNodeStatement.setInt(1, node.key) addNodeStatement.setString(2, node.value) - addNodeStatement.setDouble(3, 0.0) - addNodeStatement.setDouble(4, 0.0) addNodeStatement.execute() } catch (problem: Exception) { logger.error(problem) { "Cannot add node with key ${node.key}" } @@ -59,12 +56,13 @@ class SQLiteDB(private val path: String) : Closeable { fun selectNodes(): MutableList> { val nodesList = mutableListOf>() try { - val query = "SELECT key, value, x, y FROM nodes" + val query = "SELECT key, value FROM nodes" val rs = connection.createStatement().executeQuery(query) while (rs.next()) { val key = rs.getInt("key") val value = rs.getString("value") - nodesList.add(AVLNode(key, value)) + var node = AVLNode(key,value) + nodesList.add(node) } } catch (problem: Exception) { logger.error(problem) { "Failed to select" } @@ -80,6 +78,8 @@ class SQLiteDB(private val path: String) : Closeable { } catch (problem: Exception) { println(problem) logger.error(problem) { "Cannot delete table in database" } + } finally { + statement.close() } } } From 59974d11ac4f7eee4543383031cd571a71fae581 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev Date: Tue, 2 May 2023 21:51:02 +0300 Subject: [PATCH 78/85] feat(BSTScreen): text message about errors --- .../kotlin/trees/gui/screens/BSTScreen.kt | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 8b2af99..e2d3232 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -32,6 +32,7 @@ var enteredKey: String = "" @Composable fun BSTScreen(toMenu: () -> Unit) { val tree = remember { mutableStateOf(BSTree()) } + var textMessage by remember { mutableStateOf(mutableListOf()) } var screenReload by remember { mutableStateOf(false) } if (screenReload) { screenReload = false @@ -85,27 +86,32 @@ fun BSTScreen(toMenu: () -> Unit) { Button( onClick = { findMode = false - var coordinate = Coordinate(0f, 0f) - var parent = BSNode(0, "") - var value = - settingValue(enteredValue, coordinate.x + 50.0f, coordinate.y + .0f, 0, false) - var curNode = BSNode(enteredKey.toInt(), value) - tree.value.add(curNode.key, curNode.value) - if (tree.value.root?.key != enteredKey.toInt()) { - parent = getParent(enteredKey.toInt(), tree.value.root!!) - coordinate = getCoordinate(parent.value) - println("${coordinate.x} ${coordinate.y}") + try { + textMessage = mutableListOf("") + var coordinate = Coordinate(0f, 0f) + var parent = BSNode(0, "") + var value = + settingValue(enteredValue, coordinate.x + 50.0f, coordinate.y + .0f, 0, false) + var curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + if (tree.value.root?.key != enteredKey.toInt()) { + parent = getParent(enteredKey.toInt(), tree.value.root!!) + coordinate = getCoordinate(parent.value) + println("${coordinate.x} ${coordinate.y}") + } + val point = findBracketPoint(value) + val offset = 85f + value = if (parent.left?.key == enteredKey.toInt()) { + settingValue(value, coordinate.x - offset, coordinate.y + offset, point, true) + } else { + settingValue(value, coordinate.x + offset, coordinate.y + offset, point, true) + } + curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + screenReload = true + } catch (_: NumberFormatException) { + textMessage = mutableListOf("Write number as a key") } - val point = findBracketPoint(value) - val offset = 85f - value = if (parent.left?.key == enteredKey.toInt()) { - settingValue(value, coordinate.x - offset, coordinate.y + offset, point, true) - } else { - settingValue(value, coordinate.x + offset, coordinate.y + offset, point, true) - } - curNode = BSNode(enteredKey.toInt(), value) - tree.value.add(curNode.key, curNode.value) - screenReload = true }, ) { Text( @@ -117,11 +123,14 @@ fun BSTScreen(toMenu: () -> Unit) { } Button( onClick = { - if (findMode) { - findMode = false + findMode = false + try { + textMessage = mutableListOf("") + tree.value.remove(enteredKey.toInt()) + screenReload = true + } catch (_: NumberFormatException) { + textMessage = mutableListOf("Node with this key doesn't exist") } - tree.value.remove(enteredKey.toInt()) - screenReload = true }) { Text( text = "Remove node", @@ -132,6 +141,7 @@ fun BSTScreen(toMenu: () -> Unit) { } Button( onClick = { + textMessage = mutableListOf("Write key that you are looking for") findMode = !findMode screenReload = true }) { @@ -200,6 +210,7 @@ fun BSTScreen(toMenu: () -> Unit) { .padding(vertical = 5.dp), value = key, onValueChange = { + textMessage = mutableListOf("") key = it enteredKey = it }, @@ -219,6 +230,15 @@ fun BSTScreen(toMenu: () -> Unit) { label = { Text("Enter value", textAlign = TextAlign.Center) } ) } + val text = textMessage.toString().slice(1 until textMessage.toString().length - 1) + Text( + text = text, + fontSize = 18.sp, + modifier = Modifier + .offset((10).dp, (-55).dp) + .width(150.dp), + textAlign = TextAlign.Center, + ) } } } From 68efb51cc5a348e05d7d1ae9d207484c73fd25a1 Mon Sep 17 00:00:00 2001 From: Rafik Nurmuhametov <113212617+raf-nr@users.noreply.github.com> Date: Tue, 2 May 2023 22:32:33 +0300 Subject: [PATCH 79/85] add: BSTree draw implementation * add: menu of functions for actions on trees, implementation of the remaining windows * fix: fixed crooked ribs; window dimensions are now static --- app/src/main/kotlin/trees/App.kt | 9 ++-- .../main/kotlin/trees/gui/nodeFunctions.kt | 36 ++++++++-------- .../kotlin/trees/gui/screens/AVLScreen.kt | 2 +- .../kotlin/trees/gui/screens/BSTScreen.kt | 42 ++++++++++++++++++- .../main/kotlin/trees/gui/screens/RBScreen.kt | 2 +- 5 files changed, 67 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/trees/App.kt b/app/src/main/kotlin/trees/App.kt index 445049a..fbbdc0d 100644 --- a/app/src/main/kotlin/trees/App.kt +++ b/app/src/main/kotlin/trees/App.kt @@ -13,6 +13,7 @@ import trees.gui.screens.AVLScreen import trees.gui.screens.BSTScreen import trees.gui.screens.RBScreen import trees.gui.screens.homeScreen +import java.awt.Dimension fun main() = application { Window( @@ -20,6 +21,8 @@ fun main() = application { title = "Trees by ", state = rememberWindowState(width = 1920.dp, height = 1080.dp) ) { + window.minimumSize = Dimension(1600, 900) + window.maximumSize = Dimension(1600, 900) var screenState by remember { mutableStateOf(Screen.homeScreen) } when (val screen = screenState) { is Screen.homeScreen -> @@ -29,13 +32,13 @@ fun main() = application { toRB = { screenState = Screen.RBScreen }) is Screen.BSTScreen -> - BSTScreen(toMenu = { screenState = Screen.homeScreen }) + BSTScreen(toMenu = { screenState = Screen.homeScreen}) is Screen.AVLScreen -> - AVLScreen(toMenu = { screenState = Screen.homeScreen }) + AVLScreen(toMenu = { screenState = Screen.homeScreen}) is Screen.RBScreen -> - RBScreen(toMenu = { screenState = Screen.homeScreen }) + RBScreen(toMenu = { screenState = Screen.homeScreen}) } } } diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt index 1469cb9..d247e14 100644 --- a/app/src/main/kotlin/trees/gui/nodeFunctions.kt +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -22,6 +22,8 @@ import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp +import trees.gui.screens.getX +import trees.gui.screens.getY import trees.nodes.BSNode import kotlin.math.roundToInt @@ -52,18 +54,25 @@ fun findBracketPoint(value: String): Int { } @Composable -fun printLine(start: BSNode, end: BSNode) { - val coorSt = getCoordinate(start.value) - val x0 = coorSt.x.dp - val y0 = coorSt.y.dp - val coorEn = getCoordinate(end.value) - val x1 = coorEn.x.dp - val y1 = coorEn.y.dp - Box(modifier = Modifier.offset(x0 / 2 + 13.dp, y0 / 2 + 10.dp)) { - Box(modifier = Modifier.offset(x0 / 2 + 13.dp, y0 / 2 + 10.dp)) { +fun printLine(start: BSNode, end: BSNode, marker: Boolean) { + //If marker is true then draw left, else draw ridth + val x0 = getX(start).dp + val y0 = getY(start).dp + val x1 = getX(end).dp + val y1 = getY(end).dp + + if (marker == true) { + Box(modifier = Modifier.offset(x0 + (0.4 * 25).dp, y0 + ((1.82*25).dp))) { + Box( + modifier = Modifier.size(x1 - x0, y1 - y0) + .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 4f) } + ) + } + } else { + Box(modifier = Modifier.offset(x0 + (1.82*25).dp, y0 + ((1.82*25).dp))) { Box( modifier = Modifier.size(x1 - x0, y1 - y0) - .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 1f) } + .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 4f) } ) } } @@ -136,13 +145,6 @@ fun printNode(node: BSNode, difColour: Boolean = false) { .requiredSize(54.dp) .clip(CircleShape) .background(color) - .pointerInput(x, y) { - detectDragGestures { change, dragAmount -> - change.consume() - x.value += dragAmount.x.toDp() - y.value += dragAmount.y.toDp() - } - } .onPointerEvent(eventType = PointerEventType.Release) { node.value = settingValue(node.value, x.value.value, y.value.value, p2, true) }, diff --git a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt index 43468fe..c4b4f8a 100644 --- a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt @@ -36,7 +36,7 @@ fun AVLScreen(toMenu: () -> Unit) { horizontalAlignment = Alignment.Start, ) { Column( - Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), + Modifier.width(200.dp).height(300.dp).offset(0.dp, 0.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index e2d3232..6fabf41 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -1,5 +1,11 @@ package trees.gui.screens +import androidx.compose.foundation.border +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.TextField +import androidx.compose.ui.draw.clip import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -11,7 +17,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -20,8 +25,35 @@ import trees.dataBases.BST.insertAllNodesToTree import trees.dataBases.BST.removeFile import trees.dataBases.BST.writeAllNodesToFile import trees.gui.* +import trees.gui.printNode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.layout +import androidx.compose.ui.modifier.modifierLocalMapOf +import trees.gui.printLine import trees.nodes.BSNode +fun getX(node: BSNode): Float { + return node.value.split(";")[0].toFloat() +} + +fun getY(node: BSNode): Float { + return node.value.split(";")[1].toFloat() +} + +fun newXY(node: BSNode, x: Float, y: Float) { + var count = 0 + var value = "" + for(i in 0..node.value.length - 1) { + if (node.value[i] == ';') { + count++ + } + if (count == 2) { + value += node.value[i] + } + } + node.value = x.toString() + ";" + y.toString() + ";" + value +} + var treeInit = false var findMode = false var enteredValue: String = "" @@ -31,6 +63,8 @@ var enteredKey: String = "" @Preview @Composable fun BSTScreen(toMenu: () -> Unit) { +// val tree = insertAllNodesToTree() +// drawTree(tree.root, null, 70) val tree = remember { mutableStateOf(BSTree()) } var textMessage by remember { mutableStateOf(mutableListOf()) } var screenReload by remember { mutableStateOf(false) } @@ -61,7 +95,11 @@ fun BSTScreen(toMenu: () -> Unit) { val parent = tree.value.root?.let { it1 -> getParent(it.key, it1) } parent?.let { it1 -> if (tree.value.root != it) { - printLine(it1, it) + if(it1.left == it) { + printLine(it1, it, true) + } else { + printLine(it1, it, false) + } } } } diff --git a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt index 6fff47c..e5b7e33 100644 --- a/app/src/main/kotlin/trees/gui/screens/RBScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/RBScreen.kt @@ -36,7 +36,7 @@ fun RBScreen(toMenu: () -> Unit) { horizontalAlignment = Alignment.Start, ) { Column( - Modifier.width(200.dp).height(260.dp).offset(0.dp, 0.dp), + Modifier.width(200.dp).height(300.dp).offset(0.dp, 0.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { From 2c627dbaf12093ac84f0733cc11de4aad42b63d9 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Wed, 3 May 2023 00:53:40 +0300 Subject: [PATCH 80/85] fix: beautify nodes --- app/src/main/kotlin/trees/gui/nodeFunctions.kt | 2 +- .../main/kotlin/trees/gui/screens/BSTScreen.kt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt index d247e14..814f804 100644 --- a/app/src/main/kotlin/trees/gui/nodeFunctions.kt +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -137,7 +137,7 @@ fun printNode(node: BSNode, difColour: Boolean = false) { val color = if (difColour) { Color.Green } else { - Color.Red + Color.LightGray } Box(modifier = Modifier.offset((x.value), (y.value))) { Box( diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 6fabf41..d2e9bc2 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -84,14 +84,6 @@ fun BSTScreen(toMenu: () -> Unit) { if (current?.right != null) current.right?.key.apply(stack::add) current?.let { - if (findMode && (enteredKey.toIntOrNull() != null) && it.key == enteredKey.toInt() && (tree.value.find( - enteredKey.toInt() - ) != null) - ) { - printNode(it, true) - } else { - printNode(it) - } val parent = tree.value.root?.let { it1 -> getParent(it.key, it1) } parent?.let { it1 -> if (tree.value.root != it) { @@ -102,6 +94,14 @@ fun BSTScreen(toMenu: () -> Unit) { } } } + if (findMode && (enteredKey.toIntOrNull() != null) && it.key == enteredKey.toInt() && (tree.value.find( + enteredKey.toInt() + ) != null) + ) { + printNode(it, true) + } else { + printNode(it) + } } } Box { From dbf68621e6d2b2320174add5226c221942b051b9 Mon Sep 17 00:00:00 2001 From: gladiuswq Date: Wed, 3 May 2023 01:19:27 +0300 Subject: [PATCH 81/85] fix: empty file error in bstscreen --- .../kotlin/trees/dataBases/BST/flatFilesDB.kt | 26 +++++++------------ .../kotlin/trees/gui/screens/BSTScreen.kt | 2 -- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt index 4a439c9..2e3d75e 100644 --- a/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt +++ b/app/src/main/kotlin/trees/dataBases/BST/flatFilesDB.kt @@ -10,26 +10,15 @@ import java.io.File const val fileName = "./app/src/main/kotlin/trees/dataBases/BST/flatFile" val curFile = File(fileName) -fun checkIfFileExist(file: File): Boolean { - return file.exists() -} - -fun removeFile(file: File = curFile): Boolean { - if (checkIfFileExist(file)) { - var result = file.delete() - if (result) { - result = file.createNewFile() - if (result) { - return result - } - throw Exception("Fail with file create") - } - throw Exception("Fail with file remove") - } - throw Exception("File with tree not exist") +fun removeFile(file: File = curFile) { + file.delete() + file.createNewFile() } fun writeAllNodesToFile(node: BSNode?, tree: BSTree, file: File = File(fileName)) { + if (!file.exists()) { + file.createNewFile() + } val stack = mutableListOf(node?.key) file.bufferedWriter().use { val csvPrinter = CSVPrinter(it, CSVFormat.DEFAULT) @@ -45,6 +34,9 @@ fun writeAllNodesToFile(node: BSNode?, tree: BSTree, f } fun insertAllNodesToTree(file: File = curFile): BSTree { + if (!file.exists()) { + file.createNewFile() + } val tree = BSTree() file.bufferedReader ().use { val csvParser = CSVParser(it, CSVFormat.DEFAULT) diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index d2e9bc2..78cecf2 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -63,8 +63,6 @@ var enteredKey: String = "" @Preview @Composable fun BSTScreen(toMenu: () -> Unit) { -// val tree = insertAllNodesToTree() -// drawTree(tree.root, null, 70) val tree = remember { mutableStateOf(BSTree()) } var textMessage by remember { mutableStateOf(mutableListOf()) } var screenReload by remember { mutableStateOf(false) } From 14603b0567d2b820dc1c655ab2395c42faa26943 Mon Sep 17 00:00:00 2001 From: raf-nr Date: Wed, 3 May 2023 02:37:44 +0300 Subject: [PATCH 82/85] fix: tree draw in the center --- .../main/kotlin/trees/gui/nodeFunctions.kt | 62 ++++++++++++++++++- .../kotlin/trees/gui/screens/BSTScreen.kt | 25 +------- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/app/src/main/kotlin/trees/gui/nodeFunctions.kt b/app/src/main/kotlin/trees/gui/nodeFunctions.kt index 814f804..301b863 100644 --- a/app/src/main/kotlin/trees/gui/nodeFunctions.kt +++ b/app/src/main/kotlin/trees/gui/nodeFunctions.kt @@ -22,8 +22,7 @@ import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp -import trees.gui.screens.getX -import trees.gui.screens.getY +import trees.nodes.AVLNode import trees.nodes.BSNode import kotlin.math.roundToInt @@ -40,6 +39,20 @@ fun getParent(key: Int, rootNode: BSNode): BSNode { return current } +fun getAVLParent(key: Int, rootNode: AVLNode): AVLNode { + // Functions will be merged in the future. + var current: AVLNode = rootNode + while (current.left?.key != key && current.right?.key != key) { + println("${current.key} $key ${current.right?.key} ${current.left?.key}") + current = when { + current.key < key -> current.right!! + current.key > key -> current.left!! + else -> return current + } + } + return current +} + fun findBracketPoint(value: String): Int { var count = 0 for (i in value.indices) { @@ -78,6 +91,51 @@ fun printLine(start: BSNode, end: BSNode, marker: Bool } } +@Composable +fun printLine(start: AVLNode, end: AVLNode, marker: Boolean) { + // Functions will be merged in the future. + + //If marker is true then draw left, else draw ridth + val x0 = getX(start).dp + val y0 = getY(start).dp + val x1 = getX(end).dp + val y1 = getY(end).dp + + if (marker == true) { + Box(modifier = Modifier.offset(x0 + (0.4 * 25).dp, y0 + ((1.82*25).dp))) { + Box( + modifier = Modifier.size(x1 - x0, y1 - y0) + .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 4f) } + ) + } + } else { + Box(modifier = Modifier.offset(x0 + (1.82*25).dp, y0 + ((1.82*25).dp))) { + Box( + modifier = Modifier.size(x1 - x0, y1 - y0) + .drawBehind { drawLine(Color.Black, Offset.Zero, Offset((x1 - x0).toPx(), (y1 - y0).toPx()), 4f) } + ) + } + } +} + +fun getX(node: BSNode): Float { + return node.value.split(";")[0].toFloat() +} + +fun getY(node: BSNode): Float { + return node.value.split(";")[1].toFloat() +} + +fun getX(node: AVLNode): Float { + // Functions will be merged in the future. + return node.value.split(";")[0].toFloat() +} + +fun getY(node: AVLNode): Float { + // Functions will be merged in the future. + return node.value.split(";")[1].toFloat() +} + class Coordinate( var x: Float, var y: Float diff --git a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt index 78cecf2..f5473c0 100644 --- a/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/BSTScreen.kt @@ -32,28 +32,6 @@ import androidx.compose.ui.modifier.modifierLocalMapOf import trees.gui.printLine import trees.nodes.BSNode -fun getX(node: BSNode): Float { - return node.value.split(";")[0].toFloat() -} - -fun getY(node: BSNode): Float { - return node.value.split(";")[1].toFloat() -} - -fun newXY(node: BSNode, x: Float, y: Float) { - var count = 0 - var value = "" - for(i in 0..node.value.length - 1) { - if (node.value[i] == ';') { - count++ - } - if (count == 2) { - value += node.value[i] - } - } - node.value = x.toString() + ";" + y.toString() + ";" + value -} - var treeInit = false var findMode = false var enteredValue: String = "" @@ -102,6 +80,7 @@ fun BSTScreen(toMenu: () -> Unit) { } } } + var dop = 0 Box { Column( Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), @@ -124,7 +103,7 @@ fun BSTScreen(toMenu: () -> Unit) { findMode = false try { textMessage = mutableListOf("") - var coordinate = Coordinate(0f, 0f) + var coordinate = Coordinate(850f, 0f) var parent = BSNode(0, "") var value = settingValue(enteredValue, coordinate.x + 50.0f, coordinate.y + .0f, 0, false) From 4a5c68c1f92bc28fa50978790e347646c84a5dfd Mon Sep 17 00:00:00 2001 From: raf-nr Date: Wed, 3 May 2023 02:40:00 +0300 Subject: [PATCH 83/85] add: avl tree drawing --- .../kotlin/trees/gui/screens/AVLScreen.kt | 301 +++++++++++++----- 1 file changed, 214 insertions(+), 87 deletions(-) diff --git a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt index c4b4f8a..a74b2e5 100644 --- a/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt +++ b/app/src/main/kotlin/trees/gui/screens/AVLScreen.kt @@ -5,10 +5,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Text -import androidx.compose.material3.TextField +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,105 +18,235 @@ import trees.BSTree import trees.dataBases.BST.insertAllNodesToTree import trees.dataBases.BST.removeFile import trees.dataBases.BST.writeAllNodesToFile -import trees.gui.printNode import trees.nodes.BSNode import androidx.compose.ui.graphics.Color import androidx.compose.ui.modifier.modifierLocalMapOf +import main.kotlin.trees.dataBases.AVL.SQLiteDB +import trees.AVLTree +import trees.gui.* +import trees.nodes.AVLNode @OptIn(ExperimentalMaterial3Api::class) @Composable fun AVLScreen(toMenu: () -> Unit) { - Box() { + val db = SQLiteDB("./app/src/main/kotlin/trees/dataBases/AVL/base.db") + val tree = remember { mutableStateOf(AVLTree()) } + var textMessage by remember { mutableStateOf(mutableListOf()) } + var screenReload by remember { mutableStateOf(false) } + if (screenReload) { + screenReload = false + } + if (!treeInit) { + db.open() + var nodes = db.selectNodes() + for (i in 0..nodes.size-1) { + tree.value.add(nodes[i].key, nodes[i].value) + } + treeInit = !treeInit + } + val node = tree.value.root + val stack = mutableListOf(node?.key) + while (stack.isNotEmpty()) { + val current = stack.removeLast()?.let { current -> tree.value.find(current) } + if (current?.left != null) + current.left?.key.apply(stack::add) + if (current?.right != null) + current.right?.key.apply(stack::add) + current?.let { + val parent = tree.value.root?.let { it1 -> getAVLParent(it.key, it1) } + parent?.let { it1 -> + if (tree.value.root != it) { + if(it1.left == it) { + printLine(it1, it, true) + } else { + printLine(it1, it, false) + } + } + } + if (findMode && (enteredKey.toIntOrNull() != null) && it.key == enteredKey.toInt() && (tree.value.find( + enteredKey.toInt() + ) != null) + ) { + printNode(BSNode(it.key, it.value), true) + } else { + printNode(BSNode(it.key, it.value)) + } + } + } + Box { Column( Modifier.padding(10.dp, 10.dp, 0.dp, 0.dp).background(Color.Unspecified).shadow(1.dp), verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start, ) { - Column( - Modifier.width(200.dp).height(300.dp).offset(0.dp, 0.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, + Row( + verticalAlignment = Alignment.Top ) { - var key by remember { mutableStateOf("") } - TextField( - modifier = Modifier.width(180.dp).padding(vertical = 15.dp).clip(RoundedCornerShape(25.dp)), - value = key, - onValueChange = { key = it }, - label = { Text("Enter key") } - ) - var value by remember { mutableStateOf("") } - TextField( - modifier = Modifier.width(180.dp).padding(bottom = 15.dp).clip(RoundedCornerShape(25.dp)), - value = value, - onValueChange = { value = it }, - label = { Text("Enter value") } - ) - Button( - onClick = { - }, + Column( + verticalArrangement = Arrangement.SpaceBetween ) { - Text( - text = "Insert node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - }) { - Text( - text = "Remove node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - }) { - Text( - text = "Find node", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - } - Column( - Modifier.width(200.dp).height(180.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button( - onClick = { - }) { - Text( - text = "Save tree", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) - } - Button( - onClick = { - }) { - Text( - text = "Clear DB file", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center - ) + Column( + Modifier.width(200.dp).height(150.dp).offset(0.dp, 0.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + findMode = false + try { + textMessage = mutableListOf("") + var coordinate = Coordinate(850f, 0f) + var parent = AVLNode(0, "") + var value = + settingValue(enteredValue, coordinate.x + 50.0f, coordinate.y + .0f, 0, false) + var curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + if (tree.value.root?.key != enteredKey.toInt()) { + parent = getAVLParent(enteredKey.toInt(), tree.value.root!!) + coordinate = getCoordinate(parent.value) + println("${coordinate.x} ${coordinate.y}") + } + val point = findBracketPoint(value) + val offset = 85f + value = if (parent.left?.key == enteredKey.toInt()) { + settingValue(value, coordinate.x - offset, coordinate.y + offset, point, true) + } else { + settingValue(value, coordinate.x + offset, coordinate.y + offset, point, true) + } + curNode = BSNode(enteredKey.toInt(), value) + tree.value.add(curNode.key, curNode.value) + screenReload = true + } catch (_: NumberFormatException) { + textMessage = mutableListOf("Write number as a key") + } + }, + ) { + Text( + text = "Insert node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + findMode = false + try { + textMessage = mutableListOf("") + tree.value.remove(enteredKey.toInt()) + screenReload = true + } catch (_: NumberFormatException) { + textMessage = mutableListOf("Node with this key doesn't exist") + } + }) { + Text( + text = "Remove node", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + textMessage = mutableListOf("Write key that you are looking for") + findMode = !findMode + screenReload = true + }) { + Text( + text = "Find mode", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + Column( + Modifier.width(200.dp).height(150.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Button( + onClick = { + db.open() + db.writeAllNodesToDB(tree.value.root, tree.value) +// removeFile() +// writeAllNodesToFile(tree.value.root, tree.value) + }) { + Text( + text = "Save tree", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + db.delete() + }) { + Text( + text = "Clear DB file", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + Button( + onClick = { + treeInit = !treeInit + toMenu() + }) { + Text( + text = "Back to menu", + modifier = Modifier.width(110.dp), + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } } - Button( - onClick = { - toMenu() - }) { + Column() { + Column( + Modifier.width(200.dp).height(260.dp).offset(25.dp, 10.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + val fieldsWidth = 140 + var key by remember { mutableStateOf("") } + OutlinedTextField( + modifier = Modifier + .width(fieldsWidth.dp) + .height(65.dp) + .padding(vertical = 5.dp), + value = key, + onValueChange = { + textMessage = mutableListOf("") + key = it + enteredKey = it + }, + label = { Text("Enter key", textAlign = TextAlign.Center) } + ) + var value by remember { mutableStateOf("") } + OutlinedTextField( + modifier = Modifier + .width(fieldsWidth.dp) + .height(65.dp) + .padding(vertical = 5.dp), + value = value, + onValueChange = { + value = it + enteredValue = it + }, + label = { Text("Enter value", textAlign = TextAlign.Center) } + ) + } + val text = textMessage.toString().slice(1 until textMessage.toString().length - 1) Text( - text = "Back to menu", - modifier = Modifier.width(110.dp), - fontSize = 16.sp, - textAlign = TextAlign.Center + text = text, + fontSize = 18.sp, + modifier = Modifier + .offset((10).dp, (-55).dp) + .width(150.dp), + textAlign = TextAlign.Center, ) } } From c34268e40c1eec57e30f8003c0587dbe21c75117 Mon Sep 17 00:00:00 2001 From: Alexandr Lecomcev <111463436+vacmannnn@users.noreply.github.com> Date: Wed, 3 May 2023 01:20:52 +0300 Subject: [PATCH 84/85] feat: added test coverage --- README.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index dffaf4b..11900b3 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,8 @@ A project created to study the mechanics of AVL, Red-Black and BST trees, unit t - [Download and build](#download-and-build) - [Trees](#trees) - - [Unit tests](#unit-tests) + - [Unit tests](#unit-tests-by-junit-5) - [Databases](#databases) - - [Future](#future) - [Authors](#authors) ## Download and build: @@ -28,18 +27,24 @@ Implemented 3 basic tree: Each tree support add, remove, find and print functions. Node consist of **comparable** key and value - -*Attention: undefined behavior when adding more than 100000 values* ## Unit tests (by [JUnit 5](https://junit.org/junit5/)) Tests aimed at checking different cases: - Find existing and not existing nodes - Add up to 10000 nodes +- Special cases of balancing - Remove existing and not existing nodes For all trees, the corresponding validator is implemented +Test coverage by Intellej IDEA: +| Tree | Methods % | Lines % | +| ------------------- |:---:|:--:| +| Binary search tree | 100 | 96 | +| AVL tree | 100 | 98 | +| Reb-Black tree | 100 | 93 | + ## Databases -Binary search tree mapped to flat file database, AVL mapped to sqlite +Databases created to store tree nodes Capability: - Insert all nodes to tree @@ -47,16 +52,9 @@ Capability: - Delete itself *It's okey if*: relationships of nodes changed when the application is restarted -### Future: -- Neo4j database to store red-black tree (in progress) -- GUI support -- CONTRIBUTING.md ## Authors - gladiuswq - [github](https://github.com/gladiuswq), [contact](https://t.me/gladiuswq) - raf-nr - [github](https://github.com/raf-nr), [contact](https://t.me/nrrafik) - vacmannnn - [github](https://github.com/vacmannnn), [contact](https://t.me/vacmannnn) - - - From e96d46f761415ac1d1d83620d0a97e46b0bb374a Mon Sep 17 00:00:00 2001 From: Alexandr Lekomtsev <111463436+vacmannnn@users.noreply.github.com> Date: Fri, 8 Dec 2023 23:28:58 +0300 Subject: [PATCH 85/85] feat: add photo in README.md with small changes --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 11900b3..895d4d0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Trees by team 9 A project created to study the mechanics of AVL, Red-Black and BST trees, unit tests, databases and graphic user interface +![image](https://github.com/spbu-coding-2022/trees-9/assets/111463436/930cd060-9e55-42db-9967-cf8ff1e3081b) + ## Navigation @@ -12,11 +14,15 @@ A project created to study the mechanics of AVL, Red-Black and BST trees, unit t ## Download and build: Possible way to clone project by SSH key in command line: - git clone git@github.com:spbu-coding-2022/trees-9.git + git clone git@github.com:spbu-coding-2022/trees-9.git You may build it by [gradle](https://gradle.org/): - ./gradlew build + ./gradlew build + +To run app: + + ./gradlew run *Aware : build method depends on operating system* ## Trees @@ -36,15 +42,8 @@ Tests aimed at checking different cases: For all trees, the corresponding validator is implemented -Test coverage by Intellej IDEA: -| Tree | Methods % | Lines % | -| ------------------- |:---:|:--:| -| Binary search tree | 100 | 96 | -| AVL tree | 100 | 98 | -| Reb-Black tree | 100 | 93 | - ## Databases -Databases created to store tree nodes +3 different ways to store trees: SQLite, Neo4j and .csv files Capability: - Insert all nodes to tree