It is currently March 29th, 2024, 2:15 pm

[NOT A BUG] Nesting limit in Max function

Report bugs with the Rainmeter application and suggest features.
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

[NOT A BUG] Nesting limit in Max function

Post by Yincognito »

Test skin:

Code: Select all

[Rainmeter]
Update=1000
AccurateText=1
DynamicWindowSize=1

---Measures---

[Max]
Measure=Calc
; ***Always works - yields the maximum from enumerated values (47 of them)***
; Formula=Max(12.9,Max(12.7,Max(12.4,Max(12.2,Max(12.2,Max(12.2,Max(12.4,Max(12.5,Max(12.4,Max(12.3,Max(12.1,Max(12.2,Max(12.0,Max(11.7,Max(11.6,Max(11.6,Max(11.6,Max(11.6,Max(11.7,Max(11.8,Max(11.8,Max(11.8,Max(11.8,Max(11.7,Max(11.8,Max(11.7,Max(11.8,Max(11.4,Max(11.1,Max(10.8,Max(10.7,Max(10.6,Max(10.4,Max(10.2,Max(10.1,Max(10.0,Max(10.0,Max(10.2,Max(10.5,Max(10.2,Max(10.2,Max(10.2,Max(9.9,Max(9.8,Max(9.6,Max(9.5,9.4))))))))))))))))))))))))))))))))))))))))))))))
***Doesn't work - yields "Internal Error" in a Calc measure (48 of them)***
Formula=Max(12.9,Max(12.9,Max(12.7,Max(12.4,Max(12.2,Max(12.2,Max(12.2,Max(12.4,Max(12.5,Max(12.4,Max(12.3,Max(12.1,Max(12.2,Max(12.0,Max(11.7,Max(11.6,Max(11.6,Max(11.6,Max(11.6,Max(11.7,Max(11.8,Max(11.8,Max(11.8,Max(11.8,Max(11.7,Max(11.8,Max(11.7,Max(11.8,Max(11.4,Max(11.1,Max(10.8,Max(10.7,Max(10.6,Max(10.4,Max(10.2,Max(10.1,Max(10.0,Max(10.0,Max(10.2,Max(10.5,Max(10.2,Max(10.2,Max(10.2,Max(9.9,Max(9.8,Max(9.6,Max(9.5,9.4)))))))))))))))))))))))))))))))))))))))))))))))
UpdateDivider=-1

---Meters---

[MaxText]
Meter=String
SolidColor=0,0,0,128
FontColor=255,255,255,255
FontFace=Consolas
FontSize=16
AntiAlias=1
Text=Max = [Max]
UpdateDivider=-1
DynamicVariables=1
Result:
As mentioned above, it appears there is a limit of 47 nested Max() functions in Rainmeter. Granted, it's not exactly a few, but should it happen? :???:

Notes:
1) This occurred to me when I tried to write a native Rainmeter implementation of the Lua code here, where there are as many as 280 such values to get the maximum from (the native code is a perfectly valid regex manipulation and can be examined if needed in the spoiler below - I know regex is often the black sheep in programming, but still).

Code: Select all

[Rainmeter]
Update=1000
AccurateText=1
DesktopWorkAreaType=1
AlwaysOnTop=-2

---Measures---

[TodaysDate]
Measure=Time
Format=%F

[MeasureEcowittHistory]
Measure=WebParser
URL=https://api.ecowitt.net/api/v3/device/history?application_key=20B5F2F46B4D5474E8713C269B4FE2F5&api_key=2b0e4d11-2a9c-4f33-a672-c1cdef6bbe5e&mac=BC:FF:4D:0F:44:F2&start_date=[&TodaysDate]00:00:00&end_date=[&TodaysDate]23:59:59&cycle_type=auto&call_back=outdoor.temperature&temp_unitid=1
RegExp=(?siU)"list":{(.*)}}}}}.*
StringIndex=1
UpdateRate=60
RegExpSubstitute=1
Substitute="\d{10}":"",'":"':"",'"':""
FinishAction=[!UpdateMeasureGroup WebParserGroup][!UpdateMeter MeterMaxTemp][!Redraw]

[MaxFormula]
Group=WebParserGroup
Measure=String
String=[MeasureEcowittHistory]
UpdateDivider=-1
RegExpSubstitute=1
Substitute="(?:null,|,null)":"","^(.*)$":"\1;\1","[\+\-\.\d]+,(?=.*?;)":")","[\+\-\.\d]+;":"","^(\)+)(.*)$":"\2\1","([\+\-\.\d]+,)":"Max(\1","^\\\\1$":"0"
DynamicVariables=1

[Max]
Group=WebParserGroup
Measure=Calc
Formula=[MaxFormula]
UpdateDivider=-1
DynamicVariables=1

---Styles---

