From df4b4ca353176c7c554480210bf2f01958153707 Mon Sep 17 00:00:00 2001 From: Steve Fanning Date: Fri, 20 Dec 2024 16:51:22 +0000 Subject: [PATCH] Update to FV description and associated pages in the Features area. --- docs/src/.vitepress/config.mts | 12 ++ docs/src/features/error-types.md | 134 ++++++++++++++---- docs/src/features/optional-arguments.md | 49 +++++++ docs/src/features/units.md | 13 ++ docs/src/features/value-types.md | 67 +++++++++ docs/src/functions/financial/fv.md | 48 ++++--- .../markdown-snippets/error-type-details.md | 1 - .../markdown-snippets/error-type-details.txt | 1 + xlsx/tests/docs/FV.xlsx | Bin 10164 -> 10987 bytes 9 files changed, 275 insertions(+), 50 deletions(-) create mode 100644 docs/src/features/optional-arguments.md create mode 100644 docs/src/features/units.md create mode 100644 docs/src/features/value-types.md delete mode 100644 docs/src/functions/markdown-snippets/error-type-details.md create mode 100644 docs/src/functions/markdown-snippets/error-type-details.txt diff --git a/docs/src/.vitepress/config.mts b/docs/src/.vitepress/config.mts index c6d7d94..4f42410 100644 --- a/docs/src/.vitepress/config.mts +++ b/docs/src/.vitepress/config.mts @@ -58,6 +58,18 @@ export default defineConfig({ text: "Error Types", link: "/features/error-types", }, + { + text: "Value Types", + link: "/features/value-types", + }, + { + text: "Optional Arguments", + link: "/features/optional-arguments", + }, + { + text: "Units", + link: "/features/units", + }, { text: "Unsupported Features", link: "/features/unsupported-features", diff --git a/docs/src/features/error-types.md b/docs/src/features/error-types.md index c392e7b..7f361a4 100644 --- a/docs/src/features/error-types.md +++ b/docs/src/features/error-types.md @@ -7,31 +7,39 @@ lang: en-US # Error Types ::: warning -**Note:** This page is in construction 🚧 +**Note:** This draft page is under construction 🚧 ::: -When working with formulas, you may encounter these common errors: +The result of a formula is sometimes an _error_. In some situations those errors are expected and your formulas might be dealing with them. +The error `#N/A` might signal that there is no data to evaluate the formula yet. Maybe the payrol has not been introduced for that month just yet. ---- +Some other errors like `#SPILL!`, `#CIRC!` or `#ERROR!` signal an error in your spreadsheet logic and must be corrected. -### **`#ERROR!`** +The first kind of errors or 'common errors' are found in other spreadsheet engines like Excel while other errrors like `#ERROR!` or `#N/IMPL` are particular to IronCalc. -**Cause:** General formula issue, like syntax errors or invalid references. -**Fix:** Check the formula for mistakes or invalid cell references. - ---- +## Common Errors ### **`#VALUE!`** -**Cause:** Mismatched data types (e.g., text used where numbers are expected). -**Fix:** Ensure input types are correct; convert text to numbers if needed. +It might be caused by mismatched data types (e.g., text used where numbers are expected): ---- +``` +5+"two" +``` + +The engine doesn't know how to add the number `5` to the string `two` resulting in a `#VALUE!`. + +It is an actual error in your spreadsheet. It indicates that the formula isn’t working as intended. ### **`#DIV/0!`** -**Cause:** Division by zero or an empty cell. -**Fix:** Ensure the denominator isn’t zero or blank. Use `IF` to handle such cases: +Division by zero or an empty cell. + +``` +=1/0 +``` + +Usually this is an error. However, in cases where a denominator might be blank (e.g., data not yet filled in), this could be expected. Use `IFERROR` or `IF` to handle it. ``` =IF(B1=0, "N/A", A1/B1) @@ -39,23 +47,42 @@ When working with formulas, you may encounter these common errors: ### **`#NAME?`** -**Cause:** Unrecognized text in the formula (e.g., misspelled function names or undefined named ranges). -**Fix:** Correct spelling or define the missing name. +Found when a name is not recognized. Maybe a misspeled name for a function. Could be a referenceto defined name that has been deleted. + +``` +=UNKOWN_FUNCTION(A1) +``` + +This indicates an error in your spreadsheet logic. ### **`#REF!`** -**Cause:** Invalid cell reference, often from deleting cells used in a formula. -**Fix:** Update the formula with correct references. +Indicates an invalid cell reference, often from deleting cells used in a formula. + +They can appear as a result of a computation or in a formula. Examples: + +``` +=Sheet34!A1 +``` + +If `Sheet34` doesn't exist it will return `#REF!` + +This is a genuine error. It indicates that part of your formula references a cell or range that is missing. ### **`#NUM!`** -**Cause:** Invalid numeric operation (e.g., calculating a square root of a negative number). -**Fix:** Adjust the formula to ensure valid numeric operations. +Invalid numeric operation (e.g., calculating a square root of a negative number). +Adjust the formula to ensure valid numeric operations. + +Sometimes a `#NUM!` might be expected signalling the user that some parameter is out of scope. ### **`#N/A`** -**Cause:** A value is not available, often in lookup functions like VLOOKUP. -**Fix:** Ensure the lookup value exists or use IFNA() to handle missing values: +A value is not available, often in lookup functions like VLOOKUP. + +This is frequnly not an error in your spreadsheetlogic. + +You can produce a prettier answer using the [`IFNA`](/functions/information/isna) formula: ``` =IFNA(VLOOKUP(A1, B1:C10, 2, FALSE), "Not Found") @@ -63,17 +90,68 @@ When working with formulas, you may encounter these common errors: ### **`#NULL!`** -**Cause:** Incorrect range operator in a formula (e.g., missing a colon between cell references). -**Fix:** Use correct range operators (e.g., A1:A10). +Incorrect range operator in a formula (e.g., missing a colon between cell references). +### **`#SPILL!`** + +A cell in a formula will overwrite content in other cells. +This cannot happen riht now in IronCalc as formulas don't spill yet. ### **`#CIRC!`** -**Cause:** Circular reference. -**Fix:** Remove the circular reference. +Circular reference. This is an error is your spreadsheet and must be fixed. +Means that during teh course of a computation a circular dependency was found. + +A circular dependency is a dependency of a formula on itself. + +For instance in the cell `A1` the formula `=A1*2` is a circular dependency. + +Other spreadsheet engines use circular dependencies to do "loop computations", run "sensitivity analysis" or "goal seek". + +IronCalc doesn't support any of those at the moment. + +## IronCalc specific errors + +--- + +### **`#ERROR!`** + +General formula issue, like syntax errors or invalid references. +In general Excel does not let you enter incorrect formulas but IronCalc will. + +This will make your workbook imcompatible with Excel + +For instace an incomplete formula + +``` +=A1+ +``` + +### **`#N/IMPL!`** + +A particular feature is not yet implemented in IronCalc + +Look if there is a Github ticket or contact us via email, Discord or bluesky + +## Error propagation + +Some errors a created by some formulas. For instance the function `SQRT` can create the error `#NUM!` but can't ceate the error `#DIV/0`. + +Once an error is created it is normally _propagated_ by all the formulas. So if cell `C3` evaluates to `#ERROR!` then the formula +`=SQRT(C3)` will return `#ERROR!`. + +Not all functions propagate errors in their arguments. For instancethe function `IF(condition, if_true, if_false)` will only propagate an error in the `if_false` argument if the `condition` is `FALSE`. This is called _lazy evaluation_, the function `IF` is _lazy_, it only evaluates the arguments when needed. The opposite of lazy evaulaution is called _eager evaluation_. + +Some functions also expect an error as an argument like [`ERROR.TYPE`](/functions/information/error.type) and will not propagate the error. -### **`#####`** +## See also -**Cause:** The column isn’t wide enough to display the value. -**Fix:** Resize the column width to fit the content. +The following functions are convenient when working with errors + +- [`ISERR(ref)`](/functions/information/iserr), `TRUE` if `ref` is any error type except the `#N/A` error. +- [`ISERROR(ref)`](/functions/information/iserror), `TRUE` if `ref` is any error. +- [`ISNA(ref)`](/functions/information/isna), `TRUE` if ref is `#N/A`. +- [`ERROR.TYPE`](/functions/information/error.type) returns the numeric code for a given error. +- [`IFERROR(ref, value)`](/functions/logical/iferror) returns `value` if the content of `ref` is an error. +- [`IFNA(ref, value)`](/functions/logical/ifna) return `value` if `ref` is #N/A errors only. diff --git a/docs/src/features/optional-arguments.md b/docs/src/features/optional-arguments.md new file mode 100644 index 0000000..8940765 --- /dev/null +++ b/docs/src/features/optional-arguments.md @@ -0,0 +1,49 @@ +--- +layout: doc +outline: deep +lang: en-US +--- + +# Optional Arguments + +::: warning +**Note:** This draft page is under construction 🚧 +::: + +Any IronCalc function may accept zero, one or more arguments, which are values passed to the function when it is called from a spreadsheet formula. + +Many function arguments are _required_. For such arguments, always pass a suitable value in the function call. + +Some function arguments are _optional_. Optional arguments need not be passed in the function call and, in such cases, the function instead uses a predefined default value. + +Consider a notional function called _FN\_NAME_, with the following syntax: + +

FN_NAME(arg1, arg2, arg3, arg4=def1, arg5=def2, arg6=def3) => fn_name

+ +Notes about this syntax: + +* _FN_NAME_ is a function that takes six arguments (_arg1_, _arg2_, _arg3_, _arg4_, _arg5_ and _arg6_) and returns a value referred to as _fn_name_. +* For convenience in this case, all arguments and the returned value are colour-coded to indicate that they are numbers. +* Arguments _arg1_, _arg2_ and _arg3_ are required arguments and this would normally be stated in the **Argument descriptions** section of the function's description page. +* Arguments _arg4_, _arg5_ and _arg6_ are optional arguments and again this would normally be stated in the **Argument descriptions** section of the function's description page. In addition, optional arguments are usually indicated by the specification of a default value in the syntax. + * If _arg4_ is omitted, then the value _def1_ is assumed. + * If _arg5_ is omitted, then the value _def2_ is assumed. + * If _arg6_ is omitted, then the value _def3_ is assumed. + +With this syntax, the following would all be valid calls to the _FN_NAME_ function: + +**=FN\_NAME(1,2,3)**. All optional arguments omitted. + +**=FN\_NAME(1,2,3,4)**. _arg4_ set to 4; optional arguments _arg5_ and _arg6_ assume default values. + +**=FN\_NAME(1,2,3,,5)**. _arg5_ set to 5; optional arguments _arg4_ and _arg6_ assume default values. + +**=FN\_NAME(1,2,3,,,6)**. _arg6_ set to 6; optional arguments _arg4_ and _arg5_ assume default values. + +**=FN\_NAME(1,2,3,4,5)**. _arg4_ and _arg5_ set to 4 and 5 respectively; optional argument _arg6_ assumes default value. + +**=FN\_NAME(1,2,3,4,,6)**. _arg4_ and _arg6_ set to 4 and 6 respectively; optional argument _arg5_ assumes default value. + +**=FN\_NAME(1,2,3,,5,6)**. _arg5_ and _arg6_ set to 5 and 6 respectively; optional argument _arg4_ assumes default value. + +**=FN\_NAME(1,2,3,4,5,6)**. Values passed for all optional arguments. diff --git a/docs/src/features/units.md b/docs/src/features/units.md new file mode 100644 index 0000000..2a6c246 --- /dev/null +++ b/docs/src/features/units.md @@ -0,0 +1,13 @@ +--- +layout: doc +outline: deep +lang: en-US +--- + +# Units + +::: warning +**Note:** This draft page is under construction 🚧 +::: + +Some IronCalc functions return values that have units like currencies, percentage or dates. \ No newline at end of file diff --git a/docs/src/features/value-types.md b/docs/src/features/value-types.md new file mode 100644 index 0000000..0bd2efd --- /dev/null +++ b/docs/src/features/value-types.md @@ -0,0 +1,67 @@ +--- +layout: doc +outline: deep +lang: en-US +--- + +# Value Types + +::: warning +**Note:** This draft page is under construction 🚧 +::: + +In IronCalc a value, a result of a calculation can be one of: + +## Numbers + +Numbers in IronCalc are [IEEE 754 double precission](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). + +Numbers are only displayed up to 15 significant figures. That's why '=0.1+0.2' is actually '0.3' + +Also numbers are compared up to 15 significant figures. So `=IF(0.1+0.2=0.3, "Valid", "Invalid")` will return `Valid`. + +However `=0.3-0.2-0.1` will not result in `0` in IronCalc. + +### Casting into numbers + +Strings and booleans are sometimes coverted to numbers + +`=1+"2"` => 3 + +Some functions cast in weird ways: + +SUM(1, TRUE) => 2 +SUM(1, "1") => 2 + +But SUM(1, A1) => 1 (where A1 is TRUE or "1") + + +Sometimes the conversion happens like => "123"+1 is actually 124 and the SQRT("4") is 2 or the SQRT(TRUE) is 1. + +Some functions, however are more strict BIN2DEC(TRUE) is #VALUE! + +### Dates + +On spreadsheets a date is just the number of days since January 1, 1900. + + +## Strings + + +### Complex numbers + +On IronCal a complex number is just a string like "1+j3". + + +## Booleans + +### Casting from numbers + +## Errors + + +### Casting from strings + +"#N/A" => #N/A + +## Arrays \ No newline at end of file diff --git a/docs/src/functions/financial/fv.md b/docs/src/functions/financial/fv.md index a71ff3b..36d6303 100644 --- a/docs/src/functions/financial/fv.md +++ b/docs/src/functions/financial/fv.md @@ -3,8 +3,10 @@ layout: doc outline: deep lang: en-US --- - # FV function +::: warning +**Note:** This draft page is under construction 🚧 +::: ## Overview FV (Future Value) is a function of the Financial category that can be used to predict the future value of an investment or asset based on its present value. @@ -13,34 +15,38 @@ FV can be used to calculate future value over a specified number of compounding If your interest rate varies between periods, use the [FVSCHEDULE](/functions/financial/fvschedule) function instead of FV. ## Usage ### Syntax -**FV(rate, nper, pmt, pv, type)** +**FV(rate, nper, pmt, pv=0, type=FALSE) => fv** ### Argument descriptions -* *rate*. The fixed percentage interest rate or yield per period. -* *nper*. The number of compounding periods to be taken into account. While this will often be an integer, non-integer values are accepted and processed. -* *pmt*. The fixed amount paid or deposited each compounding period. -* *pv* (optional). The present value or starting amount of the asset (default 0). -* *type* (optional). A logical value indicating whether the payment due dates are at the end (0) of the compounding periods or at the beginning (any non-zero value). The default is 0 when omitted. +* *rate* ([number](/features/value-types#numbers), required). The fixed percentage interest rate or yield per period. +* *nper* ([number](/features/value-types#numbers), required). "nper" stands for number of periods, in this case the number of compounding periods to be taken into account. While this will often be an integer, non-integer values are accepted and processed. +* *pmt* ([number](/features/value-types#numbers), required). "pmt" stands for payment, in this case the fixed amount paid or deposited each compounding period. +* *pv* ([number](/features/value-types#numbers), [optional](/features/optional-arguments.md)). "pv" is the present value or starting amount of the asset (default 0). +* *type* ([boolean](/features/value-types/#booleans), [optional](/features/optional-arguments.md)). A logical value indicating whether the payment due dates are at the end (FALSE or 0) of the compounding periods or at the beginning (TRUE or any non-zero value). The default is FALSE when omitted. ### Additional guidance * Make sure that the *rate* argument specifies the interest rate or yield applicable to the compounding period, based on the value chosen for *nper*. -* The *pmt* and *pv* arguments should be expressed in the same currency unit. The value returned is expressed in the same currency unit. +* The *pmt* and *pv* arguments should be expressed in the same currency unit. * To ensure a worthwhile result, one of the *pmt* and *pv* arguments should be non-zero. * The setting of the *type* argument only affects the calculation for non-zero values of the *pmt* argument. - - +### Returned value +FV returns a [number](/features/value-types/#numbers) representing the future value expressed in the same [currency unit](/features/units) that was used for the *pmt* and *pv* arguments. +### Error conditions +* In common with many other IronCalc functions, FV propagates errors that are found in any of its arguments. +* If too few or too many arguments are supplied, FV returns the [`#ERROR!`](/features/error-types.md#error) error. +* If the value of any of the *rate*, *nper*, *pmt* or *pv* arguments is not (or cannot be converted to) a [number](/features/value-types#value), then FV returns the [`#VALUE!`](/features/error-types.md#value) error. +* If the value of the *type* argument is not (or cannot be converted to) a [Boolean](/features/value-types#booleans), then FV again returns the [`#VALUE!`](/features/error-types.md#value) error. +* For some combinations of valid argument values, FV may return a [`#NUM!`](/features/error-types.md#num) error or a [`#DIV/0!`](/features/error-types.md#div-0) error. + ## Details -* If *rate* = 0, FV is given by the equation: -$$ -FV = -pv - (pmt \times nper) -$$ +* If $\text{type} \neq 0$, $\text{fv}$ is given by the equation: +$$ \text{fv} = -\text{pv} \times (1 + \text{rate})^\text{nper} - \dfrac{\text{pmt}\times\big({(1+\text{rate})^\text{nper}-1}\big) \times(1+\text{rate})}{\text{rate}}$$ -* If *rate* <> 0 and *type* = 0, FV is given by the equation: -$$ FV = -pv \times (1 + rate)^{nper} - \dfrac{pmt\times\big({(1+rate)^{nper}-1}\big)}{rate} -$$ -* If *rate* <> 0 and *type* <> 0, FV is given by the equation: -$$ FV = -pv \times (1 + rate)^{nper} - \dfrac{pmt\times\big({(1+rate)^{nper}-1}\big) \times(1+rate)}{rate} -$$ +* If $\text{type} = 0$, $\text{fv}$ is given by the equation: +$$ \text{fv} = -\text{pv} \times (1 + \text{rate})^{\text{nper}} - \dfrac{\text{pmt}\times\big({(1+\text{rate})^\text{nper}-1}\big)}{\text{rate}}$$ + +* For any $\text{type}$, in the special case of $\text{rate} = 0$, $\text{fv}$ is given by the equation: +$$ \text{fv} = -\text{pv} - (\text{pmt} \times \text{nper}) $$ ## Examples -[See this example in IronCalc](https://app.ironcalc.com/?example=fv). +[See some examples in IronCalc](https://app.ironcalc.com/?example=fv). ## Links * For more information about the concept of "future value" in finance, visit Wikipedia's [Future value](https://en.wikipedia.org/wiki/Future_value) page. diff --git a/docs/src/functions/markdown-snippets/error-type-details.md b/docs/src/functions/markdown-snippets/error-type-details.md deleted file mode 100644 index e4829bc..0000000 --- a/docs/src/functions/markdown-snippets/error-type-details.md +++ /dev/null @@ -1 +0,0 @@ -* For information about the different types of errors that you may encounter when using IronCalc functions, visit our [Error Types](/features/error-types) page. \ No newline at end of file diff --git a/docs/src/functions/markdown-snippets/error-type-details.txt b/docs/src/functions/markdown-snippets/error-type-details.txt new file mode 100644 index 0000000..a5674b3 --- /dev/null +++ b/docs/src/functions/markdown-snippets/error-type-details.txt @@ -0,0 +1 @@ +* For more information about the different types of errors that you may encounter when using IronCalc functions, visit our [Error Types](/features/error-types) page. \ No newline at end of file diff --git a/xlsx/tests/docs/FV.xlsx b/xlsx/tests/docs/FV.xlsx index 0f76d4dbbe2bee4962bde577eb82d63dcf91d05d..28575ad444d644426a5c09be81e71a936abbde8d 100644 GIT binary patch delta 5019 zcmZ8lcQ72>*IopPx>z+>oz=)FbRMDJD$5kW%q z7X0$Q^Uh?x@BVS`nK@^kxiil>=RW5)Jl!*_o5#cL5-(m&MJ?koAe}cQfVW*9cfA+w z&LfA?+8Gz|gbl?-i)SR*Pa4yfW}50#mWxtv4y3&{idN`VYY@H3ypAW|Gcz5&9@l1< z%vo|#Q#ubx$tks|U~(u8uSMp%M}_<6MVZyjlCm0os=A@?kewHspDwcww^awxUZ4+X4No9(I&X7=NU|$F-6r=$*$bDgK^KZ<$|N4##1Q4|L6-J6}GY2B!u{hGP^q5B*!+A7KY48Ye(Zc-# zt|J)%Z_k*(AmL?VZxtz%eS?Dhp*G?a3DY)~Y#ZFdy2==wc=3j7+KRhk+POfk$k_n+ zv&lw@@9M;jqXDy2F0ymPF2VY>;K;qe!!B;S2U3Jd#2lDbdB;-&Y}+4jC-6-CbFsNK zmo;QG=Y8CW>_s4enjHwBe;7@D+4az_NnRbH@=aRfW|VN|%F_ATsnxLgQ3Q|WX+V;1 zeB>^X9G^$tfQ=bROvtXTg*%P49WFY=?Q3yT0BK)U5M$HifKsq)zRy>hCa`gYdm=Au z69tcNhlo0>>XvGNWZ&K9?Tj(3WDf24~_1{oPsS+!Enx$NTbA*%>> zj9-Ls{~(r7!Ehrn=ZH!|Buc`!9>-+s_6XIS;eOT*NoFu;;@%CUuMoqG?sH~fqAlLa z%~(ok8Z5tTzZ?BU)ls_>J-E0M9dUR%`+HXz^U3GdBQMuUrZpyIWakXE=yG%}b3^Vd z(f4~OC@Y}P5c6q<-p=cg0@cF+`jAPWj7_j_@GB}24*;;BLTwW2%d*x0Loow5Qt=orgN^@z4lk98+|z4-ndNrjS{XA-0Q4?zU`G;!v~nB0Moa zUe{IOI};mYgLztk1?AVF^Kf~Vx9_X?ObSi8h8c-kb32gYtK6MpqCuxg6ImfMbAGRY zG?t-uFY={eo+VIIq)=?PKjj-893DinO9&Uy^kbpP6ddOHc|ili2PDED#mP+(K`_{x zCwC=OxssD?-FTdtmhMt{Jd4cFA#^t9vZ?Tt?ej__$0iq#73nO&{huOoWL<~lTJnK< z>I5}udT9(yk`nMP^FS&)tWAfht*hF;3ATw}T+3K;g$2zf$bx=h9{-4s?qS6aZ!rA1 z?#d=f@{VF{n&eXFAGqRf*enJDRkq*DYpR|o(T{Dq(A}}4JR!frRw`tO4Hfobx6vGI zZ{n^2H=sIEfvdOnpYUNpW<4yaa3kNmclG)(IuPdo0;(bBG1o&)txjLW9I^5dUOuyQ zxUWb1+LDQ4G+Vy)e!C==tc0me^N+Dp4TR~FvsxZfdeTDJ4XxW06!PJJ7{{dJNg`;&1l5(+kJ{ zJQskRWp5-q57@pFrgc?LC|83n!L7$y@XsT&GUA1=vAqlSZqsDKvcIAWXMqD4`Y@f;(nsKz_29lJ?MtKQu)S zA5Sbj4qAa#s!^Wo04>M53P96d?Jw5?@qE^acarqTDX0eH6vJe~gLPkbSREi^q)VH5 znru1G$B%~f>=4&^zoSI4=Z@2Ys_jLXNu-#m_WcHgmO_m2Fu+e`(F**D=C~}Uhk_WJb2W1}3IBAnq`9ceL1}fV5FNEq&g=i~- zBiJVapb?AHWQfGJ1_Gf%%8Q8Kw(e! z(Gx_2`mTJGR5@_tBjI6pKl$a?x3;*E5yNVvXii2igh?$ z6lqG`4e(*$3H>0@`Ose2-p}Z~0JXuXc4qYI@L086qEpNzopFbS7mL+{7~u1 zfPGCMS%Tb}`RkDQ3_3S?x$1-S)D)-4SRS&^&zpSIXZb43nqH^dE+myN;sjjO`y1oF z3NEJC)a0$^9VsEbUP2~LJ^RU&ZzZOP-TAO(K12M2pVWbw6h7?BA1$WpAlKAr&kVnA6&TF9yq|yZK%n zt5Ks|(%E`C)8EPI5G#=M%7%8jdplf*LmzU8q3?_gn0StQf(chi__Q_J8Z0O^;Rq1 zM|42kO^KFAj+&lMG!_l|;nsL#It;~P#jiqYv_;+7_y)dEJbe{l^54Z%UUuJR3?Tu3 z%KpEqcNtS42@wS1PE?9`1Kxq2Ki3d%5jq5xO{W{vOeGv%QBn9zul&BFIzEk?{1U{> z=2<%5C+lNo)G%0F;mK#a0+JlYBhe7juUXV5@FAhM9G$x}vo(Ke_S)N1A^sd+# zp!Iy5o^$F%*Vf7XU&MuTabMhRJ zzy2Wons2*K1f-ceOo8eEC&lK{F&XoI*sJP$s!&Bb-X}#98MyeS;sylAP_s7F*1+Fl zrx02bMzf0}NgH3xrCebev==2({ODDg+uDw_#3~ck(Wj)29?u&-jOL|cQ3C4--|YSe z6{W7>0xe1|D#9AY%1$K5JEtuRa?|w*H0L%;u>RG~c(|^6*R5H=(?B^%PqUiqZ~g9U zmvCG8E+-i405nE=Gf-dfhKk;Q*aQk#{a$Q>b_BQwI$~9iLx)t(N zO`S=UDa1EQvq6BdSc&3 zRp;E?tY3NR7IvJ&=Nqy+Q~AOnZVax%Jq3$;rY}OB5{Sez%58g8h2maBpBUoNtHCS| zy0#J#=~n#pkmO{?7)Mx0J15;e;BrezK>Jz1G=*ry!yfy@}<$e(&hK zoHsJ|D<{d$aF$GWMNr777oxjPK~GN6RK9v3AxW~z=^Nc8+8?KLmool4t?%oXjZ2lK ztKLUrfkUB=_McVT;rsOEv!49v3vH*d?($GCJbh=JJ+)wuh(oQVxf!I{6@7+ z+L~kQTi5z-ec=gaXqjye+A|PV1n-?W^?%eLwrQoNNKG4NFnY? zO*$4;m%zFau~kEoNW*O#P2L$Nb+Y^Uvgi1I15(}kJoIpYL~46;gl)II3vv5$6Zb6U zJmZz1;xar}p?5uv!`aq(t5&RG!byw!RNDrxF}v;jrUpR;uVHAk7~l8#1LKPcX25+b zzvDMZtpF17A+@~F@n#sWw0v!PX`K&Kq1ocGs+O543PD+4g^`V+DaO8u%3Xo~=~t)$ z1|R`Hx03l5769OYi&|x%L(UjzCr%3jJ6R9Gc;5EeP1s>Zjz)F5j-*u*E{G@7c1xy5 z^Gll(M|-s5xM}G)8$pj_$S>nL-G6_J-^YR}Ay8zdNR?3#8H){l+6oPCHJ zJ?RVUD9l{ahl-C*bNXr!rR+^Z8~$#t4$EKMa@ z2}89!u}ctGfFEy1Ey%7EEWE-PoKaDZa&vNH5t^kG_cffu`G)<`oA-n|YKt(c;B3V~ zQm-RwRCrshP8lU|f7|Pg3y=}^GE;nDtlX`$@vB7cCfE-a%KXbYpC%v-7|=sp|EKdF zf8*K~V6iFUp-tlM(6&Rb_JE)uXmrmGgaW)|b?dDkfdFp>rn8Z(sXWcxsS*Ph=sDhGL@ zH1T6J?(<_PKNj{tzmkgI$g>>F(Z^rb#{3>bC|#@!mU4UBupZY5FQXIc`77iLiiN^1pfUU7~v&E9T;#u%D=aE z^1{07bB=dJYI~*Vj}T^(RdwJoHjUKJ_TF4}deYHtJYy}iFB%?6?nYztEa0<7b=>X# z?7A`1$U{#tZ>nWWW|y%###_GNlK>~UN~4w7fpI9PB3AZ!eS|>F^HEo-po$q@wS`#r zI_5~gi$EUauUh0STkL}oGi3XC=4w&H@FtuzSxKOg-fO~<6(g7FqygXS^OB!Nl@zbF##*h$TG>qKD})w8-#@8_Di_e; z<}33NHfm35b`d#n)g|zZd@@ytNwA(|Y46eqdr=_v%f*R!kkT6GDKz~-Zb7bhmW|6t z5^guv16|AInUak$BzTm{nP2XLN-%yCQq$u(!Z5@qIDa)@WKgML|xEdHnqqa+ZP{?*Cqy@jP<; zjIt*z(Krg=RsRddUS<#TM7JaPVUo&ML>F^>vn(gi54pAUroXOdY&dB9_`Kjzm)lEj zKCQ11+d>4H)w{pL(W1N?2W`z1O_|ST_~lgXnbYOT%WSm3$6~C^@qYHnuVj%+y-vu8CJlP>kLNZuXgMn$@<*`mu=EsQhQ0 zRu!@()I&2_iuIJ(Mt;y*(!*vZvb(wYWY)~*vwq1rlbliE;uYo{p#?2gtDT!=PC2Sp zi$waH)2ZFKE*^mgl~_TW8!F|J!6QJsLE0$Ah;i__i>vb~AFvhvog*jSH9MyxLo}Y! zYW7S}>@aHidZ+ZUn>^h(z7ceL=hpKTu8+3I`#~_P{pvT>P{r&TlvQSVNEN?ku@1yj zW~^V@)$Z6S=0kR=gqTf}s?Sl2irT zEY2#p%IEs8yvv~s0s_{oPq^mYhwOzy4XNvxhGLeTk4(mw8Y9o2mFidXyJ?m_4m&=~ z@1}70eV=hVW$9YmXu6o*yu_nu%b5F7R@Uos1FYB8UOpOB<~pKBn-gv-chB*9pk8gX8?fwaf6xGd4X0d{D(|y2x&@+s#}U=F4%8L8 zr^A+OTNnriEv&j~WrgTgsq7eEa7(mA0R8|MFe5Sl266A%$P?FR`s#<268S$qbKG=k zV!%UXve*34@YNnZ%?_-4=mU(T0o`bS-OFTw(crUTVppS!YsdSExwhL?8%@O(uk9Xk zx_tZvTOhZc3NIL0Y#qsL#m zVDkL0=Bd>uV@xV1u9oOuQxR3i4>6tc9C}#ubnfVz!^Nj!hseQ4a!&?ULRgtdV({Fg zd4xe|DtjT*okqDFhzy@V;=P%Gbf2Z)))x7mwwwE>lLb2e&XdGE&z!kjPY!sy!@YdZh*y%=w;O^}x;I3qM?WyE z?(Nj*Fdwf5#2&pMQD}{=KqJdk23(KHkYoHT4R2|*Nom-`w%Iy>0D##Qr~veZxefwtx?VIShX`{_CpQ_8k-?jFqDOzW=BfU#BqG0AER>SUk5 zVG%;@5*%V77HI*Dj11SR4aX{JcF$(Ic$i0T{KzoEQ_utf47ZDU)Qv)?D}AplstU@Z zd7FZLP0E%PMBFJ`5ERy;zAVA;KVDW7XbPL3V2J*EUcEBscYL~$ytulocnjhQY+N5&M`dE&>@IHxE$a%W3=LtVDa|P zEu(I`DI&DH;IDm$`k;|BaT;fJefGM1#2zNU2)ARwHH_QjJ2xywnlJKfx9rkck~cH# z^~)4~*C*ujkcJW`XM=r?=s+LICa9ht+Ib%gXU2}^k_h#YHimjk7ya0fJfl{kD0Mk$ zG>H!cqjvzH4^q&z6S!l$tclrchY-inQ3dhG*>D*LYStOOLm1(@Mx|GLmzGaQu&Kj! z_xmc-YzWG++V?>cOwp#M&$mwtw=f;NZ|j+hDr7(mP^YwB0lMEQ5AU9-O5}{cc)ku{ z>Q@3)-3+;Ia-Q`0*}OL{T(f74_ZmaDQ({vGpUKIu3lad};sOr%J7>JM_s^E*KwQxL zDo1i)`h*#oP{7)tG0*WI#;w_mLU@!yChtZI{L@qKt4i~bYCva93HmM>u zq63;LQMYQZEAv(Ol;}ccy1|&&WPa{9QsVKbJU^*{%>g3yGcIqbuBQn7WvVV z)o!xT<-R<;phHlQ=#Y*Po9Xe(D!^&qF#dznRy3{+ha)6j@jDt@v}WcUAu;S@L(Bs?e;xrZq?N{ggX=9#wMqns z4hPB-C5iN2Eh2X0lC))1Z=-H$Cjb1Y70n-74MV_TY+h(2p+=oS*E;faA3JnLROLH;M7x;2ldW$$QIYCSj~^93;D55{CN35N+4{3mHO~B|bpQ0p z3sTJm{WDq(6Kg*`EvUZs+XM~;YR%rLg2zup@%S+`9$#B`c>b6NBVkiP*8@o=XqaBOjr(xCc8$kZ1@CRtY+%s2`}`fbQBHRMAr9h% z;X7NCxJ(?x4_nqXq&jB5_@7C9aTsu%jtUz)s0j6WWT_+Bla18?FLqFWI z`HfT#invhuRN(BxO77Pa&Pb)f@3$RN+J=~b$K`ZWTsZpo7k3Hxh|tw$YQ}W|vNO{} zfHa$Hde%w_oxZ=0W6@Jr&V(_)4i1t3!?OQUtsbIhWO`sMZZJA5W;LLH%UG-qCi_j! z+Fay|(P&hi;V$jxcf|%Y`bR=SN6pd>P%mc#FQx>KN>tkGd`D8;+RV?jH>>P!>>jA% z*BBDyH-6TJLgsZ!nrxj37=a7`h^9woaEih~x}QmB3Lcmje0FDO)c)DCt6Q{I*`_1) z^p(RQglKjw$ema(;UB3av-wPiK7L_1;C$fsMxtDZmZg4CLT&->WS09> zw{q!(jq6)|4QCG>gVlqj6GC5+gG^8}ueLxGquZuFQpEXHuS&N*S_AfR5F=O3_C5Se$nOpb|V&!4=8O_^(YVcl8@e;a4%YO|Uk^~$W)uQ1aWeD@BnUH(= zIFDax;^Sy$a&J2L-W!ouhJ`LAt$cj&)?MKw?ts#R!OY>sjL73)$U%kF#WZ1TR982$ z|Io4hB*m3bGuLX(?JrS|;M|XB9f;b)spX7GuD0@=pR6`;c6@?z9!+igz9ZSY#tg>Y zOROs5Q||p3v9B@kf?vjv@$3B#Y*zA+=^BMx0gyuAm60flb}i(eZ28i#^^tJh|? zfgFwPth}{A*LxT8O+7n#@_$+v560l%foqHzzk-X`;D$eZy;Q+xGU zSCA&Nr43mTvItDu&0Q6|$$1@x8OiP|JF6aB@8hQ5l|46{BE+_1cm60#f5LUvde?ef zQDVJXMx`wZ%=jHXMDy0xe!~<_9fA5%5MD(#A3mEd>;etCGQ*lHHEcc9HIQ~!E5@|% zYHu(^T8{L`%`B47#j$mU7K`TtVYL-)tULEpRU(lV?o{BPbP{>F9H)v|y?)(Uf@h)k zESEV+4!7vHC%qatvG4ASA*FaFg3gES8KtA z#z&8ac^qvc>z3r+rMKvA{RYNF+GFWUlBtO|Z2eSli{+{;S1GNFN6x`q!$^ze?Wmu@ zq#8;QZS|W{={(9PGVk3j7&(j2o0%F~PL^dd)IqnwEVJwhtg2(Oy>`Abf;FVXigwmB zD%&b<_!T5n(rDQ=;0W&3!jvTec_s|k-6I>WoJLzLxE3iY9kh)3p>B(P)k}S2ND)(eN7q z5%!?w?zIp+w7uc`y8PASLO3yOqkSi4O7pBOn#i#a6?&$Gu<=Uv&G# zJK?u+i|*uHpXHC=_yO66seZDsZv?0xh#cFva8vC?dTCL_{;)NIKpy=xpJVy`%iK*bH92CK{Le=eY)VP3trulesi8nhSix z?O*YQiGu4I81RF30}OX-v@1vsIo{C@;ZKM-?a0r^@&tb@E|s)VajoC^$a$O3_z`^w zI^ag?4Er7NJa-wV&^@lh#5)c5rcMtBQGL%!c+kM<%A@nO z2)rm~c4H!bt{;6vnFx`1{IF*uKW|$qVBv3Ba1)|nZ{uZInwOoK@p29Kk#ceJanX9< zyq_XpYuW=9{zUC0<8 z%4VsK#Shw2oV)|ZShYCS3p_iiTF8x#71`u%Y#3NfA!!M1^+bK!E$?yF52}rhPBaPZ zxN{AqDWHjUZ(QFEm``|Xr9Lhl!J>61VeG-(S8m3}vQg-lJ#uT_ycT$XHXLlfvmS39 zFx<&U38M&?#5TvSLY{aw^z5ozv_vjM{w<08*_^DXZUdfQm;`Qb*nR0R2;>)9gHN*~ zhgCFZqQ6vgQDuwFBbMgae8$sy9-bT)^ZdXArI5k%;MF!k$2?b7u1*`X3Q{4nWYRm8 zE1Fxh`)&3JS{55!))H7%!EKM)2euB5%SOyroZ+%f6gVK}GcrTSh<$A_N0`Gd6rk_B z#Qi0yby^R93Lo37E8fiACMgM2?FVr!PngY;YN>(7qLzHL@eCFZ0VC3uyc#M=NRiG3 z2TJI)I^_+wUm0DwO$7O#+`o8_;7;qF|6Wn&ZDcFI+V$|o_b)pJ-t4B&sry)$ z8sS3Ng!MT_KX^jZ-=aSFZ=I;G%#@xXxXe!tjVOQlX!zAr|E9gO^5<7XM*iRPM3#us zH-rdDkp1sxyI91WBnf#WBEk93)dK(=m)!X`Mv)p29nx+lWDPSbG8e+g^{-O`09XFu zE~Cn;$Uz7X*FSjy04)Cp(8rAgiVAc6f1>`wbY4e7Mfth@d+nDj_9g`YnEquiqxU?> USWz`nK7M4kC_lxb;NRqb00;HZm;e9(