Optional values

24 views
Skip to first unread message

Lukas Bonauer

unread,
Nov 14, 2020, 3:13:06 PM11/14/20
to juggling-interchange-format

Hi all,

a topic that has been briefly touched on are that many fields in JIF will be optional. I would like to propose a stronger version of this: ALL fields should be optional! Yes, I want the string {} to be valid JIF. Hear me out.

The goal of this is (a) to make the user experience of passing JIF around different programs as seamless as possible, and (b) to reduce the barrier of entry for programmers looking to support JIF by making it simple to generate.

# Optionality and user experience

I think we all agree that many values are going to be irrelevant to certain software types -- classic example of the vanilla siteswap generator which does not care about limb movement, prop types, dwell times, etc.. This ties into the and physical vs. logical throws discussion.

For the user's sake, I strongly want to avoid having different "versions" or "levels" of JIF. As a user, I don't want to pipe a generator's JIF output through some converters before I can give it to an animator to see what is going on. As a user, if I click "Export JIF ..." in one software and then "Import JIF ..." in a different software, I expect things to just work.

As a tiny community, I don't think we can afford fragmentation.

So for all fields, this rules out any option related to "this is required to be present only for some programs". That would automatically create fragmentation, because as soon as program X requires a field to be there that program Y does not export, they become incompatible.

Conclusion: Every field MUST be either optional or required for ALL software, nothing in-between.

# Optionality and simplicity

Now, let's discuss "required" vs "optional". There is a trade-off here: Programs that only generate JIF greatly benefit from not having to bother with unnecessary boilerplate that they don't care about. If a developer only supports vanilla solo siteswaps, they do not want to think about how many hands are in the pattern, that beats could have different timings, or that objects could have different colors.

On the other hand, programs that consume JIF need to always fill in missing values with defaults, for every optional field that they care about. So a developer might prefer required values. More on this in the section "Default values".

(Side note: Exact field names of the following JIF examples are not too relevant and I don't want to make them point of the discussion in this thread. I use integer references as was discussed in a different thread, where "throws" reference "limbs", and "limbs" reference jugglers.)

Example 1: The vanilla siteswap generator needs to export 531, so it generates:

{throws: [{duration: 5}, {duration: 3}, {duration: 1}]}

It does not bother with number of jugglers, how many hands they have, synchronous throws, polyrhythms, etc.

Example 2: A generator color-codes magic throws wants to visualize how the 3 in 531 is always the same object, so it generates:

{objects: [{color: 'red'}, {color: 'red'}, {color: 'green'}],
 throws: [
   {duration: 5, object: 0},
   {duration: 3, object: 2},
   {duration: 1, object: 1},
 ]}

Example 3: In a 2D top-down view, I want to visualize who passes with whom when, by drawing lines between circles, but only when they pass ("(A) ----- (B)          (C)"). Here you don't even care about the throw duration, only the time and between whom. For a 3-person feast, you generate:

{jugglers: [{}, {}, {}],
 limbs: [{juggler: 0}, {juggler: 1}, {juggler: 2}],
 throws: [
   {time: 0, from: 0, to: 1},
   {time: 1, from: 0, to: 2},
   {time: 2, from: 1, to: 2},
 ]}

This is slightly cumbersome because you first assert that each juggler only has 1 limb (not something the software programmer would naturally model in this use case), but I would be ok with that. An alternative would be to rely on the default that jugglers have 2 limbs and just use only the first (default: right) limb of each juggler for the throws (0, 2, 4):

{jugglers: [{}, {}, {}],
 throws: [
   {time: 0, from: 0, to: 2},
   {time: 1, from: 0, to: 4},
   {time: 2, from: 2, to: 4},
 ]}

Again, "limbs" is optionally and 6 limbs would be the default for 3 jugglers (Or we could decide to allow specifying either "fromLimb" and "toLimb", or "fromJuggler" and "toJuggler", with the limbs implicit. But this adds redundancy, which I am not really a fan of.)

Note how I use [{}, {}, {}] for 3 jugglers, and not "numJugglers: 3". A field like "numJugglers" introduces redundancy, because to add properties to the jugglers you need a "jugglers" array anyway. A programmer can easily extend {} to {label: "Alice", position: [4,2]} without changes to the JIF structure.

While [{}, {}, {}] is also boilerplate in the eyes of a programmer who does not care about naming or positioning their jugglers, I believe that in this case the extensibility benefits outweigh the cost.

You could argue that it is allowed to leave out the "jugglers" line completely, because there exists a throw to limb #4, so it can be implicitly inferred that there must be 3 jugglers. But this has a cost for the JIF consumers, see the section "Default values" below.

Example 4: I want to animate 2 people without clubs who are facing each other and who are dancing the Macarena.

{jugglers: [{}, {}],
 jugglerMovement: [/* lots of instructions */],

 limbMovement: [/* lots of instructions */]
}

All clear? Great :)

