Win a copy of Programming with Types this week in the Angular and TypeScript forum
or The Design of Web APIs in the Web Services forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Bear Bibeault
  • Paul Clapham
  • Jeanne Boyarsky
Sheriffs:
  • Junilu Lacar
  • Knute Snortum
  • Henry Wong
Saloon Keepers:
  • Ron McLeod
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
Bartenders:
  • Frits Walraven
  • Joe Ess
  • salvin francis

Program Flow

 
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, I'm watching Begging Java on YouTube and the presenter shares the following code.  I'm not understanding the line: Integer result = cal.add(10,30);  Not clear on how there a need for two assignment statements for 'result'  If 10 and 30 are passed into the add() Method isn't the result returned from this Method and already stored in 'result' so why the need to have the assignment of it with Integer result = cal.add(10,30)?  Can anyone explain the flow to me?  Thanks.


 
Saloon Keeper
Posts: 6610
61
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Programs may contain many variables that have the same name as long as there is no scope conflict. In your example the add() method has a local variable named 'result' that is not visible outside of the method. add() puts the value it has in 'result' on the stack when it returns, the stack does not retain the knowledge that the value came from a variable named 'result', it could have been named anything.

The main() method also has a local variable named result which is not know outside the scope of main(). This is a different local variable than the one in add(). When cal.add() returns with a result on the stack, the value on the stack is assigned because of the  '=' operator to main()'s 'result' variable.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey -

Here’s my understanding (please correct me where I’m wrong):

1. main() creates a new object of cal (where add() is called; is it line 11)?
a. If ‘yes,’ then the values 10 and 30 are ‘passed’ to add() into arg1 and arg2 respectively?
b. Next, the values are added together and assigned to the ‘results’ variable.
c. Not sure what happens next.  You stated that is goes to the Stack in memory (I understand that the name is irrelevant -- so, I’m clear on that part).  
d.  Control of the program goes back to main().
2.  I’m confused on the functionality of getting the output into ‘result.’
a. If 10 and 30 is passed to add() how is the answer passed into main()?
b. You stated that the assignment operator moves the answer into the ‘result’ variable inside of main() but I’m not following how the answer of 40 gets to the ‘result’ variable in main().  I’m lost as to how the values are getting passed around.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey -



So, here's his next example:




What happens to z; is the data flowing into main()?  I feel so dumb but I have been watching videos and reading and still can't seem to grasp the movement of the data....
 
Carey Brown
Saloon Keeper
Posts: 6610
61
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The returned value from any method is returned on a little piece of memory know as the "stack".  The caller then retrieves this value from the stack and does what ever it wants with it. It can assign it to a variable using the assignment (=) operator, it can use it as part of an expression, or it can ignore it completely. The exception to this is a method defined as returning 'void', in which case nothing is put on the stack when it returns.

The return value from someMethod() is put on the stack. When the call returns, the value is retrieved from the stack and assigned right-to-left to the 'result' variable.

Here the length() method is called which calculates the length of a string and puts that on the stack. When the call returns, the stack value is retrieved and compared for equality (==) to the constant  value 8. In this case the returned value is being used in an expression and not being assigned to a variable.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey -

I think I'm starting to understand.

So, to use my example:



The value of 40 is just sitting on the Stack and when the program arrives at the line above this is where the 40 is being retrieved from the Stack and being assigned to 'result; in main()?
 
Carey Brown
Saloon Keeper
Posts: 6610
61
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Just an example to point out that here 40 will be put on the stack by add(), but when the method returns nobody is waiting to do anything with the stack value and the stack value ultimately disappears. In some cases this is a mistake resulting in a bug but there are times that you won't care what the method returns and so this is ok. Of course, in your case calling add() and not doing something with the return value doesn't make sense, so this is a contrived example.
 
Marshal
Posts: 67014
255
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Lots of people use add(E) on a List and ignore the value returned.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey -

Thanks.  Are you aware of any videos or articles that describe this process in more details?

