Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Developing U++ » U++ Developers corner » Value with type float
Re: Value with type float [message #58295 is a reply to message #58294] Tue, 12 April 2022 13:54 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Thanks! This works great!

Best regards,

Tom
Re: Value with type float [message #58367 is a reply to message #58295] Tue, 10 May 2022 13:21 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Sorry to return to this subject, but could you consider adding Null support for float?

I had something like this in mind... (in Core/Defs.h):
const int    INT_NULL           =    INT_MIN;
const int64  INT64_NULL         =    INT64_MIN;

constexpr double DOUBLE_NULL    =    -std::numeric_limits<double>::infinity();
constexpr float FLOAT_NULL      =    -std::numeric_limits<float>::infinity();

class Nuller {
public:
	operator int() const                { return INT_NULL; }
	operator int64() const              { return INT64_NULL; }
	operator double() const             { return DOUBLE_NULL; }
	operator float() const              { return FLOAT_NULL; }
	operator bool() const               { return false; }

	Nuller() {}
};

extern const Nuller Null;

template <class T> void SetNull(T& x) { x = Null; }

template <class T> bool IsNull(const T& x)       { return x.IsNullInstance(); }

template<> inline bool  IsNull(const int& i)     { return i == INT_NULL; }
template<> inline bool  IsNull(const int64& i)   { return i == INT64_NULL; }
template<> inline bool  IsNull(const double& r)  { return !(std::abs(r) < std::numeric_limits<double>::infinity()); }
template<> inline bool  IsNull(const float& r)   { return !(std::abs(r) < std::numeric_limits<float>::infinity()); }
template<> inline bool  IsNull(const bool& r  )  { return false; }


Although, I'm not entirely sure, if this is completely correct way to do it.

Best regards,

Tom

PS. EDIT: I think the above should work mostly. Only the "Cout() << FLOAT_NULL;" prints -inf instead of empty field, which is the default for "Cout() << DOUBLE_NULL;":
CONSOLE_APP_MAIN{
	double d=Null;
	float f=Null;
	double a=f;
	float b=d;
	Cout() << "d = " << d << "\n";
	Cout() << "f = " << f << "\n";
	Cout() << "a = " << a << "\n";
	Cout() << "b = " << b << "\n";
	Cout() << "a == f : " << (bool)(a==f) << "\n";
	Cout() << "b == d : " << (bool)(b==d) << "\n";
	Cout() << "IsNull(d) = " << IsNull(d) << "\n";
	Cout() << "IsNull(f) = " << IsNull(f) << "\n";
	Cout() << "IsNull(a) = " << IsNull(a) << "\n";
	Cout() << "IsNull(b) = " << IsNull(b) << "\n";
}

[Updated on: Tue, 10 May 2022 13:31]

Report message to a moderator

Re: Value with type float [message #58368 is a reply to message #58367] Tue, 10 May 2022 15:44 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Tue, 10 May 2022 13:21
Hi Mirek,

Sorry to return to this subject, but could you consider adding Null support for float?

I had something like this in mind... (in Core/Defs.h):
const int    INT_NULL           =    INT_MIN;
const int64  INT64_NULL         =    INT64_MIN;

constexpr double DOUBLE_NULL    =    -std::numeric_limits<double>::infinity();
constexpr float FLOAT_NULL      =    -std::numeric_limits<float>::infinity();

class Nuller {
public:
	operator int() const                { return INT_NULL; }
	operator int64() const              { return INT64_NULL; }
	operator double() const             { return DOUBLE_NULL; }
	operator float() const              { return FLOAT_NULL; }
	operator bool() const               { return false; }

	Nuller() {}
};

extern const Nuller Null;

template <class T> void SetNull(T& x) { x = Null; }

template <class T> bool IsNull(const T& x)       { return x.IsNullInstance(); }

template<> inline bool  IsNull(const int& i)     { return i == INT_NULL; }
template<> inline bool  IsNull(const int64& i)   { return i == INT64_NULL; }
template<> inline bool  IsNull(const double& r)  { return !(std::abs(r) < std::numeric_limits<double>::infinity()); }
template<> inline bool  IsNull(const float& r)   { return !(std::abs(r) < std::numeric_limits<float>::infinity()); }
template<> inline bool  IsNull(const bool& r  )  { return false; }


Although, I'm not entirely sure, if this is completely correct way to do it.

Best regards,

Tom

PS. EDIT: I think the above should work mostly. Only the "Cout() << FLOAT_NULL;" prints -inf instead of empty field, which is the default for "Cout() << DOUBLE_NULL;":
CONSOLE_APP_MAIN{
	double d=Null;
	float f=Null;
	double a=f;
	float b=d;
	Cout() << "d = " << d << "\n";
	Cout() << "f = " << f << "\n";
	Cout() << "a = " << a << "\n";
	Cout() << "b = " << b << "\n";
	Cout() << "a == f : " << (bool)(a==f) << "\n";
	Cout() << "b == d : " << (bool)(b==d) << "\n";
	Cout() << "IsNull(d) = " << IsNull(d) << "\n";
	Cout() << "IsNull(f) = " << IsNull(f) << "\n";
	Cout() << "IsNull(a) = " << IsNull(a) << "\n";
	Cout() << "IsNull(b) = " << IsNull(b) << "\n";
}



IDK. Does it solve any real problem?

Up until this year, I have considered double-float relation to be similar to int-int16. You use float to reduce memory consumption or for special things (GPU), but you really do not need to support it in Value or widgets. Served me well for many many years. What has changed? Smile

Mirek
Re: Value with type float [message #58369 is a reply to message #58368] Tue, 10 May 2022 20:08 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi,

What has changed is that I have really started to enjoy the new EditFloatSpin! Smile

As a result, initializing and reading my float variables to/from EditFloatSpin (as used in e.g. filter parameters) is clean and easy. However, I found that initializing my EditFloatSpin to empty field (e.g. when such filtering is not currently used) requires my float variable to be at Null:
EditFloatSpin hpfedit;
...
float hpf=(float)(double)Null;
...
hpfedit <<= hpf;

Alternatively, some value of hpf (e.g.< 0) could be interpreted as empty filtering and I could do hpfedit.Clear(); if such is the case.

However, I prefer nice, short and clean code, so I would like to write just:
float hpf=Null;

I.e. without any type cast. So, in effect, this is just to make the code cleaner.

Best regards,

Tom

[Updated on: Tue, 10 May 2022 20:28]

Report message to a moderator

Re: Value with type float [message #58370 is a reply to message #58369] Tue, 10 May 2022 21:26 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Tue, 10 May 2022 20:08
Hi,

What has changed is that I have really started to enjoy the new EditFloatSpin! Smile

As a result, initializing and reading my float variables to/from EditFloatSpin (as used in e.g. filter parameters) is clean and easy. However, I found that initializing my EditFloatSpin to empty field (e.g. when such filtering is not currently used) requires my float variable to be at Null:
EditFloatSpin hpfedit;
...
float hpf=(float)(double)Null;
...
hpfedit <<= hpf;

Alternatively, some value of hpf (e.g.< 0) could be interpreted as empty filtering and I could do hpfedit.Clear(); if such is the case.

However, I prefer nice, short and clean code, so I would like to write just:
float hpf=Null;

I.e. without any type cast. So, in effect, this is just to make the code cleaner.

Best regards,

Tom


Why dont you just use

double hpf;

?
Re: Value with type float [message #58373 is a reply to message #58370] Wed, 11 May 2022 08:20 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Quote:

Why dont you just use

double hpf;

?

Well, all my signal processing code runs on 32-bit floats, and therefore, the various coefficients/parameters are also floats. It is straight forward to keep it up in the user interface too. If I used doubles in the GUI, I would end up converting forth and back all those parameters. Never wish to go back there, now that I have EditFloat and EditFloatSpin! Smile

Anyway, if you feel seriously reluctant to add Null support for float, I can live with it: I figured out a way to do it outside of Core almost cleanly:
constexpr float Nullf = -std::numeric_limits<float>::infinity();
inline bool IsNull(const float& r)   { return !(std::abs(r) < std::numeric_limits<float>::infinity()); }
inline void SetNull(float& x) { x = Nullf; }

Still, it would be nicer inside Core... After all, it would introduce only three lines of new code in Core/Defs.h.

Best regards,

Tom
Re: Value with type float [message #58501 is a reply to message #58373] Fri, 03 June 2022 10:52 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Wed, 11 May 2022 08:20
Quote:

Why dont you just use

double hpf;

?

Well, all my signal processing code runs on 32-bit floats, and therefore, the various coefficients/parameters are also floats. It is straight forward to keep it up in the user interface too. If I used doubles in the GUI, I would end up converting forth and back all those parameters. Never wish to go back there, now that I have EditFloat and EditFloatSpin! Smile

Anyway, if you feel seriously reluctant to add Null support for float, I can live with it: I figured out a way to do it outside of Core almost cleanly:
constexpr float Nullf = -std::numeric_limits<float>::infinity();
inline bool IsNull(const float& r)   { return !(std::abs(r) < std::numeric_limits<float>::infinity()); }
inline void SetNull(float& x) { x = Nullf; }

Still, it would be nicer inside Core... After all, it would introduce only three lines of new code in Core/Defs.h.

Best regards,

Tom


Got a new idea for a bit more universal solution:

The most important difference between Value(double) and Value(float) is the precision used when formatting it. What about to add a general precision hint to Value(double)? That would make possible to e.g. specify arbitrary precision for Json output etc...
Re: Value with type float [message #58503 is a reply to message #58501] Fri, 03 June 2022 12:03 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi Mirek,

Sounds interesting, but I cannot really figure out all consequences this change may have. If I may express my needs from the user's viewpoint, I hope:

- I can use Value with float as easily as with double without explicit type casts anywhere
- Assigning a float to Value reads back from Value exactly the same as it went in
- If EditFloat* is again replaced by EditDouble*, the behavior is exactly the same as it is now with EditFloat*
- float supports Null

Maybe the precision hint you are suggesting could be automatically initialized in Value(double) / Value(float) constructors and assignment operators =(double) / =(float) to suit the assigned data type?

Best regards,

Tom
Re: Value with type float [message #58807 is a reply to message #58151] Tue, 06 September 2022 01:41 Go to previous messageGo to next message
jimlef is currently offline  jimlef
Messages: 90
Registered: September 2020
Location: US
Member
Related to all this, the innacuracies due to the way the pc handles float/double numbers have been ... irking me as well. I was thinking of float instead of double, but that doesn't fix the issue (and there isn't any json support for float anyway). In my case, I just want sales tax to be 0.08 (or whatever, it's stored in json) - not 0.080000000000000002. I've tried to utilize FormatG and round functions to no joy. My solution has been to store and use ints instead of floating point, and do conversions in the program where needed. It handles positive and negative values effectively enough for my needs, and my little app was trivial to rework. Anyone wishing to torture themselves with my coding can view it at https://github.com/phoenixjim/Invoices Laughing . Some relevant portions are in config.cpp/h and the report files (In the reports folder) regarding how this is used. Convert.h/cpp has a few altered convert functions to match...

It's great how supportive the folks on this forum are - I hope this helps someone else Smile
Re: Value with type float [message #58808 is a reply to message #58807] Tue, 06 September 2022 22:42 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello jimlef,

Do you mean this?

	RLOG(Format(
		"Pi(2): %.02f\n"
		"PI(4): %.04f\n"
		"PI(8): %.08f\n",
		roundr(M_PI, 2),
		roundr(M_PI, 4),
		roundr(M_PI, 8)
		));

	double tax = 0.0800000000123;
	String stax = FormatG(tax, 3);
	RLOG("Formatted tax: " << stax);
	RLOG("Scanned   tax: " << ScanDouble(stax));

	Json jtax_r("tax-rounded", roundr(tax, 2));
	Json jtax_n("tax", tax);

	RDUMP(jtax_r);
	RDUMP(jtax_n);



Pi(2): 3.10
PI(4): 3.1420
PI(8): 3.14159270

Formatted tax: 0.08
Scanned   tax: 0.08
jtax_r = {"tax-rounded":0.08}
jtax_n = {"tax":0.0800000000123}


[Edit: Code updated.]

Best regards,
Oblivion


[Updated on: Tue, 06 September 2022 23:17]

Report message to a moderator

Re: Value with type float [message #58810 is a reply to message #58808] Thu, 08 September 2022 14:49 Go to previous messageGo to next message
jimlef is currently offline  jimlef
Messages: 90
Registered: September 2020
Location: US
Member
Yes Cool That would be it I'm thinking

Thank you!

Jim

Oblivion wrote on Tue, 06 September 2022 16:42
Hello jimlef,

Do you mean this?

	RLOG(Format(
		"Pi(2): %.02f\n"
		"PI(4): %.04f\n"
		"PI(8): %.08f\n",
		roundr(M_PI, 2),
		roundr(M_PI, 4),
		roundr(M_PI, 8)
		));

	double tax = 0.0800000000123;
	String stax = FormatG(tax, 3);
	RLOG("Formatted tax: " << stax);
	RLOG("Scanned   tax: " << ScanDouble(stax));

	Json jtax_r("tax-rounded", roundr(tax, 2));
	Json jtax_n("tax", tax);

	RDUMP(jtax_r);
	RDUMP(jtax_n);



Pi(2): 3.10
PI(4): 3.1420
PI(8): 3.14159270

Formatted tax: 0.08
Scanned   tax: 0.08
jtax_r = {"tax-rounded":0.08}
jtax_n = {"tax":0.0800000000123}


[Edit: Code updated.]

Best regards,
Oblivion

Re: Value with type float [message #60197 is a reply to message #58503] Wed, 04 October 2023 09:55 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Just revisting this issue...

Tom1 wrote on Fri, 03 June 2022 12:03

- I can use Value with float as easily as with double without explicit type casts anywhere


Yes.

Quote:

- Assigning a float to Value reads back from Value exactly the same as it went in


If "reads back" means text output, then yes.

Quote:

- If EditFloat* is again replaced by EditDouble*, the behavior is exactly the same as it is now with EditFloat*


Should work.

Quote:

- float supports Null


No. Is that a problem?

Quote:

Maybe the precision hint you are suggesting could be automatically initialized in Value(double) / Value(float) constructors and assignment operators =(double) / =(float) to suit the assigned data type?


That was exactly the plan.

I think advantage of this solution is that it allows you to specify arbitrary precision for any double-holding Value. E.g.

	double x = 3.14159;
	Value json;
	json("pi") = Precision(x, 2); // Precision does not exist yet
	DDUMP(AsJSON(json));


{"pi":3.14}


Still an idea though.
Re: Value with type float [message #60198 is a reply to message #60197] Wed, 04 October 2023 12:08 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
mirek wrote on Wed, 04 October 2023 10:55
Just revisting this issue...

Tom1 wrote on Fri, 03 June 2022 12:03

- I can use Value with float as easily as with double without explicit type casts anywhere


Yes.

Quote:

- Assigning a float to Value reads back from Value exactly the same as it went in


If "reads back" means text output, then yes.

Quote:

- If EditFloat* is again replaced by EditDouble*, the behavior is exactly the same as it is now with EditFloat*


Should work.

Quote:

- float supports Null


No. Is that a problem?

Quote:

Maybe the precision hint you are suggesting could be automatically initialized in Value(double) / Value(float) constructors and assignment operators =(double) / =(float) to suit the assigned data type?


That was exactly the plan.

I think advantage of this solution is that it allows you to specify arbitrary precision for any double-holding Value. E.g.

	double x = 3.14159;
	Value json;
	json("pi") = Precision(x, 2); // Precision does not exist yet
	DDUMP(AsJSON(json));


{"pi":3.14}


Still an idea though.


Hi Mirek,

Thanks for the update on this subject.

For me 'reads back exactly the same' means binary equality:
float a = 1.234562f;
float b = 1.234562f;
Value c = b;
b = c;
if(a == b) Cout() << "Great, it works!\r\n"
else Cout() << "No, it does not read back the same...\r\n";


As for float Null, yes, please include the changes shown in:

https://www.ultimatepp.org/forums/index.php?t=msg&th=120 82&goto=59874&#msg_59874

Best regards,

Tom
Re: Value with type float [message #60200 is a reply to message #60198] Fri, 06 October 2023 12:51 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Wed, 04 October 2023 12:08
mirek wrote on Wed, 04 October 2023 10:55
Just revisting this issue...

Tom1 wrote on Fri, 03 June 2022 12:03

- I can use Value with float as easily as with double without explicit type casts anywhere


Yes.

Quote:

- Assigning a float to Value reads back from Value exactly the same as it went in


If "reads back" means text output, then yes.

Quote:

- If EditFloat* is again replaced by EditDouble*, the behavior is exactly the same as it is now with EditFloat*


Should work.

Quote:

- float supports Null


No. Is that a problem?

Quote:

Maybe the precision hint you are suggesting could be automatically initialized in Value(double) / Value(float) constructors and assignment operators =(double) / =(float) to suit the assigned data type?


That was exactly the plan.

I think advantage of this solution is that it allows you to specify arbitrary precision for any double-holding Value. E.g.

	double x = 3.14159;
	Value json;
	json("pi") = Precision(x, 2); // Precision does not exist yet
	DDUMP(AsJSON(json));


{"pi":3.14}


Still an idea though.


Hi Mirek,

Thanks for the update on this subject.

For me 'reads back exactly the same' means binary equality:
float a = 1.234562f;
float b = 1.234562f;
Value c = b;
b = c;
if(a == b) Cout() << "Great, it works!\r\n"
else Cout() << "No, it does not read back the same...\r\n";




This works (and I believe it should) right now. Am I missing something?

Quote:


As for float Null, yes, please include the changes shown in:

https://www.ultimatepp.org/forums/index.php?t=msg&th=120 82&goto=59874&#msg_59874



I am reluctant adding yet another Null...

Mirek
Re: Value with type float [message #60201 is a reply to message #60200] Fri, 06 October 2023 13:40 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
mirek wrote on Fri, 06 October 2023 13:51
This works (and I believe it should) right now. Am I missing something?

Quote:


As for float Null, yes, please include the changes shown in:

https://www.ultimatepp.org/forums/index.php?t=msg&th=120 82&goto=59874&#msg_59874



I am reluctant adding yet another Null...

Mirek


Hi Mirek,

No, you're not missing anything here. My code was there just to demonstrate what I mean by 'reads back exactly the same'.

As for the float Null: Instead of writing this:
	float f = (float)(double)Null;
	...
	Cout() << IsNull((double)f) << "\r\n";

I would like to do this:
	float f = Null;
	...
	Cout() << IsNull(f) << "\r\n";


Is there a specific reason why you wish to avoid adding another Null to support float?

Best regards,

Tom
Re: Value with type float [message #60202 is a reply to message #60201] Fri, 06 October 2023 13:54 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Fri, 06 October 2023 13:40
mirek wrote on Fri, 06 October 2023 13:51
This works (and I believe it should) right now. Am I missing something?

Quote:


As for float Null, yes, please include the changes shown in:

https://www.ultimatepp.org/forums/index.php?t=msg&th=120 82&goto=59874&#msg_59874



I am reluctant adding yet another Null...

Mirek


Hi Mirek,

No, you're not missing anything here. My code was there just to demonstrate what I mean by 'reads back exactly the same'.


I am asking because it looks like you had problems with this in the past....

Quote:

Is there a specific reason why you wish to avoid adding another Null to support float?


Reluctance to add features that are not necessarry nor very helpful (not that U++ is not already full of them, but still).

I still see no reason to use float instead of double except for saving the storage space...

Mirek
Re: Value with type float [message #60203 is a reply to message #60202] Fri, 06 October 2023 14:28 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Yes, for storage space, RAM space, disk bandwidth (for speed), memory bandwidth (for speed), CPU cache space (for speed), ... I use floats all the time for any large data sets where float's accuracy is sufficient. It really pays off. Also, I mark missing data/observations using '(float)(double)Null' to handle the situation properly. And thanks to your efforts float edit fields, these days it is also handy to pull the float observations directly to dialogs for editing.

I think float is and will still remain very much alive. But if your reluctance results in refusal, I will have to keep working around this issue. That would be a pity since, after all, it is just three more lines of code in Core/Defs.h.

Best regards,

Tom
Re: Value with type float [message #60273 is a reply to message #60203] Mon, 30 October 2023 10:34 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
OK, float support (very hesitantly and conditionally) added.

https://github.com/ultimatepp/ultimatepp/blob/cb32981c04d35f 061c0d700dac26e0fe546d36a7/autotest/Float/main.cpp

Some other tests I should add?
Re: Value with type float [message #60274 is a reply to message #60273] Mon, 30 October 2023 12:27 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
mirek wrote on Mon, 30 October 2023 11:34
OK, float support (very hesitantly and conditionally) added.

https://github.com/ultimatepp/ultimatepp/blob/cb32981c04d35f 061c0d700dac26e0fe546d36a7/autotest/Float/main.cpp

Some other tests I should add?

Hi Mirek,

Thank you very, very much! Actually, the float Value support is even wider than I would have imagined... but very much appreciated. Smile

No other tests come to mind.

BTW: What are the conditions and consequences of your hesitation you're referring to above?

Thanks and best regards,

Tom

EDIT: PS. In Core/Value.cpp, is it intentional or a mistake to have different return typecast for float in GetOtherInt(), GetOtherInt64(), GetOtherBool() and GetOtherDouble()?

[Updated on: Mon, 30 October 2023 12:47]

Report message to a moderator

Re: Value with type float [message #60276 is a reply to message #60274] Mon, 30 October 2023 13:37 Go to previous message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Tom1 wrote on Mon, 30 October 2023 12:27
mirek wrote on Mon, 30 October 2023 11:34
OK, float support (very hesitantly and conditionally) added.

https://github.com/ultimatepp/ultimatepp/blob/cb32981c04d35f 061c0d700dac26e0fe546d36a7/autotest/Float/main.cpp

Some other tests I should add?

Hi Mirek,

Thank you very, very much! Actually, the float Value support is even wider than I would have imagined... but very much appreciated. Smile

No other tests come to mind.

BTW: What are the conditions and consequences of your hesitation you're referring to above?


If it breaks something, I rollback. But frankly it is unlikely.

Quote:

EDIT: PS. In Core/Value.cpp, is it intentional or a mistake to have different return typecast for float in GetOtherInt(), GetOtherInt64(), GetOtherBool() and GetOtherDouble()?


Thanks, fixed (it was harmless, but anyway).
Previous Topic: Build U++ with CMake and Clang Example
Next Topic: A temporary solution to garbled code in U++applications built through MSVC
Goto Forum:
  


Current Time: Thu Mar 28 13:53:06 CET 2024

Total time taken to generate the page: 0.01780 seconds