[StyleLiteText]
SolidColor=0,0,0,1
AntiAlias=1
FontColor=100,255,100,255
FontFace=System
FontSize=16

---Meters---

[MeterMaxTemp]
Meter=String
MeterStyle=StyleLiteText
Text=MaxTemp = [Max]
DynamicVariables=1
W=240
H=80
2) One "bonus bug" to the above is that, when making the first number 120000000000.9 in the version that works, Rainmeter is losing precision. This is aggravated if more zeros are added to the number. Is this related to the Float / Double precision limits or is it something else (if the former, could the manual briefly mention this somewhere, maybe using a link if typing is an issue)?
3) I have not tested other functions for this nesting issue, but I imagine that at least Min() would exhibit the same behavior.
Last edited by Yincognito on May 27th, 2023, 2:12 pm, edited 1 time in total.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Brian
Developer
Posts: 2674
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: [BUG] Nesting limit in Max function

Post by Brian »

Yincognito wrote: May 23rd, 2023, 4:43 pm As mentioned above, it appears there is a limit of 47 nested Max() functions in Rainmeter. Granted, it's not exactly a few, but should it happen? :???:

...

3) I have not tested other functions for this nesting issue, but I imagine that at least Min() would exhibit the same behavior.
This really has nothing to do with the Max function. Instead, the issue comes down to how many pending operations are on the stack. Our math parser has defined the operation stack size of 96. Since it takes 2 operands per operation, this limit is half the stack size or 96 / 2 = 48. Once this is hit, it errors out.

Examples:

Code: Select all

; This errors out because the stack limit is reached since there are 48 pending operations on the stack
Formula=(1+(2+(3+(4+(5+(6+(7+(8+(9+(10+(11+(12+(13+(14+(15+(16+(17+(18+(19+(20+(21+(22+(23+(24+(25+(26+(27+(28+(29+(30+(31+(32+(33+(34+(35+(36+(37+(38+(39+(40+(41+(42+(43+(44+(45+(46+(47+48)))))))))))))))))))))))))))))))))))))))))))))))

Code: Select all

; This is ok since the stack limit is full, but not at the limit
Formula=(1+(2+(3+(4+(5+(6+(7+(8+(9+(10+(11+(12+(13+(14+(15+(16+(17+(18+(19+(20+(21+(22+(23+(24+(25+(26+(27+(28+(29+(30+(31+(32+(33+(34+(35+(36+(37+(38+(39+(40+(41+(42+(43+(44+(45+(46+47))))))))))))))))))))))))))))))))))))))))))))))
Bonus fun!

Code: Select all

; This is also ok since the "(10+10)" is resolved (meaning the stack operations are now empty) before moving on to the rest of the operations (which will fill up the stack, but not overflow it)
Formula=(10+10)+(1+(2+(3+(4+(5+(6+(7+(8+(9+(10+(11+(12+(13+(14+(15+(16+(17+(18+(19+(20+(21+(22+(23+(24+(25+(26+(27+(28+(29+(30+(31+(32+(33+(34+(35+(36+(37+(38+(39+(40+(41+(42+(43+(44+(45+(46+47))))))))))))))))))))))))))))))))))))))))))))))



Yincognito wrote: May 23rd, 2023, 4:43 pm 2) One "bonus bug" to the above is that, when making the first number 120000000000.9 in the version that works, Rainmeter is losing precision. This is aggravated if more zeros are added to the number. Is this related to the Float / Double precision limits or is it something else (if the former, could the manual briefly mention this somewhere, maybe using a link if typing is an issue)?
Yes, this is double's significant digit precision described in the link you posted. It affects floating numbers as well as whole numbers.

Here are some weird examples:

Code: Select all

; For both number and string values, the last digit appears as"8" instead of "7"
Formula=12345678901234567

Code: Select all

; Fun!
Formula=179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

; Number value cuts off after 255 characters for a value of:
;       179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797

; String value cuts off after 127 characters for a value of:
;       1797693134862315708145274237317043567980705675258449965989174768031572607800285387605895586327668781715404589535143824642343213

; Note how the string and number values are accurate given their length restrictions.
-Brian
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: [BUG] Nesting limit in Max function

Post by Yincognito »

Brian wrote: May 23rd, 2023, 8:44 pm Yes, this is double's significant digit precision described in the link you posted. It affects floating numbers as well as whole numbers.
Yep, this is understandable - I actually noticed and appreciated the accuracy in such constrained circumstances.
Brian wrote: May 23rd, 2023, 8:44 pm This really has nothing to do with the Max function. Instead, the issue comes down to how many pending operations are on the stack. Our math parser has defined the operation stack size of 96. Since it takes 2 operands per operation, this limit is half the stack size or 96 / 2 = 48. Once this is hit, it errors out.
Ouch, this is worse than I thought then. Must keep this in mind when regex substituting to a more complex formula. I guess it's something normal for a parser based (or not?) on recursivity (I suppose that's where the stack limiting comes into play, if I'm not mistaken).