This is the first place I've been where I'm actually getting answers that I comprehend so again thanks everyone for their support of my journey, my mission!
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Marshell Campbell -

If I only understood what you are sharing...
 
Carey Brown
Saloon Keeper
Posts: 6610
61
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In your 'next' example:
could have been written (and usually is) as:
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey -

Thanks for Part 2.  Now, that I understand to flow, I think I'm good.  'Z' just sits on the Stack to be picked-up once control goes back to main().  Maybe, the presenter was using that approach for clarity?  I'm searching Google Images for pictures on this 'passing' topic.  I haven't found a solid resource yet...
 
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Royale Summers wrote: why the need to have the assignment of it with Integer result = cal.add(10,30)?  Can anyone explain the flow to me?  Thanks.



It's true. This is redundant. So there are two common reasons.

One would be to have more precise control over data type flow. If you simply computed, the result would be an int, and the compiler would auto-box the result as part of the return statement compilation. But by expressly defining things, you assure that you know exactly when and how the conversion will occur. A step up from that, of course, would be "Integer result = new Integer( cal.add(10,30))" or something similar such as a cast operation, but that's just gilding the lily.

The second reason is more practical. In Real Life, sometimes you want to capture the result before the return statement is executed. For example, when debugging, you might breakpoint the return statement, in which case, the debugger's data display services could allow the developer to confirm that the expected value had indeed been computed. Another reason for doing this is that you might want to be able to inject a logging statement to capture and display the computed value before returning.

In any event, while doing it this way does require some extra coding, sometimes it's useful. And the compiler knows how to squash it all down so that the generated code is going to be about the same either way, in most cases (Your Mileage May Vary Depending on Compiler and Options).
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Tim.

I was just confused as to how the values were being passed.  I haven't located any solid explanation on any source during my research that explains in a way that I can comprehend how: main() calls the add(), passes the two arguments, and then how or where the result of the arithmetic operations is stored and then when the value is passed back to main() how/where is the answer coming from.

To me, 10 and 30 is input to add().
It's assigned to 'results' variable in the add() method.
Control passes back to main() then this line had me confused because I'm not clear on how the value of 40 gets into result in the main() method:



Carey explained that 40 is just sitting on the Stack and I guess behind the scenes the application knows to assign 40 to the 'result' field in main().

If this is correct then I believe that I now understand the flow of the passing of arguments.

Please let me know if there's anything that I misunderstood.

Thanks!
 
Tim Holloway
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's the general idea.

What an assignment statement does is it takes the value from the right-hand side of the statement (the expression after the "=") and stores it in the left-hand side (which must be a non-final variable of some sort).

Since the right-hand side (or RHS in compiler terminology) should always end up with a single value (on the stack), there's no confusion about what to assign to the LHS.

Note that in Java, as in C/C++, you can do multiple assignments in a single statement. That is "a = b = c = 123 + 456;" And in such cases, the RHS value remains on the stack and is copied to each LHS element in turn, going from the rightmost to leftmost. At the end of the assignment(s). the RHS value is popped off the stack and discarded.

Although also note that as a general rule it's considered better to simply code multiple assignment statements over a single multiple-assignment statement. That is:


You wouldn't however want to do this:


Because there's a possibility that redundant (inefficient) code could be generated. And worse, if the RHS contains one or more method calls that alter object states, the values of a, b, and c might not match.

In fact, there's a technical term for a method that returns the exact same value no matter how many times you invoke it: idempotent.
 
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't know why you even have to think about the mechanics of this (stack, parameters, etc.) -- I would think that as a beginner, you would focus on the semantics instead. If I were learning how to drive a car, I wouldn't care about what kind of steering mechanism I had under the hood (rack and pinion, recirculating ball, or ropes and pulleys, whatever) connected to the steering wheel. All I'd care about is the fact that if I turned the wheel one way, the car would go that way instead of the other way. Turning the wheel one way and making the car go the same way is my intent.

