Hi!
On Thu, 12 Nov 2009, radoslav hodnicak wrote: > > In all my time I've been programming in Smalltalk (since about 2000), the > idea that Sets simply do not contain nil was a core assumption of using > collection classes for me, not just an implementation detail. I routinely do > "something asSet" to get rid of nils, and this change would break all my > code. > I doubt that. I get an Error when I'm evaluating this: #(1 nil 2) asSet You should use #(1 nil 2) reject: [ :each | each isNil ] instead. Cheers, Levente |
In reply to this post by Bert Freudenberg
On 12.11.2009, at 11:35, Bert Freudenberg wrote: > > On 12.11.2009, at 09:42, Andreas Raab wrote: > >> Levente Uzonyi wrote: >>> But we now have a lot more proposals, let me summarize them all: >>> - (1)add a new instance variable: containsNil >>> - (2)use tally to indicate if nil is in the set >>> - (a)negative values mean nil is contained by the set >>> - (b)floats - (3)use a marker object >>> - (a)self >>> - (b)a unique object in each set >>> - (c)a class variable >>> - (4)use container objects in occupied slots, like associations in dictionaries >>> I prefer 3c, 2a, 3a, 1, 4 in the given order. >>> What about you? >> >> Nice summary. My current preferences would be (1) (clear and obvious) (3b) (ditto) and (3a)/(3c) (both with some hesitation). I'm not sure about (4) (I've only just heard of it; it sounds cool but I haven't seen no code yet). I would veto both (2a) and (2b) as obscure hacks compared to (1) unless the size overhead is significant. > > I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker (either the class itself [I'd like that] or its singleton instance [for the purists]). This would make it blatantly obvious what's happening when inspecting the innards of a Set. > > I'd reluctantly accept (1) but fear it would complicate the logic - containsNil checks will have to be scattered all over the place. > > - Bert - Thinking about migrating (possibly) old data - how do the options fare with respect to reloading serialized Sets? I guess we could count nils and detect a mismatch with tally? - Bert - |
In reply to this post by Bert Freudenberg
2009/11/12 Bert Freudenberg <[hidden email]>:
> > On 12.11.2009, at 12:49, radoslav hodnicak wrote: > >> >> In all my time I've been programming in Smalltalk (since about 2000), the idea that Sets simply do not contain nil was a core assumption of using collection classes for me, not just an implementation detail. I routinely do "something asSet" to get rid of nils, and this change would break all my code. > > (Array new: 5) asSet > > raises an error in Squeak ... > >> Now, I obvioulsy do not see the value of allowing Sets to contain nil, but other people do. How about creating a new class to handle this? NilSet or whatever >> >> rado > > Not a bad suggestion. SetWithNil? > class, which allows nils, and only then a subclass which doesn't, because IMO, a set which doesn't allows nils is specialized version of more generic which does. > - Bert - > > > -- Best regards, Igor Stasenko AKA sig. |
In reply to this post by Bert Freudenberg
Hi!
On Thu, 12 Nov 2009, Bert Freudenberg wrote: > > On 12.11.2009, at 09:42, Andreas Raab wrote: > >> Levente Uzonyi wrote: >>> But we now have a lot more proposals, let me summarize them all: >>> - (1)add a new instance variable: containsNil >>> - (2)use tally to indicate if nil is in the set >>> - (a)negative values mean nil is contained by the set >>> - (b)floats - (3)use a marker object >>> - (a)self >>> - (b)a unique object in each set >>> - (c)a class variable >>> - (4)use container objects in occupied slots, like associations in dictionaries >>> I prefer 3c, 2a, 3a, 1, 4 in the given order. >>> What about you? >> >> Nice summary. My current preferences would be (1) (clear and obvious) (3b) (ditto) and (3a)/(3c) (both with some hesitation). I'm not sure about (4) (I've only just heard of it; it sounds cool but I haven't seen no code yet). I would veto both (2a) and (2b) as obscure hacks compared to (1) unless the size overhead is significant. > > I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker (either the class itself [I'd like that] or its singleton instance [for the purists]). This would make it blatantly obvious what's happening when inspecting the innards of a Set. > What about this? (3e): make a class called UnoccupiedSlot with a single instance. Add EmptyFlag to Set, and make sure that EmptyFlag is the instance of UnoccupiedSlot. This way the inspector would print "an UnoccupiedSlot" for empty slots and we could save a message send in the implementation. Cheers, Levente |
On 12.11.2009, at 13:05, Levente Uzonyi wrote: > Hi! > > On Thu, 12 Nov 2009, Bert Freudenberg wrote: > >> >> On 12.11.2009, at 09:42, Andreas Raab wrote: >> >>> Levente Uzonyi wrote: >>>> But we now have a lot more proposals, let me summarize them all: >>>> - (1)add a new instance variable: containsNil >>>> - (2)use tally to indicate if nil is in the set >>>> - (a)negative values mean nil is contained by the set >>>> - (b)floats - (3)use a marker object >>>> - (a)self >>>> - (b)a unique object in each set >>>> - (c)a class variable >>>> - (4)use container objects in occupied slots, like associations in dictionaries >>>> I prefer 3c, 2a, 3a, 1, 4 in the given order. >>>> What about you? >>> >>> Nice summary. My current preferences would be (1) (clear and obvious) (3b) (ditto) and (3a)/(3c) (both with some hesitation). I'm not sure about (4) (I've only just heard of it; it sounds cool but I haven't seen no code yet). I would veto both (2a) and (2b) as obscure hacks compared to (1) unless the size overhead is significant. >> >> I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker (either the class itself [I'd like that] or its singleton instance [for the purists]). This would make it blatantly obvious what's happening when inspecting the innards of a Set. >> > > What about this? > (3e): make a class called UnoccupiedSlot with a single instance. Add EmptyFlag to Set, and make sure that EmptyFlag is the instance of UnoccupiedSlot. > This way the inspector would print "an UnoccupiedSlot" for empty slots and we could save a message send in the implementation. Which message send would you save in ... == UnoccupiedSlot ? - Bert - |
In reply to this post by Igor Stasenko
On Thu, 12 Nov 2009, Igor Stasenko wrote:
>> a) Run benchmarks (tiny and macroBenchmarks). I expect no difference but I >> only believe that after having run the benchmarks. >> > before: > 1 tinyBenchmarks > '436115843 bytecodes/sec; 12718765 sends/sec' > '473635522 bytecodes/sec; 12718765 sends/sec' > '473635522 bytecodes/sec; 12727537 sends/sec' > > after: > 1 tinyBenchmarks > '472324723 bytecodes/sec; 12657701 sends/sec' > '472324723 bytecodes/sec; 12683800 sends/sec' > '472760849 bytecodes/sec; 12969029 sends/sec' Well, I doubt Andreas ment to use Integer >> #tinyBenchmarks, because it doesn't use sets. I think he ment microbenchmarks like this one: http://leves.web.elte.hu/collections/Set%20class-microBenchmark.st I just ran them with the current implementation, 2a and 3c, the results are here: http://leves.web.elte.hu/collections/SetBenchmarkResults.txt The values are microseconds/element in the table (smaller valuesa are better). I summed up the rows and created a table (smaller numbers are better): trunk 2a 3c add: (included) 2,594 2,599 2,443 add: (not included) 9,370 9,957 9,441 includes: (included) 2,869 2,961 2,822 includes: (not included) 3,685 3,717 3,671 like: (included) 2,446 2,486 2,508 like: (not included) 3,268 3,258 3,310 rehash 2,924 2,841 2,238 remove:ifAbsent: (included) 7,190 7,228 7,240 remove:ifAbsent: (not included) 3,966 3,980 3,856 I don't see any real differences. Some values are pretty strange (like rehash at 3c, I expected it to be slower than trunk). It may be because of noise, even though I tried to avoid noise as much as possible. Since these benchmarks show no significant differences, I doubt that macrobenchmarks would show any differences for these implementations. Levente |
In reply to this post by Bert Freudenberg
On Thu, 12 Nov 2009, Bert Freudenberg wrote:
> > On 12.11.2009, at 13:05, Levente Uzonyi wrote: > >> Hi! >> >> On Thu, 12 Nov 2009, Bert Freudenberg wrote: >> >>> >>> On 12.11.2009, at 09:42, Andreas Raab wrote: >>> >>>> Levente Uzonyi wrote: >>>>> But we now have a lot more proposals, let me summarize them all: >>>>> - (1)add a new instance variable: containsNil >>>>> - (2)use tally to indicate if nil is in the set >>>>> - (a)negative values mean nil is contained by the set >>>>> - (b)floats - (3)use a marker object >>>>> - (a)self >>>>> - (b)a unique object in each set >>>>> - (c)a class variable >>>>> - (4)use container objects in occupied slots, like associations in dictionaries >>>>> I prefer 3c, 2a, 3a, 1, 4 in the given order. >>>>> What about you? >>>> >>>> Nice summary. My current preferences would be (1) (clear and obvious) (3b) (ditto) and (3a)/(3c) (both with some hesitation). I'm not sure about (4) (I've only just heard of it; it sounds cool but I haven't seen no code yet). I would veto both (2a) and (2b) as obscure hacks compared to (1) unless the size overhead is significant. >>> >>> I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker (either the class itself [I'd like that] or its singleton instance [for the purists]). This would make it blatantly obvious what's happening when inspecting the innards of a Set. >>> >> >> What about this? >> (3e): make a class called UnoccupiedSlot with a single instance. Add EmptyFlag to Set, and make sure that EmptyFlag is the instance of UnoccupiedSlot. >> This way the inspector would print "an UnoccupiedSlot" for empty slots and we could save a message send in the implementation. > > Which message send would you save in > > ... == UnoccupiedSlot > UnoccupiedSlot instance. I wouldn't use a class, because I think we should be able create a set with any classes in it. Levente > ? > > - Bert - > > > > |
In reply to this post by Igor Stasenko
On Thu, Nov 12, 2009 at 3:44 AM, Igor Stasenko <[hidden email]> wrote:
> Testing against virtually every existing application is unreasonable > demand and nobody having time to do that. But did you run it against the standard set of tests in Squeak? The question is whether changing the behavior of Set will break the image. And if it does, how hard will it be to fix? i can't remember ever reading or writing code that would break if Sets could include nil, but that doesn't mean there isn't any. The whole reason for having these tests is so we can see if changes break something. But if we don't run the tests then they don't do us any good. Since there are no unit tests that ensure that Sets never contain nil, it should not be hard to make a version of Set that can contain nil. That is why I said it was boring and obvious to say that your version of Set plassed the unit tests for Collections. I loaded the changes into Squeak 10.2. First, I ran the tests; two failed. Then I loaded the new version of Set and ran the tests again. Now only one failed, an obscure unwind error that has been around a long time. So, your change doesn't break any of the 2254 tests in that image. it probably wouldn't be hard to test it with squeak-dev, too. It is not hard to run all the tests. Sure, it takes a little time, but you can run them in the background and go read your e-mail. But just running the collection tests doesn't mean much, since getting the collection tests to run is easy so telling us that they work doesn't tell us much. -Ralph |
In reply to this post by Levente Uzonyi-2
2009/11/12 Levente Uzonyi <[hidden email]>:
> On Thu, 12 Nov 2009, Igor Stasenko wrote: > >>> a) Run benchmarks (tiny and macroBenchmarks). I expect no difference but >>> I >>> only believe that after having run the benchmarks. >>> >> before: >> 1 tinyBenchmarks >> '436115843 bytecodes/sec; 12718765 sends/sec' >> '473635522 bytecodes/sec; 12718765 sends/sec' >> '473635522 bytecodes/sec; 12727537 sends/sec' >> >> after: >> 1 tinyBenchmarks >> '472324723 bytecodes/sec; 12657701 sends/sec' >> '472324723 bytecodes/sec; 12683800 sends/sec' >> '472760849 bytecodes/sec; 12969029 sends/sec' > > Well, I doubt Andreas ment to use Integer >> #tinyBenchmarks, because it > doesn't use sets. I think he ment microbenchmarks like this one: > http://leves.web.elte.hu/collections/Set%20class-microBenchmark.st > > I just ran them with the current implementation, 2a and 3c, the results are > here: http://leves.web.elte.hu/collections/SetBenchmarkResults.txt > The values are microseconds/element in the table (smaller valuesa are > better). > > I summed up the rows and created a table (smaller numbers are better): >     trunk  2a    3c > add: (included) 2,594  2,599  2,443 > add: (not included)   9,370  9,957  9,441 > includes: (included)   2,869  2,961  2,822 > includes: (not included)     3,685  3,717  3,671 > like: (included)     2,446  2,486  2,508 > like: (not included)   3,268  3,258  3,310 > rehash  2,924  2,841  2,238 > remove:ifAbsent: (included)   7,190  7,228  7,240 > remove:ifAbsent: (not included) 3,966  3,980  3,856 > > I don't see any real differences. Some values are pretty strange (like > rehash at 3c, I expected it to be slower than trunk). It may be because of > noise, even though I tried to avoid noise as much as possible. > (Set new: 10000) or #rehash you need to fill all array slots with own empty slot marker instead of nil. But if its so unsignificant ... then why really care about macro benchs. > Since these benchmarks show no significant differences, I doubt that > macrobenchmarks would show any differences for these implementations. > > Levente > > -- Best regards, Igor Stasenko AKA sig. |
In reply to this post by Ralph Johnson
2009/11/12 Ralph Johnson <[hidden email]>:
> On Thu, Nov 12, 2009 at 3:44 AM, Igor Stasenko <[hidden email]> wrote: > >> Testing against virtually every existing application is unreasonable >> demand and nobody having time to do that. > > But did you run it against the standard set of tests in Squeak? > > The question is whether changing the behavior of Set will break the > image. Â And if it does, how hard will it be to fix? i can't remember > ever reading or writing code that would break if Sets could include > nil, but that doesn't mean there isn't any. Â The whole reason for > having these tests is so we can see if changes break something. Â But > if we don't run the tests then they don't do us any good. > > Since there are no unit tests that ensure that Sets never contain nil, > it should not be hard to make a version of Set that can contain nil. > That is why I said it was boring and obvious to say that your version > of Set plassed the unit tests for Collections. > Sure, it needs to be tested by all tests which shipped with image. > I loaded the changes into Squeak 10.2. Â First, I ran the tests; two > failed. Â Then I loaded the new version of Set and ran the tests again. > Â Now only one failed, an obscure unwind error that has been around a > long time. Â So, your change doesn't break any of the 2254 tests in > that image. Â it probably wouldn't be hard to test it with squeak-dev, > too. > it could be because my changeset version based on current trunk version of Set, which already contains refactorings comparing to 10.2. So, loading it into 3.10.2 may even crash the whole system. > It is not hard to run all the tests. Â Sure, it takes a little time, > but you can run them in the background and go read your e-mail. Â But > just running the collection tests doesn't mean much, since getting the > collection tests to run is easy so telling us that they work doesn't > tell us much. > I just finished running tests on just-updated-from-trunk image i'm using for development: 2524 run, 2466 passes, 0 expected failures, 40 failures, 18 errors, 0 unexpected passes .. it might be better to do a clean-room test, because my code, which is unrelated to sets, but still might break something occasionally. Will report back later. > -Ralph > -- Best regards, Igor Stasenko AKA sig. |
In reply to this post by Levente Uzonyi-2
Levente Uzonyi wrote:
> > Which message send would you save in > > > > ... == UnoccupiedSlot > > > > UnoccupiedSlot instance. > I wouldn't use a class, because I think we should be able create a set > with any classes in it. Indeed - I am sure there are several places in the image that deal with sets of all classes or all globals. One example of a Smalltalk with a non nil object to mark empty slots in sets is Self, so it has been done before. On the other hand, Self doesn't use associations but instead implements dictionaries with two parallel arrays. -- Jecel |
In reply to this post by Levente Uzonyi-2
ok, I've probably picked up this habit in VisualWorks, and patched all my Squeak images to do nothing instead of raising an error in #add: if the parameter is nil rado On Thu, 12 Nov 2009, Levente Uzonyi wrote: > Hi! > > On Thu, 12 Nov 2009, radoslav hodnicak wrote: > >> >> In all my time I've been programming in Smalltalk (since about 2000), the >> idea that Sets simply do not contain nil was a core assumption of using >> collection classes for me, not just an implementation detail. I routinely >> do "something asSet" to get rid of nils, and this change would break all my >> code. >> > I doubt that. I get an Error when I'm evaluating this: #(1 nil 2) asSet > You should use #(1 nil 2) reject: [ :each | each isNil ] instead. > > Cheers, > Levente > |
In reply to this post by Levente Uzonyi-2
On 12.11.2009, at 14:30, Jecel Assumpcao Jr wrote: > Levente Uzonyi wrote: >>> Which message send would you save in >>> >>> ... == UnoccupiedSlot >>> >> >> UnoccupiedSlot instance. >> I wouldn't use a class, because I think we should be able create a set >> with any classes in it. > > Indeed - I am sure there are several places in the image that deal with > sets of all classes or all globals. Good point. > One example of a Smalltalk with a non nil object to mark empty slots in > sets is Self, so it has been done before. On the other hand, Self > doesn't use associations but instead implements dictionaries with two > parallel arrays. It has even been done in Squeak - see class WeakSet. - Bert - |
In reply to this post by Levente Uzonyi-2
On Thu, 12 Nov 2009, Jecel Assumpcao Jr wrote:
> Levente Uzonyi wrote: >>> Which message send would you save in >>> >>> ... == UnoccupiedSlot >>> >> >> UnoccupiedSlot instance. >> I wouldn't use a class, because I think we should be able create a set >> with any classes in it. > > Indeed - I am sure there are several places in the image that deal with > sets of all classes or all globals. > > One example of a Smalltalk with a non nil object to mark empty slots in > sets is Self, so it has been done before. On the other hand, Self > doesn't use associations but instead implements dictionaries with two > parallel arrays. I have an implementation with 2 arrays for Dictionary and also one with only one array with alternating keys and values which has the best cache locality, but both of them have slower access time than the current implementation and none of them can have nil keys. :) Levente |
In reply to this post by Bert Freudenberg
On Thu, 12 Nov 2009, Bert Freudenberg wrote:
> > On 12.11.2009, at 14:30, Jecel Assumpcao Jr wrote: > >> Levente Uzonyi wrote: >>>> Which message send would you save in >>>> >>>> ... == UnoccupiedSlot >>>> >>> >>> UnoccupiedSlot instance. >>> I wouldn't use a class, because I think we should be able create a set >>> with any classes in it. >> >> Indeed - I am sure there are several places in the image that deal with >> sets of all classes or all globals. > > Good point. > >> One example of a Smalltalk with a non nil object to mark empty slots in >> sets is Self, so it has been done before. On the other hand, Self >> doesn't use associations but instead implements dictionaries with two >> parallel arrays. > > It has even been done in Squeak - see class WeakSet. That's right. And with a "global" flag its implementation could be simpler. It won't be able to contain nil with the marker based implementations, but it's not a real issue IMO. Levente |
In reply to this post by Bert Freudenberg
2009/11/12 Bert Freudenberg <[hidden email]>:
> > On 12.11.2009, at 11:35, Bert Freudenberg wrote: > >> >> On 12.11.2009, at 09:42, Andreas Raab wrote: >> >>> Levente Uzonyi wrote: >>>> But we now have a lot more proposals, let me summarize them all: >>>> - (1)add a new instance variable: containsNil >>>> - (2)use tally to indicate if nil is in the set >>>> - (a)negative values mean nil is contained by the set >>>> - (b)floats - (3)use a marker object >>>> - (a)self >>>> - (b)a unique object in each set >>>> - (c)a class variable >>>> - (4)use container objects in occupied slots, like associations in dictionaries >>>> I prefer 3c, 2a, 3a, 1, 4 in the given order. >>>> What about you? >>> >>> Nice summary. My current preferences would be (1) (clear and obvious) (3b) (ditto) and (3a)/(3c) (both with some hesitation). I'm not sure about (4) (I've only just heard of it; it sounds cool but I haven't seen no code yet). I would veto both (2a) and (2b) as obscure hacks compared to (1) unless the size overhead is significant. >> >> I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker (either the class itself [I'd like that] or its singleton instance [for the purists]). This would make it blatantly obvious what's happening when inspecting the innards of a Set. >> >> I'd reluctantly accept (1) but fear it would complicate the logic - containsNil checks will have to be scattered all over the place. >> >> - Bert - > > Thinking about migrating (possibly) old data - how do the options fare with respect to reloading serialized Sets? I guess we could count nils and detect a mismatch with tally? > > - Bert - > > Solution 2a) is most transparent w.r.t. migration, no supplementary ivar, old ivar interpreted the same way Solution 1) could be if using lazy initialization of ivar containsNil (or using containsNil == true) Solution 3b) would be transparent too with an ivar marker being nil by default (you could not add nil to old Set but who cares) I think every other solution would require a little migration hack. |
In reply to this post by Bert Freudenberg
>>>>> "Bert" == Bert Freudenberg <[hidden email]> writes:
Bert> I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker Bert> (either the class itself [I'd like that] or its singleton instance [for Bert> the purists]). This would make it blatantly obvious what's happening Bert> when inspecting the innards of a Set. The problem with that is that you're simply moving the problem. Now I can't have a Set of all classes. :( That's the beauty of my "each set contains its own unique nil marker" solution. Any set can contain anything except its own insides, which is a limitation that would only occur if you're doing something very very nasty. A set of all classes is far more likely to occur. -- Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095 <[hidden email]> <URL:http://www.stonehenge.com/merlyn/> Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc. See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion |
On 12.11.2009, at 16:22, Randal L. Schwartz wrote: >>>>>> "Bert" == Bert Freudenberg <[hidden email]> writes: > > Bert> I'd prefer (3d): make a class called UnoccupiedSlot, use it as marker > Bert> (either the class itself [I'd like that] or its singleton instance [for > Bert> the purists]). This would make it blatantly obvious what's happening > Bert> when inspecting the innards of a Set. > > The problem with that is that you're simply moving the problem. > > Now I can't have a Set of all classes. :( > > That's the beauty of my "each set contains its own unique nil marker" > solution. Any set can contain anything except its own insides, which is a > limitation that would only occur if you're doing something very very nasty. A > set of all classes is far more likely to occur. I concur. It still could be an UnoccupiedSlot instance, though "Object new" works just the same except if you basic-inspect a Set. This still feels "cleaner" to me than the extra "contains nil" flag / tally hack. Besides, we'd end up with net code savings, basically moving the stuff from WeakSet up to its super class. - Bert - |
In reply to this post by Igor Stasenko
Ok, here the test results, as i promised.
Starting image: http://ftp.squeak.org/trunk/Squeak3.10.2-Trunk-091024.zip + pushed update button. Run all tests except DecompilerTests and DecompilerTestFailuresCollector (because it takes eons to wait). Before any changes to sets: 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 unexpected passes After applying changes to sets using nil wrappers [1]: 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 unexpected passes After adding changes to sets using negative tally[2]: 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 unexpected passes [1] http://bugs.squeak.org/file_download.php?file_id=3829&type=bug [2] http://bugs.squeak.org/file_download.php?file_id=3830&type=bug Also, see Mantis entry http://bugs.squeak.org/view.php?id=7413 -- Best regards, Igor Stasenko AKA sig. |
Igor Stasenko wrote:
> 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 > unexpected passes > > > After applying changes to sets using nil wrappers [1]: > > 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 > unexpected passes > > > After adding changes to sets using negative tally[2]: > > 2428 run, 2406 passes, 0 expected failures, 15 failures, 7 errors, 0 > unexpected passes Those are great results! > [1] http://bugs.squeak.org/file_download.php?file_id=3829&type=bug Yeah... seeing the code I like the wrapper solution even better. It's just so elegant. Virtually no overhead, nicely dealing with all sorts of nestings, having the option for future extensions (weak elements, collection elements etc). I think I've just promoted that to my top choice ;-) Seriously folks, look at that code. It's a great solution. Cheers, - Andreas |
Free forum by Nabble | Edit this page |