{"id":400589,"date":"2024-06-29T15:21:36","date_gmt":"2024-06-29T15:21:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=400589"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=400589","title":{"rendered":"<span>How PVS-Studio Checked ELKI in January<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>If you feel like the New Year just came, and you missed the first half of January, then all this time you&#8217;ve been busy looking for tricky bugs in the code you maintain. It also means that our article is what you need. PVS-Studio has checked the ELKI open source project to show you errors that may occur in the code, how cunningly they can hide there, and how you can deal with them.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4c\/iu\/l9\/4ciul9sa13mp2e2g441f53dzt-e.png\" alt=\"ELKI\/image1.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4c\/iu\/l9\/4ciul9sa13mp2e2g441f53dzt-e.png\"\/><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"what-kind-of-library-is-elki\">What kind of library is ELKI?<\/h2>\n<p>  <\/p>\n<p>The abbreviation <a href=\"https:\/\/elki-project.github.io\/\">ELKI<\/a> stands for <strong>E<\/strong>nvironment for Deve<strong>l<\/strong>oping <strong>K<\/strong>DD-Applications Supported by <strong>I<\/strong>ndex-Structures. This project is written in Java and is designed for data mining. Most users of this library are students, researchers, data scientists, and software engineers. No wonder since this library was developed for research only.<\/p>\n<p>  <\/p>\n<p>The algorithms included in the library are mainly related to cluster analysis and outlier detection that may indicate experimental errors. Every year, there are more and more references to research conducted using ELKI. On the developers&#8217; official website, you can find a whole <a href=\"https:\/\/elki-project.github.io\/references\">list<\/a> of scientific works that were conducted via this library. From year to year, this list is constantly growing and being supplemented, and the topics of scientific works are getting more diverse.<\/p>\n<p>  <\/p>\n<p>The only obstacle to using this library may be the AGPL license, which requires the full source code of the application to be open to any user. In other words, you won&#8217;t be able to use this library in commercial projects with closed source code.<\/p>\n<p>  <\/p>\n<h2 id=\"lets-start-checking\">Let&#8217;s start checking<\/h2>\n<p>  <\/p>\n<p>The ELKI <a href=\"https:\/\/github.com\/elki-project\/elki\/tree\/474659eba15a27525db5f4c76162f28a7f1733aa\">library<\/a> contains 2 630 java files with 186 444 lines of code, except for comments. Therefore, the project seems to be quite small compared to some open source giants.<\/p>\n<p>  <\/p>\n<p>However, the small size of the library does not guarantee the absence of errors. As you know, errors can occur in any projects, regardless of their scale. That&#8217;s why we are here now! Let&#8217;s see my top 10 most interesting warnings found when viewing the report.<\/p>\n<p>  <\/p>\n<h2 id=\"unchecked-boundaries\">Unchecked boundaries<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6001\/\">V6001<\/a> There are identical sub-expressions &#8216;bounds[j + 1]&#8217; to the left and to the right of the &#8216;!=&#8217; operator. CLIQUEUnit.java(252)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">private boolean checkDimensions(CLIQUEUnit other, int e) {     for(int i = 0, j = 0; i &lt; e; i++, j += 2) {         if (dims[i] != other.dims[i]             || bounds[j] != other.bounds[j]             || bounds[j + 1] != bounds[j + 1]) {           return false;         }     }     return true; }<\/code><\/pre>\n<p>  <\/p>\n<p>In the <em>if<\/em> block of the <em>checkDimensions<\/em> method, the value of <em>bounds[j + 1]<\/em> is checked for inequality with its own value. Naturally, this condition will always be false, and one of the boundaries will never be checked. That is, the <em>checkDimensions<\/em> method may return <em>true<\/em> when checking, even if the boundaries of the arrays do not match.<\/p>\n<p>  <\/p>\n<p>The correct check in the <em>if<\/em> block must look this way:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">bounds[j + 1] != other.bounds[j + 1]<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"lazy-constructor\">Lazy constructor<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6022\/\">V6022<\/a> Parameter &#8216;updates&#8217; is not used inside constructor body. DataStoreEvent.java(60)<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6022\/\">V6022<\/a> Parameter &#8216;removals&#8217; is not used inside constructor body. DataStoreEvent.java(60)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public DataStoreEvent(DBIDs inserts, DBIDs removals, DBIDs updates) {     super();     this.inserts = inserts;     this.removals = inserts;     this.updates = inserts; }<\/code><\/pre>\n<p>  <\/p>\n<p>Two warnings were received for this code snippet. Here, three parameters are passed to the <em>DataStoreEvent<\/em> constructor, two of which are not used for initialization. Looking closer at the code in the constructor, it becomes clear that the programmer used copy-paste and forgot to correct the names of variables that should have been involved in initialization.<\/p>\n<p>  <\/p>\n<p>If you go even further and take a closer look at the <em>DataStoreEvent<\/em> class, you can see three methods in it that are actively using the above constructor.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public static DataStoreEvent insertionEvent(DBIDs inserts) {   return new DataStoreEvent(inserts, DBIDUtil.EMPTYDBIDS, DBIDUtil.EMPTYDBIDS); }  public static DataStoreEvent removalEvent(DBIDs removals) {   return new DataStoreEvent(DBIDUtil.EMPTYDBIDS, removals, DBIDUtil.EMPTYDBIDS); }  public static DataStoreEvent updateEvent(DBIDs updates) {   return new DataStoreEvent(DBIDUtil.EMPTYDBIDS, DBIDUtil.EMPTYDBIDS, updates); }<\/code><\/pre>\n<p>  <\/p>\n<p>Due to incorrect initialization in the constructor, the behavior of these methods will also differ from what is expected.<\/p>\n<p>  <\/p>\n<p>The correct code version should be as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">this.inserts = inserts; this.removals = removals; this.updates = updates;<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"opaque-variable\">Opaque variable<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6012\/\">V6012<\/a> The &#8216;?:&#8217; operator, regardless of its conditional expression, always returns one and the same value &#8216;0.5&#8217;. ClusterHullVisualization.java(173), ClusterHullVisualization.java(173)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public void fullRedraw() {     ....     boolean flat = (clusters.size() == topc.size());     \/\/ Heuristic value for transparency:     double baseopacity = flat ? 0.5 : 0.5;     .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In this code snippet, the value of the <em>baseopacity<\/em> variable will always be 0.5, regardless of what the value of the <em>flat<\/em> variable is. It&#8217;s clear that there should have been two different values, but due to inattention, the author of the code forgot to correct one of them.<\/p>\n<p>  <\/p>\n<p>The mistake is simple and very disappointing. It immediately strikes your eye if you exclude lines of code that are not related to this variable from the method. But it&#8217;s not an easy task to find a mistake in a method consisting of almost 90 lines. It looks like this is exactly what became the obstacle for the programmer.<\/p>\n<p>  <\/p>\n<h2 id=\"in-pursuit-of-something-beyond-the-limits\">In pursuit of something beyond the limits<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6025\/\">V6025<\/a> Index &#8216;1&#8217; is out of bounds. GeneratorStatic.java(104)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">@Override public double[] computeMean() {     \/\/ Not supported except for singletons.     return points.size() == 1 ? points.get(1) : null; }<\/code><\/pre>\n<p>  <\/p>\n<p>In the <em>computeMean<\/em> method, the code author checks that the size of the <em>points<\/em> collection is equal to one. If so, the developer tries to return\u2026 a collection element with index 1 from the method. Since indexing starts from zero, an <em>IndexOutOfBoundsException<\/em> is inevitable.<\/p>\n<p>  <\/p>\n<p>The correct version of the code must look like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">return points.size() == 1 ? points.get(0) : null;<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"is-it-possible-to-divide-by-zero\">Is it possible to divide by zero?<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6020\/\">V6020<\/a> Divide by zero. The range of the &#8216;referenceSetSize&#8217; denominator values includes zero. PreDeConNeighborPredicate.java(138)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">protected PreDeConModel computeLocalModel(DoubleDBIDList neighbors, ....) {     final int referenceSetSize = neighbors.size();     ....     \/\/ Shouldn't happen:     if(referenceSetSize &lt; 0) {         LOG.warning(\"Empty reference set \u2013              should at least include the query point!\");         return new PreDeConModel(Integer.MAX_VALUE, DBIDUtil.EMPTYDBIDS);     }     ....     for(int d = 0; d &lt; dim; d++) {         s[d] \/= referenceSetSize;         mvVar.put(s[d]);     }     .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Every programmer knows that you can&#8217;t divide by zero. Every programmer tries to avoid the occurrence of such an error in their code. And it works in most cases. But not always.<\/p>\n<p>  <\/p>\n<p>In this code snippet, everything depends on the size of the <em>neighbors<\/em> parameter passed to the <em>computeLocalModel<\/em> method. The code author checks the <em>neighbors<\/em> size and cuts off the values that are less than zero by checking in the <em>if<\/em> statement, but isn&#8217;t taking any actions if the size is zero. Since the check of <em>referenceSetSize &lt; 0<\/em> doesn&#8217;t make any sense, and the message for logging also says about an empty <em>set<\/em>, all of that seems like a typo. The check of <em>referenceSetSize == 0<\/em> was most likely to be here.<\/p>\n<p>  <\/p>\n<p>If an empty <em>neighbors<\/em> container is passed in this method anyway, the division by zero will occur in the <em>for<\/em> loop. We can only hope that this will never really happen.<\/p>\n<p>  <\/p>\n<h2 id=\"endless-initialization\">Endless initialization<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6062\/\">V6062<\/a> Possible infinite recursion inside the &#8216;setInitialMeans&#8217; method. Predefined.java(65), Predefined.java(66)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public void setInitialMeans(List&lt;double[]> initialMeans) {     this.setInitialMeans(initialMeans); }<\/code><\/pre>\n<p>  <\/p>\n<p>This one-line method is a true recursion. It&#8217;s difficult to guess what exactly the code author wanted to write here. The only line of this method probably should have looked as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">this.setInitialMeans(initialMeans.toArray(new double[0][0]));<\/code><\/pre>\n<p>  <\/p>\n<p>Since there is another method with the same name in this class, the programmer most likely wanted to pass data for initialization to it, but in the end, something went wrong. This is what the body of the second method looks like, by the way:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public void setInitialMeans(double[][] initialMeans) {     double[][] vecs = initialMeans.clone(); \/\/ TODO: deep copy?     this.initialMeans = vecs; }<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"lost-weight\">Lost weight<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6094\/\">V6094<\/a> The expression was implicitly cast from &#8216;int&#8217; type to &#8216;double&#8217; type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) \/ Y;. ProbabilityWeightedMoments.java(130)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public static &lt;A> double[] alphaBetaPWM(...., final int nmom) {     final int n = adapter.size(data);     final double[] xmom = new double[nmom &lt;&lt; 1];     double aweight = 1. \/ n, bweight = aweight;     for(int i = 0; i &lt; n; i++) {         ....         for(int j = 1, k = 2; j &lt; nmom; j++, k += 2) {             xmom[k + 1] += val * (aweight *= (n - i - j + 1) \/ (n - j + 1));             xmom[k + 1] += val * (bweight *= (i - j + 1) \/ (n - j + 1));         }     }     return xmom; }<\/code><\/pre>\n<p>  <\/p>\n<p>In the <em>for<\/em> loop of this code snippet, the <em>(n \u2014 i \u2014 j + 1) \/ (n \u2014 j + 1) <\/em>expression is converted implicitly from the <em>int<\/em> type to the <em>double<\/em> type. In this case, the loss of accuracy may be quite different: from a few digits after the decimal point to a complete nullification if the number modulo is less than one. Probably, it&#8217;s not exactly the expected behavior given that the <em>xmom<\/em> array is of the <em>double<\/em> type. The <em>(n \u2013 i \u2013 j + 1) \/ (n \u2013 j + 1)<\/em> expression helps to prove that the programmer meant something completely different here. Let&#8217;s say <em>n \u2013 j + 1<\/em> equals <em>x<\/em>. Then we get the expression: <em>(x \u2013 i) \/ x<\/em>. This expression will always give 0 for integer division unless we start going into negative ranges. But since the values of <em>n<\/em> in this code snippet are always greater than zero, we can conclude that the programmer did not intend to use integer division here.<\/p>\n<p>  <\/p>\n<p>Not to lose precision, an explicit conversion to the <em>double<\/em> type is required:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">xmom[k + 1] += val * (aweight *= (double) (n - i - j + 1) \/ (n - j + 1)); xmom[k + 1] += val * (bweight *= (double) (i - j + 1) \/ (n - j + 1));<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"out-of-bounds\">Out of bounds<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6079\/\">V6079<\/a> Value of the &#8216;splitpoint&#8217; variable is checked after use. Potential logical error is present. KernelDensityFittingTest.java(97), KernelDensityFittingTest.java(97)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public final void testFitDoubleArray() throws IOException {     ....     int splitpoint = 0;     while(fulldata[splitpoint] &lt; splitval &amp;&amp; splitpoint &lt; fulldata.length) {         splitpoint++;     }     .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In this code fragment, in the <em>while<\/em> loop, first, the <em>fulldata<\/em> array element with the <em>splitpoint<\/em> index is compared with the value of the <em>splitval<\/em> variable, and only then we check whether the <em>splitpoint<\/em> value is smaller than the size of the array itself. These two checks in the <em>while<\/em> loop need to be changed, otherwise, you can very easily find yourself outside the array&#8217;s bounds.<\/p>\n<p>  <\/p>\n<h2 id=\"unreachable-code\">Unreachable code<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6019\/\">V6019<\/a> Unreachable code detected. It is possible that an error is present. Tokenizer.java(172)<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6007\/\">V6007<\/a> Expression &#8216;c != &#8216;\\n&#187; is always true. Tokenizer.java(169)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">public String getStrippedSubstring() {     int sstart = start, ssend = end;     while(sstart &lt; ssend) {         char c = input.charAt(sstart);         if(c != ' ' || c != '\\n' || c != '\\r' || c != '\\t') {             break;         }         ++sstart;     }     .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Two diagnostics decided to act together this time. They both issued warnings. The V6019 diagnostic pointed to an unreachable code fragment: <em>++sstart<\/em>, and V6007 pointed to a condition in the <em>if<\/em> statement that will always be true.<\/p>\n<p>  <\/p>\n<p>Why will there always be true in the <em>if<\/em> block? The answer is very simple. In this statement several conditions are checked at once: <em>c !=&#8217; &#8216;<\/em> or <em>c != <\/em> <em>&#8216;\\n&#8217;<\/em>, or <em>c != &#8216;\\r&#8217;<\/em>, or <em>c != &#8216;\\t&#8217;<\/em>. Whatever the input data, some of those will be true. Even if one of the checks is <em>false<\/em>, the next check will return <em>true<\/em>, and because of the <em>||<\/em> (or) operator, the condition in <em>if<\/em> will eventually be true. The condition in the <em>if<\/em> block will always be true. Therefore, the <em>break<\/em> statement, which prematurely ends the <em>while<\/em> loop, will trigger. As a result, the increment of the <em>sstart<\/em> variable will never be executed. This is exactly what the V6019 diagnostics noticed and started sounding the alarm.<\/p>\n<p>  <\/p>\n<p>Most likely, the programmer wanted to write something like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">if(c != ' ' &amp;&amp; c != '\\n' &amp;&amp; c != '\\r' &amp;&amp; c != '\\t')<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"override-but-check\">Override but check<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v6009\/\">V6009<\/a> Function &#8216;equals&#8217; receives an odd argument. An object &#8216;other.similarityFunction&#8217; is used as an argument to its own method. AbstractSimilarityAdapter.java(91)<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">@Override public boolean equals(Object obj) {     if(obj == null) {         return false;     }      if(!this.getClass().equals(obj.getClass())) {         return false;     }      AbstractSimilarityAdapter&lt;?> other = (AbstractSimilarityAdapter&lt;?>) obj;     return other.similarityFunction.equals(other.similarityFunction); }<\/code><\/pre>\n<p>  <\/p>\n<p>The code author decided to override the <em>equals<\/em> method in the <em>AbstractSimilarityAdapter<\/em> class. If it is assumed that objects in the program will be stored in containers, or checked for equality, the <em>equals<\/em> override is required. However, the whole author&#8217;s idea was spoiled by the method&#8217;s last line, in which <em>equals<\/em> is called for the same object. As a result, even the most common comparison will be incorrect.<\/p>\n<p>  <\/p>\n<p>The code was probably meant to be like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">return this.similarityFunction.equals(other.similarityFunction);<\/code><\/pre>\n<p>  <\/p>\n<p>I&#8217;d like to note that this error pattern is often found in programs in different languages, and not only in Java. We&#8217;ve got an article &#8216;<a href=\"https:\/\/www.viva64.com\/en\/b\/0509\/\">The Evil within the Comparison Functions<\/a>&#8216; on this topic. You must read it if you&#8217;re interested in finding out why programmers often make mistakes in fairly simple functions designed to compare two objects. <\/p>\n<p>  <\/p>\n<h2 id=\"lets-recap\">Let&#8217;s recap<\/h2>\n<p>  <\/p>\n<p>As you can see, mistakes may appear in any project. Some of them are simple and disappointing, others are cunning and tricky. And it doesn&#8217;t matter whether your project is small or large. It doesn&#8217;t matter if you&#8217;re a professional developer or just starting to write code. Mistakes can appear anywhere in your program and hide safely from your eyes. <\/p>\n<p>  <\/p>\n<p>For every developer and every project, static code analysis is an indispensable tool. As essential as a code review or unit tests. That&#8217;s why, we recommend using static code analysis in your work <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio-download\">right now<\/a>, so that in the New Year your code will become better and clearer than in the previous one.<\/p>\n<p>  <\/p>\n<h2 id=\"in-conclusion\">In conclusion<\/h2>\n<p>  <\/p>\n<p>It&#8217;s not the first time we are checking packages related to calculations, statistics, and scientific researches. And every time we worry about mistakes in the code of such projects. Can we trust the data calculated using these libraries?<\/p>\n<p>  <\/p>\n<p>Of course, it&#8217;s incredibly difficult to write code without mistakes, and in practice, it&#8217;s even impossible. However, libraries used as building blocks in scientific, medical, and research programs should be checked as carefully as possible. The code should be as clean as possible. After all, every mistake can lead to very unpleasant consequences in the future. In any case, static analysis will prove to be extremely useful for such projects.<\/p>\n<p>  <\/p>\n<p>We have already covered this topic in some of our other articles, so if this question is interesting for you, don&#8217;t hesitate to check out some of them:<\/p>\n<p>  <\/p>\n<ol>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0156\/\">Analysis of the Trans-Proteomic Pipeline (TPP) project<\/a>.<\/li>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0212\/\">The Big Calculator Gone Crazy<\/a>.<\/li>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0271\/\">Can We Trust the Libraries We Use?<\/a><\/li>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0682\/\">Analyzing the Code of ROOT, Scientific Data Analysis Framework<\/a>.<\/li>\n<li><a href=\"https:\/\/www.viva64.com\/en\/b\/0591\/\">NCBI Genome Workbench: Scientific Research under Threat<\/a>.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/538652\/\"> https:\/\/habr.com\/ru\/articles\/538652\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>If you feel like the New Year just came, and you missed the first half of January, then all this time you&#8217;ve been busy looking for tricky bugs in the code you maintain. It also means that our article is what you need. PVS-Studio has checked the ELKI open source project to show you errors that may occur in the code, how cunningly they can hide there, and how you can deal with them.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4c\/iu\/l9\/4ciul9sa13mp2e2g441f53dzt-e.png\" alt=\"ELKI\/image1.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4c\/iu\/l9\/4ciul9sa13mp2e2g441f53dzt-e.png\"\/><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-400589","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400589","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=400589"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400589\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=400589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=400589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=400589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}