If I were a beginner, I would focus on the intent. Let me try to break this down for you with a focus on intent.

The intent of a method declared as public int add(int addend1, int addend2) seems pretty clear: you want to add the numbers represented by addend1 and addend2, correct? So, to get the sum of two numbers, you'd call the add() method with those two values. Good so far?

In code, the expression calculator.add(1, 2) represents your intent to use the sum of the numbers 1 and 2, correct? That is, calls like this:

How you carry out that intent depends on the context in which you use that expression:

You see how that works?

So, when you use the method call expression, calculator.add(1, 2), in an assignment statement, you'd expect the result to be assigned to whatever variable you're assigning the value represented by that expression to:

These two statements result in sumOfTwoPositiveNumbers being assigned the value of 3 and sumOfPositiveAndNegativeNumbers being assigned the value of 4.

Are you with me so far?
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu -

All I asked was how the values where passed so I can understand how the values get to the result.  I accept any and all additional information.  I was a programmer and systems analyst so I'm just new to Java but not programming in general.
 
Junilu Lacar
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Now that you understand that a call to a method like add(int num1, int num2) can be thought of as a discrete value, let's look at how producing that discrete value happens.

What if you just coded the method add() like this:

You'd probably say that was a wrong implementation. But if you really think of it, it represents an infinite subset of the set of all correct answers:

So, while the code is incorrect for all possible input values, it is still correct for an infinite subset of all possible input values. While it is nonetheless an incorrect implementation, it clearly illustrates that the return statement represents the value of the method call expression, calculator.add(1, 2). That is, the value that comes after the return keyword is going to be the value that the method call expression calculator.add(whatever, whatever) represents.

So, what can come after the return keyword? Technical, it is any expression that is compatible with the declared type of the method it's in. That is, the add() method is declared to have a return type of int, so any return statement in the add() method must have an int expression after it. So any of the following qualify as syntactically valid implementations of the add() method:

The code inside the body of the method is the implementation -- this is how the intent of getting the sum of two numbers is carried out. The names used in this scope have nothing at all to do with any similar names used outside of the method. I think that's where you were getting confused, because the example you were looking at used a name that also was used elsewhere. I'm guessing that's what got you thinking there was some kind of mysterious connection and flow of values between one result to the other result. If my guess is correct, then you were just overthinking it.

It's really as simple as this:

Line 3 represents some optional internal work you can do to calculate the result of adding num1 and num2.

LIne 5 returns that result to the caller of add. Alternatively, you can think of line 5 as the value represented by the method call expression add(num1, num2). So either of these implementations would be correct:


 
Junilu Lacar
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Royale Summers wrote:I was a programmer and systems analyst so I'm just new to Java but not programming in general.


Well, now I'm confused. I would expect someone who has experience as a programmer and systems analyst to at least know the fundamental mechanics of how method calls, parameters, return statements, and call stacks work.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu -

Well, let me help you...I was a Cobol programmer...it's not OOP.  

It was Batch Processing...no interface for OLTP.
 
Junilu Lacar
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Royale Summers wrote:Junilu -

Well, let me help you...I was a Cobol programmer...it's not OOP.  

It was Batch Processing...no interface for OLTP.



I see. Not sure what OLTP has to do, if anything, with all this but anyway...

It has been quite a while (a brief period in the mid-1990s that my brain has worked hard to suppress) since I worked with COBOL and I had to refer to the Wikipedia page to clear some of the cobwebs. Are you at least familiar with subroutines or subprograms in COBOL? Method calls are kind of like that. In fact, this is what Wikipedia says:

A PERFORM statement somewhat resembles a procedure call in a modern language in the sense that execution returns to the code following the PERFORM statement at the end of the called code; however, it does not provide any mechanism for parameter passing or for returning a result value. If a subroutine is invoked using a simple statement like PERFORM subroutine, then control returns at the end of the called procedure.


This is kind of what happens when you call a method.