Anyway, in this particular case, your "(10+10)" hint along with the "parser of alternatives" from my brain directed me to another, much more forgiving approach (this is a max of 512 values that works):

Code: Select all

Formula=Max(Max(Max(Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))),Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))))),Max(Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))),Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))))),Max(Max(Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))),Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))))),Max(Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))),Max(Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))))),Max(Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))),Max(Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7))),Max(Max(Max(12.9,12.7),Max(12.9,12.7)),Max(Max(12.9,12.7),Max(12.9,12.7)))))))))
The above is, of course, based on writing Max(Max(A,B),Max(C,D)) instead of Max(A,Max(B,Max(C,D))) and so forth, which, judging by the number of brackets in the other non-working example, has the potential to support a maximum from 2^47 = ‭‭140 737 488 355 328‬ values, if my calculations are correct. Now all I need to do is figure out an optimal way to regex substitute an "A,B,C,D"-like string into a "Max(Max(A,B),Max(C,D))"-like one, but that shouldn't be a problem.

Thanks for answering and taking a look! :thumbup:
Loved your "bonus" comeback - touché! :thumbup:
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth
User avatar
Yincognito
Rainmeter Sage
Posts: 7030
Joined: February 27th, 2015, 2:38 pm
Location: Terra Yincognita

Re: [BUG] Nesting limit in Max function

Post by Yincognito »

Brian wrote: May 23rd, 2023, 8:44 pm This really has nothing to do with the Max function. Instead, the issue comes down to how many pending operations are on the stack. Our math parser has defined the operation stack size of 96. Since it takes 2 operands per operation, this limit is half the stack size or 96 / 2 = 48. Once this is hit, it errors out.
Yincognito wrote: May 23rd, 2023, 10:16 pm The above is, of course, based on writing Max(Max(A,B),Max(C,D)) instead of Max(A,Max(B,Max(C,D))) and so forth, which, judging by the number of brackets in the other non-working example, has the potential to support a maximum from 2^47 = ‭‭140 737 488 355 328‬ values, if my calculations are correct. Now all I need to do is figure out an optimal way to regex substitute an "A,B,C,D"-like string into a "Max(Max(A,B),Max(C,D))"-like one, but that shouldn't be a problem.
Slightly more brute force in the regex than I'd normally want, but, as a proof of concept (scroll to test lists of various number of values):

Code: Select all

[Variables]
V=(?:[\+\-\.\d]+,)
M=257
C=256

[Rainmeter]
Update=1000
AccurateText=1
DynamicWindowSize=1
DefaultUpdateDivider=-1

---Measures---

[List]
Measure=String
String=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,
RegExpSubstitute=1
Substitute="^(#V#{0,#C#}+).*$":"\1","\\1":"",",":", "
DynamicVariables=1

[MaxFormula]
Measure=String
String=[List]
RegExpSubstitute=1
Substitute="\s+":"","(.)$":"\1,",",+":",","(#V#{129,256})":"(\1)","(#V#{65,128})":"(\1)","(#V#{33,64})":"(\1)","(#V#{17,32})":"(\1)","(#V#{9,16})":"(\1)","(#V#{5,8})":"(\1)","(#V#{3,4})":"(\1)","(#V#{2,2})":"(\1)","(,)([)]+)":"\2\1","([(])":"Max\1",",$":""
DynamicVariables=1

[Max]
Measure=Calc
Formula=[MaxFormula]
DynamicVariables=1

---Meters---

[Result]
Meter=String
SolidColor=0,0,0,128
FontColor=255,255,255,255
FontFace=Consolas
FontSize=16
AntiAlias=1
ClipString=2
W=600
Text="Max. = [Max]#CRLF#List = [List]"
MouseScrollUpAction=[!SetVariable C ((#M#+#C#+1)%#M#)][!UpdateMeasure *][!UpdateMeter *][!Redraw]
MouseScrollDownAction=[!SetVariable C ((#M#+#C#-1)%#M#)][!UpdateMeasure *][!UpdateMeter *][!Redraw]
DynamicVariables=1
The [MaxFormula] substitute is limited to 256 values as per the [​List] used for testing, but the number of values it accepts is probably only limited by regex and its quantifiers. If more values are desired, the needed number of "(#V#{((2^N)+1),(2^(N+1))})":"(\1)" can be inserted before "(#V#{129,256})":"(\1)" (with N decreasing accordingly, e.g. ...,"(#V#{513,1024})":"(\1)","(#V#{257,512})":"(\1)"). At the end of the said substitute, Max can be changed to Min at any point.
Profiles: Rainmeter ProfileDeviantArt ProfileSuites: MYiniMeterSkins: Earth