# Default values

A program that reads JIF always need to check for presence of fields to validate the input. When a field you care about is absent, you usually have the choice of throwing an error or filling in a default value. In many cases, default values are trivial and even less work than throwing a (good) error.

By making developers ALWAYS check for presence and using default values, I claim that we even encourage more robust implementations of JIF, and prevent things like "Oh I guess the 'throws' property always exists, it's fine if my program crashes if it is missing". Consistency is king.

However, the computation of some default values also introduces complexities that may hinder adoption. I don't wanna sweep this under the rug. Some we can just specify the procedure in the spec and it becomes relatively easy and uncontroversial to adopt, for example:

* If "jugglers" is missing, there is 1 juggler.
* If "limbs" is missing, there are 2 limbs per juggler, with juggler n "owning" limb 2n (right hand) and limb 2n+1 (left hand).
* If "throws" is missing, there are no throws.

Some other default rules we can put in the spec, but different programmers might disagree with them, or they might be cumbersome to implement or have hard edge cases:

* Jugglers are labeled A, B, C, ... by default. (Not too hard to implement, but already forces a decision on what happens if there happen to be more than 26 jugglers.)

* 1 juggler stands at position (0,0). More jugglers stand evenly spaced around a circle with radius 1. (arbitrary)

* "time" starts at 0, and each throw without a time defaults to the time of the previous throw plus 1.

* The default throw duration is 3. (arbitrary choice)

* In a pattern with k limbs, for a throw at time t and duration d:
    * "from" (fromLimb) defaults to limb (t mod k)
    * "to" (toLimb) defaults to limb (t+d mod k)
(This is elegant for n-handed siteswap aka asynchronous passing, but what to do if t or d are not integers?)

* Patterns repeat by default. (But what is the period if the last throw is written to be at time=5.333?!)

* A throw's dwell time defaults to 1. (But what if duration is <=1? See the discussion on zip timings.)

.... the list goes on for quite a bit, which is a downsides in itself, since there will be many such rules that need to be implemented. Luckily, most programs won't need to bother with many of them (the causal editor does not care where jugglers stand by default).

One more thing to throw in is that we may want to adopt W3C's wording of MUST, SHOULD and MAY that is used in RFCs. Some defaults are really not crucial (e.g. beats per second, number of spins) and if a developer wants to change it for aesthetic reasons, that's ok, but others are REALLY important to ensure compatibility.

## Example 1 with defaults filled in

Just to illustrate what I mean more, here is what {throws: [{duration: 5}, {duration: 3}, {duration: 1}]} could be expanded to (with some made up field names, of course not complete):

{
  jugglers: [
    {label: "A", position: [0,0]}
  ],
  limbs: [
    {juggler: 0, label: "R", type: "right_hand"},
    {juggler: 1, label: "L", type: "left_hand"},
  ],
  throws: [
    {time: 0, duration: 5, from: 0, to: 1, dwellTime: 1},
    {time: 1, duration: 3, from: 1, to: 0, dwellTime: 1},
    {time: 2, duration: 1, from: 0, to: 1, dwellTime: 1},
  ],
  repeating: true,
  beatsPerSecond: 3,
  jugglerMovement: [],
  limbMovement: [],
}

# Conclusion

I believe this topic is important because thinking about default values helps while deciding the overall structure of JIF, and also while deciding how certain fields should work (e.g. optional values are a strong argument for integer references over strings).

Despite the drawbacks, I believe that making ALL values optional maximizes compatibility and mostly minimizes developer annoyance.

Thanks for reading, would love to hear your thoughts!

Cheers,
Lukas

JaCo Stuifbergen