It might have helped you if you knew a structured programming language like Pascal or even Fortran but from what I can tell, the concept of subroutines in COBOL is not totally foreign.
 
Junilu Lacar
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The example you cited might become clearer if you looked at this site for a little bit: https://www.calculator.org/CalcHelp/basics.html or think back to your experience with your simple everyday calculator. The buttons marked "sin", "cos", "tan", and "1/x" are function buttons. Think of these as having method calls underneath them.

So pressing the 1/x button would result in the number currently displayed by the calculator being inverted, i.e., the reciprocal of the displayed number is calculated and displayed. Similarly, if you type in 45 then hit the "sin" button, you'd get the sin of a 45 degree angle. In code, that would look something like this:

You might want to try reading the section in Bruce Eckel's "Thinking In Java" about methods, arguments, and return values
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu -

I mentioned OLTP just to provide context (tough crowd).

Yes, my program was 100k lines of code with 3 subprograms and everything was in Working Storage and we used Copybooks for file layouts.

Thanks for putting it in terms more consumable....

P.S.

I did a contract using CICS and this used TS-Queue...https://www.tutorialspoint.com/cics/cics_temporary_storage.htm

I understand the calls to subroutines and Carey cleared the confusion a lot with how values are passed.
 
Junilu Lacar
Sheriff
Posts: 14618
243
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Just think of the stack as a special area in working storage that has very strict rules of use between calling code and sub-programs code. The protocol would be:

1. Before calling a sub-program, move "parameter" values into "IN-" variables.
2. In the sub-program, treat "IN-" variables as "read only"
3. Use local storage variables to store intermediate results
4. Move final sub-program results to "OUT-" variables in working storage.
5. The calling program can only use one "OUT-" variable per sub-program call.

Caveat: I'm just spitballing here so this may or may not actually work in a real COBOL program. Again, it has been a while since I've touched that language and I never really did get down and dirty with it. I did just enough to produce a few reports and that's it. But assuming there is nothing wildly inaccurate with the above description in terms of COBOL structure and legal operations, that's the general idea of how call stacks and method calls with parameters work in Java.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the additional.  I understand what a Stack is (including the Heap)...again, just didn't understand the movement of the values within the program logic....
 
Tim Holloway
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

Royale Summers wrote:I was a programmer and systems analyst so I'm just new to Java but not programming in general.


Well, now I'm confused. I would expect someone who has experience as a programmer and systems analyst to at least know the fundamental mechanics of how method calls, parameters, return statements, and call stacks work.



Well, "call stacks" as such aren't as familiar to COBOL programmers. COBOL wasn't designed for a stack-based architecture or recursive programming. In fact, even the calling mechanisms are a bit foreign.

COBOL, however doesn't preclude OLTP. CICS - one of the original mass-market OLTP systems - has had COBOL as one of its primary applications development languages for a LOOOONNNGG time.  
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim -

Yes, we didn't worry about 'stacks' in Cobol.  Mostly, I had to focus on JCL, DB2, and SQL using SQL-Exec Statements...
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I found this to be interesting on Cobol and parameter passing:

"COBOL programs were infamous for being monolithic and lacking modularization.[147] COBOL code could only be modularized through procedures, which were found to be inadequate for large systems. It was impossible to restrict access to data, meaning a procedure could access and modify any data item. Furthermore, there was no way to pass parameters to a procedure, an omission Jean Sammet regarded as the committee's biggest mistake.[148] Another complication stemmed from the ability to PERFORM THRU a specified sequence of procedures. This meant that control could jump to and return from any procedure, creating convoluted control flow and permitting a programmer to break the single-entry single-exit rule"

https://en.wikipedia.org/wiki/COBOL#Lack_of_structure

When I coded in Cobol, we did it as modular (each module only performed one function) as possible and we didn't break the single-entry/single-exit rule...
 
Tim Holloway
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
IBM System/360 COBOL (at least) did have a proper subroutine calling mechanism, but it was not used for general application purposes, and in fact its primary use was to invoke non-COBOL code.

