Section 4.2. Sub Procedures, Part II


[Page 154]

4.2. Sub Procedures, Part II

The previous section introduced the concept of a Sub procedure, but left some questions unanswered. Why can't the value of a variable be passed from an event procedure to a Sub procedure by just using the variable in the Sub procedure? How do Sub procedures pass values back to an event procedure? The answers to these questions provide a deeper understanding of the workings of Sub procedures and reveal their full capabilities.

Passing by Value

In Section 4.1, all parameters appearing in Sub procedures were preceded by the word ByVal, which stands for "By Value." When a variable is passed to such a parameter, we say that the variable is "passed by value." A variable that is passed by value will retain its original value after the Sub procedure terminatesregardless of what was done to the corresponding parameter inside the Sub procedure. Example 1 illustrates this feature.

Example 1.

The following program illustrates the fact that changes to the value of a parameter passed by value have no effect on the value of the calling argument:

 Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   'Illustrate that a change in value of parameter does not alter the   'value of the argument   Dim amt As Double = 2   lstResults.Items.Add(amt)   Triple(amt)   lstResults.Items.Add(amt) End Sub Sub Triple(ByVal num As Double)   'Triple a number   lstResults.Items.Add(num)   num = 3 * num   lstResults.Items.Add(num) End Sub


[Run, and then click the button. The following is displayed in the list box.]

 2 2 6 2 


When a variable is passed by value, two memory locations are involved. At the time the Sub procedure is called, a temporary second memory location for the parameter is set aside for the Sub procedure's use and the value of the argument is copied into that location. After the completion of the Sub procedure, the temporary memory location is released, and the value in it is lost. So, for instance, the outcome in Example 1 would be the same even if the name of the parameter were amt.


[Page 155]

Passing by Reference

Another way to pass a variable to a Sub procedure is "By Reference." In this case the parameter is preceded by the reserved word ByRef. Suppose a variable, call it arg, appears as an argument in a call statement, and its corresponding parameter in the Sub procedure's header, call it par, is preceded by ByRef. After the Sub procedure is executed, arg will have whatever value par had in the Sub procedure. Hence, not only is the value of arg passed to par, but the value of par is passed back to arg.

In Example 1, if the first line of the Sub procedure is changed to

Sub Triple(ByRef num As Double)


then the last number of the output will be 6.

Although this feature may be surprising at first glance, it provides a vehicle for passing values from a Sub procedure back to the place from which the Sub procedure was called. Different names may be used for an argument and its corresponding parameter, but only one memory location is involved. Initially, the btnDisplay_Click() event procedure allocates a memory location to hold the value of amt (Figure 4.4(a)). When the Sub procedure is called, the parameter num becomes the Sub procedure's name for this memory location (Figure 4.4(b)). When the value of num is tripled, the value in the memory location becomes 6 (Figure 4.4(c)). After the completion of the Sub procedure, the parameter name num is forgotten; however, its value lives on in amt (Figure 4.4(d)). The variable amt is said to be passed by reference.

Figure 4.4. Passing a variable by reference to a Sub procedure.


Passing by reference has a wide variety of uses. In the next example, it is used as a vehicle to transport a value from a Sub procedure back to an event procedure.

Example 2.
(This item is displayed on pages 155 - 156 in the print version)

The following variation of Example 5 from the previous section uses a Sub procedure to acquire the input. The variables x and y are not assigned values prior to the execution of the first call statement. Therefore, before the call statement is executed, they have the value 0. After the call statement is executed, however, they have the values entered into the text boxes. These values then are passed by the second call statement to the Sub procedure DisplaySum.


[Page 156]

Object

Property

Setting

frmAdd

Text

Add Two Numbers

lblFirstNum

Text

First Number:

txtFirstNum

  

lblSecondNum

Text

Second Number:

txtSecondNum

  

btnCompute

Text

Compute Sum

txtResult

ReadOnly

