<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr">
                                        Hi all!<div class="mb_sig"></div>
                                        <div><br></div><div>This breaks subclassing for Interval. TextLineInterval is an example. Yet, it is used only in ST80, so I will change it there.</div><div><br></div><div>What about #species? Or something similar? Do we really want to break subclassing on Interval?</div><div><br></div><div>Best,</div><div>Marcel</div><blockquote class="history_container" type="cite" style="border-left-style: solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px">
                        <p style="color: #AAAAAA; margin-top: 10px;">Am 03.03.2021 02:14:24 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style="font-family:Arial,Helvetica,sans-serif">Nicolas Cellier uploaded a new version of Collections to project The Trunk:<br>http://source.squeak.org/trunk/Collections-nice.925.mcz<br><br>==================== Summary ====================<br><br>Name: Collections-nice.925<br>Author: nice<br>Time: 3 March 2021, 2:14:13.09562 am<br>UUID: ee92856e-98d1-3a41-a3f2-3529927e7a02<br>Ancestors: Collections-jar.924<br><br>Distinguish questionable LimitedPrecisionInterval (those with Float bounds or step) from ordinary Interval of Integer, Fraction or ScaledDecimals.<br><br>The main interest of having a specific class is to avoid crippling Interval with Float workarounds, for the rare use case.<br><br>Explain some of the pitfalls of LimitedPrecisionInterval, and encourage alternatives in class comment, which is a second advantage of having a separate class.<br><br>Abandon fuzzy inclusion logic, which is considered to introduce more discrepancies than it tries to solve<br>See http://bugs.squeak.org/view.php?id=6455.<br>and method #testSurprisingFuzzyInclusion<br><br>Fix two other failing tests for Interval of Floats.<br><br>Fix size so that (0.3 to: 1.2 by: 0.1) includes: 1.2.<br>See https://github.com/dolphinsmalltalk/Dolphin/issues/1108<br>This makes size a bit less performant than super, a 3rd reason why a specific class is neat.<br><br>Huh, that's more comments than code ;).<br><br>=============== Diff against Collections-jar.924 ===============<br><br>Item was changed:<br>  ----- Method: Interval class>>from:to: (in category 'instance creation') -----<br>  from: startInteger to: stopInteger <br>        "Answer an instance of me, starting at startNumber, ending at <br>   stopNumber, and with an interval increment of 1."<br>  <br>+   ^((startInteger hasLimitedPrecision or: [stopInteger hasLimitedPrecision])<br>+           ifTrue: [LimitedPrecisionInterval]<br>+           ifFalse: [Interval]) basicNew<br>-        ^self basicNew<br>                setFrom: startInteger<br>                 to: stopInteger<br>               by: 1!<br><br>Item was changed:<br>  ----- Method: Interval class>>from:to:by: (in category 'instance creation') -----<br>  from: startInteger to: stopInteger by: stepInteger <br>         "Answer an instance of me, starting at startNumber, ending at <br>   stopNumber, and with an interval increment of stepNumber."<br>  <br>+  ^((startInteger hasLimitedPrecision or: [stopInteger hasLimitedPrecision or: [stepInteger hasLimitedPrecision]])<br>+             ifTrue: [LimitedPrecisionInterval]<br>+           ifFalse: [Interval]) basicNew<br>-        ^self basicNew<br>                setFrom: startInteger<br>                 to: stopInteger<br>               by: stepInteger!<br><br>Item was changed:<br>  ----- Method: Interval>>indexOf:startingAt: (in category 'accessing') -----<br>  indexOf: anElement startingAt: startIndex<br>       "startIndex is an positive integer, the collection index where the search is started."<br>-     "during the computation of val , floats are only used when the receiver    contains floats"<br>  <br>+    | index |<br>-    | index val |<br>         (self rangeIncludes: anElement) ifFalse: [ ^0 ].<br>+     index := (anElement - start / step) rounded + 1.<br>-     val := anElement - self first / self increment.<br>-      val isFloat<br>-          ifTrue: [<br>-                    (val - val rounded) abs * 100000000 < 1 ifFalse: [ ^0 ].<br>-                  index := val rounded + 1 ]<br>-           ifFalse: [<br>-                   val isInteger ifFalse: [ ^0 ].<br>-                       index := val + 1 ].<br>-  "finally, the value of startIndex comes into play:"<br>         (index between: startIndex and: self size) ifFalse: [ ^0 ].<br>+  (self at: index) = anElement ifFalse: [ ^0 ].<br>         ^index!<br><br>Item was added:<br>+ Interval subclass: #LimitedPrecisionInterval<br>+   instanceVariableNames: ''<br>+    classVariableNames: ''<br>+       poolDictionaries: ''<br>+         category: 'Collections-Sequenceable'!<br>+ <br>+ !LimitedPrecisionInterval commentStamp: 'nice 3/3/2021 01:47' prior: 0!<br>+ A LimitedPrecisionInterval is an Interval whose bounds or step haveLimitedPrecision.<br>+ Due to inexact arithmetic, special precautions must be taken in the implementation,<br>+ in order to avoid unconsistent and surprising behavior as much as possible.<br>+ <br>+ Despite those efforts, LimitedPrecisionInterval is full of pitfalls.<br>+ It is recommended to avoid using LimitedPrecisionInterval unless understanding those pitfalls.<br>+ For example, (0.2 to: 0.6 by: 0.1) last = 0.5.<br>+ This interval does not includes 0.6 because (0.1*4+0.2) is slightly greater than 0.6.<br>+ Another example is that (0.2 to: 0.6 by: 0.1) does not include 0.3 but a Float slightly greater.<br>+ <br>+ A usual workaround is to use an Integer interval, and reconstruct the Float inside the loop.<br>+ For example:<br>+     (0 to: 4) collect: [:i | 0.1*i+0.2].<br>+ or better if we want to have 0.3 and 0.6:<br>+     (2 to: 6) collect: [:i | i / 10.0].<br>+ Another workaround is to not use limited precision at all, but Fraction or ScaledDecimal when possible:<br>+     (1/10 to: 7/10 by: 1/10).<br>+     (0.1s to: 0.7s by: 0.1s).<br>+ <br>+ Yet another pitfall is that optimized to:by:do: might differ from (to:by:) do:<br>+ In the former case, repeated addition of increment is used, in the later, a multiplication is used.<br>+ Observe the differences:<br>+     Array streamContents: [:str | 0 to: 3 by: 0.3 do: [:e | str nextPut: e]].<br>+     Array streamContents: [:str | (0 to: 3 by: 0.3) do: [:e | str nextPut: e]].<br>+ <br>+ There are many more discrepancies, so use carefully, or not use it at all.!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>copyFrom:to: (in category 'copying') -----<br>+ copyFrom: startIndex to: stopIndex<br>+    startIndex = 1 ifTrue: [^super copyFrom: startIndex to: stopIndex].<br>+  stopIndex < startIndex ifTrue: [^self copyEmpty].<br>+         ^Array new: stopIndex - startIndex + 1 streamContents: [:stream |<br>+            startIndex to: stopIndex do: [:i | stream nextPut: (self at: i)]]!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>last (in category 'accessing') -----<br>+ last <br>+         "Refer to the comment in SequenceableCollection|last."<br>+ <br>+         ^start + (step * (self size - 1))!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>reversed (in category 'converting') -----<br>+ reversed <br>+        "There is no guaranty that super reversed would contain same elements.<br>+  Answer an Array instead"<br>+ <br>+    ^Array new: self size streamContents: [:stream | self reverseDo: [:each | stream nextPut: each]]!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>size (in category 'accessing') -----<br>+ size<br>+   "Answer how many elements the receiver contains."<br>+ <br>+      | candidateSize |<br>+    candidateSize := (stop - start / step max: 0) rounded.<br>+       step > 0<br>+          ifTrue: [candidateSize * step + start <= stop ifTrue: [^candidateSize + 1]]<br>+               ifFalse: [candidateSize * step + start >= stop ifTrue: [^candidateSize + 1]].<br>+     ^candidateSize!<br><br><br></div></blockquote></div>