From f75b2bab640e8b52e94d383ad2805ef487d2984a Mon Sep 17 00:00:00 2001 From: Arihant Yadav <147732947+arihunter-18@users.noreply.github.com> Date: Fri, 31 May 2024 22:22:54 +0530 Subject: [PATCH 01/44] Add files via upload --- contrib/advanced-python/closures.md | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 contrib/advanced-python/closures.md diff --git a/contrib/advanced-python/closures.md b/contrib/advanced-python/closures.md new file mode 100644 index 0000000..87ad058 --- /dev/null +++ b/contrib/advanced-python/closures.md @@ -0,0 +1,101 @@ +# Closures +In order to have complete understanding of this topic in python, one needs to be crystal clear with the concept of functions and the different types of them which are namely First Class Functions and Nested Functions. + +### First Class Functions +These are the normal functions used by the programmer in routine as they can be assigned to variables, passed as arguments and returned from other functions. +### Nested Functions +These are the functions defined within other functions and involve thorough usage of **Closures**. It is also referred as **Inner Functions** by some books. There are times when it is required to prevent a function or the data it has access to from being accessed from other parts of the code, and this is where Nested Functions come into play. Basically, its usage allows the encapsulation of that particular data/function within another function. This enables it to be virtually hidden from the global scope. + +## Defining Closures +In nested functions, if the outer function basically ends up returning the inner function, in this case the concept of closures comes into play. + +A closure is a function object that remembers values in enclosing scopes even if they are not present in memory. There are certain neccesary condtions required to create a closure in python : +1. The inner function must be defined inside the outer function. +2. The inner function must refer to a value defined in the outer function. +3. The inner function must return a value. + +## Advantages of Closures +* Closures make it possible to pass data to inner functions without first passing them to outer functions +* Closures can be used to create private variables and functions +* They also make it possible to invoke the inner function from outside of the encapsulating outer function. +* It improves code readability and maintainability + +## Examples implementing Closures +### Example 1 : Basic Implementation +```python +def make_multiplier_of(n): + def multiplier(x): + return x * n + return multiplier + +times3 = make_multiplier_of(3) +times5 = make_multiplier_of(5) + +print(times3(9)) +print(times5(3)) +``` +#### Output: +``` +27 +15 +``` +The **multiplier function** is defined inside the **make_multiplier_of function**. It has access to the n variable from the outer scope, even after the make_multiplier_of function has returned. This is an example of a closure. + +### Example 2 : Implementation with Decorators +```python +def decorator_function(original_function): + def wrapper_function(*args, **kwargs): + print(f"Wrapper executed before {original_function.__name__}") + return original_function(*args, **kwargs) + return wrapper_function + +@decorator_function +def display(): + print("Display function executed") + +display() +``` +#### Output: +``` + Wrapper executed before display + Display function executed +``` +The code in the example defines a decorator function: ***decorator_function*** that takes a function as an argument and returns a new function **wrapper_function**. The **wrapper_function** function prints a message to the console before calling the original function which appends the name of the called function as specified in the code. + +The **@decorator_function** syntax is used to apply the decorator_function decorator to the display function. This means that the display function is replaced with the result of calling **decorator_function(display)**. + +When the **display()** function is called, the wrapper_function function is executed instead. The wrapper_function function prints a message to the console and then calls the original display function. +### Example 3 : Implementation with for loop +```python +def create_closures(): + closures = [] + for i in range(5): + def closure(i=i): # Capture current value of i by default argument + return i + closures.append(closure) + return closures + +my_closures = create_closures() +for closure in my_closures: + print(closure()) + +``` +#### Output: +``` +0 +1 +2 +3 +4 +``` +The code in the example defines a function **create_closures** that creates a list of closure functions. Each closure function returns the current value of the loop variable i. + +The closure function is defined inside the **create_closures function**. It has access to the i variable from the **outer scope**, even after the create_closures function has returned. This is an example of a closure. + +The **i**=*i* argument in the closure function is used to capture the current value of *i* by default argument. This is necessary because the ****i** variable in the outer scope is a loop variable, and its value changes in each iteration of the loop. By capturing the current value of *i* in the default argument, we ensure that each closure function returns the correct value of **i**. This is responsible for the generation of output 0,1,2,3,4. + + +For more examples related to closures, [click here](https://dev.to/bshadmehr/understanding-closures-in-python-a-comprehensive-tutorial-11ld). + +## Summary +Closures in Python provide a powerful mechanism for encapsulating state and behavior, enabling more flexible and modular code. Understanding and effectively using closures enables the creation of function factories, allows functions to have state, and facilitates functional programming techniques. \ No newline at end of file From 50f1e2c3deb4d244b052fcc4c9e4b7d3b6836551 Mon Sep 17 00:00:00 2001 From: Arihant Yadav <147732947+arihunter-18@users.noreply.github.com> Date: Fri, 31 May 2024 22:24:16 +0530 Subject: [PATCH 02/44] Updated index.md --- contrib/advanced-python/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index b95e4b9..d9a9056 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -7,3 +7,4 @@ - [Regular Expressions in Python](regular_expressions.md) - [JSON module](json-module.md) - [Map Function](map-function.md) +- [Closures](closures.md) From 843eb9b01959f199b6bba37ac3839356c3023f6d Mon Sep 17 00:00:00 2001 From: anamika123 Date: Sun, 2 Jun 2024 22:04:58 +0530 Subject: [PATCH 03/44] eval function --- contrib/advanced-python/eval_function.md | 75 ++++++++++++++++++++++++ contrib/advanced-python/index.md | 1 + 2 files changed, 76 insertions(+) create mode 100644 contrib/advanced-python/eval_function.md diff --git a/contrib/advanced-python/eval_function.md b/contrib/advanced-python/eval_function.md new file mode 100644 index 0000000..9e3ec54 --- /dev/null +++ b/contrib/advanced-python/eval_function.md @@ -0,0 +1,75 @@ +# Understanding the `eval` Function in Python +## Introduction + +The `eval` function in Python allows you to execute a string-based Python expression dynamically. This can be useful in various scenarios where you need to evaluate expressions that are not known until runtime. + +## Syntax +```python +eval(expression, globals=None, locals=None) +``` + +### Parameters: + +* expression: String is parsed and evaluated as a Python expression +* globals [optional]: Dictionary to specify the available global methods and variables. +* locals [optional]: Another dictionary to specify the available local methods and variables. + +## Examples +Example 1: +```python +result = eval('2 + 3 * 4') +print(result) # Output: 14 +``` +Example 2: + +```python +x = 10 +expression = 'x * 2' +result = eval(expression, {'x': x}) +print(result) # Output: 20 +``` +Example 3: +```python +x = 10 +def multiply(a, b): + return a * b +expression = 'multiply(x, 5) + 2' +result = eval(expression) +print("Result:",result) # Output: Result:52 +``` +Example 4: +```python +expression = input("Enter a Python expression: ") +result = eval(expression) +print("Result:", result) +#input= "3+2" +#Output: Result:5 +``` + +Example 5: +```python +import numpy as np +a=np.random.randint(1,9) +b=np.random.randint(1,9) +operations=["*","-","+"] +op=np.random.choice(operations) + +expression=str(a)+op+str(b) +correct_answer=eval(expression) +given_answer=int(input(str(a)+" "+op+" "+str(b)+" = ")) + +if given_answer==correct_answer: + print("Correct") +else: + print("Incorrect") + print("correct answer is :" ,correct_answer) + +#2 * 1 = 8 +#Incorrect +#correct answer is : 2 +#or +#3 * 2 = 6 +#Correct +``` +## Conclusion +The eval function is a powerful tool in Python that allows for dynamic evaluation of expressions. \ No newline at end of file diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index b31544e..9419480 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,3 +10,4 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) +- [Eval Function](eval_function.md) From ea37cbb25457eaf0c650cd6822ff62fdb63da3a9 Mon Sep 17 00:00:00 2001 From: Lingamuneni Santhosh Siddhardha <103999924+Santhosh-Siddhardha@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:07:01 +0530 Subject: [PATCH 04/44] Create list_comprehension.md Added Introduction Added Syntax Added Examples Added Conclusion --- contrib/advanced-python/list_comprehension.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 contrib/advanced-python/list_comprehension.md diff --git a/contrib/advanced-python/list_comprehension.md b/contrib/advanced-python/list_comprehension.md new file mode 100644 index 0000000..d9ab589 --- /dev/null +++ b/contrib/advanced-python/list_comprehension.md @@ -0,0 +1,73 @@ +# List Comprehension + +Creating lists concisely and expressively is what list comprehension in Python does. You can generate lists from already existing iterables like lists, tuples or strings with a short form. +This boosts the readability of code and reduces necessity of using explicit looping constructs. + +## Syntax : + +### Basic syntax + +```python +new_list = [expression for item in iterable] +``` +- **new_list**: This is the name given to the list that will be created using the list comprehension. +- **expression**: This is the expression that defines how each element of the new list will be generated or transformed. +- **item**: This variable represents each individual element from the iterable. It takes on the value of each element in the iterable during each iteration. +- **iterable**: This is the sequence-like object over which the iteration will take place. It provides the elements that will be processed by the expression. + +This list comprehension syntax `[expression for item in iterable]` allows you to generate a new list by applying a specific expression to each element in an iterable. + +### Syntax including condition + +```python +new_list = [expression for item in iterable if condition] +``` +- **new_list**: This is the name given to the list that will be created using the list comprehension. +- **expression**: This is the expression that defines how each element of the new list will be generated or transformed. +- **item**: This variable represents each individual element from the iterable. It takes on the value of each element in the iterable during each iteration. +- **iterable**: This is the sequence-like object over which the iteration will take place. It provides the elements that will be processed by the expression. +- **if condition**: This is an optional part of the syntax. It allows for conditional filtering of elements from the iterable. Only items that satisfy the condition + will be included in the new list. + + +## Examples: + +1. Generating a list of squares of numbers from 1 to 5: + +```python +squares = [x ** 2 for x in range(1, 6)] +print(squares) +``` + +- **Output** : +```python +[1, 4, 9, 16, 25] +``` + +2. Filtering even numbers from a list: + +```python +nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +even = [x for x in nums if x % 2 == 0] +print(even) +``` + +- **Output** : +```python +[2, 4, 6, 8, 10] +``` + +3. Flattening a list of lists: +```python +matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +flat = [x for sublist in matrix for x in sublist] +print(flat) +``` + +- **Output** : +```python +[1, 2, 3, 4, 5, 6, 7, 8, 9] +``` + +List comprehension is a powerful feature in Python for creating lists based on existing iterables with a concise syntax. +By mastering list comprehension, developers can write cleaner, more expressive code and leverage Python's functional programming capabilities effectively. From b1f8d147a2abd103e4029c4e366011c1831fe784 Mon Sep 17 00:00:00 2001 From: Lingamuneni Santhosh Siddhardha <103999924+Santhosh-Siddhardha@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:08:52 +0530 Subject: [PATCH 05/44] Update index.md Added List Comprehension section --- contrib/advanced-python/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index b31544e..7ae2ede 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,3 +10,4 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) +- [List Comprehension](#list_comprehension.md) From 53efed98073fba08e3c2867676273f654882c9cb Mon Sep 17 00:00:00 2001 From: Lingamuneni Santhosh Siddhardha <103999924+Santhosh-Siddhardha@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:11:33 +0530 Subject: [PATCH 06/44] Update index.md made a small change --- contrib/advanced-python/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index 7ae2ede..631423a 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,4 +10,4 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) -- [List Comprehension](#list_comprehension.md) +- [List Comprehension](list_comprehension.md) From 0f0d58ce2dc3b013c9e0a12561e7208ff58cf93a Mon Sep 17 00:00:00 2001 From: Dishika Vaishkiyar <152963337+Dishika18@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:18:27 +0530 Subject: [PATCH 07/44] Added Content: Scatter Chart in Matplotlib --- .../images/img_colorbar.png | Bin 0 -> 2386 bytes .../images/scatter_color.png | Bin 0 -> 12534 bytes .../images/scatter_coloreachdot.png | Bin 0 -> 12330 bytes .../images/scatter_colormap1.png | Bin 0 -> 12824 bytes .../images/scatter_colormap2.png | Bin 0 -> 17457 bytes .../images/scatter_compare.png | Bin 0 -> 12762 bytes .../images/simple_scatter.png | Bin 0 -> 11079 bytes 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 contrib/plotting-visualization/images/img_colorbar.png create mode 100644 contrib/plotting-visualization/images/scatter_color.png create mode 100644 contrib/plotting-visualization/images/scatter_coloreachdot.png create mode 100644 contrib/plotting-visualization/images/scatter_colormap1.png create mode 100644 contrib/plotting-visualization/images/scatter_colormap2.png create mode 100644 contrib/plotting-visualization/images/scatter_compare.png create mode 100644 contrib/plotting-visualization/images/simple_scatter.png diff --git a/contrib/plotting-visualization/images/img_colorbar.png b/contrib/plotting-visualization/images/img_colorbar.png new file mode 100644 index 0000000000000000000000000000000000000000..acc1ec5bd84d52a0f8bb3a9a4fc88405ca6e8edd GIT binary patch literal 2386 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 literal 0 HcmV?d00001 diff --git a/contrib/plotting-visualization/images/scatter_colormap1.png b/contrib/plotting-visualization/images/scatter_colormap1.png new file mode 100644 index 0000000000000000000000000000000000000000..212b368023d9babfbd95dc3e92106512ecb0ef55 GIT binary patch literal 12824 zcmeHuc{tVYyY7dG$j~5t6)K91DH1YNqDAH@vkV!RaiPqmQb{61na9kQjG<+y5Hip6 zl(3eeu*{bItZ!%U^E=nszw4a6|2XH`*EwCfus+lKdEfWBpZmU_$8&X6MFu)HIs`!& zP`BhY5ri@TK`2IP_roXj$rTdtjmqshN}Cq``OsQEgWnH0-_mzO5T?82{}h?B8Mg4@ z70k^$7%iuJ7|*+|RtWko#@WFM<6vuk%EQXl&DP2Bl8}gyh`=cu48~bfSopsm7jkm7 z7G}Q3`WJ$nLQwM8wC~3)jClJTCDyL(*?SzheBc~E#hVtT!;ki>%RLKneNA`g=AmnK zN1Ry?8RB=x^Y#m#(GkGkU>;1#+u}y;IHwAa)oB8z>Kiry*=RJ>^nDBX{&DPeJRzszN@EXUzOk?01B7cmU89{0; z9F>D#zNjM<2y*ZS1r>r^IQah${O6|R>lOASLHhlHgVp}khmRbw(vRwLfhk56pbc4A zSeCbUUCY0pq)$Z>p6+h1^Gi#Q%pFINaVcIQA>EnQ`1n^7v~=UPw{G8#z^u~no zAqs~rBch|>T=^uF3nr@MUV^NdG%G5$*m z?R!cD>3BiePuJbulj!`HvZji$@x~Vf3CL8WaG-8gau9a%F)C{ArVhkUs6AIdgCNW; zya9P~FNe}U?AsFEWS46WZ+&K;k>l+d?0gZ9hQQIUX{X1u5?_k?R_?1XjxskA+O?af ztt$(5KX($w$p_;+BXn88f2Sj4YwlMw#v_OurI|aFKEaCV3$LuVRWB22-4C&%@ujf* zFJbq zdE&A8uuu~xg3tzE)<>AwCDUYlw`@o|n<}lD|_L+F+;R$L!sADi|rMW7j^!GP@O|Z%M+<&vu5Xvcx8pL^frmpOJ7(m)+kj2-r zns&438J$KM6x)P#c6AZk{7G6cNS$D7*lIR{gFk4;=LSEzq}tiri#hZcw2M}cVNGye zwhMzLDpmO~`swLu1`dcJbVwL=L0XzT9MB44WM{jrrlO*QLZRMZW2L=IKbbhqh9x9$ zEM~%~g00^u`;QKl#!CM--&&ocqoPb!ba8Q^>H=IUKFCF%)Hb7~q7u^7)WmjMSWr;M z&d%-)cIEz6B_$=hUvRyR{Y!xWR%1+y*rM7*Nx zR@h?oo}c@CzeZzYaY#HDa6y#D21sc~^91@9D`TIEoa*-Xo?CV7pxHVan3+C{#8oup^# ztK(QEwR{3L7W1`XxAPiGRFQN5A*+$SD*`#d=s1jm*T64v88A$ zeS<2W2Z5WVX#1bv-vN%k2o3FfefmZ*dZ=`NGiI*yW|WoK{i_?}fnmc3VVV?+8!7M4 zwx1@>bt$gQv^il&!{u&bPNP>_5@k)FJbALb=~J=X#IK{F(P38Jz~OzHhVdk=G>6~D z#I3J12;!{$vWucBc;ZWtJ%mX}YZ2N|O_~_N;Wc|YghSdOyx4i-diwkKcE2aWA7(glSwOU9>bK3W@mhmQ4|Jf_%=dSxuh^xs zoyMwS#O$MB7svQ6;qi8n(b3}fVd%wTu%8Mf&YnFRp4Q#ny}wIKORL1XEunqIdkLq< zYH~kW5;L1bB=p%sknr*Gb#jt^1)Ur7UYj5ATJ2E$as=S()b*5ngu_B#H*lsDE&leh zRZ~Q}zo~OAHNHJL(6M6feS4;v#e22H{O!d`;nh!{J{ir;%?ZYlulz*5a$D6Dg?irE z*?GN7Sy?&s1k28Q((bl$xW~d^k0r(G{j1COuL`~eEIGBDvM3dxudfdi+SJy@q>_`B zrE%@r!~KjW`+pEFJBgltnRF|5gyWTsO#uNo>NQ6EiAkU)1}6*m#=MZ zo|2Xt|tKHMn)Aap&cN*%=YRx{TD}~Z9|6!H`rD!SklsrCR;b?g3DQ0y| z&G0{I+-#5i$rza-$*u4Pz`0M85g5err<9Na&5qNZ0&u{-rF zSk|$zlo6WdqpU|#4-JeEQ{#doM?R!>{f3)HBq61Td)(b)K2BQUD$DMKTPjOx8|G9m z_P^JY8BCT9VaubK8_odA5GTV(QFgd`=uRead#$OrW7+dI4xMxPc=wNLYUsMB<)@L{T8=!t#E7-7c=j zFpWIH1f=wgr*Ws+m$sJPaHi3|d~B8Gw#NrHKSxjF0piK3JqSvJgVY6)bAzY#Ww%Mp z85S_ty!EHe;PU_D)b)0(KtPTjw)a~Y*Yx@}kh~~i(^0c3^iYh1+xs2A>dC1oG0Shv zfK7R|7p<(U*kt`aeKNtqSA8faHjsu%!4-f4IoWZJejGM#cZM1#;8JzRrrS(w&!-0~ zqW&ZwmSe{NQRbTXvD!Cp-n4lK0YT1IUc1CT;uZmG4?n|gebO#NcB~PJf@gJ|Rt*B2 zgv3uyD=0<9Js66so0}MVP!y%`eu zl3e<|R)wnmoxcu-r7YJMs9Sln7#8$u>kIfIrI!v%K(p8`wr@*DGAM}?oe z{WfVP(cgsW>aT$8>}=5o%a#D4S*F!~2Ia2lpf7GMUteaa@P^3@9K6x^;H$AkjIC%mw0y=PqoK|(s|D9y?UT2)>Bf5Hmu!B z!V?~ar|nDh%csfrx$R!!&X_?3>4P`mLQ`is_EuDyLOqT^cr;j@Bfn>0a{RXu2+qUQ z%=I9J2)al4Hi*wmEsPwad-IY2LsL$`5!(HM**SSm9)C$4*LK%Yd3=}x89zvO>g9w- zWlwqK-t6h|b9aQI3%#u5g^nT!V-oV2SA5tvB=;vDuE?5KH6>TwBsOXTx*0 zIdC&8;$BJ3zN>l@qhqR_`teed5sXN{H}a(-A4I$`oBOufOjTZEm6dF~5>HEuSx+A{ zzBETi1DuojFC*G5QRmZ*r7fjsb_tC9V*l}ym9LWQGzjzQ`RbwByzzi2GySN^wr8G_ zuz-ge{_lX%e_E?+7$W?9d=as+Ip4qEu4`zphh5JnAaJmydUtKe3b=#f9%+~THaSfG z5>ch0jpp3jXxJJLWLljJ76t`l-;r{&V)6xlvG2A=JW%KGv>a@e0qjMiSuvpdZhS(J zAHK>@)0#mZCT;aq+bs^4SIieSV;0M2?107rlkdFLcJ+us>(uXWGb>LxfLk-ZCsXQ| znF0as?(W%Gte%mPQQ+L~-@grt?y1afX6xicgomd%j#St}An^$bhG4WI5j+cSqI~=I zZ6<{5?C%E7J|HVPrl#1N!N=xCJjZ$-?xW#GeFXlTn7tyYK#L@Wv#1njPUhHM`VA{e^ z8E4rJ+&JRhyR5UyQ8ufy9U)b~?Jf~#Q$sn#rI~3#zh~8zk;TiWKTw*wKBgf-$%~qa zb9+TcN2d)!WOC9x&$Qa4q@)BU!w$1qTr6sJ?;d_CRMv4>Is*ur+$mY3I;N|3mX%ppS@{J8^BUKIox#_{3vj%c55Y2z z!D$1fd5a^J_JCP2lJ40Ro=f@9kMLxJG`wWp+8rW3nhB3y7{wXNZV$9|XKC?_IDcVK zRfM!lo0BOR@K8>!D?_ahL{DT~T02q{HN*~}pym5GLZeV#@ zW~9>b-5LkoH!FR~wbUyYQ!|!@{z~3>dnf1!paU6%I4I-^YpBg?;+HZLP4@!T&W^Ae z3)0$vhfcRHVSTI`6o3TFA@BrPN~|eT-3<#Y^6VZF8D5NuTpREr)Ci$Tu8I4A_B``D zDkpE`dm&R#b|~LVs{hADktk*UHHTm`J_PxGjY9q_dlps;T~IY(^{6y2P)z5L5)~4l z&l~XUI>@dh}{)2KZnxzkz(&!3!DkoVV$uJn*Dzx&^@x7B6<$pd@N?|7P9bZ{+wK9TCd?CfsUrS}VwZ_f6 znM*|`L}mw{8bE^juVdoLyHn(5W~w_n=3l&YiG`CB3jvi~P;fzXtQ6f6C!yt6Z3>~! zcG2=#DSGIqnK=aKuV6go#uVq?oz2Znuitg7C9wNIWZl*?a(Mz)2@fhp$}|6GOKisG z%1ll{fx1khnx4snbP!4>RT&{3Wd2a4&&k=@O0Y$}rX%%4&A&ctU#;@p#t+(Kdm#3N zz|DY6t`d7$C0rYTp}SkV<=rJkkRme&`ZWI0=_x?dL+X~8uTH&BRgj;rN+c3r1P9w+ zkd~1#vG2{*0HFi+gech}xf-NCqMp87p2UcDJY zrW<8L><0CPx%;sn(Aytv8@?w<8=7FnS8u7QG1>dUBsxFCQ{Hctn8{2^k^@G;CnVIn zMI`7L8s_BY3T*nwpd=lOvBkx!A2n+KmWlt0LcS+pdl?vLv({J)mN~&0qHy&bphvrV zpD`=nLs*mKX;8G<>gq|bl$W<;I6@V{?fGK`seq{dOg{THdI&#?s{}Qfo~4yRrkgQW zTv7y@HDM!=^JeQl~f#7$E2Z}fAgI7meq1I%x2aU=)CGVPYRH>yhTX#mzJ<<=mvn8Q!l4p=p zpRZ6Il>dNjVzTedO&`?RdBWXDs@Oqw4(8pgn2Gk+G4^dQ4*&;49eK#RHnumM8_1%Q z&BWo~ykyqiyOz>MT>GhZ^45OD8OYQ#bL_5t<)gLV(@V#rt>%{O%M>phLXyOQQfUaN zKS&$cyf&n(e^ZCdj11`MPBGW{upbFc*yx}tYwjGde!#^_i?s8RpLE%4mb#@C%QRTA zSZH!cX&;z;ya94YD;En#@lj!Cq6r&!<&--MNF$M5IJX~x6v)Y&6jUj9;&^o1ZddAa z(GayAzVDGL%0itoIF0*EFYz!$_Y8}eBXZh>4wdh;s5)~0QBFv()7a>4CUcQrf7mg) z!umaLzOh4ppT3q>h7L8S-O!q^Yq@~&+c#Mc_Z3ci1g$%@;kP7lRo)rB(esg3`fRVG z^6$PIc;R0DrNRV3@7s0)+kAqC)@E>RVSBZm%U|P^It!l6n!z=3ui| z8L3kKh#pDDRGVG%r8@@a?x9}w_)_t&?ARWz&D?@qR=J^CQtXWyznfyTu40_qbfd*IYWRo6S69g}+2oW@J6Izgg8 zmz1?V9ygC72sN`@u(ws+IUT*e^Zj4hZ#6YB+FFrl1gl1)WrkCl24fV2{fwQ4B=7jj zD|=RrO%kXBO2JD=s>|c+@37gFsxM7{)Wcc2;bpqL`sLn|k9l+Nm<)Q8NGWFsb(;3c zS0}TiCobv)*&F!ag7B)JM&Ib~M^eBclnZXgyXvO)aa^rmD8;DuMI19-deCUyIP%>J zJsg$3a^(OO0zQSosC%lA!y2=*FD_4mOW5Hz@s+HwBC+KpWquhtL{>PXu8$r;Gm!7M zO-fqas-2$U6yNP1AAW1NP5893122*sUy1K^RCMxRZ*i%y*z?`qWX-M?#{IdlkK!tW zks_^)!37&aBungSbTqyDc+EAGrf#$nN=RKUBa=5dAck+{a~!;=8BW}1Bqi&HiT~zY7T8wo7y za33taT(zt5=hB!I=@%|y#>x(;L_hua{+yC0%hferQqs!Hcb%Q*H7q0;_~q3e$T{Tj z`LGKU2s8B5T~?9($l)J$lLDs7osXBMzm0sqYaVx%WJ-0W_R?*okGlk(MW^r1%C3!) zZV3XO&0{ClCz~q>y?buu++xQMU@DCZjiMG4y>m3%7?;FoDaI{}gP(U?DtoDEbo+Lo zw-+b>;~PblcIMT=aW7ciKQfNnLbAESQi@jY$+6`0yO!;1s`>IqxsmSnfkJcd$L=F1 zbp6xxj^%f}YVihbJ{Nvk8ngTOnq-;vOhG<*Aw~l!a3AjmlS&u3bb5O6ajzP;LW8$$ zEZaB)FK;!Fq+ZaQ85_snYmLi_Ht|A%Nu#8!+_g|PmLJ6FcPAg~AK+NM>mg|Lv7nX! z$(?MZ&ZPC$GBLMHMzWvRcGn{*$maVPZ7RS@#Ko*m-iMj2ONIuiGG ztl{p-wXVPwWK5e!Oqm1RD0`M{^#)F&=pw6~Nb9Xwj=py;taC3tCZvBbB6-H$2~-zt z+E3v&f5r}$yJc|C!I-*$31C{p`b6(I_GIfQp->uSjw7PBo!pU8QIdyP<>>njNh!Je zc?Lu+8`E*q!CY@?5rd3Oc8a*m=ord}0bZ-Kr^|-&wzj-lV#Jg!8eTC7Uh>&+9`RXe z0gqT4nB2YZZGJ4Q52Ni0g|~)E0zg18xRvF|x2!a5#a)u{GOytqK?4UiF)&s6hn}-l);aQ0)n1 zlF1aYsL%9V8YTOUWT$+f$Xa|e<{vaAhO|1@#X?JUJ#=z%(z%Z27dz$!XXpHBv&QsS zzXQ3VXb^*(%v~+0d3;H3#+{tz-%$S=upOBXWs-;=Q#oC|~V8MF0La(L(smuau-)HKNgYrWzn62Ovs6YjQ zOziVlS6AN{(6EM68Dwy|pB*&)plxMpN-HVh7qDYJETDEfyQ>2!rUW|w5mBI&mHWdL zQ#Y6|09RyF{C}!90@Misk6-=ug^8b^Um0W=ey}8m!+TzYIv6!?C=_cSzXG?Na(!5B%Rigst7i`iNQzbWJXSz+`3>^sW>P) zpYj1|#K)Mi!f5!nKzj}sgEqp~(r|Xa6|vw(;k?&-;VGitYYr-JFI)pzOEy4? zi;LR{fcsFQyIKz}+SKpIvR6SX9lyCiI}YW`X;-n~tDpue4&K()jq;I6TwYnZv10#k z3Qa+jAI|0`pAE$fXciEFf?Ot48wWodVU|s%qyp5Ev6BS(9xp{}9J*l0~Xn6B5BNyrn6P-Hu zOm}1lb;{MkyCp~hBX4Eo`_hQf&67($NJA<@DR*Mcr(=xRnc$;ec)e>)*EiX8X~zl8 zG~e6JVF7;Vr6r_Be#e#;L3amDd#}QIsPMcgrtP2pCYk=b-u7u06gK;SRkthbqW28S zVfH`E zBNv$dK65FtN3kV-bRt|iP-{y;fGMCf5Q>cG?h9ktBhxQ;k=e&QK4Ya&O3|!A=e0lwin$o!m<}EV`~hi zbx+6Kfrf{R@(M*h?9eM#2SlD%NZUVE3Z3uCZ(2WEOo&W)o%ixFN1;IT+T6UO9Y8K!$<*u`M;_j|9fR+sfTgkQkaJ(Db@yNGt`zOU^g@uLnyu2#FvI0Qt zPFWgZ`+(~RSqO22zQ*ACg_3{hTHCpNk}DvicG@+|ZRXb~mGt@Z=k5F^RQC^^80jCK ztzT0pvl%kcQY5_+DO#h1w*uNvu8FS$kF=I#rwnnc93lI7=H}n8Xtu2>h?k02z_VJ4 z7sM!d9s-6Oe+s($pH5 z43s$~`fYsS%mzB3bnBM-Se0*y%alTf#S>^5i--P>yHJij59S>E!Hc7OwUQptNOW@vP9(12_W0wU{fuPs0Y z*nJ3HW(&m<#&a^#JX`C#@1>SLX+>GhL7sYH_BqP^;!veU17irAn34%r4@^h=Sar3` z_WUK9&Pfc^$HN{yqJ(T`>@^eb8IhQXt*DSBXFF(@a+NgbCzmM(tP^{vS&hcw4bSuQ zccn+0WcT;q*##T49XwBBj_IzFb;5)9T^*l64oDajm_7X2mWXAS^00xD%T}GRUvfsG z-{72$;ADI94SZKdC>$P}-GRJpH~Q%TIZe~i(@(E#V!#c3y2v%!D_VvNV&d$D^e1Xj ze-J%bj1sQiDTG{_1>F}CuZykXX2;gwPB%vrpuQ40wiK#f`0Gl?njaKQ*hFn!1D8(L z$&Gnug)V>gwcI$An^oDTESc3 z(k;lLVT;YeO?D18P@)%i9b>SDgd4 z&tCXWLCy1Lg7~4Kp|{ZVF%&*8?Y$zt) zL>!&|@nd$qdJot2;j>+P(lxJ@7V#3d8C6s#wCZF)Hl2;J&v&_^zX3s*1x;|!Bu1|S z9cye??-%su8xzb5za5~WEEM3kQ!EdxO(Hi3ko{0dzp4!fa?CDdLff7S)YQ9l^o-Nt zaqvRefiIIbAbRt_hOAhs+7^Y0BI0|R=dRo>vTQtz9{#9NWZmW=snqYgHJ9P_^PM%> zO%)%#e}i22%JN<;hZ4iDN93I{SZvEC<&jHXE7(l%D&$5Sh#Pm2!KI|N* z5XzAU+dE@wLTWrgD6iXpx6glWcYUl6#6jGv!MGVpy-QrDmAI;I96q?;9Xx%0 zVPUm!a@QPmHO#%UPUt|Fjr?@3_;sfOlXXncAPtCK4w<7s-5vTHAOOYgex}Npy7|~j zpCMtW)OWbpMhm=bP(!@O58}(Jck{YjKAVG)yhX(N=Rxe=+^xn?Y*&X{`K0{*&=fkAe#P{l0~ z5s??qpOgEY{8nPec+Z_v0RLZA(Lux3)>d+FXXV6g0D_g2t%DW+K;^ojku!u-)5}RT;;JEpPT>t;3%MZ>$`zV^etzArNH{3Qf`ZXeP*9j%v2RO|uGk}O z_oOScW=xfQRE?KBf9_oU$F>5SdKj+xQ0ZdTwxgh7>FpfdJWXJHU16W`G_o5HOC3iB z%?7_dKhl$`$V3J&$FiYh%Ly0g9^mlQPmGuH^rfHI63^r05hOQ5QH4V`f5(9$N!e%=B0u>bIS6t)7- zM}A4L3@{LU#rA!w(MGP%T@vqhyrk@ZF$`!&=ENosrdiIOJNHz95qe{wYldC5ZyDMe zLbv@a09kuJSTtnrj7~+I*AjIemxE+C7iH+!0cHkqzNn21isY^O5^$SbjD#LeueJHC zmRNCnz%+8%HttpL8IYPKKeQ2Ei_ypdiD*kZSDGvV7gQw zK;Dqn%Br)fs$^FGih{x?h)W5UZ7Hda>ZLMzM<{;+qlQu{$6Ti1r2LSSJ6?MPwhFn% zO|F9CAWL~c&j(rG&Rn0WblVWH^a)H;;Dj1^Tvx}Lb1}mEEQU&V!Acp#c@YbxIYLKZ zdA9$4G0i)`vWFlvu<>DGcN^;J1|VgXa6$)SWkto`P@Y#dYPag?@%izAQZy&-=y&MR zbtB^;M`%M8j9@;`uuSsAmMB0u^r)!#JtbqDs^Xri>gv)q8ohh*0#6ane`ktw0c@PU zSHZzW+pBXH!lqTj5wj!Pcg#8F7-RbUmg4op2Wbx?2y$5V##I#r!N(v7 z-Vg~9{N~WRG7pP0*n0kX5uMB;P4;06{RmN2svyJP4&O3kLo=;!Q{iL;Ls& z!AFp*G)JZ2EhN(TB=82K8wehPoF*e>Mi8l|r|<|7#Fv)^gZ%#=`oAWE@>O$`u=VJt z=l9?1ULcdssaU#Mxtnasap{r{_Zzvz#YI|i*TS`5eX2Q1mX>MB3Q^~LYESvpzO(F1 zp8l3+@}c5|?-g|V@$c_!+&fGwaHA=V%i0kaFXLbMaM74cr&xZV#Qsg^bXV!UF*SYt zxfy;ZtlDgUk(_@45^tX3a|`J=2@h+pTH9HLbSV zI)0y>lT&VaqB)pFU0t31>{*#ZC%C)5M(`IsnAeYfFyzu-Y#Zf$nC_hH{tpyP*W|V$Pcyei*jXiHx{d4~P99N3X ziVu@I))`*I>$O&+6r3*t0ulu^o?YFBQA!O;HYO&rz!8%2Cp`0RI?^K5+IV2NK4Wd$ z-9a=uNKt)LMOD>fK)8)j|#xnkyWYo z+;!X>e`Edm#mTMZSloHpt?nGpB+i_QpaaYdPtow;zt|X}Y1@kExcz#`a7n=IE6wnX zh{ME`+ck?B4dgrm1O?S#xAkM~BK`Iw5&FVY^IWrlrlv*j~K{gX20}VwN3= z=jJ(mYwj`CAR(cl4L|OFnRVtTrbOB@4c*PUDz7hA>ErR>--~hcZ52M00A~PG_i$W& zhUgw0BZc)D2tPH(V#(}E2!Z<}djAEf{WlQ0ku!g!D0u{d%9r=|T;#~Rq$2SrSum&6 z^k&+1n3&pRdT;pb*23*@)jZSw9T)n@ zr1d+yblfDmV~6_F>sbRkyQQ~$M6V)=ix@XEvfUI^8FSd_*hP`JP%*r0 zeDa@KPl-GHDW_&cEBki66D7pU*oFG3-$GOl8|5Rg&lQ3P3Kw(4#7CaQ7dksAW_))6 zA0qpaXK=f+2MTSEt$9>KjJl!@X{{TWY(*!{cwXQY_<)CeWb~IBp>RN?e>NW4!oIMR zb#-1Qx7}JdUwWvJ6dRKnbxk+Unuk8s;v5bS>Hoqh%_u0|(G@giWDCN>{E~rFR=dC~prfN> zWg^NR3nR5&5644D!lZ&P9+jFo)O64=&Ux;8T4W@xshQbU@q1nF!!*275XTf4HPzLV z^7Hdu4?|M@e)SH%macB*w{KcbGd)>DYat=WsZN{_cG0!7%cdvy$w^8|a&k;Xkiy7b z0erjMVqbYA$~ZEaEMeMfWqGpo&A{whQG9$n^EF~bs)?7Fat|&zmC+zF(4KI8wf#DiQSk zRCk{gJsam3@$);#aPe-HgkKfT#0~~d?o835yGq+f!8v|A3jqtCvZ{84zu$erG7aNWcXlpjnQmZ>RdaMKOqP#` zZ*07&=Q^E)zTHMhXMvj#!I0P38jb`SMV_N@w@k>-zaT;&7VaJ-$cd3^$51IF4Q3akv zOMrS!&;NK=E(zft3Z)GB^kwdyu@Lb8eaCQbF*9Txk8A`^?*zOINQ(oGtkw(G8uc1R zZw%L;kwq#m5#^G6YC28XWI{p%Tj=Ah{d>{idrPwHe?aq5*3Z>q1GcF|8hpU8e}Z0k zjXj})#}_P$*mA>bm=0XWw{ZIN0q=@~cgUHQ@bg4f;9&vW9Y7P z2nXi+(_*hi3I2XeGU=K>GrP!^Yox2R{p_)#-~M%BZoT&JjF*;`)h$gSp188&SXW;^ zlUdNxF%~GvozpurV-lI1Ur+$J;y4pi4s2)Z))?_qFGTZS?@u%%KgUQ9Eec;cl_axF zo5C#ThbpxTEna&9VzHj?NUDAPS%T&1U&qB{0DK-I;?#mzuM8*|DK z7XNd|DSN~LY1UvYcbd`AbLv))6z;Ltsz|a_2w%ViP)GXL5@XbKeOdn25i^wGX?pjH8Hz318`(cT* z=gw_yiSKQUMe><=&pY*I>peLti69JVT(zZ=V){Xijg45_l7ZR$f!V`btx>{Cb|pF< z+iRiCX2JDi?T-%}avTc~z8U1_?|)X7ot=I4rJSs6`-dmQ1N5x@+~<$gERNM%?`$jv z%vmc$i16_#E2i+X@ev@D=k+^Z_1bJx-W|-dw6t6;mfV_3Xl!Y*Yx5c`b3y}aPqtph z-tM+Fj0naM)HL3)GTrI1OP1g%pKK}g&3lBT>D0(XVe;3CyFact%@66F(7pSJ`n*;g z7K=4&)%z#HW^izD1ffB4b9qv@-+u1zUdV2c4Xmn#!)ADRi zcD9D6XVtYAH0_tY4q7N`k>iy-t-i1cSijOW%?r2ZirZZ;0`x!VJYXB7=yA^czhk*Bq59{= z1-kefyB8d+tkUhv;jS_#^jKs??~X_XlNzc98{i>R@)dqT!kEidJ&fg!(<j)K2g_|RK*yv zw4EJyNR`Y_m9H1)W+EE@9jaEFPGsqnSPc~0((;?VfJ9krKb-A0_x?J70+k&7$}3K{ zA!i;6wES|c2GTkNo2RDur6%>{0VnxCub0{B55ayax<7ui+SIIkFdK63bY4B9s=9ip z>-t>QNq(~_7&}G7DuT4dJQgRW{87)V&`IV*`2z+oPwpcP=K$9E9XT=5_WZyYf5M!& znO)VjwQN3H2_p@xuvd zYVt@Z5;qXEUOu@0dqD!7FF#D3Fyp@?*AOy^5Z#AQdDZ^wO`#Bo4vFB4{1tkC$f9Y9 z_o>8Az7}r1lEJlLLACp#7WnixPOa}V|GdU@!nh92E85=814*84tz2xHH+ z1H6RS)@5^q*XMm~yx+;Wt{WX8LE;p!8hFGVhPFG0eQHc48((qqPIz9-k5zl#T$f_d;jAVHMrFN1_J)Y>vux!6u3k5>eB&qlQ_U``wOak<~$Qe zcbORd_5KHH>7_Pm|BY7u!69(F*j#%2q5p*8_WvcVu5Lk{ZPj0BdH*6ke4!x}Dn9D3 zP4Ivu&vWIGKqkrT%TI_b`|?c#<_sXqQuF9%qDdEUB1Ax1VY{~w1wA~H6{FMj%AKbJ zgooPb>?_{E&z5l64`r|=MCJbjjnfU9M<$1zza3A^rF zrd7JFW6^6B+YSiWeB+hv;BOU6fC9mKxXJ^iC7w8QK}-i!<9_Z)6>;7G&FKxil5Rooy9=*4N43N z32_~Ol;OS&Ik3CHyp^aPh?~yd6u$TG-&ct|flvVnZk9#_L1t@1?nL4h-ZDT$-;N)x z5%pHCcDIc*tF)E;KCBhX6e;Od_1&%{2kMd{1K2Eu!DXzE2w{sGi4f&OWNUpWpm69ZpRbykfr4rts_gA8SUxRv2w0pP9F8M3c*8RvKVCtJ zqK1YBYRs8~XRgA-o7ATzylhYe@~Qc}`LQKxj4G=pl-vYn+SL6gs9yPK_EVJy2? z&FT(7H4O(15vgy>k=|r>gaKy%4#2K0Z$*L6VQXkF}>ZdoO|^@k+MDwr8BM#I>}t$i`>~RxsvN z=&QzP=fp&=z8G?`HTO**#I*RcLNqukpy?B!AHUmC#q-7`jz9pwdi|sy4|QoD z@@}VIy_6VnH>-t_53Umt=I+~v{#(3>tK_E3Lb)o0Rvis6KywK8&lj{xbS{lIg}#3M z4#={p-s-)gxw08aNqt-)N9GR4U&j*aVe1d)b`$YaG>*$ z%<0Ww!fn(yHZ-&Y@Dc>*)*Q|!-Smbhr+40G{l1_=@OHU337R%?X*(@e#j1$vi)iY>iB}$69S-I*!gw^;lZCX4gire^7B;+%$ikz zR|o1kIe<=>gO867Bh{|qZCUJ`Cyf1@cI=HhgNM{m{04WfSr4x&listH2weXp%dpiw z2H=k7PqsxfyVj90Hw&WuJZnORfei2e`NX%$XDBKhwUpB5kp z#Zf7=ROMAFLWAIKXOhy23SFSSu!H3#qhn(zQ4IC%U#7xSR#~?Tu0dJbUC-o|$=`BOItXQfXZp-zR~e4c(b1*f#X~G4 z&*ML03In9Bp@B_$qu&8Cgk!_mH&ed~S~Y;Ufn_gp-@I=i3Hd3dbJ`lx$*C8#2*z1O zn3(q!kz4@~U^+USf%OTcrKPR|V`F1QrcKntGYU~cY38jlqLXhO1R%47hJ+AaQbUmb z*go>ZVP*}4yiiU;9_zLDT&80Xxqp>0@Va$RW;^WFgoNvXH-kP0GUWhqr;pp2lwAFE zOvwV!$5vmfXENmRbFDi6REW&qwJzX=Bmx26B%ly|@AFA0c3rV)DJb*ZXCoxrvD#~p zLwf9-MGXcIExPo*cAaL2s||80*R+Etzy2u!3kwSk^~e!~>-qsoPc0$5o%TS_MA+k? zH>83Zfo^6iE31&0m>~;XSC`uA+@MaZ`;u(%=-b`0yyn z4qRr+xm?g|&z(WST?-bgY$DRS(C`C(-KS5f#l;%s59WK|1}U&B*x^dR-?f)H&49u~ zgKevAe0|K+b^8mesIpE1*>#Ftf${Tw@#ilUAjn62CHzN{VG2kJ(0A;r$pU3O5)%4! zLN+A#ulA^nr*)sXq~9wS>%}Guf|!112)~4~oVP*NA*Fe$Q4%PjRN1=Y6y^rkge!M8 z?S^%Ya$K-8#eCfBcE?Bf6x6#kh|e-YK`4s;+T2+@tNq9DynjjEmm_q&HcW^V@ed*1g(nA@ zwQcXmE=NT(+wha=J?LSsyW02o#BpRqQi~8@@&dZ zIN-u6WOxY*X0gI5;=l9?SH`$RUE}40j+@ey{rXzvYO!!})pv##W`p_h0qwd3dr$=jy+bW?&g4p2jOvZb9R~V>~t70*P_)sGpdBs+H3uo7I%Lfj*_C8ma z0o0!7J_jkt0^vp-BG_LAXzgjr4OH93bT6<_ZU9PwpMX~Ogl~w^f4D{nJaAz&X?SCP z5dW*R(Kt3j$x9_QvoFP10VT=w@91@yO6vC&>+aIg{8fzn5siN*;I9x3SuhY!$sGP? z&7^UailTX+RQ->rM}rn#Xw=K}K4rM^;djveU3g)=F$9*jp;1wswY(76#|Jdv*h7R+ zg?)buj+TIB2McWTQikl~wle#V8a~_BZwQe>JJ0x8izIHxb)X4vB5(E7bJm!)Z$(d^euv54_X6aZ2tHgeMB^}e@}lgkPNHxpOcC+g zY%<{B;Ze%a%;}jMEDw!{NXs*+$NKYBy<*|gF1+D?f~P0nw5iZz+gT)95fYsu4D{4X zdIZx30%k@pgu%#Pm)zED6vBBg!)eYc@tAChPESiyHZx1Mj{?RGgGZot>sFglUV;l8 zT=>vxha8V3grTyZL2{03ede4XSOcr2UFH5>zOyUntlY}T6AG)PpAEFUh8T@3?WAL@ ziYP{UN6v&`gsgK!Fi;CY$ygN@6*Vxmy+5Vz9n2H9{r&x^=;(BEIM`4Y5tLuMb?fU* zT5CeQMMXtR55=Ii$|wjOawqS1iAL$taH9?s**)q%vPRQT194AQ_tvKt%Q5>>lYzOY|jm6_xl?(%x*M zqT224?PzhOswAE?HDv+NFtw~q8_?+LOQ<_67f!wy$+N*{lZCYkvy2oSd%=IFWMH*R zRml?Dm6qRUQzap}v)2AyIOzNL@2*J;kW0t|&?zGwJ^f0Kr&0}F`fR;&ciXA9cV*Mb zQSLYffVdSEV#9pb0~x!oGkGM!zALP)i|0CjiSWFv@UKr6 z!f;mLbWOCz<^Wva6cM@ItLqSeGN+KUWIJ}(hjTzqYK|0?gY}{nvI!Tm`=0vg(z>H1raZLC$zY?wA>` zf^)^Gs=rOu&~CIWvoK_m8DOg*Ai)p1C;)hI3SrFA^xB%CuFU-*-tAi9cY(}BK~-FnaRz0WelIQxTI{U_#)-Ay^Mb@|z@5m@H zb=_R}q=_H;;l9pm<0a$TZ`M5%w&j=A+_sW%hwrN)r~Ob22KcqGprm--e(gaD5N|_O zwOi?hr!DyKe9!-8soGjzLZGA`$6dZ@TCYTO3ZzF`<0B+(Eh=s-z&d`fPQEI066>k` zhFOM?c@#|vmk&@{n2&kh&o(x;QJ5={IZ3-aR^X)FyTN?z0CNz`1!SE^&OL%3vNP$f z3zzY5G~RO8%{0@ybfNSz9biS*QS@#%FKn~wb-}4a_d6^*DBu* zUZG(?P}vMkehfdCj_iiGC>zX5s>S;?azx+Tvb;I_x+{A=4;O4~+h3?TPQ^NYb2j{Ulw%uOr!T;Rtf)-mu=RBji*8m)m(5h@0~BNG?Vvo zmklB>{Y;4zoQhv7g>dEzS(ilGOKXS1cF4HohvfOXu2+<+{*Z zyh|jhqMTgqx%9fC`32tY;x7L1*ev3BMU0dXog<^>CRlEZrmWYW{J-y4ah3V}gZ1XhlI-=JAy*|=ujF8+5Jj3?I$gkqTx zN>>()6b%-X)ru*WA6r{+*0=>N`62Z~|Xn(@55&#@;(IJ3e>9?z8C8(f)km%xEfWTt!!#q!~rwO&ZpSkUhFJV!m>Ia z6Q9CUsSOa1BPYAVa_wPVfB*cBl(|`C@{qWr7Or6C48zV^c-h%c)*YCt#$QoKs3nR2 z5GyPg-y6x3zF+NAWMfXtdb6me_i-)%l)G@u?)B@7!}D1hk;~0*?3$z2%srGhie9iu zC--)-GI_icHW*4HRrRrUUhBq-#%+&|nE=8W;GVQv*=gtV3w$}9yG#%rP(Ra6;5D3D z?NO*%%q9&s6}H;8WP)ed0f%ypA;+wsW24dx=O`(?F-PvncZS>+Rb8S_8(12|xhd`r zKMz$I$DL@~WQr}iS?8lT=+nA0=iNLsuQ69u?3|&mr`I;IOfaRzd9ZlKo@q9e#Rco> zS*>#t*PuB(HKe(BB>$WC3jlqW@qNFj?!HtjSE_oNgC{+)Xx_iW65us2nz+lvXt$=p z<>g@(JgoB`A*sUlYwxaKniqUOY!bOO{875!V9ALx!Ae{MHTp0GDq=uC$3T*Ps2YR?6;M$H~GrQz7vx%KKaz9^9ca^#V= zmJ7f2fTMF@eMXr4{BoBH9-(Mdn8aBB*z8Zq>hC19aHvj?((paG)2MX+MCkcGU0YGF z;X|&Q3Ug)W>Ntxu*M%UM)Qgl~e^4iL9cq%!>N6F|X_w6tiTKisjc86Z19E>|@0oVI zTU7zKEPRJWKG>*mcDwymY)nQ7e;IpY&%={#z84(F6>6!hC$~?u^*I*RWBU&g32d{x zbx@qoK4o4!d&syg+-wAm;J&^lH0>A4xZb=VlpU^)?R_9go+lYv_SIr})3%sRJm0=v+<29Fi_>FmtzWSyX$`&#Ka{P9`>u|ET>f zt6u9n{`o$lV$8n?_vJos{be1Q`cFlhVnu?iMPo@m8}0?XO&Z7vZrxN6H?=b~vQ%(; zyG0;5qHY#Iy=CBp@z~|A-nlQn{i~p=H!qK2{ptZ`-M6XiT`dX1X`gk{%hWsTt=Ft^ zC4p(Bqf~k$?i%gN(bWg>5sSB`$7(?E!~(MFcuIPVMb=|$r5(twIEG8kT{Gq-13;}w zA35E|4)y=w*#~K#-Q-v8BOI)9<1$@KBsldl9m*u>gEo*2^M`*iLr1=Rd3^>90``_L z&Y(oFEJy*oZl}`JRn8d((`i?Mxl*)4iwJ5B0IKoI5$-n2(u|?b#mHT&Em?MbhO~_g zt4gX5WQU}z7uYUt81)?-sc>2T>0I7jVs8M<3BYMCtz0SLp@#`Mtzf5e8g%MSdoVx5 zasGT)Ern!e?Cvk~{`U?kzt+4R-lFP?y81W@M5#lp@gApTVhN(R?7lgY)>y}MEn<~^ z`qI%CmV%5@pHDghltZCeGi{b$xdvDxz$8!(<=xpgO-Udr$OUCr@3|V_@Cb-3057A& zm%iJqphAq;`wt=x4i2l|Oac$g(qeA$YG33vq{+NPB+7}N{-$#n6l@Yuh{e&_m0k`4 zfGfoy_V5-eVoD6V%je?Qno!#OIxuj+##P8+(RBM}I zKs9mQ_>~a_g7%b;=K=pe1V6y=tUmS|Oih%F=a}%YdqRBt>7-<#Jzpk}Zl>CJ4bSSC z@ON?9J$)JNe%h+Z^odub~J8zwhdoGl4fm)enEKbSsH*n-hU8hn5tm z@ZOW9ld_MMDV}O%a^05J!zJV>R-}#kX{wKHYAo_q{=~XQEBbGf27bU7Etk?O=h zg+?t08u$Su*P+1hfAF9=F6p2(oCib#JgN~n^UiNO7TJM|#JPi~FPqoV16}aj!;b0h z5+`MO)pHP*xoqL%A&{L?fwIS9Gje*WJhri`Y3b=5;C4=96n9O9k|I*TGKic>;(Lr{ zfX45uV(kcy0p|}~UvrGO4mc82_4Q+{tgX?C915`QtvjZsmb+W4u8VaiP-+tx!71=K zhSZN)PG($}+b32Ce}ZtCy1oU>syaBAiX0{t_3k&E1q1I^lfhm*aFf=*zU2iag4co# z2!8vj&1CNKU-`G5GPLhg?}BER3X#%5J6Y79$71bSXzWyQ)ZY(_sQ&Mjig^25$xb;e??z(&%OfgctUVgXJ>JVL)>*z~Zz zU57#IXMvi2Us@1QP(|;i@40EZ3tnV3S6A1{U1@JJWqJ7*OG``c15mrC2TE-Dg3aOg zSy&Si9@7_J_GRL$qZoU4V)V7!VrC@lmSo4?^`m)zB^|c*9E$Hg#WcV<;-T>CeFEFt8 zws~|CNCUe1^Im)EQORk)e%XL%0Ny<7m8tfA$R8vL;24~G2=vikc@bI_I-vq6vS>fO zySHB53lYb|gPKb_vCjX5H8=+Pt31lzDyDz{oC1qUc@5>I|9CyZ81RQ6uw|EFIZ)0j zs4GQ@3Egk~c_u!Is@>K9NOZwxWzqb5^QIr_Ba7fS_df3o?Ghis0)b|!|1IyGl7-Zy z2Alo@Kfg!)w^`?a->nA$F$L-AH_?*;jKzIl)fDi$3qY(W_`iuXJ5SvCS%2q^uW5>&j+}2UwzMx0-{u|!`RRq{N5#E(m7;DA zX=Y8o7U#GY(GqK6HJ^tylH}_8fydwvPAnj9Fyr)Wl9?jqVQKxUX95+~FQiupy3T&l zu-(K;i?5EI7a0qdray~3B?q-p%g-`$Yj8*~eMxOQs9IFtWjtFXJ4IqMmQse8@Odv; z#fJ_EELx5Arm-_4&w0jejSnPh>f7ShhPRe}mRx>GMiO6kYD9__O^lc%ch4OQ|1Acw zJyYWA_(=4L{TqmL6r1ka@G;PnQeH{M*Cp(*dmR@H(aezj#JP3#LL=3zY0G>iW5>rW za@b=PaTr4rD&8GK5*kd>MdNbp&0dm|7b1T-nB?U7Js;iXIsbL$@Sj1od}Y#0l2i4* z5oB{M#Kuph>p}Hga$-Pxk!6B3kTOph4IbfXDLRHIEN=}avRj(0OuV_|l$m@bWk5+a zdHKrwQMbYwHxE6ht5ikxAvDpMbI#`FF?1IEr~m;$uIc7Hw&O*1Ho3?HqdLY)PsgOH z)Sa%9EX|Ma%RV|~bR2D&ezi`h{elpb3~*XyImWra+1oC-?v%Jx;5y*1_^X#cRwD69 z{kk{X1;pYcgnf$xIn&)+wMtDpb6&&djUl}&Q}za#uVfD#c=st-Y6QJ$;m65($)3E( zAfg5X2G!&$F_&A{m8gkCE{sUZchZ4@9z1+fob(yomde!~)N2HoTZZ6WE4)&XW$nLp z_S?}*7mvoBG{e-E${Xd1p|^ZiaE#}bILBr6p<@bbvM=V6_4T%h_&7$0-nrAS<|JiHRE$;*gLj}B@Q7D z#W7X;_Q2r!r=nM{isoouz}Ncn0o>!D(O@({Z95Swp)4I-j~&Z@pr-5~FU!|}o!oeC zoG^NYfe|F^9aw#gQ2xNEVCldVXh;Hvu9X)EQjz;~G&3gT%n%xWyWjs%m8d8L#|)$? z+JEapCXqq-?+=5CK4V`RGM3L_=f|8f4F3H{fa*t;ytaZtD~9HW6d={73~*bTsnS=D z{g#C+DGiEC{A0d#>O#&GL*@b2=twQYV8}_ORse zFX!@K&TdckAP}NO3gg3(N3H|>{QT-5iA-EZc*-s*Wn*Wt1Tlz{&~|hj?G>7 z_4ii=StLCpgFxa9ao$@{f^NSNW^1<4n41IiE(9xI$uJvc4nL+GH&Cj6dqcs(e=i(u-EU-L1(mOfdirWVBB6}@w;hlyc=4;<7!ah|)gQY^G zfGn5?pFe+IHWlyh2=hpJ^^l_{bW)+hPG5n!HB=g~4649+09K?*lZ&ex9x@Z8j!IB~w!jjDdLgC?I~5N(S#afpd0u>Xr4xFkBG%>xGi3a! zr`M}-l>k#|v2C<)(f_yhQuo&H!?j6M=B7Z2EFl-Pm- zIngXUOds#7tr+wk#M@!(oL+0uXvO6R)!Whb0}a8f3O=<)67b~jNb+t=zNqFy{Tb{H zVj#7?inn^7JSyqFk?ZpLqHbE*-}@D$S=C^Lse|0igpBg{}qACOv-qnDSbWGq}#Zjh-sV^v$fI z2HeO?53;w`=b5l~wd-b9vCuEjDKhysp^3X(9^Ll2MzxU)6GW#ONDWzyB9lSqOMrGQ zE3mP=BTuPA``~83zgGoV3WM#I*#5=OWl*IH&7b|bMo&Cv00xrhNy@ z!DWk|>Cn{zCx`_&+Taj%0N4fJhj!K+-jbxdg*(thPkfquo`5?S3r#GjJqvt0$6AGJYR%DY$sdpSS2+>H$a} zFf#&ozEH|szs?CYBRnGNFgnh_0J-28mNF_dGBO>ExH&_`p1WJmn$thLcbMFt!NabP zoFVWMJV5_n0O9X(j|hm-l`or&ar>B0I|Ej{w97>~z+GNe&*jfZOKT5dS5?!}O0B4< z$W7aWMZ~FqT^Ks{#6D5>{ zZ2O_fYUP$s7YA7W(wsr{wI9eT8A=C6#0qu-NY#`(z`~!3Dyy1Kj(v$B-$ZUXMu&mM>LT- zO2}6GiAcp8J?99ZAd^A$6}+lkq-$f7$z8GVF?Y{}=rMG~K1EMjk##TD-fKJR=&@rS z-4xIszp=4Fd41Br*x2~VrXOma0{3XGG2<9mCKEs&M?K3>tha+3AOkX!MV%kjJMt_U zXgp0laELlpGe;jgTwRSK3oiW%t&%}!om6RSH84)?;hg7al3rgV8(S!Oc1|n#AT|5r z%ZEPD5mr25PHZ?*n0M3YjWo$9)l;o+b;o=SIe(HnQ!m-(z0zZ`zGL(t-@TPURet@# z*P5INTvJoil4qXQr_N4QN73l1Ro8o-o{XK-0z^+PlMy`$S4_z(C^(kTp8BC%(xcex z!2@AMMa9v@MZUSYxzXKSk4^ChqS4=mhU~1?I?5(-d|D;N#qk5PpZE6ms$?(Cu2UQ*Wq`aOOlsMJ1=~=HaGk!oPm)vkCL@ z@#(WFS{fN)hPI^0m=)8UoE#Tt=denB9^i%6ws+skpW9qp>scubomk#%T=x)TG@l4e zFy!XpDF7`zqr36P-SF1d*5;Rcb>`bA{OjziCpX=PR#v|1;&vqa3r!5G@^M>RqU&pG z`Gao-q?o8E8ja63DyZ`?CPwQLX`quKfp&&y}N==F&2suVpS_wf2 zu?RxYN3s`wIS^Yc1Q%la%NP|B_~$`#_bJ@(w~^JbN03AM=zoGY63M3UqoBi;TMo+B zMh?#Uc7}+hzJra0wS$G}9Y!ZZJ9|@WD_+hEoEO*`O&lC-gt@r>e9mcYXUs)mM0FBD z7!i#0B^8&5nO<)PmF39Y`S!b5Vm@Ae7hHHFMf??064f`)q|XnM?!9oc@w|=X=Qc-6 zb#k=>v{#;TN{@${JwH&~t$g!Z-dOi#{a#-qZJ!ZhvLm)1Zh2dNCh_S!Eg1BM_)E_m zgH+N%3}gA-+1hPUhOt^7`>~*&Rl1;Xs%VkbZCi~{!~HOsO4*zexFF2O2`CVxoSRq@ zZaz^U1PDSVg%BeMH`)Jx@ZVdKrJNG1%JG9w@|@>%42_JcN9;Pa4%D-Bb98Jh3VB8Rtq zZ=6f$>FF8YEcAh=F^jobRYRu`LL}hM^_{iQaF#N3=dS)cb`HjmFzSD#Ynr7_ncautZW6 z$af3N=n7|A1UV>)Jj{uVI@)gwmWL2fd<#{lEb3Cvq~BY}T(1!bzn-{{kQmF^%V?I$ zPEr&4_i%YdcwrGFo&q`CdN@CKB))!mjLsdswjQ3Ht?r~$xpBUCA!2yG%4(IEse6Km z1P-}x%qdK2Piw-0@3I*w{Zp$&ci!B$36d3$b__g>pPf4w@>t&J3|I(r5qpi@7e2+o zN0(2zmg?+zaLHAazNALTG^FH}!|JPI&d3E}#g~~87phO5g62YF+nXvjq#ZVN`f?*Q zQ~s@IHQ*9yFMI+)C=hw|?zdc{xpR%|82JxGsg@mKg>eWX8O?-HXC~&2{cz?Pg4>1D z2sNh&@9E4xZf2Xd*w;%>R+$B{<1b2ujT)cHW=D{QCnVU%_Q^%)u$BBRad~i^&5dGW zLJ(PgV#zm!4Ot<)SHF(=iq%am8Z{6C?Crbu6$KuP@Vmwn)Y}$~8$rWQH#!G<33AQ9{1u6m#U@WxPOr z=`}olkw;8unQ43HTTQO(tm;Dv1fe~5hG-y6*DK`!*ZUi6YU$(i^EF&}Eq{p}7k`QP z^z`dfZ5hq*ET-=D#k7nJ`F3sRNMT{&jnwIcF$H_mTGt1T+gFUytI*5=1S^#|tYi~( zR=z0#6XfRMFBDS{VIZB%kB)5=_#_;;A&bI$NJ8nKR>+e z7GD4sO02dI_L$-7UdibCneFauU+BKAsTmv;)I79W7cHUtL66(b+`?jdN(nwY z`0Wd_RyyaYxV^cmuBxi)a9de9$Z4i4F)l8S9?vBvrcLk9G77sEV5YRdi?y`0-1zb7 zQ2Z(lHFd5*)j>l;L(?w-r@9KPqs7;UkIwX#sN9=9RExD%wmnP3?+zC71WQT(8Y1^5T)u+Yaj^Vdf1nm%0jV<9n=} z2M>{ev%9AiYN+8v6_|QCHsHNZ*8LBR$qxt+*~9O>lgG&z(QniLzw2X~>t_jV$i5#m zb@pvp33qVw3sz@NEQ-tl)6jbk2hC%9xm{IBDNd4+F{~zWd8)li)Jh}tIEQ8eIEZ^h z`zEW|vRXvu-XG;1jj;a~STpjh2rPvw@QJO(GED*tX<}w{QUeE48l0IQ$IY|Hx>7s zvuUv6x#N$44+cBaD=SWqF2}yS-7}LpA$*kT6YjD*(~y#_H;@-6Rv{u6cCcGqLPSKw zPowQBb$4Fc@m%dqz8QaY;sx)w8Z zD8kvf0(NULR)}W&bykK5huo%2#Rj<9`fRkl7lp04MN_Om&MmvEDy zBxSvL@nYGd=qUTG3oJZ;>qEIbGJ0YvV*hkYQ&W?n+K<`XM4M+*taTLX{qLZ6b#V^}M{wJjA0@Q&Z!*57LXinVd8`c5IpUPM_Np z2L{(aH@8Ub&sxWEj7!XJZ2X4Pu-E+1dE%q{Bn!3V#?dMH*hNI>d6edYgHRly1S{ctFQ;A zlPxJ5o%LQxmmch0F7~@|jv0ob$K7!;`GLlMVgy?_wYWM*8Gs|;*7$LN7?GU(zTJH< zngCd8`Mbma70P=3S|;)Rvc|*tD&21tIWqPIM~27BE#?d>Oqu%X0kWo&U;~&{XU&w> zE8Y0_9Fi^oH>-I|DQ58U>vkD{UfT**y*GC1L9;r_R7ZE1Q-oY3owHJuM>5a8J`aoT zc@Hru^n05KBx2G^to)lL`1;nvMir6>5^*0Mbv^dfHrgrsiff@1je#27 z;dhF!UAsm}LOg6&lAdO8F`i0# zZ8f>DmTg#*>#=SpwmF?6yt}oythl?R_=;QqKDbFSVCdIyNs7^#iW1u@U?jK%{O%^c z9ln%jJ5>3QUasj)Kcnimkjw77`E^GFo`qf8lbC{s&r!hPugNZXs{9?MVAU&(xIPe_HveY z>mDkthBVJ7f3nM{GNE#5`1ba90gTM<`9daLu)(recGS!VXlNIAJV})nD1W@;cbwaYGwh=>R$iOkH*kUqM6p&+=RnfGu8F<*0eq>9jP|>no&r<;s<%xWiz@K#10kS%m$Gu*Ei7 z$kMt8uEp?dR$}5AVYczb^`%Z@ZKBHE?fzZIaG|ZF^~Eu^6J%GdeSLjx(|gV)uX?;^ z;s1rL=-ajR^@aa7w@~vJdCe=y!CMd zS^2(W(eO{;ZJjqs(9cF@crYfvo{8b`7q&qLQbgfdd>>v>3E0Bj?}fTC()bU;YviQ> zZ`Bvu(pX&>i|gj<8b0C(k5O${rn6sZ6r`1;6`I}NWTrqA3GkBR3V?gPrAe8|0_~bY z29&3MNH;)}i20q!H~?jws!Rvq=!QXFks83MxSj0vd_mts!eDQBAyVO^0BaGaYZK8CMpvckS)adbZ#6e%3>gxPsMv|&#tK4N^(iuNGmJD1(ijZST3Kn38o zGU8&#EKkJgI*t+V{@L?L`QiU_kon(+WpwlibE3_Y+>)zio7aWKWUAS4O+8NPdl1|2 z9Vg4%+SCB@Sh=_qiS`{-xp^}%-?IC-=hjNMbr?!GwVgT*1=PqGO9TCk zKMBlIq`aC>ge4LbKz7DuB$QNDL!!KQTxWWUlmUcVAO_12b6=6JqNJiqOG#mM+w|XE zJ4C~_H-Pe|Iv`Iv!^9QHZ|GRMt4I*d*d9XJ<$MTiFxt2t%|n^4N+eN z5m|jdGHiHwI5n@)3wrnI3ur7wM@NUolYK5jVWm7RnfO^-aG|JTRJj2dUi4ZVZPjLJWsa|!BiV+s5y@bEA;{GP7s+bltMjV zk5$1{U+TogdI?=eN5|BxtT~>tloX7stE=1hM^t=<)xlQYM(*zJ*CojwT?U;*U^a*UmJKp?``DfRxC=dSWAw5F#H!b`g1kNv5{!RzrO7?18x{X zi|kDN&W2T$=g#_=nT17g|D2-DBLsK#)$Q{-Zo}UiPyP6^JyV($4}U0qA%M2Ad(cW$f=`RAE7pA88P95}=UNnE*ZRH{c+qK!Q+JQwy~!o;b^)^ZphD&+Tvm^6bYH z#8!LkHwK?`4XF6^og!CDVQtea2Z+F?qvAOF@S< zK1PLFptGoIU%884DGBr8H44+oH~&l+idd1*U#4N&UMTD2sW|#HLfr5%nIvv#yZb{j z_MXCTIq2mN7EIj-qGEUX$n(zpohlm7?j({SvaX?sHscp@hX#Cp4V&-2_#Qsobq*976?FC!W8>#{W(E=muL8*teKoOKO&?9JXg%BLcPzNAc6(y7IEXsI%k)cr6opY zgNe#v8bT1vbF@iQPoNqXuK02J>DA{9$qbALvk)O8_Nekd$dXTIABrSA#>JGE1 zKZ~Z0QjB1dxp?2o(z5mbo&zY?%*)L+8U7OJw(yn3@#~o|SsZ(flB;V`lIXq2RVp>~V{LFSFF(Hvx+84rQQBEeJbY26?8r_79rcqc zs(O~e&nUvtX)E`&>w0I@)?VLUulH^n{u&hD%~d?kr0v}6=r;YX3i!Q>np#qk{n#}e zNNOQLLGg)lVUw99v%<6Df5=2JfJo7F2atpWnhty?4!_rlxM`=}GrF%5s(AZ&bM1^Jw>P@qh-2*~fR# zpDRl7bD#l{o{>>f<%^T?RrK!H-B)K-Wn`YTrEA1r>~oO3A4 za-TgTbuH~1=eT&Wpx&PdQ9ADXY3TXbQQP4TmWKKoYGIa2LYHl8xce!JQVCfqKMax{o56k!+{HHK& zLXw`E`pi$n#(@5c&I=8nKwK=INP3;? zGJj`zu3s0oloP#uUDta%&kNkU49z9{LNyx-#YJ`ytp4tz{GBIq^q%--1WBOp!_#?@ zflaK@TyY1W_gcbxpyb9LRe}Z(%s=1njccJ5jVWnwNdYP>4~noGYPIdcA45@I zT(W#xq#(pPU0vqov9kn>;V39*0)OS?_3pp>Cz;)Y2K1OO2_95Q$)f@#_3m@^Qrv%fD?NkRu)6 zM}lDd5R!q1lvEpe5YMl_f0yj)*+&L+_L(>q2_Q5fCLeeRT%A9*Jggx^@<{0d30ov$ z!vnnq4gQkSk7j2{XNi8i#)3rnDVHNS%{X;>{luflSw@6VR?|nv^E+xMX4T&1pRaE zX!r*L-Yrwpz#jL_nF90W$<_<~7a@?h>@Chy-;D96z`+cLY9^|}Z2nRAoyjD{q-O2z z{E8?fbu~Z|P|MIvY)w-)U;FXhZL#hmzx#>>sP(zqFp+@(&13k}lM#XT}08VKdM?EnI^ z`NHs5T!Q^KAsPJc_T2L5YwOAvzu0t6yflJ&lN>_w>cKw9UkNKnMZVb}Z(^_t^7FbX zB_B9bASy#bY%$PLkNDAvsAZLHr1p95dSaTot??NkL{D{@XZfBi0On?BY&-=%q#ccf zLqm_lU;~J6`7nO_NEaB6HxP>tSp*Nr9Bg2WfR_PP?~VSa$6sfh(&FQ#o>OtP<(f9f z8KA71VQXMtbxn;j(8c5Iw_?&Vi?4MTSgX6axrIe{0%KoUSsCBHaV|Ij=>~up z$!_}2^3$gMoImr2oV%O**$!YI5Dh%%Dtp6k-exk>l}9b^QRp$pvvkq_rdtf^0b^X3My?~i^2l6T%6xfGG-c^uM@gO%@+ zu?!wRF2S@Fm6H`(pB_HVXFqzqE}U29*s)_`UjBbFYAc3l3GcG|AM}>mIyySkM~L1M zupR!~IUxj50HYPH2+!3k(ht1#qP)U; zu^Q-({o*HX9#?;|6Fs6Tfq&o}=O{=_x^hk3h682z;9Ppb2`m2om}aT%w)F%odH`4n zC>zTi2L2$67l26wG6zWRw_v``P%b!?e`r7>@Q2KS6CK!9>wWvmr3a`;WGQH<_vaju zjd*v21V=L0Ph772k&7Y#5#v8HEBZ+Gv_8V}6dHT)rN9z3$e#EqAxJI=2VVV%V0BId zcIogP*^3niPz`u*!c6v4L|}Av6gV>5B$XM&)}-(dTH0Jj(TmFWAwi5HU@>#S{U+Ho zs`{JcFU1Hn%aL-7-85Wi6VL!49NJT`0Dz~Q`oAyGM_9RfPR(Qnc8Lcm)CVp+OW-T$ zRV5I!nPcsQE6xb=(~-eaAh_-hi0xU$d>4n5Otjt=RVh}%2Wb` zH5qb3(y_T4!V~@yFFZWsxU=bGmHf!Da$%gUsj6wYphlsZre?lHxYy2N^i(^3$I+qo zx7wO<>)qwURV-5D6CV zY)tXHF5Ha-xGG*BMH55PrEoXuiwG0B#pUD7r>Nq}Hr z-XPK5bzB?>v_$a+%RJP92s%yQD&3e&bKBisJ%h2vZ|4hG_obU>m8#)d8X96oM~&no z1TSoeO-xKsU$lxaGczmR4*xB5UPQ35PVEkn>(BKuv0{O?k~rJxD~$GI-yjBWd8_|v z*r@~+8!DbtnS(*#5}-4Tl7fOl?dr(zu*vFdZ|_bm5XO+OuvN?N2)3nRMFYaK{lj3S zRJtBF&D+u&fmf+QDg->hRP^q8be`Mtc}ylF_+$N{#_e8q@x+sk`tQ&Us3xR%uEnxY~K zr2SNf5LqwRD>xR4qJjACdV*pm|FL6dig^YKe?t>12gk|}NZ{s@U;i@+<zr0i9ivVXdz)c{F?0s0TZi`)84mGOn?G9>eJ7WrAP5mhEii#+90%Y4ceRUiCuv)}=d9up*#7|*m?kLy>OvGS^M z6GCvY1Ig(anshA!NdN-weQfH8AW$PDNpFM{kpToLr-Ww%B!rAWnL^kQT7Vx!*&$P_ zG11hP1=Qu|@FUS*+YNuIJ{DTVkkh$9%*3YIu#bZKzCqZ1BE%t!$HRwS-!KJ zG~C9X`o&Wj(#UgBx_Y{nG#1+oB(1tH3;c_@&VUZRmXw9A4*Lontl;Jh1Z5a6QfZwEnQQM*M zN>`a@Nv~l=#eeFjI%b;`s;9E>nZR*?$w6lsx_I$X+nVfLlSU@^g`8IKhPsYxVxQ|6 z-Ro2_+bZEQsV4_bXM2jMFWh;E%9zlo*QwjGtHe3W_gM7Hq3zwxBq&;tgYqZvFVci; zqWq;oWkA2x6B82`+|Vs(fy`NwqWJpf$7Z(KKXb(D>QgKd>puBby`Y_(rxW+Mg=*eV z=Va<^YFl90Eg-g7%eyh_j5l{?waxL~_0slQADQgO<98M%c9oZxmp3Z7yke~(iQ^P@ zoa$dV(!Acp$l(7%HOJFTiKuBmM-N@k<&eqA$y~h;gaFkOXek#;*r8gh>&C=ap}t+b z0u*_*(^$Wcm1GQvsU941sakE6VK9Y+7qx)JO94>I3;MloA2`l_mmqCElr=1a;ww`?Cuma+YupAjV*1>CFs-LYZ%Rf+ z9*)D!O zz-0nSKJeUcOM(kG`**i>Gqj47;N-SK8av&4?TL2-ngAS&a{o5lGF@mcqY%14Vs4O!K!-Qb;x zro_qitmxU&RUO#T5{TFG^71C;s~FU6ZQtp*jvVWC8S;I_WB3dtqZ!UaEZ97)h6;(D zA&E)voiXoR*F__E?KQ+GXqf6Y5~5DKE))W@M%P#_ls%9cEgYMBzv!oysg=@gRZ=%P zsnGkc;<1|9g^h=3uE`oz$Wmq@OMzCIef<7*XGC|W0v$7~4=Qv5SsE+c)y2h8>1caz zFTdh7_G6xa8X+a9-$O%b$kumi&?_Lk*r&GjVyh-lxaJ0%>F~3pR-wnDzf7BhgJYv_ zwJaG54pMaSUa6UxZCPH+Y*2iq2zef-FqdmvUU*Vc(mVN{V