True


 Private Sub btnCompute_Click(...) Handles btnCompute.Click   'This program requests two numbers and   'displays the two numbers and their sum.   Dim x, y As Double   GetNumbers(x, y)   DisplaySum(x, y) End Sub Sub GetNumbers(ByRef x As Double, ByRef y As Double)   'Record the two numbers in the text boxes   x = CDbl(txtFirstNum.Text)   y = CDbl(txtSecondNum.Text) End Sub Sub DisplaySum(ByVal num1 As Double, ByVal  num2 As Double)   'Display numbers and their sum   txtResult.Text = "The sum of " & num1 & " and " & num2 _                    & " is " & (num1 + num2) & "." End Sub


[Run, type 2 and 3 into the text boxes, and then click the button.]

In most situations, a variable with no preassigned value is used as an argument of a call statement for the sole purpose of carrying back a value from the Sub procedure.

Example 3.
(This item is displayed on pages 156 - 157 in the print version)

The following variation of Example 2 allows the btnCompute_Click event procedure to be written in the input-process-output style. Just before the call statement CalculateSum (x, y, t) is executed, the value of t is 0. After the call, the value of t will be the sum of the two numbers in the text boxes.


[Page 157]

 Private Sub btnCompute_Click(...) Handles btnCompute.Click   'This program requests two numbers and   'displays the two numbers and their sum.   Dim  x As Double       'First number   Dim  y As Double       'Second number   Dim  t As Double       'Total   GetNumbers(x, y)   CalculateSum(x, y, t)   DisplayResult(x, y, t) End Sub Sub GetNumbers(ByRef num1 As Double, ByRef  num2 As Double)   'Retrieve the two numbers in the text boxes   num1 = CDbl(txtFirstNum.Text)   num2 = CDbl(txtSecondNum.Text) End Sub Sub CalculateSum(ByVal num1 As Double, ByVal num2 As Double, _                  ByRef total As Double)   'Add the values of num1 and num2   total = num1 + num2 End Sub Sub DisplayResult(ByVal num1 As Double, ByVal num2 As Double, _                   ByVal total As Double)   txtResult.Text = "The sum of " & num1 & " and " & num2 _                    & " is " & total & "." End Sub


Visual Basic provides a way to override passing by reference, even if the ByRef keyword precedes the parameter. If you enclose the variable in the call statement in an extra pair of parentheses, then the variable will be passed by value.

For instance, in Example 1, if you change the call statement to

Triple((amt))


then the fourth number of the output will be 2 even if the parameter num is preceded with ByRef.

Local Variables

When a variable is declared in an event or Sub procedure with a Dim statement, a portion of memory is set aside to hold the value of the variable. As soon as the End Sub statement for the procedure executes, the memory location is freed up; that is, the variable ceases to exist. The variable is said to be local to the procedure.

When variables of the same name are declared with Dim statements in two different procedures (either event or Sub), Visual Basic gives the variables separate identities and treats them as two different variables. A value assigned to a variable in one part of the program will not affect the value of the like-named variable in the other part of the program.


[Page 158]

Example 4.

The following program illustrates the fact that each time a Sub procedure is called, its variables are set to their initial values; that is, numerical variables are set to 0 and string variables are set to the keyword Nothing.

 Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   'Demonstrate that variables declared in a Sub procedure   'do not retain their values in subsequent calls   Three()   Three() End Sub Sub Three()   'Display the value of num and assign it the value 3   Dim num As Double   lstResults.Items.Add(num)   num = 3 End Sub


[Run, and then click the button. The following is displayed in the list box.]

0 0


Example 5.
(This item is displayed on pages 158 - 159 in the print version)

The following program illustrates the fact that variables are local to the part of the program in which they reside. The variable x in the event procedure and the variable x in the Sub procedure are treated as different variables. Visual Basic handles them as if their names were separate, such as xbtnDisplay_Click and xTrivial. Also, each time the Sub procedure is called, the value of variable x inside the Sub procedure is reset to 0.

 Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   'Demonstrate the local nature of variables   Dim x As Double = 2   lstResults.Items.Clear()   lstResults.Items.Add(x)   Trivial()   lstResults.Items.Add(x)   Trivial()   lstResults.Items.Add(x) End Sub Sub Trivial()   'Do something trivial   Dim x As Double   lstResults.Items.Add(x)   x = 3   lstResults.Items.Add(x) End Sub