unread,
Nov 14, 2020, 5:06:56 PM11/14/20
to juggling-interchange-format
It sounds like a great idea, especially because it makes the files more elegant.
This is especially nice if you want to combine different aspects of juggling (say, combine causal diagrams from one program with the walks from another program)

However, it may require more from the programmers to fill the gaps.
It will not be difficult to fill in missing durations if the value is always 3, but to fill missing jugglers or limbs requires already a tiny bit of arithmetic.
And a simple parameter like "repeating" may require very precise descriptions (how do the limbs permute?  what is the period of the pattern?).

Lukas Bonauer

unread,
Nov 14, 2020, 10:12:11 PM11/14/20
to juggling-inte...@googlegroups.com

Correction: For 4-handed siteswap to fit in elegantly, the default limb order has to be different from what I described!

The assumption I put forward was that "default throw order equals default limb order". I assumed the default limbs of 2 jugglers to be: [A:right, A:left, B:right, B:left], which of course is not the natural throwing order for 4-handed siteswap. That would be [A:right, B:right, A:left, B:left].

Sadly, that ordering looses the nice property that you wouldn't have to change limb indices when adding a juggler:
With [A, A, B, B, C, C, ...], limbs #0 and #1 always belong to A, no matter how many jugglers.
But with [A, B, C, ..., A, B, C, ...] it depends on the total number of jugglers, making code slightly more complicated for all use cases except n-handed siteswap (at least the ones that want to use the default limbs settings, which I expect to be most common).

Not sure right now which option I would even prefer ... Some defaults are hard :(


On 2020-11-14 23:06, JaCo Stuifbergen wrote:
It sounds like a great idea, especially because it makes the files more elegant.
This is especially nice if you want to combine different aspects of juggling (say, combine causal diagrams from one program with the walks from another program)

However, it may require more from the programmers to fill the gaps.
It will not be difficult to fill in missing durations if the value is always 3, but to fill missing jugglers or limbs requires already a tiny bit of arithmetic.
And a simple parameter like "repeating" may require very precise descriptions (how do the limbs permute?  what is the period of the pattern?).

On Saturday, 14 November 2020 at 21:13:06 UTC+1 lukas....@yahoo.de wrote:

Hi all,

a topic that has been briefly touched on are that many fields in JIF will be optional. I would like to propose a stronger version of this: ALL fields should be optional! Yes, I want the string {} to be valid JIF. Hear me out.

The goal of this is (a) to make the user experience of passing JIF around different programs as seamless as possible, and (b) to reduce the barrier of entry for programmers looking to support JIF by making it simple to generate.

# Optionality and user experience

I think we all agree that many values are going to be irrelevant to certain software types -- classic example of the vanilla siteswap generator which does not care about limb movement, prop types, dwell times, etc.. This ties into the and physical vs. logical throws discussion.

For the user's sake, I strongly want to avoid having different "versions" or "levels" of JIF. As a user, I don't want to pipe a generator's JIF output through some converters before I can give it to an animator to see what is going on. As a user, if I click "Export JIF ..." in one software and then "Import JIF .." in a different software, I expect things to just work.

--
You received this message because you are subscribed to the Google Groups "juggling-interchange-format" group.
To unsubscribe from this group and stop receiving emails from it, send an email to juggling-interchang...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/juggling-interchange-format/b6405a10-824c-4c7f-af53-bdb35ed6a9f8n%40googlegroups.com.

JaCo Stuifbergen

unread,
Nov 15, 2020, 7:55:48 AM11/15/20
to juggling-interchange-format
I would still attribute limbs #0 and #1 to A, and set the limb order for 3 jugglers as:
[0, 2, 4, 1, 3, 5]

But I am a bit concerned (worried) that we cannot foresee the behaviour of a default value in every situation.
On the other hand, if a pattern doesn't work with the default value, the programmer just has to specify the value by himself.

Co Stuifbergen
=========================
in...@jacos.nl
www.jacos.nl
+31 6 13 02 44 69 (mobile)

Kloosterwei 102
2361 XN  Warmond
the Netherlands
=========================



Christian Helbling

unread,
Nov 16, 2020, 4:56:04 PM11/16/20
to juggling-inte...@googlegroups.com
Hi Lukas

Interesting thoughts, thank you very much!

I agree that we should make as many fields as possible optional.
JIF generating code should not be forced to set fields they don't care about.
JIF reading code on the other side might require a lot of details (especially animators).

In your proposal this gap is quite big, which creates the need to define lots of defaults.
One reason this gap is so big, is that you would basically allow some high-level descriptions
to still be valid jifs. Example 1 is nothing more than siteswap.

In another thread (so deep into the discussion I cannot find it..) we concluded that jif should
not itself be used for such high-level descriptions (except that those can be stored in some jif-field).
It should rather have some minimum "level" that contains about as much information needed to
draw a causal diagram.
Requiring such a minimum level would make this gap a bit smaller.
For example, this would mean, that every throw MUST declare the fields time, duration, from and to.
Some of the hard decisions can be avoided by this.

By looking at the list of JIF use cases we can find some reasonable minimum level of detail that most
of the JIF reading applications need anyway.


Up to the level of detail an animator requires, there are still lots of details to be filled in.
Deciding on all the defaults here often seems opinionated. For example:
- whats the default prop type
- is a siteswap 2 active or not
- how is the zip timing exactly (lots of discussions about that in another thread)
- position of 3 jugglers (as you mentioned)
- ..

I fear that deciding on all that would take us too long, seeing that we need a lot of time just to
decide on naming things and that there are still some complicated topics undiscussed.

When I look at the current list of use-cases, most of them seem to be fine with a causal-diagram-level
jif. Its mostly a problem for animation software.
Adding lots of opinionated defaults to the standard just for them seems overkill to me.

I think we can fill the level-of-detail gap by writing specializer software that add all the details
needed for animation. Things like default prop type, active twos, zip details and juggler positions
would probably end up as specializer defaults and options. Animation software can use some specializer,
inheriting its defaults and overwriting some, possibly by user choice.

This would of course cause a bit of fragmentation. However:
- it is still possible to specify things you care about in great detail
- we avoid people not specifying fields they care about that happen to match an opinionated default
- different animators look a lot different anyway
- we can still make sure that the logical level of jif is very well defined

Of course, we should still have well-defined defaults for fields that have undisputed defaults.
We might still be able to make {} valid jif :)