COBOL was designed to give PHB's the dangerous idea that because it looked like English and didn't use scary mathematical constructs like FORTRAN and other languages of the day that a boss could read and understand it (assuming that his valuable time was worth the effort ) and that little expertise was required to program in it. Not until Visual Basic came along was such a menace again devised.

COBOL PROCEDURE DIVISION's were organized into labelled "paragraphs" and, as Royale noted, there were no restrictions on PERFORMing one or more sequential paragraphs and one could neither pass parameters in or out - to achieve that effect required futzing with global variables. Indeed, COBOL programs were monolithic, almost invariably a single source code file to produce the entire program. A lot of the sins of early programming we learned from COBOL, including spaghetti code.

Some of these programs were monsters. A shop I worked in blew out the IBM compiler by having more that 65536 labelled paragraphs in it (which was the compiler's nametable maximum). I was actually commissioned to write a system in a macro--like language called MetaCOBOL to produce customized spin-offs of that code when we discovered that for a given customer almost a third of what was in there didn't even apply to that customer.

I think perhaps one of the best ways to consider how different COBOL is* than Java is to compare Java to LISP. Before Java stole LISP's Lambda concept and added it to the language.

====
* Yes "is", not "was" COBOL may not be in much demand for new applications, but like Java, it runs forever, and even today some shops still have mainframes running COBOL. Also LISP isn't dead either. It's the macro language for Emacs, among other things.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim -

Hence, my whole confusion with parameter passing but I worked for Amex Express and the credit card processing system was "parameter-driven."  Yes, we had cute little 'paragraph's' with comments details what the paragraph's function was and any additional and relevant details.

I like VB and I did take a class in it.  MS way to monetize the Basic language.  Thankfully, I never came across spaghetti code.  The batch train that processed credit cards each night was around one million lines of code.  I owned the Disputes Module (the program was called when a customer disputed a charge).  

Yes, the mainframe is called the Legacy Server running Cobol and they threw on a web front-end.  It was great reminiscing thanks for the conversation.


Last question, for now, do you know anything about Pathrise and if so please share your thoughts...https://www.pathrise.com/
 
Tim Holloway
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Visual Basic was a very good language for teaching programming without fear, not to mention a lot of quick-and-dirty work. It ran into 2 problems, however. First, it was proprietary, so it only ran on platforms that Microsoft wanted it to run on, and more fatally, when they "upgraded" it to VB.Net, they massively broke things in ways that couldn't be easily remedied.

The modern-day successor to VB I would argue it Python. Although JavaScript may be in wide use by beginners, Python is a more friendly notation (except for the spacing!) than JavaScript. and it is available on almost all platforms.

Alas, like VB/VB.Net, the transition from Python2 to Python3 isn't totally smooth. Fortunately, however, Python2 and Python3 have been co-existing in parallel for several years now. But in a matter of days from now, Python2 will becine widely deprecated, so prudent people will be migrating.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim -

I watched a YouTube video and saw how easy Python was to code (with no configuration)....it's next on my list!
 
Tim Holloway
Saloon Keeper
Posts: 21458
143
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Fair warning. Going from Java to Python does have some "gotchas".

Python's package mechanism isn't as straight-forward as Java. And you have to work harder to get true OOP out of it. But when I need to dash out something quick-and-dirty for a command-line application, Python is my most common choice. Unless there's lots of regular expressions involved. In which case I generally use Perl.
 
Royale Summers
Ranch Hand
Posts: 85
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Tim -

Also, I found a pretty good link (after days of searching that you might wish to share in the event someone has the query I had in the future:  https://www.leepoint.net/JavaBasics/methods/methods-25-calls.html
 
I wish to win the lottery. I wish for a lovely piece of pie. And I wish for a tiny ad:
Sauce Labs - World's Largest Continuous Testing Cloud for Websites and Mobile Apps
https://coderanch.com/t/722574/Sauce-Labs-World-Largest-Continuous
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!