[Run, and then click the button. The following is displayed in the list box.]


[Page 159]

2 0 3 2 0 3 2 


Class-Level Variables

Visual Basic provides a way to make a variable visible to every procedure in a form's code without being passed. Such a variable is called a class-level variable. The Dim statement for a class-level variable can be placed anywhere between the statements Public Class formName and End Class, provided that the Dim statement is not inside a procedure. Normally, we place the Dim statement just after the Public Class formName statement (We refer to this region as the Declarations section of the Code window.) A class-level variable is visible to every procedure. When a class-level variable has its value changed by a procedure, the value persists even after the procedure has finished executing. We say that such a variable has class-level scope. Variables declared inside a procedure are said to have local scope.

In general, the scope of a variable is the portion of the program that can refer to it. Class-level scope also is referred to as module-level scope, and local scope also is referred to as procedure-level scope. If a procedure declares a local variable with the same name as a class-level variable, then the name refers to the local variable for code inside the procedure.

Example 6.
(This item is displayed on pages 159 - 160 in the print version)

The following program contains the class-level variables num1 and num2. Their Dim statement does not appear inside a procedure. It appears immediately following the statement Public Class frmAdd.

 Dim num1, num2 As Double     'Class-level variables Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   'Display the sum of two numbers   num1 = 2   num2 = 3   lstResults.Items.Clear()   AddAndIncrement()   lstResults.Items.Add("")   lstResults.Items.Add("num1 = " & num1)   lstResults.Items.Add("num2 = " & num2) End Sub Sub AddAndIncrement()   'Display numbers and their sum   lstResults.Items.Add("The sum of " & num1 & " and " & _                        num2 & " is " & (num1 + num2) & ".")   num1 += 1      'Add 1 to the value of num1   num2 += 1      'Add 1 to the value of num2 End Sub



[Page 160]

[Run, and click the button. The following is displayed in the list box.]

The sum of 2 and 3 is 5. num1 = 3 num2 = 4


In the preceding example, we had to click a button to assign values to the class-level variables. In some situations, we want to assign a value immediately to a class-level variable, without requiring the user to perform some specific action. This can be accomplished by declaring each class-level variable with a statement of the type

Dim variableName As varType = value


Example 7.

The following program assigns a value to a class-level variable as soon as it is created:

 Dim pi As Double = 3.14159 Private Sub btnCompute_Click(...) Handles btnCompute.Click   'Display the area of a circle of radius 5   txtArea.Text = "The area of a circle of radius 5 is " & (pi * 5 * 5) End Sub


[Run, and then click the button. The following is displayed in the text box.]

The area of a circle of radius 5 is 78.53975


Debugging

Programs with Sub procedures are easier to debug. Each Sub procedure can be checked individually before being placed into the program.

In Appendix D, the section "Stepping through a Program Containing a General Procedure: Chapter 4" uses the Visual Basic debugger to trace the flow through a program and observe the interplay between arguments and parameters.

Practice Problems 4.2

1.

What does the following code display in the list box when the button is clicked?

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim b As Integer = 1, c As Integer = 2   Rhyme()   lstOutput.Items.Add(b & " " & c) End Sub Sub Rhyme()   Dim b, c As Integer   lstOutput.Items.Add(b & " " & c & " buckle my shoe.")   b = 3 End Sub



[Page 161]
2.

Determine the output displayed in the list box when the button is clicked.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim amt1 As Integer = 1, amt2 As Integer = 2   lstOutput.Items.Add(amt1 & " "& amt2)   Swap(amt1, amt2)   lstOutput.Items.Add(amt1 & " "& amt2) End Sub Sub Swap(ByRef num1 As Integer, ByRef num2 As Integer)   'Interchange the values of num1 and num2   Dim temp As Integer   temp = num1   num1 = num2   num2 = temp   lstOutput.Items.Add(num1 & " "& num2) End Sub 