>> Note how I use [{}, {}, {}] for 3 jugglers, and not "numJugglers: 3". A field like "numJugglers" introduces redundancy, because to add
>> properties to the jugglers you need a "jugglers" array anyway. A programmer can easily extend{} to {label: "Alice", position: [4,2]} without
>> changes to the JIF structure.
>>
>> While [{}, {}, {}] is also boilerplate in the eyes of a programmer who does not care about naming or positioning their jugglers, I believe that
>> in this case the extensibility benefits outweigh the cost.
I like that.

Cheers
Christian


On 15.11.20 04:12, 'Lukas Bonauer' via juggling-interchange-format wrote:
> Correction: For 4-handed siteswap to fit in elegantly, the default limb order has to be different from what I described!
>
> The assumption I put forward was that "default throw order equals default limb order". I assumed the default limbs of 2 jugglers to be: [A:right,
> A:left, B:right, B:left], which of course is not the natural throwing order for 4-handed siteswap. That would be [A:right, B:right, A:left, B:left].
>
> Sadly, that ordering looses the nice property that you wouldn't have to change limb indices when adding a juggler:
> With [A, A, B, B, C, C, ...], limbs #0 and #1 always belong to A, no matter how many jugglers.
> But with [A, B, C, ..., A, B, C, ...] it depends on the total number of jugglers, making code slightly more complicated for all use cases except
> n-handed siteswap (at least the ones that want to use the default limbs settings, which I expect to be most common).
>
> Not sure right now which option I would even prefer ... Some defaults are hard :(
>
>
> On 2020-11-14 23:06, JaCo Stuifbergen wrote:
>> It sounds like a great idea, especially because it makes the files more elegant.
>> This is especially nice if you want to combine different aspects of juggling (say, combine causal diagrams from one program with the walks from
>> another program)
>>
>> However, it may require more from the programmers to fill the gaps.
>> It will not be difficult to fill in missing durations if the value is always 3, but to fill missing jugglers or limbs requires already a tiny bit of
>> arithmetic.
>> And a simple parameter like "repeating" may require very precise descriptions (how do the limbs permute?  what is the period of the pattern?).
>>
>> On Saturday, 14 November 2020 at 21:13:06 UTC+1 lukas....@yahoo.de wrote:
>>
>> Hi all,
>>
>> a topic that has been briefly touched on are that many fields in JIF will be optional. I would like to propose a stronger version of this: ALL
>> fields should be optional! Yes, I want the string {} to be valid JIF. Hear me out.
>>
>> The goal of this is (a) to make the user experience of passing JIF around different programs as seamless as possible, and (b) to reduce the
>> barrier of entry for programmers looking to support JIF by making it simple to generate.
>>
>> *# Optionality and user experience*
>>
>> I think we all agree that many values are going to be irrelevant to certain software types -- classic example of the vanilla siteswap generator
>> which does not care about limb movement, prop types, dwell times, etc.. This ties into the and physical vs. logical throws discussion.
>>
>> For the user's sake, I strongly want to avoid having different "versions" or "levels" of JIF. As a user, I don't want to pipe a generator's JIF
>> output through some converters before I can give it to an animator to see what is going on. As a user, if I click "Export JIF ..." in one
>> software and then "Import JIF .." in a different software, I expect things to just work.
>>
>> As a tiny community, I don't think we can afford fragmentation.
>>
>> So for all fields, this rules out any option related to "this is required to be present only for some programs". That would automatically create
>> fragmentation, because as soon as program X requires a field to be there that program Y does not export, they become incompatible.
>>
>> Conclusion: Every field MUST be either optional or required for ALL software, nothing in-between.
>>
>> *# Optionality and simplicity*
>>
>> Now, let's discuss "required" vs "optional". There is a trade-off here: Programs that only _generate_ JIF greatly benefit from not having to
>> bother with unnecessary boilerplate that they don't care about. If a developer only supports vanilla solo siteswaps, they do not want to think
>> about how many hands are in the pattern, that beats could have different timings, or that objects could have different colors.
>>
>> On the other hand, programs that _consume_ JIF need to always fill in missing values with defaults, for every optional field that they care
>> about. So a developer might prefer required values. More on this in the section "Default values".
>>
>> /(Side note: Exact field names of the following JIF examples are not too relevant and I don't want to make them point of the discussion in this
>> thread. I use integer references as was discussed in a different thread, where "throws" reference "limbs", and "limbs" reference jugglers.)/
>>
>> *Example 1*: The vanilla siteswap generator needs to export 531, so it generates:
>>
>> {throws: [{duration: 5}, {duration: 3}, {duration: 1}]}
>>
>> It does not bother with number of jugglers, how many hands they have, synchronous throws, polyrhythms, etc.
>>
>> *Example 2*: A generator color-codes magic throws wants to visualize how the 3 in 531 is always the same object, so it generates:
>>
>> {objects: [{color: 'red'}, {color: 'red'}, {color: 'green'}],
>>  throws: [
>>    {duration: 5, object: 0},
>>    {duration: 3, object: 2},
>>    {duration: 1, object: 1},
>>  ]}
>>
>> *Example 3*: In a 2D top-down view, I want to visualize who passes with whom when, by drawing lines between circles, but only when they pass
>> *Example 4*: I want to animate 2 people without clubs who are facing each other and who are dancing the Macarena.
>>
>> {jugglers: [{}, {}],
>>  jugglerMovement: [/* lots of instructions */],
>>  limbMovement: [/* lots of instructions */]}
>>
>> All clear? Great :)
>>
>> *# Default values*
>>
>> A program that reads JIF always need to check for presence of fields to validate the input. When a field you care about is absent, you usually
>> have the choice of throwing an error or filling in a default value. In many cases, default values are trivial and even less work than throwing a
>> (good) error.
>>
>> By making developers ALWAYS check for presence and using default values, I claim that we even encourage more robust implementations of JIF, and
>> prevent things like "Oh I guess the 'throws' property always exists, it's fine if my program crashes if it is missing". Consistency is king.
>>
>> However, the computation of some default values also introduces complexities that may hinder adoption. I don't wanna sweep this under the rug.
>> Some we can just specify the procedure in the spec and it becomes relatively easy and uncontroversial to adopt, for example:
>>
>> * If "jugglers" is missing, there is 1 juggler.
>> * If "limbs" is missing, there are 2 limbs per juggler, with juggler n "owning" limb 2n (right hand) and limb 2n+1 (left hand).
>> * If "throws" is missing, there are no throws.
>>
>> Some other default rules we can put in the spec, but different programmers might disagree with them, or they might be cumbersome to implement or
>> have hard edge cases:
>>
>> * Jugglers are labeled A, B, C, ... by default. (Not too hard to implement, but already forces a decision on what happens if there happen to be
>> more than 26 jugglers.)
>>
>> * 1 juggler stands at position (0,0). More jugglers stand evenly spaced around a circle with radius 1. (arbitrary)
>>
>> * "time" starts at 0, and each throw without a time defaults to the time of the previous throw plus 1.
>>
>> * The default throw duration is 3. (arbitrary choice)
>>
>> * In a pattern with k limbs, for a throw at time t and duration d:
>>     * "from" (fromLimb) defaults to limb (t mod k)
>>     * "to" (toLimb) defaults to limb (t+d mod k)
>> (This is elegant for n-handed siteswap aka asynchronous passing, but what to do if t or d are not integers?)
>>
>> * Patterns repeat by default. (But what /is/ the period if the last throw is written to be at time=5.333?!)
>>
>> * A throw's dwell time defaults to 1. (But what if duration is <=1? See the discussion on zip timings.)
>>
>> .... the list goes on for quite a bit, which is a downsides in itself, since there will be many such rules that need to be implemented. Luckily,
>> most programs won't need to bother with many of them (the causal editor does not care where jugglers stand by default).
>>
>> One more thing to throw in is that we may want to adopt W3C's wording of MUST, SHOULD and MAY <https://www.rfc-editor.org/rfc/rfc2119.txt> that
>> is used in RFCs. Some defaults are really not crucial (e.g. beats per second, number of spins) and if a developer wants to change it for
>> aesthetic reasons, that's ok, but others are REALLY important to ensure compatibility.
>>
>> *## Example 1 with defaults filled in*
>>
>> Just to illustrate what I mean more, here is what {throws: [{duration: 5}, {duration: 3}, {duration: 1}]} could be expanded to (with some made
>> up field names, of course not complete):
>>
>> {
>>   jugglers: [
>>     {label: "A", position: [0,0]}
>>   ],
>>   limbs: [
>>     {juggler: 0, label: "R", type: "right_hand"},
>>     {juggler: 1, label: "L", type: "left_hand"},
>>   ],
>>   throws: [
>>     {time: 0, duration: 5, from: 0, to: 1, dwellTime: 1},
>>     {time: 1, duration: 3, from: 1, to: 0, dwellTime: 1},
>>     {time: 2, duration: 1, from: 0, to: 1, dwellTime: 1},
>>   ],
>>   repeating: true,
>>   beatsPerSecond: 3,
>>   jugglerMovement: [],
>>   limbMovement: [],
>> }
>>
>> *# Conclusion*
>>
>> I believe this topic is important because thinking about default values helps while deciding the overall structure of JIF, and also while
>> deciding how certain fields should work (e.g. optional values are a strong argument for integer references over strings).
>>
>> Despite the drawbacks, I believe that making ALL values optional maximizes compatibility and mostly minimizes developer annoyance.
>>
>> Thanks for reading, would love to hear your thoughts!
>>
>> Cheers,
>> Lukas
>>
>> --
>> You received this message because you are subscribed to the Google Groups "juggling-interchange-format" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to juggling-interchang...@googlegroups.com
>> <mailto:juggling-interchang...@googlegroups.com>.
>> <https://groups.google.com/d/msgid/juggling-interchange-format/b6405a10-824c-4c7f-af53-bdb35ed6a9f8n%40googlegroups.com?utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google Groups "juggling-interchange-format" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to juggling-interchang...@googlegroups.com
> <mailto:juggling-interchang...@googlegroups.com>.
> To view this discussion on the web visit https://groups.google.com/d/msgid/juggling-interchange-format/a0e9c349-10ee-126c-d1d1-fdd55b7b31b2%40yahoo.de
> <https://groups.google.com/d/msgid/juggling-interchange-format/a0e9c349-10ee-126c-d1d1-fdd55b7b31b2%40yahoo.de?utm_medium=email&utm_source=footer>.


Reply all
Reply to author
Forward
0 new messages