wwW_X%#MffrR3xV; zC!2kKdia%)eG+W>(3eiF=z126w$X2bm6Q!THW7N;>gKOx2QBaBY8Za&3Ve{7=dSEh zo)OTq4KT`7Ah%Vs3ksd##!)FS455wXO3GT~veI3r?@{ivjm3^ru$gB;_v_YH(%SCr z-;Q*e?KzoaxiZ~}TIuMwNVbA*OJ&1(Td25mHtk-70|nftKIz%uR9aSgq4wL>rHLaoVN-r0vu{gsgPI_Dbx2~1+YKKZdr8D;`Ud2C9+RDngJ7ORBU>6Z z5;!h-kfQ%Xn|wFfC(<^L0=>s#9US3iw3Y*uhSM8vK_MZD1Vno~RGqTu=63rV@8x^% z?kuc(6c5}#a723&Bh)u91}U*(pskJ-$J9->*5)C3)l%t8gQll8pH=$gte?NjydTrs z=e610-=(b?ZL36cq9!{X3fD9tC{cq{8oxyc_Y`dHJAlRgk1n@C0h%#P?sN5*FK@qz zt!hm{;aoQ8-ZPvt3GY|B%zJlAL6d+!WZ7OmIzXi<9$gPqy&$u2kQCMASB`rU0<=RR zmsnXJ+TJOwkF9ER*Ev_koT&Nr&}N;rmw_jYCZC6gCmo7u8#M7=rJj3+5(=R-WkcKu w3V0YbYz{*O&;RD4p1)*f{_TZBqq{_``+NF|9zJLRaTvi|QIO8OtatxE0MK}v%m4rY literal 0 HcmV?d00001 diff --git a/contrib/plotting-visualization/images/simple_scatter.png b/contrib/plotting-visualization/images/simple_scatter.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa5b408811a67c88c7a42c396be6a327a3a971e GIT binary patch literal 11079 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 Date: Tue, 4 Jun 2024 00:22:12 +0530 Subject: [PATCH 08/44] Create matplotlib-scatter-plot.md --- .../matplotlib-scatter-plot.md | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 contrib/plotting-visualization/matplotlib-scatter-plot.md diff --git a/contrib/plotting-visualization/matplotlib-scatter-plot.md b/contrib/plotting-visualization/matplotlib-scatter-plot.md new file mode 100644 index 0000000..a9fa500 --- /dev/null +++ b/contrib/plotting-visualization/matplotlib-scatter-plot.md @@ -0,0 +1,155 @@ +# Scatter() plot in matplotlib +* A scatter plot is a type of data visualization that uses dots to show values for two variables, with one variable on the x-axis and the other on the y-axis. It's useful for identifying relationships, trends, and correlations, as well as spotting clusters and outliers. +* The dots on the plot shows how the variables are related. A scatter plot is made with the matplotlib library's `scatter() method`. +## Syntax +**Here's how to write code for the scatter() method:** +``` +matplotlib.pyplot.scatter (x_axis_value, y_axis_value, s = None, c = None, vmin = None, vmax = None, marker = None, cmap = None, alpha = None, linewidths = None, edgecolors = None) + +``` +## Prerequisites +Scatter plots can be created in Python with Matplotlib's pyplot library. To build a Scatter plot, first import matplotlib. It is a standard convention to import Matplotlib's pyplot library as plt. +``` +import matplotlib.pyplot as plt + +``` +## Creating a simple Scatter Plot +With Pyplot, you can use the `scatter()` function to draw a scatter plot. + +The `scatter()` function plots one dot for each observation. It needs two arrays of the same length, one for the values of the x-axis, and one for values on the y-axis: +``` +import matplotlib.pyplot as plt +import numpy as np + +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) + +plt.scatter(x, y) +plt.show() +``` + +When executed, this will show the following Scatter plot: + + + +## Compare Plots + +In a scatter plot, comparing plots involves examining multiple sets of points to identify differences or similarities in patterns, trends, or correlations between the data sets. + +``` +import matplotlib.pyplot as plt +import numpy as np + +#day one, the age and speed of 13 cars: +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) +plt.scatter(x, y) + +#day two, the age and speed of 15 cars: +x = np.array([2,2,8,1,15,8,12,9,7,3,11,4,7,14,12]) +y = np.array([100,105,84,105,90,99,90,95,94,100,79,112,91,80,85]) +plt.scatter(x, y) + +plt.show() +``` + +When executed, this will show the following Compare Scatter plot: + +## Colors in Scatter plot +You can set your own color for each scatter plot with the `color` or the `c` argument: + +``` +import matplotlib.pyplot as plt +import numpy as np + +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) +plt.scatter(x, y, color = 'hotpink') + +x = np.array([2,2,8,1,15,8,12,9,7,3,11,4,7,14,12]) +y = np.array([100,105,84,105,90,99,90,95,94,100,79,112,91,80,85]) +plt.scatter(x, y, color = '#88c999') + +plt.show() +``` + +When executed, this will show the following Colors Scatter plot: + +## Color Each Dot +You can even set a specific color for each dot by using an array of colors as value for the `c` argument: + +``**Note:** You cannot use the `color` argument for this, only the `c` argument.`` + +``` +import matplotlib.pyplot as plt +import numpy as np + +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) +colors = np.array(["red","green","blue","yellow","pink","black","orange","purple","beige","brown","gray","cyan","magenta"]) + +plt.scatter(x, y, c=colors) + +plt.show() +``` + +When executed, this will show the following Color Each Dot: + +## ColorMap +The Matplotlib module has a number of available colormaps. + +A colormap is like a list of colors, where each color has a value that ranges from 0 to 100. + +Here is an example of a colormap: + + + + +This colormap is called 'viridis' and as you can see it ranges from 0, which is a purple color, up to 100, which is a yellow color. + +## How to Use the ColorMap +You can specify the colormap with the keyword argument `cmap` with the value of the colormap, in this case `'viridis'` which is one of the built-in colormaps available in Matplotlib. + +In addition you have to create an array with values (from 0 to 100), one value for each point in the scatter plot: + +``` +import matplotlib.pyplot as plt +import numpy as np + +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) +colors = np.array([0, 10, 20, 30, 40, 45, 50, 55, 60, 70, 80, 90, 100]) + +plt.scatter(x, y, c=colors, cmap='viridis') + +plt.show() +``` + +When executed, this will show the following Scatter ColorMap: + + + +You can include the colormap in the drawing by including the `plt.colorbar()` statement: + +``` +import matplotlib.pyplot as plt +import numpy as np + +x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6]) +y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86]) +colors = np.array([0, 10, 20, 30, 40, 45, 50, 55, 60, 70, 80, 90, 100]) + +plt.scatter(x, y, c=colors, cmap='viridis') + +plt.colorbar() + +plt.show() +``` + +When executed, this will show the following Scatter ColorMap using `plt.colorbar()`: + + + + + + From c812fa29ac87b29857bb3c95ffbaeb766c5b5f8e Mon Sep 17 00:00:00 2001 From: Dishika Vaishkiyar <152963337+Dishika18@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:35:40 +0530 Subject: [PATCH 09/44] Update matplotlib-scatter-plot.md --- .../matplotlib-scatter-plot.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contrib/plotting-visualization/matplotlib-scatter-plot.md b/contrib/plotting-visualization/matplotlib-scatter-plot.md index a9fa500..535a3a3 100644 --- a/contrib/plotting-visualization/matplotlib-scatter-plot.md +++ b/contrib/plotting-visualization/matplotlib-scatter-plot.md @@ -2,7 +2,7 @@ * A scatter plot is a type of data visualization that uses dots to show values for two variables, with one variable on the x-axis and the other on the y-axis. It's useful for identifying relationships, trends, and correlations, as well as spotting clusters and outliers. * The dots on the plot shows how the variables are related. A scatter plot is made with the matplotlib library's `scatter() method`. ## Syntax -**Here's how to write code for the scatter() method:** +**Here's how to write code for the `scatter() method`:** ``` matplotlib.pyplot.scatter (x_axis_value, y_axis_value, s = None, c = None, vmin = None, vmax = None, marker = None, cmap = None, alpha = None, linewidths = None, edgecolors = None) @@ -30,7 +30,7 @@ plt.show() When executed, this will show the following Scatter plot: - +![Basic line Chart](images/simple_scatter.png) ## Compare Plots @@ -55,6 +55,8 @@ plt.show() When executed, this will show the following Compare Scatter plot: +![Compare Plots](images/scatter_compare.png) + ## Colors in Scatter plot You can set your own color for each scatter plot with the `color` or the `c` argument: @@ -75,10 +77,12 @@ plt.show() When executed, this will show the following Colors Scatter plot: +![Colors in Scatter plot](images/scatter_color.png) + ## Color Each Dot You can even set a specific color for each dot by using an array of colors as value for the `c` argument: -``**Note:** You cannot use the `color` argument for this, only the `c` argument.`` +``Note: You cannot use the `color` argument for this, only the `c` argument.`` ``` import matplotlib.pyplot as plt @@ -95,6 +99,8 @@ plt.show() When executed, this will show the following Color Each Dot: +![Color Each Dot](images/scatter_coloreachdot.png) + ## ColorMap The Matplotlib module has a number of available colormaps. @@ -102,8 +108,7 @@ A colormap is like a list of colors, where each color has a value that ranges fr Here is an example of a colormap: - - +![ColorMap](images/img_colorbar.png) This colormap is called 'viridis' and as you can see it ranges from 0, which is a purple color, up to 100, which is a yellow color. @@ -127,7 +132,7 @@ plt.show() When executed, this will show the following Scatter ColorMap: - +![Scatter ColorMap](images/scatter_colormap1.png) You can include the colormap in the drawing by including the `plt.colorbar()` statement: @@ -148,7 +153,7 @@ plt.show() When executed, this will show the following Scatter ColorMap using `plt.colorbar()`: - +![Scatter ColorMap1](images/scatter_colormap2.png) From 93eae0c0c908f7563c096c4563e9ac37a96a9b5e Mon Sep 17 00:00:00 2001 From: Dishika Vaishkiyar <152963337+Dishika18@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:38:23 +0530 Subject: [PATCH 10/44] Update index.md --- contrib/plotting-visualization/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/plotting-visualization/index.md b/contrib/plotting-visualization/index.md index 3192f03..7e43d9b 100644 --- a/contrib/plotting-visualization/index.md +++ b/contrib/plotting-visualization/index.md @@ -5,5 +5,6 @@ - [Bar Plots in Matplotlib](matplotlib-bar-plots.md) - [Pie Charts in Matplotlib](matplotlib-pie-charts.md) - [Line Charts in Matplotlib](matplotlib-line-plots.md) +- [Scatter Plots in Matplotlib](matplotlib-scatter-plot.md) - [Introduction to Seaborn and Installation](seaborn-intro.md) - [Getting started with Seaborn](seaborn-basics.md) From ff13658e4ab3cf70a592b27a8a9562847f12f624 Mon Sep 17 00:00:00 2001 From: SAM <8dmasters@gmail.com> Date: Tue, 4 Jun 2024 01:07:24 +0530 Subject: [PATCH 11/44] Create match_case_statement.md --- contrib/advanced-python/match_case_statement.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 contrib/advanced-python/match_case_statement.md diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md new file mode 100644 index 0000000..e69de29 From 3ac978f6a97fa53d1a111d3fb0132984a13ce6de Mon Sep 17 00:00:00 2001 From: SAM <8dmasters@gmail.com> Date: Tue, 4 Jun 2024 01:07:36 +0530 Subject: [PATCH 12/44] Update index.md Added match_case_statement index --- contrib/advanced-python/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index b31544e..c262aaa 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,3 +10,4 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) +- [Match Case Statement](match_case_statement.md) \ No newline at end of file From 5678bc9cf3bfb93c1d938d56b06c11a36e3f94b1 Mon Sep 17 00:00:00 2001 From: Kosuri Indu Date: Tue, 4 Jun 2024 08:19:32 +0530 Subject: [PATCH 13/44] Added Hashing through linear probing --- .../ds-algorithms/hashing-linear-probing.md | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 contrib/ds-algorithms/hashing-linear-probing.md diff --git a/contrib/ds-algorithms/hashing-linear-probing.md b/contrib/ds-algorithms/hashing-linear-probing.md new file mode 100644 index 0000000..0d27db4 --- /dev/null +++ b/contrib/ds-algorithms/hashing-linear-probing.md @@ -0,0 +1,139 @@ +# Hashing with Linear Probing + +In Data Structures and Algorithms, hashing is used to map data of arbitrary size to fixed-size values. A common approach to handle collisions in hashing is **linear probing**. In linear probing, if a collision occurs (i.e., the hash value points to an already occupied slot), we linearly probe through the table to find the next available slot. This method ensures that every element can be inserted or found in the hash table. + +## Points to be Remembered + +- **Hash Function**: A function that converts an input (or 'key') into an index in a hash table. +- **Collision**: When two keys hash to the same index. +- **Linear Probing**: A method to resolve collisions by checking the next slot (i.e., index + 1) until an empty slot is found. + +## Real Life Examples of Hashing with Linear Probing + +- **Student Record System**: Each student record is stored in a table where the student's ID number is hashed to an index. If two students have the same hash index, linear probing finds the next available slot. +- **Library System**: Books are indexed by their ISBN numbers. If two books hash to the same slot, linear probing helps find another spot for the book in the catalog. + +## Applications of Hashing + +Hashing is widely used in Computer Science: + +- **Database Indexing** +- **Caches** (like CPU caches, web caches) +- **Associative Arrays** (or dictionaries in Python) +- **Sets** (unordered collections of unique elements) + +Understanding these applications is essential for Software Development. + +## Operations in Hash Table with Linear Probing + +Key operations include: + +- **INSERT**: Insert a new element into the hash table. +- **SEARCH**: Find the position of an element in the hash table. +- **DELETE**: Remove an element from the hash table. + +## Implementing Hash Table with Linear Probing in Python + +```python +class HashTable: + def __init__(self, size): + self.size = size + self.table = [None] * size + + def hash_function(self, key): + return key % self.size + + def insert(self, key, value): + hash_index = self.hash_function(key) + + if self.table[hash_index] is None: + self.table[hash_index] = (key, value) + else: + while self.table[hash_index] is not None: + hash_index = (hash_index + 1) % self.size + self.table[hash_index] = (key, value) + + def search(self, key): + hash_index = self.hash_function(key) + + while self.table[hash_index] is not None: + if self.table[hash_index][0] == key: + return self.table[hash_index][1] + hash_index = (hash_index + 1) % self.size + + return None + + def delete(self, key): + hash_index = self.hash_function(key) + + while self.table[hash_index] is not None: + if self.table[hash_index][0] == key: + self.table[hash_index] = None + return True + hash_index = (hash_index + 1) % self.size + + return False + + def display(self): + for index, item in enumerate(self.table): + print(f"Index {index}: {item}") + +# Example usage +hash_table = HashTable(10) + +hash_table.insert(1, 'A') +hash_table.insert(11, 'B') +hash_table.insert(21, 'C') + +print("Hash Table after Insert operations:") +hash_table.display() + +print("Search operation for key 11:", hash_table.search(11)) + +hash_table.delete(11) + +print("Hash Table after Delete operation:") +hash_table.display() +``` + +## Output + +```markdown +Hash Table after Insert operations: +Index 0: None +Index 1: (1, 'A') +Index 2: None +Index 3: None +Index 4: None +Index 5: None +Index 6: None +Index 7: None +Index 8: None +Index 9: None +Index 10: None +Index 11: (11, 'B') +Index 12: (21, 'C') + +Search operation for key 11: B + +Hash Table after Delete operation: +Index 0: None +Index 1: (1, 'A') +Index 2: None +Index 3: None +Index 4: None +Index 5: None +Index 6: None +Index 7: None +Index 8: None +Index 9: None +Index 10: None +Index 11: None +Index 12: (21, 'C') +``` + +## Complexity Analysis + +- **Insertion**: Average case O(1), Worst case O(n) when many collisions occur. +- **Search**: Average case O(1), Worst case O(n) when many collisions occur. +- **Deletion**: Average case O(1), Worst case O(n) when many collisions occur. \ No newline at end of file From 96752204ead3ad9412e2da4fdd01abbbca860349 Mon Sep 17 00:00:00 2001 From: Kosuri Indu Date: Tue, 4 Jun 2024 08:28:18 +0530 Subject: [PATCH 14/44] Added Hashing through Chaining --- contrib/ds-algorithms/hashing-chaining.md | 153 ++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 contrib/ds-algorithms/hashing-chaining.md diff --git a/contrib/ds-algorithms/hashing-chaining.md b/contrib/ds-algorithms/hashing-chaining.md new file mode 100644 index 0000000..34086b5 --- /dev/null +++ b/contrib/ds-algorithms/hashing-chaining.md @@ -0,0 +1,153 @@ +# Hashing with Chaining + +In Data Structures and Algorithms, hashing is used to map data of arbitrary size to fixed-size values. A common approach to handle collisions in hashing is **chaining**. In chaining, each slot of the hash table contains a linked list, and all elements that hash to the same slot are stored in that list. + +## Points to be Remembered + +- **Hash Function**: A function that converts an input (or 'key') into an index in a hash table. +- **Collision**: When two keys hash to the same index. +- **Chaining**: A method to resolve collisions by maintaining a linked list for each hash table slot. + +## Real Life Examples of Hashing with Chaining + +- **Phone Directory**: Contacts are stored in a hash table where the contact's name is hashed to an index. If multiple names hash to the same index, they are stored in a linked list at that index. +- **Library Catalog**: Books are indexed by their titles. If multiple books have titles that hash to the same index, they are stored in a linked list at that index. + +## Applications of Hashing + +Hashing is widely used in Computer Science: + +- **Database Indexing** +- **Caches** (like CPU caches, web caches) +- **Associative Arrays** (or dictionaries in Python) +- **Sets** (unordered collections of unique elements) + +Understanding these applications is essential for Software Development. + +## Operations in Hash Table with Chaining + +Key operations include: + +- **INSERT**: Insert a new element into the hash table. +- **SEARCH**: Find the position of an element in the hash table. +- **DELETE**: Remove an element from the hash table. + +## Implementing Hash Table with Chaining in Python + +```python +class Node: + def __init__(self, key, value): + self.key = key + self.value = value + self.next = None + +class HashTable: + def __init__(self, size): + self.size = size + self.table = [None] * size + + def hash_function(self, key): + return key % self.size + + def insert(self, key, value): + hash_index = self.hash_function(key) + new_node = Node(key, value) + + if self.table[hash_index] is None: + self.table[hash_index] = new_node + else: + current = self.table[hash_index] + while current.next is not None: + current = current.next + current.next = new_node + + def search(self, key): + hash_index = self.hash_function(key) + current = self.table[hash_index] + + while current is not None: + if current.key == key: + return current.value + current = current.next + + return None + + def delete(self, key): + hash_index = self.hash_function(key) + current = self.table[hash_index] + prev = None + + while current is not None: + if current.key == key: + if prev is None: + self.table[hash_index] = current.next + else: + prev.next = current.next + return True + prev = current + current = current.next + + return False + + def display(self): + for index, item in enumerate(self.table): + print(f"Index {index}:", end=" ") + current = item + while current is not None: + print(f"({current.key}, {current.value})", end=" -> ") + current = current.next + print("None") + +# Example usage +hash_table = HashTable(10) + +hash_table.insert(1, 'A') +hash_table.insert(11, 'B') +hash_table.insert(21, 'C') + +print("Hash Table after Insert operations:") +hash_table.display() + +print("Search operation for key 11:", hash_table.search(11)) + +hash_table.delete(11) + +print("Hash Table after Delete operation:") +hash_table.display() +``` + +## Output + +```markdown +Hash Table after Insert operations: +Index 0: None +Index 1: (1, 'A') -> (11, 'B') -> (21, 'C') -> None +Index 2: None +Index 3: None +Index 4: None +Index 5: None +Index 6: None +Index 7: None +Index 8: None +Index 9: None + +Search operation for key 11: B + +Hash Table after Delete operation: +Index 0: None +Index 1: (1, 'A') -> (21, 'C') -> None +Index 2: None +Index 3: None +Index 4: None +Index 5: None +Index 6: None +Index 7: None +Index 8: None +Index 9: None +``` + +## Complexity Analysis + +- **Insertion**: Average case O(1), Worst case O(n) when many elements hash to the same slot. +- **Search**: Average case O(1), Worst case O(n) when many elements hash to the same slot. +- **Deletion**: Average case O(1), Worst case O(n) when many elements hash to the same slot. \ No newline at end of file From 0699376c4b30dbe5c767c58f184a58e2ecdb6674 Mon Sep 17 00:00:00 2001 From: Mangalya Phaye <141892171+mdphaye@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:07:11 +0530 Subject: [PATCH 15/44] Rename Queues.md to queues.md --- contrib/ds-algorithms/{Queues.md => queues.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contrib/ds-algorithms/{Queues.md => queues.md} (100%) diff --git a/contrib/ds-algorithms/Queues.md b/contrib/ds-algorithms/queues.md similarity index 100% rename from contrib/ds-algorithms/Queues.md rename to contrib/ds-algorithms/queues.md From a2f8b49b1e77b1d4d9fcc376509e401955b468b7 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:47:49 +0530 Subject: [PATCH 16/44] Update match_case_statement.md Added introduction and syntax --- .../advanced-python/match_case_statement.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index e69de29..d4ad861 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -0,0 +1,22 @@ +# Match Case Statements +## Introduction +Match and case statements are introduced in Python 3.10 for structural pattern matching of patterns with associated actions. It offers more readible and +cleaniness to the code as opposed to the traditional `if-else` statements. They also have destructuring, pattern matching and checks for specific properties in +addition to the traditional `switch-case` statements in other languages, which makes them more versatile. + +## Syntax +``` +match : + case : + + case : + + case _: + +``` +A match statement takes a statement which compares it to the various cases and their patterns. If any of the pattern is matched successively, the task is performed accordingly. If an exact match is not confirmed, the last case, a wildcard `_`, if provided, will be used as the matching case. + +## Pattern Matching +As discussed earlier, match case statements use pattern matching where the patterns consist of sequences, mappings, primitive data types as well as class instances. The structural pattern matching uses declarative approach and it nexplicitly states the conditions for the patterns to match with the data. + +### Patterns with a Literal From f0c59b288c5e5877eb1f6d69a7f995cbdf08f2f9 Mon Sep 17 00:00:00 2001 From: mankalavaishnavi Date: Tue, 4 Jun 2024 15:58:52 +0530 Subject: [PATCH 17/44] Filter definition , syntax is done --- contrib/advanced-python/filter-function.md | 86 ++++++++++++++++++++++ contrib/advanced-python/index.md | 2 + contrib/advanced-python/reduce-function.md | 72 ++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 contrib/advanced-python/filter-function.md create mode 100644 contrib/advanced-python/reduce-function.md diff --git a/contrib/advanced-python/filter-function.md b/contrib/advanced-python/filter-function.md new file mode 100644 index 0000000..cbf9463 --- /dev/null +++ b/contrib/advanced-python/filter-function.md @@ -0,0 +1,86 @@ +# Filter Function + +## Definition +The filter function is a built-in Python function used for constructing an iterator from elements of an iterable for which a function returns true. + +**Syntax**: + ```python +filter(function, iterable) +``` +**Parameters**:
+*function*: A function that tests if each element of an iterable returns True or False.
+*iterable*: An iterable like sets, lists, tuples, etc., whose elements are to be filtered.
+*Returns* : An iterator that is already filtered. + +## Basic Usage +**Example 1: Filtering a List of Numbers**: +```python +# Define a function that returns True for even numbers +def is_even(n): + return n % 2 == 0 + +numbers = [1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +even_numbers = filter(is_even, numbers) + +# Convert the filter object to a list +print(list(even_numbers)) # Output: [2, 4, 6, 8, 10] +``` + +**Example 2: Filtering with a Lambda Function**: +```python +numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +odd_numbers = filter(lambda x: x % 2 != 0, numbers) + +print(list(odd_numbers)) # Output: [1, 3, 5, 7, 9] +``` + +**Example 3: Filtering Strings**: +```python +words = ["apple", "banana", "cherry", "date", "elderberry", "fig", "grape" , "python"] +long_words = filter(lambda word: len(word) > 5, words) + +print(list(long_words)) # Output: ['banana', 'cherry', 'elderberry', 'python'] +``` + +## Advanced Usage +**Example 4: Filtering Objects with Attributes**: +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + +people = [ + Person("Alice", 30), + Person("Bob", 15), + Person("Charlie", 25), + Person("David", 35) +] + +adults = filter(lambda person: person.age >= 18, people) +adult_names = map(lambda person: person.name, adults) + +print(list(adult_names)) # Output: ['Alice', 'Charlie', 'David'] +``` + +**Example 5: Using None as the Function**: +```python +numbers = [0, 1, 2, 3, 0, 4, 0, 5] +non_zero_numbers = filter(None, numbers) + +print(list(non_zero_numbers)) # Output: [1, 2, 3, 4, 5] +``` +**NOTE**: When None is passed as the function, filter removes all items that are false. + +## Time Complexity: +- The time complexity of filter() depends on two factors: + 1. The time complexity of the filtering function (the one you provide as an argument). + 2. The size of the iterable being filtered. +- If the filtering function has a constant time complexity (e.g., O(1)), the overall time complexity of filter() is linear (O(n)), where ‘n’ is the number of elements in the iterable. + +## Space Complexity: +- The space complexity of filter() is also influenced by the filtering function and the size of the iterable. +- Since filter() returns an iterator, it doesn’t create a new list in memory. Instead, it generates filtered elements on-the-fly as you iterate over it. Therefore, the space complexity is O(1). + +## Conclusion: +Python’s filter() allows you to perform filtering operations on iterables. This kind of operation consists of applying a Boolean function to the items in an iterable and keeping only those values for which the function returns a true result. In general, you can use filter() to process existing iterables and produce new iterables containing the values that you currently need.Both versions of Python support filter(), but Python 3’s approach is more memory-efficient due to the use of iterators. \ No newline at end of file diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index b31544e..5d6a0b6 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,3 +10,5 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) +- [Filter](filter-function.md) +- [Reduce](reduce-function.md) \ No newline at end of file diff --git a/contrib/advanced-python/reduce-function.md b/contrib/advanced-python/reduce-function.md new file mode 100644 index 0000000..5c0c81b --- /dev/null +++ b/contrib/advanced-python/reduce-function.md @@ -0,0 +1,72 @@ +# Reduce Function + +## Definition: +The reduce() function is part of the functools module and is used to apply a binary function (a function that takes two arguments) cumulatively to the items of an iterable (e.g., a list, tuple, or string). It reduces the iterable to a single value by successively combining elements. + +**Syntax**: +```python +from functools import reduce +reduce(function, iterable, initial=None) +``` +**Parameters**:
+*function* : The binary function to apply. It takes two arguments and returns a single value.
+*iterable* : The sequence of elements to process.
+*initial (optional)*: An initial value. If provided, the function is applied to the initial value and the first element of the iterable. Otherwise, the first two elements are used as the initial values. + +## Working: +- Intially , first two elements of iterable are picked and the result is obtained. +- Next step is to apply the same function to the previously attained result and the number just succeeding the second element and the result is again stored. +- This process continues till no more elements are left in the container. +- The final returned result is returned and printed on console. + +## Examples: + +**Example 1:** +```python +numbers = [1, 2, 3, 4, 10] +total = reduce(lambda x, y: x + y, numbers) +print(total) # Output: 20 +``` +**Example 2:** +```python +numbers = [11, 7, 8, 20, 1] +max_value = reduce(lambda x, y: x if x > y else y, numbers) +print(max_value) # Output: 20 +``` +**Example 3:** +```python +# Importing reduce function from functools +from functools import reduce + +# Creating a list +my_list = [10, 20, 30, 40, 50] + +# Calculating the product of the numbers in my_list +# using reduce and lambda functions together +product = reduce(lambda x, y: x * y, my_list) + +# Printing output +print(f"Product = {product}") # Output : Product = 12000000 +``` + +## Difference Between reduce() and accumulate(): +- **Behavior:** + - reduce() stores intermediate results and only returns the final summation value. + - accumulate() returns an iterator containing all intermediate results. The last value in the iterator is the summation value of the list. + +- **Use Cases:** + - Use reduce() when you need a single result (e.g., total sum, product) from the iterable. + - Use accumulate() when you want to access intermediate results during the reduction process. + +- **Initial Value:** + - reduce() allows an optional initial value. + - accumulate() also accepts an optional initial value since Python 3.8. + +- **Order of Arguments:** + - reduce() takes the function first, followed by the iterable. + - accumulate() takes the iterable first, followed by the function. + +## Conclusion: +Python's Reduce function enables us to apply reduction operations to iterables using lambda and callable functions. A +function called reduce() reduces the elements of an iterable to a single cumulative value. The reduce function in +Python solves various straightforward issues, including adding and multiplying iterables of numbers. \ No newline at end of file From 55717a47dee55e6d62aa161e52840b7075dc0689 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:48:03 +0530 Subject: [PATCH 18/44] Update match_case_statement.md Added patterns with a literal section --- .../advanced-python/match_case_statement.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index d4ad861..691d23b 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -20,3 +20,60 @@ A match statement takes a statement which compares it to the various cases and t As discussed earlier, match case statements use pattern matching where the patterns consist of sequences, mappings, primitive data types as well as class instances. The structural pattern matching uses declarative approach and it nexplicitly states the conditions for the patterns to match with the data. ### Patterns with a Literal +#### Generic Case +`sample text` is passed as a literal in the `match` block. There are two cases and a wildcard case mentioned. +```python +match 'sample text': + case 'sample text': + print('sample text') + case 'sample': + print('sample') + case _: + print('None found') +``` +The `sample text` case is satisfied as it matches with the literal `sample text` described in the `match` block. + +O/P: +``` +sample text +``` + +#### Using OR +Taking another example, `|` can be used as OR to include multiple patterns in a single case statement where the multiple patterns all lead to a similar task. + +The below code snippets can be used interchangebly and generate the similar output. The latter is more consive and readible. +```python +match 'e': + case 'a': + print('vowel') + case 'e': + print('vowel') + case 'i': + print('vowel') + case 'o': + print('vowel') + case 'u': + print('vowel') + case _: + print('consonant') +``` +```python +match 'e': + case 'a' | 'e' | 'i' | 'o' | 'u': + print('vowel') + case _: + print('consonant') +``` +O/P: +``` +vowel +``` + +#### Without wildcard +When in a `match` block, there is no wildcard case present there are be two cases of match being present or not. If the match doesn't exist, the behaviour is a no-op. +```python +match 'c': + case 'a' | 'e' | 'i' | 'o' | 'u': + print('vowel') +``` +The output will be blank as a no-op occurs. From 43455d1b56fdbd35a291999f94303373c1c5ee59 Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma Date: Tue, 4 Jun 2024 17:08:51 +0530 Subject: [PATCH 19/44] Added two-pointer-technique.md and updated index.md --- contrib/ds-algorithms/index.md | 1 + .../ds-algorithms/two-pointer-technique.md | 134 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 contrib/ds-algorithms/two-pointer-technique.md diff --git a/contrib/ds-algorithms/index.md b/contrib/ds-algorithms/index.md index 1d7293b..52293d9 100644 --- a/contrib/ds-algorithms/index.md +++ b/contrib/ds-algorithms/index.md @@ -13,3 +13,4 @@ - [Stacks in Python](stacks.md) - [Sliding Window Technique](sliding-window.md) - [Trie](trie.md) +- [Two Pointer Technique](two-pointer-technique.md) diff --git a/contrib/ds-algorithms/two-pointer-technique.md b/contrib/ds-algorithms/two-pointer-technique.md new file mode 100644 index 0000000..cec588f --- /dev/null +++ b/contrib/ds-algorithms/two-pointer-technique.md @@ -0,0 +1,134 @@ +# Two-Pointer Technique + +--- + +- The two-pointer technique is a popular algorithmic strategy used to solve various problems efficiently. This technique involves using two pointers (or indices) to traverse through data structures such as arrays or linked lists. +- The pointers can move in different directions, allowing for efficient processing of elements to achieve the desired results. + +## Common Use Cases + +1. **Finding pairs in a sorted array that sum to a target**: One pointer starts at the beginning and the other at the end. +2. **Reversing a linked list**: One pointer starts at the head, and the other at the next node, progressing through the list. +3. **Removing duplicates from a sorted array**: One pointer keeps track of the unique elements, and the other traverses the array. +4. **Merging two sorted arrays**: Two pointers are used to iterate through the arrays and merge them. + +## Example 1: Finding Pairs with a Given Sum + +### Problem Statement + +Given a sorted array of integers and a target sum, find all pairs in the array that sum up to the target. + +### Approach + +1. Initialize two pointers: one at the beginning (`left`) and one at the end (`right`) of the array. +2. Calculate the sum of the elements at the `left` and `right` pointers. +3. If the sum is equal to the target, record the pair and move both pointers inward. +4. If the sum is less than the target, move the `left` pointer to the right to increase the sum. +5. If the sum is greater than the target, move the `right` pointer to the left to decrease the sum. +6. Repeat the process until the `left` pointer is not less than the `right` pointer. + +### Example Code + +```python +def find_pairs_with_sum(arr, target): + left = 0 + right = len(arr) - 1 + pairs = [] + + while left < right: + current_sum = arr[left] + arr[right] + + if current_sum == target: + pairs.append((arr[left], arr[right])) + left += 1 + right -= 1 + elif current_sum < target: + left += 1 + else: + right -= 1 + + return pairs + +# Example usage +arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] +target = 10 +result = find_pairs_with_sum(arr, target) +print("Pairs with sum", target, "are:", result) + ``` + +## Example 2: Removing Duplicates from a Sorted Array + +### Problem Statement +Given a sorted array, remove the duplicates in place such that each element appears only once and return the new length of the array. + +### Approach +1. If the array is empty, return 0. +2. Initialize a slow pointer at the beginning of the array. +3. Use a fast pointer to traverse through the array. +4. Whenever the element at the fast pointer is different from the element at the slow pointer, increment the slow pointer and update the element at the slow pointer with the element at the fast pointer. +5. Continue this process until the fast pointer reaches the end of the array. +6. The slow pointer will indicate the position of the last unique element. + +### Example Code + +```python +def remove_duplicates(arr): + if not arr: + return 0 + + slow = 0 + + for fast in range(1, len(arr)): + if arr[fast] != arr[slow]: + slow += 1 + arr[slow] = arr[fast] + + return slow + 1 + +# Example usage +arr = [1, 1, 2, 2, 3, 4, 4, 5] +new_length = remove_duplicates(arr) +print("Array after removing duplicates:", arr[:new_length]) +print("New length of array:", new_length) +``` +# Advantages of the Two-Pointer Technique + +Here are some key benefits of using the two-pointer technique: + +## 1. **Improved Time Complexity** + +It often reduces the time complexity from O(n^2) to O(n), making it significantly faster for many problems. + +### Example +- **Finding pairs with a given sum**: Efficiently finds pairs in O(n) time. + +## 2. **Simplicity** + +The implementation is straightforward, using basic operations like incrementing or decrementing pointers. + +### Example +- **Removing duplicates from a sorted array**: Easy to implement and understand. + +## 3. **In-Place Solutions** + +Many problems can be solved in place, requiring no extra space beyond the input data. + +### Example +- **Reversing a linked list**: Adjusts pointers within the existing nodes. + +## 4. **Versatility** + +Applicable to a wide range of problems, from arrays and strings to linked lists. + +### Example +- **Merging two sorted arrays**: Efficiently merges using two pointers. + +## 5. **Efficiency** + +Minimizes redundant operations and enhances performance, especially with large data sets. + +### Example +- **Partitioning problems**: Efficiently partitions elements with minimal operations. + + +These advantages make the two-pointer technique a valuable approach for many algorithmic challenges. From a4c93b95adbca63d75c05d5754ea81e540cba34c Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma <103316955+Yogeshkarma@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:13:27 +0530 Subject: [PATCH 20/44] Update two-pointer-technique.md Removed the footer line --- contrib/ds-algorithms/two-pointer-technique.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/ds-algorithms/two-pointer-technique.md b/contrib/ds-algorithms/two-pointer-technique.md index cec588f..6b8720a 100644 --- a/contrib/ds-algorithms/two-pointer-technique.md +++ b/contrib/ds-algorithms/two-pointer-technique.md @@ -130,5 +130,3 @@ Minimizes redundant operations and enhances performance, especially with large d ### Example - **Partitioning problems**: Efficiently partitions elements with minimal operations. - -These advantages make the two-pointer technique a valuable approach for many algorithmic challenges. From 5f57b24ac03006216a366219e09ea0566e231ab8 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:44:13 +0530 Subject: [PATCH 21/44] Update match_case_statement.md Added two more sections. Patterns with a literal and a variable, and patterns with classes. --- .../advanced-python/match_case_statement.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index 691d23b..57f6bfb 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -77,3 +77,86 @@ match 'c': print('vowel') ``` The output will be blank as a no-op occurs. + +### Patterns with a Literal and a Variable +Pattern matching can be done by unpacking the assignments and also bind variables with it. +```python +def get_names(names: str) -> None: + match names: + case ('Bob', y): + print(f'Hello {y}') + case (x, 'John'): + print(f'Hello {x}') + case (x, y): + print(f'Hello {x} and {y}') + case _: + print('Invalid') +``` +Here, the `names` is a tuple that contains two names. The `match` block unpacks the tuple and binds `x` and `y` based on the patterns. A wildcard case prints `Invalid` if the condition is not satisfied. + +O/P: + +In this example, the above code snippet with the parameter `names` as below and the respective output. +``` +>>> get_names(('Bob', 'Max')) +Hello Max + +>>> get_names(('Rob', 'John')) +Hello Rob + +>>> get_names(('Rob', 'Max')) +Hello Rob and Max + +>>> get_names(('Rob', 'Max', 'Bob')) +Invalid +``` + +### Patterns with Classes +Class structures can be used in `match` block for pattern matching. The class members can also be binded with a variable to perform certain operations. For the class structure: +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age +``` +The match case example illustrates the generic working as well as the binding of variables with the class members. +```python +def get_class(cls: Person) -> None: + match cls: + case Person(name='Bob', age=18): + print('Hello Bob with age 18') + case Person(name='Max', age=y): + print(f'Age is {y}') + case Person(name=x, age=18): + print(f'Name is {x}') + case Person(name=x, age=y): + print(f'Name and age is {x} and {y}') + case _: + print('Invalid') +``` +O/P: +``` +>>> get_class(Person('Bob', 18)) +Hello Bob with age 18 + +>>> get_class(Person('Max', 21)) +Age is 21 + +>>> get_class(Person('Rob', 18)) +Name is Rob + +>>> get_class(Person('Rob', 21)) +Name and age is Rob and 21 +``` +Now, if a new class is introduced in the above code snippet like below. +```python +class Pet: + def __init__(self, name, animal): + self.name = name + self.animal = animal +``` +The patterns will not match the cases and will trigger the wildcard case for the original code snippet above with `get_class` function. +``` +>>> get_class(Pet('Tommy', 'Dog')) +Invalid +``` From e5c88286d5b72b0e0e51e4f1c889544b41c91ef9 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:02:50 +0530 Subject: [PATCH 22/44] Update match_case_statement.md Added remaining topic headings --- contrib/advanced-python/match_case_statement.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index 57f6bfb..6941d98 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -160,3 +160,14 @@ The patterns will not match the cases and will trigger the wildcard case for the >>> get_class(Pet('Tommy', 'Dog')) Invalid ``` + +### Nested Patterns + +### Complex Patterns +#### Wildcard + +#### Guard + +## Summary + +## Further Reading From ffcc45ad986b70a5b1b39cc515bb7e7230db3c4d Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:28:39 +0530 Subject: [PATCH 23/44] Update match_case_statement.md Added nested patterns --- .../advanced-python/match_case_statement.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index 6941d98..22eeef9 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -162,6 +162,33 @@ Invalid ``` ### Nested Patterns +The patterns can be nested via various means. It can include the mix of the patterns mentioned earlier or can be symmetrical across. A basic of the nested pattern of a list with Patterns with a Literal and Variable is taken. Classes and Iterables can laso be included. +```python +def get_points(points: list) -> None: + match points: + case []: + print('Empty') + case [x]: + print(f'One point {x}') + case [x, y]: + print(f'Two points {x} and {y}') + case _: + print('More than two points') +``` +O/P: +``` +>>> get_points([]) +Empty + +>>> get_points([1]) +One point 1 + +>>> get_points([1, 2]) +Two points 1 and 2 + +>>> get_points([1, 2, 3]) +More than two points +``` ### Complex Patterns #### Wildcard From d345d0339f7a2bd5eea818f5a42bc4e0c5d9f5f7 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:14:09 +0530 Subject: [PATCH 24/44] Update match_case_statement.md Added the content to remaining sections --- .../advanced-python/match_case_statement.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index 22eeef9..cd7bc64 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -191,10 +191,60 @@ More than two points ``` ### Complex Patterns +Complex patterns are also supported in the pattern matching sequence. The complex does not mean complex numbers but rather the structure which makes the readibility to seem complex. + #### Wildcard +The wildcard used till now are in the form of `case _` where the wildcard case is used if no match is found. Furthermore, the wildcard `_` can also be used as a placeholder in complex patterns. + +```python +def wildcard(value: tuple) -> None: + match value: + case ('Bob', age, 'Mechanic'): + print(f'Bob is mechanic of age {age}') + case ('Bob', age, _): + print(f'Bob is not a mechanic of age {age}') +``` +O/P: + +The value in the above snippet is a tuple with `(Name, Age, Job)`. If the job is Mechanic and the name is Bob, the first case is triggered. But if the job is different and not a mechanic, then the other case is triggered with the wildcard. +``` +>>> wildcard(('Bob', 18, 'Mechanic')) +Bob is mechanic of age 18 + +>>> wildcard(('Bob', 21, 'Engineer')) +Bob is not a mechanic of age 21 +``` #### Guard +A `guard` is when an `if` is added to a pattern. The evaluation depends on the truth value of the guard. + +`nums` is the tuple which contains two integers. A guard is the first case where it checks whether the first number is greater or equal to the second number in the tuple. If it is false, then it moves to the second case, where it concludes that the first number is smaller than the second number. +```python +def guard(nums: tuple) -> None: + match nums: + case (x, y) if x >= y: + print(f'{x} is greater or equal than {y}') + case (x, y): + print(f'{x} is smaller than {y}') + case _: + print('Invalid') +``` +O/P: +``` +>>> guard((1, 2)) +1 is smaller than 2 + +>>> guard((2, 1)) +2 is greater or equal than 1 + +>>> guard((1, 1)) +1 is greater or equal than 1 +``` ## Summary +The match case statements provide an elegant and readible format to perform operations on pattern matching as compared to `if-else` statements. They are also more versatile as they provide additional functionalities on the pattern matching operations like unpacking, class matching, iterables and iterators. It can also use positional arguments for checking the patterns. They provide a powerful and concise way to handle multiple conditions and perform pattern matching ## Further Reading +- [Python Match Case Statement - GeeksForGeeks](https://www.geeksforgeeks.org/python-match-case-statement/) +- [PEP 634 – Structural Pattern Matching: Specification](https://peps.python.org/pep-0634/) +- [PEP 636 – Structural Pattern Matching: Tutorial](https://peps.python.org/pep-0636/) From fe4ce4a34df72c800d12c624385069b27b689d11 Mon Sep 17 00:00:00 2001 From: SAM <60264918+SAM-DEV007@users.noreply.github.com> Date: Tue, 4 Jun 2024 19:16:29 +0530 Subject: [PATCH 25/44] Update match_case_statement.md Added further reading topic summary --- contrib/advanced-python/match_case_statement.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match_case_statement.md index cd7bc64..b617f7a 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match_case_statement.md @@ -245,6 +245,8 @@ O/P: The match case statements provide an elegant and readible format to perform operations on pattern matching as compared to `if-else` statements. They are also more versatile as they provide additional functionalities on the pattern matching operations like unpacking, class matching, iterables and iterators. It can also use positional arguments for checking the patterns. They provide a powerful and concise way to handle multiple conditions and perform pattern matching ## Further Reading +This article provides a brief introduction to the match case statements and the overview on the pattern matching operations. To know more, the below articles can be used for in-depth understanding of the topic. + - [Python Match Case Statement - GeeksForGeeks](https://www.geeksforgeeks.org/python-match-case-statement/) - [PEP 634 – Structural Pattern Matching: Specification](https://peps.python.org/pep-0634/) - [PEP 636 – Structural Pattern Matching: Tutorial](https://peps.python.org/pep-0636/) From b3c31ea9499b068a41c10d2a444edb3c439eae5c Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma Date: Tue, 4 Jun 2024 19:57:15 +0530 Subject: [PATCH 26/44] added Universal-Functions.md and updated the index.md --- contrib/numpy/Universal-Functions.md | 130 +++++++++++++++++++++++++++ contrib/numpy/index.md | 1 + 2 files changed, 131 insertions(+) create mode 100644 contrib/numpy/Universal-Functions.md diff --git a/contrib/numpy/Universal-Functions.md b/contrib/numpy/Universal-Functions.md new file mode 100644 index 0000000..a06b0d1 --- /dev/null +++ b/contrib/numpy/Universal-Functions.md @@ -0,0 +1,130 @@ +# Universal functions (ufunc) + +--- + +A `ufunc`, short for "`universal function`," is a fundamental concept in NumPy, a powerful library for numerical computing in Python. Universal functions are highly optimized, element-wise functions designed to perform operations on data stored in NumPy arrays. + + + +## Uses of Ufuncs in NumPy + +Universal functions (ufuncs) in NumPy provide a wide range of functionalities for efficient and powerful numerical computations. Below is a detailed explanation of their uses: + +### 1. **Element-wise Operations** +Ufuncs perform operations on each element of the arrays independently. + +```python +import numpy as np + +A = np.array([1, 2, 3, 4]) +B = np.array([5, 6, 7, 8]) + +# Element-wise addition +np.add(A, B) # Output: array([ 6, 8, 10, 12]) +``` + +### 2. **Broadcasting** +Ufuncs support broadcasting, allowing operations on arrays with different shapes, making it possible to perform operations without explicitly reshaping arrays. + +```python +C = np.array([1, 2, 3]) +D = np.array([[1], [2], [3]]) + +# Broadcasting addition +np.add(C, D) # Output: array([[2, 3, 4], [3, 4, 5], [4, 5, 6]]) +``` + +### 3. **Vectorization** +Ufuncs are vectorized, meaning they are implemented in low-level C code, allowing for fast execution and avoiding the overhead of Python loops. + +```python +# Vectorized square root +np.sqrt(A) # Output: array([1., 1.41421356, 1.73205081, 2.]) +``` + +### 4. **Type Flexibility** +Ufuncs handle various data types and perform automatic type casting as needed. + +```python +E = np.array([1.0, 2.0, 3.0]) +F = np.array([4, 5, 6]) + +# Addition with type casting +np.add(E, F) # Output: array([5., 7., 9.]) +``` + +### 5. **Reduction Operations** +Ufuncs support reduction operations, such as summing all elements of an array or finding the product of all elements. + +```python +# Summing all elements +np.add.reduce(A) # Output: 10 + +# Product of all elements +np.multiply.reduce(A) # Output: 24 +``` + +### 6. **Accumulation Operations** +Ufuncs can perform accumulation operations, which keep a running tally of the computation. + +```python +# Cumulative sum +np.add.accumulate(A) # Output: array([ 1, 3, 6, 10]) +``` + +### 7. **Reduceat Operations** +Ufuncs can perform segmented reductions using the `reduceat` method, which applies the ufunc at specified intervals. + +```python +G = np.array([0, 1, 2, 3, 4, 5, 6, 7]) +indices = [0, 2, 5] +np.add.reduceat(G, indices) # Output: array([ 1, 9, 18]) +``` + +### 8. **Outer Product** +Ufuncs can compute the outer product of two arrays, producing a matrix where each element is the result of applying the ufunc to each pair of elements from the input arrays. + +```python +# Outer product +np.multiply.outer([1, 2, 3], [4, 5, 6]) +# Output: array([[ 4, 5, 6], +# [ 8, 10, 12], +# [12, 15, 18]]) +``` + +### 9. **Out Parameter** +Ufuncs can use the `out` parameter to store results in a pre-allocated array, saving memory and improving performance. + +```python +result = np.empty_like(A) +np.multiply(A, B, out=result) # Output: array([ 5, 12, 21, 32]) +``` + +# Create Your Own Ufunc + +You can create custom ufuncs for specific needs using np.frompyfunc or np.vectorize, allowing Python functions to behave like ufuncs. + +Here, we are using `frompyfunc()` which takes three argument: + +1. function - the name of the function. +2. inputs - the number of input (arrays). +3. outputs - the number of output arrays. + +```python +def my_add(x, y): + return x + y + +my_add_ufunc = np.frompyfunc(my_add, 2, 1) +my_add_ufunc(A, B) # Output: array([ 6, 8, 10, 12], dtype=object) +``` +# Some Common Ufunc are + +Here are some commonly used ufuncs in NumPy: + +- **Arithmetic**: `np.add`, `np.subtract`, `np.multiply`, `np.divide` +- **Trigonometric**: `np.sin`, `np.cos`, `np.tan` +- **Exponential and Logarithmic**: `np.exp`, `np.log`, `np.log10` +- **Comparison**: `np.maximum`, `np.minimum`, `np.greater`, `np.less` +- **Logical**: `np.logical_and`, `np.logical_or`, `np.logical_not` + +For more such Ufunc, address to [Universal functions (ufunc) — NumPy](https://numpy.org/doc/stable/reference/ufuncs.html) \ No newline at end of file diff --git a/contrib/numpy/index.md b/contrib/numpy/index.md index 50e8046..2b0c357 100644 --- a/contrib/numpy/index.md +++ b/contrib/numpy/index.md @@ -11,3 +11,4 @@ - [Sorting NumPy Arrays](sorting-array.md) - [NumPy Array Iteration](array-iteration.md) - [Concatenation of Arrays](concatenation-of-arrays.md) +- [Universal Functions (Ufunc)](Universal-Functions.md) From 0eefe006fadf5646c0be4c8283743ae6051ea5c0 Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma <103316955+Yogeshkarma@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:07:56 +0530 Subject: [PATCH 27/44] Delete contrib/numpy/Universal-Functions.md for creating two PR separately --- contrib/numpy/Universal-Functions.md | 130 --------------------------- 1 file changed, 130 deletions(-) delete mode 100644 contrib/numpy/Universal-Functions.md diff --git a/contrib/numpy/Universal-Functions.md b/contrib/numpy/Universal-Functions.md deleted file mode 100644 index a06b0d1..0000000 --- a/contrib/numpy/Universal-Functions.md +++ /dev/null @@ -1,130 +0,0 @@ -# Universal functions (ufunc) - ---- - -A `ufunc`, short for "`universal function`," is a fundamental concept in NumPy, a powerful library for numerical computing in Python. Universal functions are highly optimized, element-wise functions designed to perform operations on data stored in NumPy arrays. - - - -## Uses of Ufuncs in NumPy - -Universal functions (ufuncs) in NumPy provide a wide range of functionalities for efficient and powerful numerical computations. Below is a detailed explanation of their uses: - -### 1. **Element-wise Operations** -Ufuncs perform operations on each element of the arrays independently. - -```python -import numpy as np - -A = np.array([1, 2, 3, 4]) -B = np.array([5, 6, 7, 8]) - -# Element-wise addition -np.add(A, B) # Output: array([ 6, 8, 10, 12]) -``` - -### 2. **Broadcasting** -Ufuncs support broadcasting, allowing operations on arrays with different shapes, making it possible to perform operations without explicitly reshaping arrays. - -```python -C = np.array([1, 2, 3]) -D = np.array([[1], [2], [3]]) - -# Broadcasting addition -np.add(C, D) # Output: array([[2, 3, 4], [3, 4, 5], [4, 5, 6]]) -``` - -### 3. **Vectorization** -Ufuncs are vectorized, meaning they are implemented in low-level C code, allowing for fast execution and avoiding the overhead of Python loops. - -```python -# Vectorized square root -np.sqrt(A) # Output: array([1., 1.41421356, 1.73205081, 2.]) -``` - -### 4. **Type Flexibility** -Ufuncs handle various data types and perform automatic type casting as needed. - -```python -E = np.array([1.0, 2.0, 3.0]) -F = np.array([4, 5, 6]) - -# Addition with type casting -np.add(E, F) # Output: array([5., 7., 9.]) -``` - -### 5. **Reduction Operations** -Ufuncs support reduction operations, such as summing all elements of an array or finding the product of all elements. - -```python -# Summing all elements -np.add.reduce(A) # Output: 10 - -# Product of all elements -np.multiply.reduce(A) # Output: 24 -``` - -### 6. **Accumulation Operations** -Ufuncs can perform accumulation operations, which keep a running tally of the computation. - -```python -# Cumulative sum -np.add.accumulate(A) # Output: array([ 1, 3, 6, 10]) -``` - -### 7. **Reduceat Operations** -Ufuncs can perform segmented reductions using the `reduceat` method, which applies the ufunc at specified intervals. - -```python -G = np.array([0, 1, 2, 3, 4, 5, 6, 7]) -indices = [0, 2, 5] -np.add.reduceat(G, indices) # Output: array([ 1, 9, 18]) -``` - -### 8. **Outer Product** -Ufuncs can compute the outer product of two arrays, producing a matrix where each element is the result of applying the ufunc to each pair of elements from the input arrays. - -```python -# Outer product -np.multiply.outer([1, 2, 3], [4, 5, 6]) -# Output: array([[ 4, 5, 6], -# [ 8, 10, 12], -# [12, 15, 18]]) -``` - -### 9. **Out Parameter** -Ufuncs can use the `out` parameter to store results in a pre-allocated array, saving memory and improving performance. - -```python -result = np.empty_like(A) -np.multiply(A, B, out=result) # Output: array([ 5, 12, 21, 32]) -``` - -# Create Your Own Ufunc - -You can create custom ufuncs for specific needs using np.frompyfunc or np.vectorize, allowing Python functions to behave like ufuncs. - -Here, we are using `frompyfunc()` which takes three argument: - -1. function - the name of the function. -2. inputs - the number of input (arrays). -3. outputs - the number of output arrays. - -```python -def my_add(x, y): - return x + y - -my_add_ufunc = np.frompyfunc(my_add, 2, 1) -my_add_ufunc(A, B) # Output: array([ 6, 8, 10, 12], dtype=object) -``` -# Some Common Ufunc are - -Here are some commonly used ufuncs in NumPy: - -- **Arithmetic**: `np.add`, `np.subtract`, `np.multiply`, `np.divide` -- **Trigonometric**: `np.sin`, `np.cos`, `np.tan` -- **Exponential and Logarithmic**: `np.exp`, `np.log`, `np.log10` -- **Comparison**: `np.maximum`, `np.minimum`, `np.greater`, `np.less` -- **Logical**: `np.logical_and`, `np.logical_or`, `np.logical_not` - -For more such Ufunc, address to [Universal functions (ufunc) — NumPy](https://numpy.org/doc/stable/reference/ufuncs.html) \ No newline at end of file From 6b35fb7a8ebcbd423f5e6d4a8f95c39c3550cdfa Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma <103316955+Yogeshkarma@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:08:24 +0530 Subject: [PATCH 28/44] Update index.md for two different pRs --- contrib/numpy/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/numpy/index.md b/contrib/numpy/index.md index 2b0c357..3caac1d 100644 --- a/contrib/numpy/index.md +++ b/contrib/numpy/index.md @@ -11,4 +11,4 @@ - [Sorting NumPy Arrays](sorting-array.md) - [NumPy Array Iteration](array-iteration.md) - [Concatenation of Arrays](concatenation-of-arrays.md) -- [Universal Functions (Ufunc)](Universal-Functions.md) + From 4a9287f6f0f57a0f4490f41037e5c7489fe089ff Mon Sep 17 00:00:00 2001 From: Yogesh Vishwakarma <103316955+Yogeshkarma@users.noreply.github.com> Date: Tue, 4 Jun 2024 20:10:18 +0530 Subject: [PATCH 29/44] Update index.md --- contrib/numpy/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/contrib/numpy/index.md b/contrib/numpy/index.md index 3caac1d..50e8046 100644 --- a/contrib/numpy/index.md +++ b/contrib/numpy/index.md @@ -11,4 +11,3 @@ - [Sorting NumPy Arrays](sorting-array.md) - [NumPy Array Iteration](array-iteration.md) - [Concatenation of Arrays](concatenation-of-arrays.md) - From 218c09f481ff14f9bbecd2280561a06c82cd9de6 Mon Sep 17 00:00:00 2001 From: Saimanjari777 Date: Tue, 4 Jun 2024 20:45:53 +0530 Subject: [PATCH 30/44] Update sorting-algorithms.md I have added radix and counting sort --- contrib/ds-algorithms/sorting-algorithms.md | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/contrib/ds-algorithms/sorting-algorithms.md b/contrib/ds-algorithms/sorting-algorithms.md index 2edc265..55367b8 100644 --- a/contrib/ds-algorithms/sorting-algorithms.md +++ b/contrib/ds-algorithms/sorting-algorithms.md @@ -465,3 +465,99 @@ print("Sorted array:", arr) # Output: [1, 2, 3, 5, 8] - **Worst Case:** `𝑂(𝑛log𝑛)`. Building the heap takes `𝑂(𝑛)` time, and each of the 𝑛 element extractions takes `𝑂(log𝑛)` time. - **Best Case:** `𝑂(𝑛log𝑛)`. Even if the array is already sorted, heap sort will still build the heap and perform the extractions. - **Average Case:** `𝑂(𝑛log𝑛)`. Similar to the worst-case, the overall complexity remains `𝑂(𝑛log𝑛)` because each insertion and deletion in a heap takes `𝑂(log𝑛)` time, and these operations are performed 𝑛 times. + + ## 7. Radix Sort +Radix Sort is a non-comparative integer sorting algorithm that sorts numbers by processing individual digits. It processes digits from the least significant digit (LSD) to the most significant digit (MSD) or vice versa. This algorithm is efficient for sorting numbers with a fixed number of digits. + +**Algorithm Overview:** +- **Digit by Digit sorting:** Radix sort processes the digits of the numbers starting from either the least significant digit (LSD) or the most significant digit (MSD). Typically, LSD is used. +- **Stable Sort:** A stable sorting algorithm like Counting Sort or Bucket Sort is used as an intermediate sorting technique. Radix Sort relies on this stability to maintain the relative order of numbers with the same digit value. +- **Multiple passes:** The algorithm performs multiple passes over the numbers, one for each digit, from the least significant to the most significant. + +### Radix Sort Code in Python + +```python +def counting_sort(arr, exp): + n = len(arr) + output = [0] * n + count = [0] * 10 + + for i in range(n): + index = arr[i] // exp + count[index % 10] += 1 + + for i in range(1, 10): + count[i] += count[i - 1] + + i = n - 1 + while i >= 0: + index = arr[i] // exp + output[count[index % 10] - 1] = arr[i] + count[index % 10] -= 1 + i -= 1 + + for i in range(n): + arr[i] = output[i] + +def radix_sort(arr): + max_num = max(arr) + exp = 1 + while max_num // exp > 0: + counting_sort(arr, exp) + exp *= 10 + +# Example usage +arr = [170, 45, 75, 90] +print("Original array:", arr) +radix_sort(arr) +print("Sorted array:", arr) +``` + +### Complexity Analysis + - **Time Complexity:** O(d * (n + k)) for all cases. Radix Sort always processes each digit of every number in the array. + - **Space Complexity:** O(n + k). This is due to the space required for: +- The output array used in Counting Sort, which is of size n. +- The count array used in Counting Sort, which is of size k. + +## 8. Counting Sort +Counting sort is a sorting technique based on keys between a specific range. It works by counting the number of objects having distinct key values (kind of hashing). Then do some arithmetic to calculate the position of each object in the output sequence. + +**Algorithm Overview:** +- Convert the input string into a list of characters. +- Count the occurrence of each character in the list using the collections.Counter() method. +- Sort the keys of the resulting Counter object to get the unique characters in the list in sorted order. +- For each character in the sorted list of keys, create a list of repeated characters using the corresponding count from the Counter object. +- Concatenate the lists of repeated characters to form the sorted output list. + + +### Counting Sort Code in Python using counter method. + +```python +from collections import Counter + +def counting_sort(arr): + count = Counter(arr) + output = [] + for c in sorted(count.keys()): + output += * count + return output + +arr = "geeksforgeeks" +arr = list(arr) +arr = counting_sort(arr) +output = ''.join(arr) +print("Sorted character array is", output) + +``` +### Counting Sort Code in Python using sorted() and reduce(): + +```python +from functools import reduce +string = "geeksforgeeks" +sorted_str = reduce(lambda x, y: x+y, sorted(string)) +print("Sorted string:", sorted_str) +``` + +### Complexity Analysis + - **Time Complexity:** O(n+k) for all cases.No matter how the elements are placed in the array, the algorithm goes through n+k times + - **Space Complexity:** O(max). Larger the range of elements, larger is the space complexity. From f7dc61226c88c3f877d473cb71ca4142a5e7cad5 Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde <116059908+PradnyaGaitonde@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:33:22 +0530 Subject: [PATCH 31/44] Create Type_Hinting.md Added Content on Type hinting in python --- contrib/advanced-python/Type_Hinting.md | 106 ++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 contrib/advanced-python/Type_Hinting.md diff --git a/contrib/advanced-python/Type_Hinting.md b/contrib/advanced-python/Type_Hinting.md new file mode 100644 index 0000000..fcf1e1c --- /dev/null +++ b/contrib/advanced-python/Type_Hinting.md @@ -0,0 +1,106 @@ +# Introduction to Type Hinting in Python +Type hinting is a feature in Python that allows you to specify the expected data types of variables, function arguments, and return values. It was introduced +in Python 3.5 via PEP 484 and has since become a standard practice to improve code readability and facilitate static analysis tools. + +**Benefits of Type Hinting** + +1. Improved Readability: Type hints make it clear what type of data is expected, making the code easier to understand for others and your future self. +2. Error Detection: Static analysis tools like MyPy can use type hints to detect type errors before runtime, reducing bugs and improving code quality. +3.Better Tooling Support: Modern IDEs and editors can leverage type hints to provide better autocompletion, refactoring, and error checking features. +4. Documentation: Type hints serve as a form of documentation, indicating the intended usage of functions and classes. + +**Syntax of Type Hinting**
+Type hints can be added to variables, function arguments, and return values using annotations. + +1. Variable Annotations: + +```bash +age: int = 25 +name: str = "Alice" +is_student: bool = True +``` + +2. Function Annotations: + +```bash +def greet(name: str) -> str: + return f"Hello, {name}!" +``` + +3. Multiple Arguments and Return Types: + +```bash +def add(a: int, b: int) -> int: + return a + b +``` + +4. Optional Types: Use the Optional type from the typing module for values that could be None. + +```bash +from typing import Optional + +def get_user_name(user_id: int) -> Optional[str]: + # Function logic here + return None # Example return value +``` + +5. Union Types: Use the Union type when a variable can be of multiple types. + +```bash +from typing import Union + +def get_value(key: str) -> Union[int, str]: + # Function logic here + return "value" # Example return value +``` + +6. List and Dictionary Types: Use the List and Dict types from the typing module for collections. + +```bash +from typing import List, Dict + +def process_data(data: List[int]) -> Dict[str, int]: + # Function logic here + return {"sum": sum(data)} # Example return value +``` + +7. Type Aliases: Create type aliases for complex types to make the code more readable. + +```bash +from typing import List, Tuple + +Coordinates = List[Tuple[int, int]] + +def draw_shape(points: Coordinates) -> None: + # Function logic here + pass +``` + +**Example of Type Hinting in a Class**
+Here is a more comprehensive example using type hints in a class: + +```bash +from typing import List + +class Student: + def __init__(self, name: str, age: int, grades: List[int]) -> None: + self.name = name + self.age = age + self.grades = grades + + def average_grade(self) -> float: + return sum(self.grades) / len(self.grades) + + def add_grade(self, grade: int) -> None: + self.grades.append(grade) + +# Example usage +student = Student("Alice", 20, [90, 85, 88]) +print(student.average_grade()) # Output: 87.66666666666667 +student.add_grade(92) +print(student.average_grade()) # Output: 88.75 +``` + +### Conclusion +Type hinting in Python enhances code readability, facilitates error detection through static analysis, and improves tooling support. By adopting +type hinting, you can write clearer and more maintainable code, reducing the likelihood of bugs and making your codebase easier to navigate for yourself and others. From 23a7577e92f4d1d618ef6784feec46a4294640f2 Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde <116059908+PradnyaGaitonde@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:35:53 +0530 Subject: [PATCH 32/44] Update index.md Updated index for Type hinting content --- contrib/advanced-python/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index 5ea5081..e2179e7 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -1,3 +1,4 @@ # List of sections - [Decorators/\*args/**kwargs](decorator-kwargs-args.md) +- [Type Hinting/\*args/**kwargs](Type_Hinting.md) From a3961ff390a95c24b33bca10b047f89ec63a7201 Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde <116059908+PradnyaGaitonde@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:44:22 +0530 Subject: [PATCH 33/44] Create Itertools_module.md Added content for itertools module in python. --- contrib/advanced-python/Itertools_module.md | 144 ++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 contrib/advanced-python/Itertools_module.md diff --git a/contrib/advanced-python/Itertools_module.md b/contrib/advanced-python/Itertools_module.md new file mode 100644 index 0000000..501a127 --- /dev/null +++ b/contrib/advanced-python/Itertools_module.md @@ -0,0 +1,144 @@ +# The 'itertools' Module in Python +The itertools module in Python provides a collection of fast, memory-efficient tools that are useful for creating and working with iterators. These functions +allow you to iterate over data in various ways, often combining, filtering, or extending iterators to generate complex sequences efficiently. + +## Benefits of itertools +1. Efficiency: Functions in itertools are designed to be memory-efficient, often generating elements on the fly and avoiding the need to store large intermediate results. +2. Conciseness: Using itertools can lead to more readable and concise code, reducing the need for complex loops and temporary variables. +3. Composability: Functions from itertools can be easily combined, allowing you to build complex iterator pipelines from simple building blocks. + +## Useful Functions in itertools
+Here are some of the most useful functions in the itertools module, along with examples of how to use them: + +1. 'count': Generates an infinite sequence of numbers, starting from a specified value. + +```bash +import itertools + +counter = itertools.count(start=10, step=2) +for _ in range(5): + print(next(counter)) +# Output: 10, 12, 14, 16, 18 +``` + +2. 'cycle': Cycles through an iterable indefinitely. + +```bash +import itertools + +cycler = itertools.cycle(['A', 'B', 'C']) +for _ in range(6): + print(next(cycler)) +# Output: A, B, C, A, B, C +``` + +3.'repeat': Repeats an object a specified number of times or indefinitely. + +```bash +import itertools + +repeater = itertools.repeat('Hello', 3) +for item in repeater: + print(item) +# Output: Hello, Hello, Hello +``` + +4. 'chain': Combines multiple iterables into a single iterable. + +```bash +import itertools + +combined = itertools.chain([1, 2, 3], ['a', 'b', 'c']) +for item in combined: + print(item) +# Output: 1, 2, 3, a, b, c +``` + +5. 'islice': Slices an iterator, similar to slicing a list. + +```bash +import itertools + +sliced = itertools.islice(range(10), 2, 8, 2) +for item in sliced: + print(item) +# Output: 2, 4, 6 +``` + +6. 'compress': Filters elements in an iterable based on a corresponding selector iterable. + +```bash +import itertools + +data = ['A', 'B', 'C', 'D'] +selectors = [1, 0, 1, 0] +result = itertools.compress(data, selectors) +for item in result: + print(item) +# Output: A, C +``` + +7. 'permutations': Generates all possible permutations of an iterable. + +```bash +import itertools + +perms = itertools.permutations('ABC', 2) +for item in perms: + print(item) +# Output: ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B') +``` + +8. 'combinations': Generates all possible combinations of a specified length from an iterable. + +```bash +import itertools + +combs = itertools.combinations('ABC', 2) +for item in combs: + print(item) +# Output: ('A', 'B'), ('A', 'C'), ('B', 'C') +``` + +9. 'product': Computes the Cartesian product of input iterables. + +```bash +import itertools + +prod = itertools.product('AB', '12') +for item in prod: + print(item) +# Output: ('A', '1'), ('A', '2'), ('B', '1'), ('B', '2') +``` + +10. 'groupby': Groups elements of an iterable by a specified key function. + +```bash +import itertools + +data = [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 25}, {'name': 'Charlie', 'age': 30}] +sorted_data = sorted(data, key=lambda x: x['age']) +grouped = itertools.groupby(sorted_data, key=lambda x: x['age']) +for key, group in grouped: + print(key, list(group)) +# Output: +# 25 [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 25}] +# 30 [{'name': 'Charlie', 'age': 30}] +``` + +11. 'accumulate': Makes an iterator that returns accumulated sums, or accumulated results of other binary functions specified via the optional func argument. + +```bash +import itertools +import operator + +data = [1, 2, 3, 4, 5] +acc = itertools.accumulate(data, operator.mul) +for item in acc: + print(item) +# Output: 1, 2, 6, 24, 120 +``` + +## Conclusion +The itertools module is a powerful toolkit for working with iterators in Python. Its functions enable efficient and concise handling of iterable data, allowing you to create complex data processing pipelines with minimal memory overhead. +By leveraging itertools, you can improve the readability and performance of your code, making it a valuable addition to your Python programming arsenal. From 294afa2d325da4ce3afff3bf193a286f72832be0 Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde <116059908+PradnyaGaitonde@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:46:53 +0530 Subject: [PATCH 34/44] Update index.md Updated index for itertools module content. --- contrib/advanced-python/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index 5ea5081..f8a366f 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -1,3 +1,4 @@ # List of sections - [Decorators/\*args/**kwargs](decorator-kwargs-args.md) +- ['itertools' module](Itertools_module.md) From 7a83f1524a4c4e5a1c6b27401d98b53fcc5492c4 Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde <116059908+PradnyaGaitonde@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:48:20 +0530 Subject: [PATCH 35/44] Update index.md --- contrib/advanced-python/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index e2179e7..36bd103 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -1,4 +1,4 @@ # List of sections - [Decorators/\*args/**kwargs](decorator-kwargs-args.md) -- [Type Hinting/\*args/**kwargs](Type_Hinting.md) +- [Type Hinting](Type_Hinting.md) From 77b313abe8c4357496d4e9f3ccfb6ec0a027deb0 Mon Sep 17 00:00:00 2001 From: Arihant Yadav <147732947+arihunter-18@users.noreply.github.com> Date: Wed, 5 Jun 2024 02:39:44 +0530 Subject: [PATCH 36/44] Update closures.md --- contrib/advanced-python/closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/advanced-python/closures.md b/contrib/advanced-python/closures.md index 87ad058..e363c15 100644 --- a/contrib/advanced-python/closures.md +++ b/contrib/advanced-python/closures.md @@ -98,4 +98,4 @@ The **i**=*i* argument in the closure function is used to capture the current va For more examples related to closures, [click here](https://dev.to/bshadmehr/understanding-closures-in-python-a-comprehensive-tutorial-11ld). ## Summary -Closures in Python provide a powerful mechanism for encapsulating state and behavior, enabling more flexible and modular code. Understanding and effectively using closures enables the creation of function factories, allows functions to have state, and facilitates functional programming techniques. \ No newline at end of file +Closures in Python provide a powerful mechanism for encapsulating state and behavior, enabling more flexible and modular code. Understanding and effectively using closures enables the creation of function factories, allows functions to have state, and facilitates functional programming techniques From 76e07d587bc12188361ed05a65dae893792f2bfa Mon Sep 17 00:00:00 2001 From: Kosuri Indu Date: Thu, 6 Jun 2024 23:53:19 +0530 Subject: [PATCH 37/44] Added in index.md --- contrib/ds-algorithms/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/ds-algorithms/index.md b/contrib/ds-algorithms/index.md index 1d7293b..d39c33f 100644 --- a/contrib/ds-algorithms/index.md +++ b/contrib/ds-algorithms/index.md @@ -13,3 +13,4 @@ - [Stacks in Python](stacks.md) - [Sliding Window Technique](sliding-window.md) - [Trie](trie.md) +- [Hashing through Chaining](hashing-chaining.md) From 35c8620187ea011e49025d414c0e82fa33d31f62 Mon Sep 17 00:00:00 2001 From: Kosuri Indu Date: Thu, 6 Jun 2024 23:56:03 +0530 Subject: [PATCH 38/44] Added in index.md --- contrib/ds-algorithms/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/ds-algorithms/index.md b/contrib/ds-algorithms/index.md index 1d7293b..30881ac 100644 --- a/contrib/ds-algorithms/index.md +++ b/contrib/ds-algorithms/index.md @@ -13,3 +13,4 @@ - [Stacks in Python](stacks.md) - [Sliding Window Technique](sliding-window.md) - [Trie](trie.md) +- [Hashing through Linear Probing](hashing-linear-probing.md) From f637a28ba1c2326adedeea7fdf633f48e2199e2f Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 11:00:46 +0530 Subject: [PATCH 39/44] Update index.md --- contrib/advanced-python/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index 631423a..f61fdf0 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -10,4 +10,4 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) -- [List Comprehension](list_comprehension.md) +- [List Comprehension](list-comprehension.md) From c2740251604764cb12945841c828dc0033fbb151 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 11:01:08 +0530 Subject: [PATCH 40/44] Rename list_comprehension.md to list-comprehension.md --- .../{list_comprehension.md => list-comprehension.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contrib/advanced-python/{list_comprehension.md => list-comprehension.md} (100%) diff --git a/contrib/advanced-python/list_comprehension.md b/contrib/advanced-python/list-comprehension.md similarity index 100% rename from contrib/advanced-python/list_comprehension.md rename to contrib/advanced-python/list-comprehension.md From 98ad137d915cdb5039045aa0ee7f0c696c02ade5 Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 11:34:42 +0530 Subject: [PATCH 41/44] Rename Type_Hinting.md to type-hinting.md --- contrib/advanced-python/{Type_Hinting.md => type-hinting.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contrib/advanced-python/{Type_Hinting.md => type-hinting.md} (100%) diff --git a/contrib/advanced-python/Type_Hinting.md b/contrib/advanced-python/type-hinting.md similarity index 100% rename from contrib/advanced-python/Type_Hinting.md rename to contrib/advanced-python/type-hinting.md From 3e346a29a6bcddd25fe0c7af7b8b2dbffe74a3da Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 11:37:03 +0530 Subject: [PATCH 42/44] Rename Itertools_module.md to itertools.md --- contrib/advanced-python/{Itertools_module.md => itertools.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contrib/advanced-python/{Itertools_module.md => itertools.md} (100%) diff --git a/contrib/advanced-python/Itertools_module.md b/contrib/advanced-python/itertools.md similarity index 100% rename from contrib/advanced-python/Itertools_module.md rename to contrib/advanced-python/itertools.md From 4f096eb6a64c5e798f04a68740c7a8aae39b048c Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 23:39:18 +0530 Subject: [PATCH 43/44] Update index.md --- contrib/advanced-python/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/advanced-python/index.md b/contrib/advanced-python/index.md index 59ea9b5..b093e44 100644 --- a/contrib/advanced-python/index.md +++ b/contrib/advanced-python/index.md @@ -12,7 +12,7 @@ - [Protocols](protocols.md) - [Exception Handling in Python](exception-handling.md) - [Generators](generators.md) -- [Match Case Statement](match_case_statement.md) +- [Match Case Statement](match-case.md) - [Closures](closures.md) - [Filter](filter-function.md) - [Reduce](reduce-function.md) From 7d18fe39d45bfdd2e4d432c8bc70b4c7496f7e2d Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Sat, 8 Jun 2024 23:39:52 +0530 Subject: [PATCH 44/44] Update and rename match_case_statement.md to match-case.md --- .../advanced-python/{match_case_statement.md => match-case.md} | 1 - 1 file changed, 1 deletion(-) rename contrib/advanced-python/{match_case_statement.md => match-case.md} (95%) diff --git a/contrib/advanced-python/match_case_statement.md b/contrib/advanced-python/match-case.md similarity index 95% rename from contrib/advanced-python/match_case_statement.md rename to contrib/advanced-python/match-case.md index b617f7a..1b4f017 100644 --- a/contrib/advanced-python/match_case_statement.md +++ b/contrib/advanced-python/match-case.md @@ -247,6 +247,5 @@ The match case statements provide an elegant and readible format to perform oper ## Further Reading This article provides a brief introduction to the match case statements and the overview on the pattern matching operations. To know more, the below articles can be used for in-depth understanding of the topic. -- [Python Match Case Statement - GeeksForGeeks](https://www.geeksforgeeks.org/python-match-case-statement/) - [PEP 634 – Structural Pattern Matching: Specification](https://peps.python.org/pep-0634/) - [PEP 636 – Structural Pattern Matching: Tutorial](https://peps.python.org/pep-0636/)