3.

In Problem 2, change the Sub statement to

Sub Swap(ByRef num1 As Integer, ByVal num2 As Integer)


and determine the output.

4.

In Problem 2, change the calling statement to

Swap((amt1), (amt2))


and determine the output.

Exercises 4.2

In Exercises 1 through 18, determine the output displayed when the button is clicked.

1.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click     Dim num As Double = 7     AddTwo(num)     txtOutput.Text = CStr(num)   End Sub   Sub AddTwo(ByRef num As Double)     'Add 2 to the value of num     num += 2   End Sub


2.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim term As String   term = "Fall"   Plural(term)   txtOutput.Text = term End Sub 
[Page 162]
Sub Plural(ByRef term As String) 'Concatenate the letter "s"to the value of term term &= "s" End Sub


3.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim dance As String   dance = "Can "   Twice(dance)   txtOutput.Text = dance End Sub Sub Twice(ByRef dance As String)   'Concatenate the value of dance to itself   dance &= dance End Sub


4.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim a As Integer = 1, b As Integer = 3   lstOutput.Items.Add(a & " "& b)   Combine(a, b)   lstOutput.Items.Add(a & " "& b)   Combine((a), b)   lstOutput.Items.Add(a & " "& b) End Sub Sub Combine(ByRef x As Integer, ByVal y As Integer)   x = y - x   y = x + y   lstOutput.Items.Add(x & " "& y) End Sub


5.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim a As Double = 5   Square(a)   txtOutput.Text = CStr(a) End Sub Sub Square(ByRef num As Double)   num = num * num End Sub


6.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim state As String = "NEBRASKA"   Abbreviate(state)   txtOutput.Text = state End Sub Sub Abbreviate(ByRef a As String)   a = a.SubString(0, 2) End Sub



[Page 163]
7.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim word As String = " "   GetWord(word)   txtOutput.Text = "Less is "& word & "." End Sub Sub GetWord(ByRef w As String)   w = "more" End Sub


8.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim hourlyWage, annualWage As Double   hourlyWage = 10   CalcAnnualWage(hourlyWage, annualWage)   txtOutput.Text = "Approximate Annual Wage: "& _                     FormatCurrency(annualWage) End Sub Sub CalcAnnualWage(ByVal hWage As Double, ByRef aWage As Double)   aWage = 2000 * hWage End Sub


9.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim name As String = "", yob As Integer   GetVita(name, yob)   txtOutput.Text = name & " was born in the year "& yob End Sub Sub GetVita(ByRef name As String, ByRef yob As Integer)   name = "Gabriel"   yob = 1980   'Year of birth End Sub


10.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim word1, word2 As String   word1 = "fail"   word2 = "plan"   txtOutput.Text = "If you "   Sentence(word1, word2)   txtOutput.Text &= ","   Exchange(word1, word2)   txtOutput.Text &= " then you "   Sentence(word1, word2)   txtOutput.Text &= "." End Sub Sub Exchange(ByRef word1 As String, ByRef word2 As String)   Dim temp As String   temp = word1   word1 = word2   word2 = temp End Sub 
[Page 164]
Sub Sentence(ByVal word1 As String, ByVal word2 As String) txtOutput.Text &= word1 & " to "& word2 End Sub


11.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim state As String = "Ohio "   Team() End Sub Sub Team()   Dim state As String   txtOutput.Text = state   vtxtOutput.Text &= "Buckeyes" End Sub


12.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim a As Double = 5   Multiply(7)   lstOutput.Items.Add(a * 7) End Sub Sub Multiply(ByRef num As Double)   Dim a As Double   a = 11   lstOutput.Items.Add(a * num) End Sub


13.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim a As Double = 5   Multiply(7) End Sub Sub Multiply(ByVal num As Double)   Dim a As Double   txtOutput.Text = CStr(a * num) End Sub


14.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim name, n As String   name = "Ray"   Hello(name)   lstOutput.Items.Add(n & " and "& name) End Sub Sub Hello(ByRef name As String)   Dim n As String   n = name   name = "Bob"   lstOutput.Items.Add("Hello "& n & " and "& name) End Sub



[Page 165]
15.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim num As Double = 1   Amount(num)   Amount(num) End Sub Sub Amount(ByVal num As Double)   Dim total As Double   total += num 'Add the value of num to the value of total   lstOutput.Items.Add(total) End Sub


16.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim river As String   river = "Wabash"   Another()   lstOutput.Items.Add(river & " River")   Another() End Sub Sub Another()   Dim river As String   lstOutput.Items.Add(river & " River")   river = "Yukon" End Sub


17.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim n As Integer = 4, word As String = "overwhelming"   lstOutput.Items.Add(n & " "& word)   Crop(n, word)   lstOutput.Items.Add(n & " "& word)   Crop(n, (word))   lstOutput.Items.Add(n & " "& word) End Sub Sub Crop(ByVal n As Integer, ByRef word As String)   n = word.Length - n   word = word.Substring(word.Length - n)   lstOutput.Items.Add(n & " "& word) End Sub


18.

Private Sub btnCompute_Click(...) Handles btnCompute.Click   Dim tax, price, total As Double   tax = 0.05   GetPrice("bicycle", price)   ProcessItem(price, tax, total)   DisplayResult(total) End Sub Sub DisplayResult(ByVal total As Double)   txtOutput.Text = "With tax, price is "& FormatCurrency(total) End Sub 
[Page 166]
Sub GetPrice(ByVal item As String, ByRef price As Double) Dim strVar As String strVar = InputBox("What is the price of a "& item & "?") price = CDbl(strVar) End Sub Sub ProcessItem(ByVal price As Double, ByVal tax As Double, _ ByRef total As Double) total = (1 + tax) * price End Sub


(Assume that the cost of the bicycle is $200.)

In Exercises 19 and 20, find the errors.

19.

Private Sub btnCompute_Click(...) Handles btnCompute.Click   Dim a, b, c As Double   a = 1   b = 2   Sum(a, b, c)   txtOutput.Text = "The sum is "& c End Sub Sub Sum(ByVal x As Double, ByVal y As Double)   Dim c As Double   c = x + y End Sub


20.

Private Sub btnDisplay_Click(...) Handles btnDisplay.Click   Dim ano As String = ""   GetYear(ano)   txtOutput.Text = ano End Sub Sub GetYear(ByRef yr As Double)   yr = 2006 End Sub


In Exercises 21 through 24, rewrite the program so input, processing, and output are each performed by calls to Sub procedures.

21.

Private Sub btnCompute_Click(...) Handles btnCompute.Click   'Calculate sales tax   Dim price, tax, cost As Double   lstOutput.Items.Clear()   price = CDbl(InputBox("Enter the price of the item:"))   tax = .05 * price   cost = price + tax   lstOutput.Items.Add("Price:  "& price)   lstOutput.Items.Add("Tax:    "& tax)   lstOutput.Items.Add("-------")   lstOutput.Items.Add("Cost:  "& cost) End Sub



[Page 167]
22.

Private Sub btnDisplay_Click(...) Handles bnDisplay.Click   'Letter of acceptance   Dim name, firstName As String, n As Integer   lstOutput.Items.Clear()   name = InputBox("What is your full name?")   n = name.IndexOf(" ")   firstName = name.Substring(0, n)   lstOutput.Items.Add("Dear "& firstName & ",")   lstOutput.Items.Add("")   lstOutput.Items.Add("We are proud to accept you to Yale.") End Sub


23.

Private Sub btnDisplay_Click(...) Handles bnDisplay.Click   'Determine the area of a rectangle   Dim length, width, area As Double   length = CDbl(txtLength.Text)   width = CDbl(txtWidth.Text)   area = length * width   txtOutput.Text = "The area of the rectangle is "& area End Sub


24.

Private Sub btnCompute_Click(...) Handles btnCompute.Click   'Convert feet and inches to centimeters   Dim str As String   Dim feet, inches, totalInches, centimeters As Double   str = "Give the length in feet and inches. "   feet = CDbl(InputBox(str & "Enter the number of feet."))   inches = CDbl(InputBox(str & "Enter the number of inches. "))   totalInches = 12 * feet + inches   centimeters = 2.54 * totalInches   txtOutput.Text = "The length in centimeters is "& centimeters End Sub


In Exercises 25 and 26, write a line of code to carry out the task. Specify where in the program the line of code would occur.

25.

Declare the variable str as a string variable visible to all parts of the program.

26.

Declare the variable str as a string variable visible only to the btnTest_Click event procedure.

In Exercises 27 through 32, write a program to perform the stated task. The input, processing, and output should be performed by calls to Sub procedures.

27.

Request a person's first name and last name as input and display the corresponding initials.

28.

Request the amount of a restaurant bill as input and display the amount, the tip (15 percent), and the total amount.

29.

Request the cost and selling price of an item of merchandise as input and display the percentage markup. Test the program with a cost of $4 and a selling price of $6. Note: The percentage markup is (sellingprice cost) / cost.


[Page 168]
30.

Read the number of students in public colleges (12.1 million) and private colleges (3.7 million) from a file, and display the percentage of college students attending public colleges.

31.

Read a baseball player's name (Sheffield), times at bat (557), and hits (184) from a file and display his name and batting average. Note: Batting average is calculated as (hits)/(times at bat).

32.

Request three numbers as input, and then calculate and display the average of the three numbers.

33.

The Hat Rack is considering locating its new branch store in one of three malls. The following file gives the monthly rent per square foot and the total square feet available at each of the three locations. Write a program to display a table exhibiting this information along with the total monthly rent for each mall.

(Assume the nine lines of the file MALLS.TXT contain the following data: Green Mall, 6.50, 583, Red Mall, 7.25, 426, Blue Mall, 5.00, 823.)

34.

Write a program that uses the data in the file CHARGES.TXT to display the end-of-month credit-card balances of three people. (Each set of four lines gives a person's name, beginning balance, purchases during month, and payment for the month.) The end-of-month balance is calculated as [finance charges] + [beginning-of-month balance] + [purchases] - [payment], where the finance charge is 1.5 percent of the beginning-of-month balance.

(Assume the 12 lines of the file CHARGES.TXT contain the following data: John Adams, 125.00, 60.00, 110.00, Sue Jones, 0, 117.25, 117.25, John Smith, 350.00, 200.50, 300.00.)

35.

Write a program to produce a sales receipt. Each time the user clicks on a button, an item and its price should be read from a pair of text boxes and displayed in a list box. Use a class-level variable to track the sum of the prices. When the user clicks on a second button (after all the entries have been made), the program should display the sum of the prices, the sales tax (5 percent of total), and the total amount to be paid. Figure 4.5 shows a sample output of the program.

Figure 4.5. Sample output for Exercise 35.



[Page 169]
Solutions to Practice Problems 4.2

1.

0 0 buckle my shoe. 1 2


This program illustrates the local nature of the variables in a Sub procedure. Notice that the variables b and c appearing in the Sub procedure have no relationship whatsoever to the variables of the same name in the event procedure. In a certain sense, the variables inside the Sub procedure can be thought of as having alternate names, such as bRhyme and cRhyme.

2.

1 2 2 1 2 1


Both variables are passed by reference and so have their values changed by the Sub procedure.

3.

1 2 2 1 2 2


Here amt1 is passed by reference and amt2 is passed by value. Therefore, only amt1 has its value changed by the Sub procedure.

4.

1 2 2 1 1 2


Both variables are passed by value, so their values are not changed by the Sub procedure.




An Introduction to Programming Using Visual Basic 2005
Introduction to Programming Using Visual Basic 2005, An (6th Edition)
ISBN: 0130306541
EAN: 2147483647
Year: 2006
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net