My FIRST Class!! But it doesn't work as expected - please HELP!

  • Thread starter Thread starter Cap'n Ahab
  • Start date Start date
C

Cap'n Ahab

I have used VB3 - VB6, so learning all this OO stuff is reasonably new
to me (although I looked at Java a few years ago). Anyway, I thought I
would write a small class to begin with, with a property and 2 methods,
along with a constructor (where you use New(), I think, yes?).

So, I decided to create a class that represents a die, which can be
rolled - using a Roll() method - and which has a property to find out
the value on the face of the die after a roll - GetValue(). I then
added a Display() method that prints out the value of the die to the
console window.

Now, my thinking was that in writing the New() constructor I should
Roll() the die to get a 1st random number value. Indeed this worked
fine. However, when I instantiated (or do I say consumed?) the class
twice - the dice always show the same value!!!

I cannot see where this is going wrong - could someone please point out
the error of my ways?

The code is quite small - and here is the class (in Class1.vb)...

Public Class Die

Private _value As Int16
Dim _generator As New Random

Public Sub New()
Me.Roll()
End Sub

Public ReadOnly Property GetValue() As Int16
Get
Return _value
End Get
End Property

Public Sub Roll()
_value = _generator.Next(1, 7)
End Sub

Public Sub Display()
Console.WriteLine(Me.GetValue)
End Sub

End Class

The Module1.vb file holds this code...

Module Module1

Sub Main()

Dim One As New Die
Dim Two As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

End Sub

End Module

When run, the values are listed like this...

1
1

2
2

6
6

5
5

3
3

Why do they duplicate if they are supposed to be random - there is
either something in the Dim One/Two as New Die that I don't appreciate
or there is something in the Constructor New() that I don't understand.

Thanks in advance for any help.
 
Cap'n Ahab said:
Dim _generator As New Random

Random numbers are not really Random. To get a "more random" number, use

Dim _generator As New Random(Environment.TickCount)


Armin
 
well i would do it like this


Public Class Die

Private _value As Int16

Public Sub New()

Randomize()

Me.Roll()

End Sub

Public ReadOnly Property GetValue() As Int16

Get

Return _value

End Get

End Property

Public Sub Roll()

' Initialize the random-number generator.

Randomize()

' Generate random value between 1 and 6.

_value = CInt(Int((6 * Rnd()) + 1))

End Sub

Public Sub Display()

Console.WriteLine(Me.GetValue)

End Sub

End Class



Module Module1

Sub Main()

Dim One As New Die

Dim Two As New Die

Do

One.Display()

One.Roll()

Two.Display()

Two.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

End Sub

End Module



wich seems to work as expected



regards



Michel Posseth [MCP]
 
Random numbers are not really Random. To get a "more random" number, use
Dim _generator As New Random(Environment.TickCount)


Armin ,,,,, it does this automaticly if you do not provide an init number

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmrandomize.asp


although when i rethink this situation

this would also solve his problem , cuase it will now internally be the same
as calling randomize multiple times

Public Class Die

Private _value As Int16

Public Sub New()
Me.Roll()
End Sub

Public ReadOnly Property GetValue() As Int16
Get
Return _value
End Get
End Property

Public Sub Roll()
Dim _generator As New Random
_value = _generator.Next(1, 7)
End Sub

Public Sub Display()
Console.WriteLine(Me.GetValue)
End Sub

End Class


regards

Michel Posseth [MCP]
 
Michel,

I certainly would not do it as you showed, because that an int16 consumes
probably more than an Integer, which last is the default for at least all
32bits system and therefore perfectly fits in the register of the computer
and needs no extra accessing to use that.

For the rest I did not look, trusting on it that yours and Armins answers
are the right ones and there would not be much to add for me.

:-)

Cor
 
well it was altered TS code ,,,, i was in doubt regarding the 16 bit integer
value

but i have never found a true answer for it as the thoughts seem to change
day by day regarding this issue

one says the IA-32 architecture processes 32-bit integers just as
fast as 16-bit or 8-bit integers and that the smallest possible size will
preserve memory

others tell you to always use the 32 bit size when possible cause 16 bit
vars preserve memory but requires more CPU cycles

and again others come with the fact that 32 bit would always be better as it
is the architecture size

etc etc etc etc

i assumed the TS had it`s reasson ( or accepts one of the above for true )
i for myself would have used the integer datatype

regards

Michel Posseth [MCP]
 
Some comments to the other posts:

1. Yes, I prefer Integer (Int32) too. As I am using Option Strict in
nearly all the modules I write, I find that I need to perform fewer
explicit type conversions when interacting with the rest of the
framework :)

2. Randomize is ugly. I am not sure I would ever call it except once
and then store the result in a global variable.

---snip---
Public Shared Sub Randomize()
Dim data1 As ProjectData = ProjectData.GetProjectData
Dim single1 As Single = VBMath.GetTimer
Dim num2 As Integer = data1.m_rndSeed
Dim num1 As Integer =
BitConverter.ToInt32(BitConverter.GetBytes(single1), 0)
num1 = (((num1 And 65535) Xor (num1 >> 16)) << 8)
num2 = ((num2 And -16776961) Or num1)
data1.m_rndSeed = num2
End Sub

Private Shared Function GetTimer() As Single
Dim time1 As DateTime = DateTime.Now
Return CType((((((60 * time1.Hour) + time1.Minute) * 60) +
time1.Second) + (CType(time1.Millisecond, Double) / 1000)), Single)
End Function
---snip---

3. Finally: Your first instincts were right. With one small change,
your class will perform as required:

---snip---
Public Class Die
Private _value As Int32
Private Shared _generator As New Random

Public Sub New()
Me.Roll()
End Sub

Public ReadOnly Property Value() As Int32
Get
Return _value
End Get
End Property

Public Sub Roll()
_value = _generator.Next(1, 7)
End Sub

End Class

Private Sub TestDie()
Dim d1 As New Die
Dim d2 As New Die

Debug.WriteLine(d1.Value())
d1.Roll()
Debug.WriteLine(d2.Value())
d2.Roll()

Debug.WriteLine(d1.Value())
d1.Roll()
Debug.WriteLine(d2.Value())
d2.Roll()

End Sub

---snip---

It doesn't get any simpler than this. Why complicate things?

The main difference here is the addition of the "Shared" keyword
to the _generator member variable.

This means that the generator is only instantiated once and shared
between any instances of the Die class you might create. No need
to use the Randomize command or anything.

If you are writing a program that rolls a dice (once) when the user
clicks a button, maybe it's ok to use Randomize to seed a new
Random object, but if you are doing multiple rolls within the same
split-second, or need to use the Random object to generate a large
amount of random data for testing, statistics, noise plots, whatever,
stay away from Randomize. Try running the Sub below. Remove one
or both of the Randomize statements.

---snip---
Private Sub NotSoRandom()
For i As Integer = 1 To 200
Randomize() '<<< makes no difference
Dim r As New Random
Randomize() '<<< makes no difference
Debug.WriteLine(r.Next(1, 7))
Next
End Sub
---snip---

If you disagree with my views in these specific contexts, please post
a follow-up.

Regards,

Joergen Bech
 
Well, I cannot say how much difference it makes for a single
variable. I just use Integer as a convenience.

But for arrays, I can positively say that it makes a difference:

---snip---
Private Sub MemoryTest()
Dim mw As New MemoryWatch
mw.Start()

Dim d1(10000) As Byte
mw.CheckPoint()
Debug.WriteLine("10000 bytes: " & mw.MemoryDelta)

Dim d2(10000) As Short
mw.CheckPoint()
Debug.WriteLine("10000 words: " & mw.MemoryDelta)

Dim d4(10000) As Integer
mw.CheckPoint()
Debug.WriteLine("10000 integers: " & mw.MemoryDelta)

Dim d8(10000) As Long
mw.CheckPoint()
Debug.WriteLine("10000 longs: " & mw.MemoryDelta)

End Sub
---snip---

results in something like

---snip---
10000 bytes: 10028
10000 words: 20004
10000 integers: 40016
10000 longs: 80020
---snip---

In other words, 10000 16-bit values are not treated as 10000
32-bit values in terms of storage. Does this mean an extra shift
operation for the cpu when reading every other value in the 16-bit
array? Perhaps. Does it matter? Probably not much, considering
all the other work the clr has to do in order to address and read
the value.

So I would say: For simple member and throw-away variables (those that
will never leave the cpu cache anyway), use what is convenient, but
when working with large amounts of data or performance-critical loops,
etc, think about what you are doing. Test for time and resource usage.

In most cases, make sure the program is readable, then optimize if
necessary (though there are exceptions to this rule).

Regards,

Joergen Bech



well it was altered TS code ,,,, i was in doubt regarding the 16 bit integer
value

but i have never found a true answer for it as the thoughts seem to change
day by day regarding this issue

one says the IA-32 architecture processes 32-bit integers just as
fast as 16-bit or 8-bit integers and that the smallest possible size will
preserve memory

others tell you to always use the 32 bit size when possible cause 16 bit
vars preserve memory but requires more CPU cycles

and again others come with the fact that 32 bit would always be better as it
is the architecture size

etc etc etc etc

i assumed the TS had it`s reasson ( or accepts one of the above for true )
i for myself would have used the integer datatype

regards

Michel Posseth [MCP]


Cor Ligthert said:
Michel,

I certainly would not do it as you showed, because that an int16 consumes
probably more than an Integer, which last is the default for at least all
32bits system and therefore perfectly fits in the register of the computer
and needs no extra accessing to use that.

For the rest I did not look, trusting on it that yours and Armins answers
are the right ones and there would not be much to add for me.

:-)

Cor
 
Thanks for the responses, guys. The one that seems to make it work as
I originally planned is the change below...

Private _generator As New Random ---> Private Shared
_generator As New Random

However, this runs contrary to what I would expect! 180 degrees
wrong!!!!
I would have thought that the code in the constructor would create a
random number between 1 - 6 (which it does) but in addition it would be
a different value for every new object that is instantiated by the die
class as part of the Dim myObject as New Die command (but it doesn't).
Why do they have the same value? I thought if you wanted a value to be
common throughout instantiation of a class THEN you used the SHARED
command - not the other way round as happens here. Using shared makes
instantiations of the Die Class begin with, well, a different number
from each other.

I think this is more of my lack of understanding of how the Random
command works and not my appreciation of OO programming. Do you agree?
 
I have also noticed that instead of using Shared, if I rewrite the
New() constructor by adding the Randomize() command as below...

Public Sub New()
Randomize()
Me.Roll()
End Sub

and then consume 3 objects based on the Die Class,

Then use the same loop..

Dim One As New Die
Dim Two As New Die
Dim Three As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()
Three.Display()
Three.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

That the results are not random between the die at all!! The numbers
either are 3 of a the same number OR a pair of numbers an other other
number - no exceptions. How very odd!

Now I AM confused, lol.
 
The code i showed i have also run ,,, and it worked as dices in my situation

also nice thingy is that alter after the comments i searched the msdn site
and saw that MS does it the same way

Dim MyValue As Integer
Randomize ' Initialize random-number generator.
MyValue = CInt(Int((6 * Rnd()) + 1)) ' Generate random value between 1 and
6.above is MS code

regards

Michel Posseth [MCP]
 
Do you have a direct link to that sample?

To me, that looks like VB6 code. Not the way it would be
written in VB.Net.

/JB



The code i showed i have also run ,,, and it worked as dices in my situation

also nice thingy is that alter after the comments i searched the msdn site
and saw that MS does it the same way

Dim MyValue As Integer
Randomize ' Initialize random-number generator.
MyValue = CInt(Int((6 * Rnd()) + 1)) ' Generate random value between 1 and
6.above is MS code

regards

Michel Posseth [MCP]


Cap'n Ahab said:
I have also noticed that instead of using Shared, if I rewrite the
New() constructor by adding the Randomize() command as below...

Public Sub New()
Randomize()
Me.Roll()
End Sub

and then consume 3 objects based on the Die Class,

Then use the same loop..

Dim One As New Die
Dim Two As New Die
Dim Three As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()
Three.Display()
Three.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

That the results are not random between the die at all!! The numbers
either are 3 of a the same number OR a pair of numbers an other other
number - no exceptions. How very odd!

Now I AM confused, lol.
 
That is why I included the Randomize source from .Net Reflector.

To show you that Randomize just uses the current time and creates
a value by shuffling the bits around.

When you instantiate multiple Die objects within the same fraction
of a second, they will get the same seed value.

Which is why you are getting two or three identical start values
in your example below.

Which is why you do not want to use Randomize.

/Joergen Bech
 
nope this is VB.Net code

here it is

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmrandomize.asp

Michel Posseth [MCP]



Joergen Bech @ post1.tele.dk> said:
Do you have a direct link to that sample?

To me, that looks like VB6 code. Not the way it would be
written in VB.Net.

/JB



The code i showed i have also run ,,, and it worked as dices in my
situation

also nice thingy is that alter after the comments i searched the msdn site
and saw that MS does it the same way

Dim MyValue As Integer
Randomize ' Initialize random-number generator.
MyValue = CInt(Int((6 * Rnd()) + 1)) ' Generate random value between 1 and
6.above is MS code

regards

Michel Posseth [MCP]


Cap'n Ahab said:
I have also noticed that instead of using Shared, if I rewrite the
New() constructor by adding the Randomize() command as below...

Public Sub New()
Randomize()
Me.Roll()
End Sub

and then consume 3 objects based on the Die Class,

Then use the same loop..

Dim One As New Die
Dim Two As New Die
Dim Three As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()
Three.Display()
Three.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

That the results are not random between the die at all!! The numbers
either are 3 of a the same number OR a pair of numbers an other other
number - no exceptions. How very odd!

Now I AM confused, lol.
 
The Random object is shared and only instantiated once.

So if we call the Random object R and three Die objects D1, D2, and
D3,
you have

R = New Random(...<some seed value from the system tick count>)
D1 = R.Next(...) (=first value in the random sequence)
D2 = R.Next(...) (=second value in the random sequence)
D3 = R.Next(...) (=third value in the random sequence)

and so on.

You might say that this is not entirely correct when mapped to the
real world because the roll of one die actually uses the next rnd
value in a common sequence, which means that rolling D1, D2, D3
produces different results than D1, D3, D2, as one die "uses" a
value that might have been used by another die, i.e. the die objects
do not follow separate sequences (do not have their own Random
object).

But do we (I) care? Nah! :) Keep it simple.

As for appreciating how the Random class works vs OOP? In this
case I think it is a little bit of both. Try setting a breakpoint on
the "Private Shared _generator As New Random" line and run
the code again.

The Random object returns an Integer value (works with Integer
values internally - don't be fooled by the Double functions) from
a sequence calculated using a 56-integer array based on the seed
value in the constructor (tick count if no seed is supplied). Whenever
the .Next function is used, the "next" value from the "sequence" is
returned, the seed array is updated, some indexes to the seed array
are updated, etc. Something like that.

I have not tested how "random" the output from the Random object is.

Seems to be optimized for speed.

I am not sure the algorithm used would be accepted in a Las Vegas
gambling machine, but I suppose it is alright for most everyday uses.

Hope any of this helps.

/JB
 
Look at this sample for Visual Basic Scripting Edition (which is sort
of a subset of VB6):
http://msdn.microsoft.com/library/d...html/ac1ef1bb-f1d8-4369-af7f-ddd89c926250.asp
(tinyurl: http://tinyurl.com/ants9)

Looks familliar? If you search for Rnd and Randomize, sure you will
end up on those pages. But if you search for something about working
with random numbers in .Net in general, hopefully you will find pages
talking about the Random class.

Rnd and Randomize is part of the Microsoft.VisualBasic namespace
(in the VBMath class).

Rnd returns a single and uses the "classic VB" Rnd algorithm.
It is not the same as using the Random class. It is not even a wrapper
for the Random class.

I have said it before: Microsoft.VisualBasic.* is not "proper" .Net.
So I won't say it again. Or someone will tell me off :)

/JB



nope this is VB.Net code

here it is

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmrandomize.asp

Michel Posseth [MCP]



Joergen Bech @ post1.tele.dk> said:
Do you have a direct link to that sample?

To me, that looks like VB6 code. Not the way it would be
written in VB.Net.

/JB



The code i showed i have also run ,,, and it worked as dices in my
situation

also nice thingy is that alter after the comments i searched the msdn site
and saw that MS does it the same way

Dim MyValue As Integer
Randomize ' Initialize random-number generator.
MyValue = CInt(Int((6 * Rnd()) + 1)) ' Generate random value between 1 and
6.above is MS code

regards

Michel Posseth [MCP]


I have also noticed that instead of using Shared, if I rewrite the
New() constructor by adding the Randomize() command as below...

Public Sub New()
Randomize()
Me.Roll()
End Sub

and then consume 3 objects based on the Die Class,

Then use the same loop..

Dim One As New Die
Dim Two As New Die
Dim Three As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()
Three.Display()
Three.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

That the results are not random between the die at all!! The numbers
either are 3 of a the same number OR a pair of numbers an other other
number - no exceptions. How very odd!

Now I AM confused, lol.
 
"m.posseth" <[email protected]> ha scritto nel messaggio

but i have never found a true answer for it as the thoughts seem to change
day by day regarding this issue

one says the IA-32 architecture processes 32-bit integers just as
fast as 16-bit or 8-bit integers and that the smallest possible size will
preserve memory

Just put Option Strict On and the compiler will correct some extra issues
about 16/32 bits.
 
well in this case we do not share the same point of view , VB = VB if it
works it works

Yes i know VBS and for a fact VBA and VB starting of the folowup to Qbasic
and even better i started with Basic when a PC was a C64

The Microsoft.visualbasic namespace is standard availlable in a VB project
, Do we ask a C# coder to abandon the C# namespace ??
so what is your point in saying Microsoft.VisualBasic.* is not "proper"
..Net

i have chosen to code mainly on the .Net platform in VB.Net so i can use
all of its main advantages faster , simpler coding just to call two of
them

If its name was Microsoft.VisualBasic6.* i would say you are right but in
this case it is just a valid namespace just like anny other

And you were talking about the KIBS principle ?? ( Keep It Bloody Simple )

Are you sure you are not actually a C# progger trying to make VB just as
ineficient ? ;-)


regards

i quit for now ,,, going to travel to my family to start celebrating the
new year with a nice dinner and champagne

so finally

I wish You and your family and everyone else who might read this thread A
verry happy and healthy 2006

Watch out with fireworks !! i would say ,,, clear your thoughts give
everyone who nags you a new chance :-)


Michel Posseth [MCP]



Joergen Bech @ post1.tele.dk> said:
Look at this sample for Visual Basic Scripting Edition (which is sort
of a subset of VB6):
http://msdn.microsoft.com/library/d...html/ac1ef1bb-f1d8-4369-af7f-ddd89c926250.asp
(tinyurl: http://tinyurl.com/ants9)

Looks familliar? If you search for Rnd and Randomize, sure you will
end up on those pages. But if you search for something about working
with random numbers in .Net in general, hopefully you will find pages
talking about the Random class.

Rnd and Randomize is part of the Microsoft.VisualBasic namespace
(in the VBMath class).

Rnd returns a single and uses the "classic VB" Rnd algorithm.
It is not the same as using the Random class. It is not even a wrapper
for the Random class.

I have said it before: Microsoft.VisualBasic.* is not "proper" .Net.
So I won't say it again. Or someone will tell me off :)

/JB



nope this is VB.Net code

here it is

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmrandomize.asp

Michel Posseth [MCP]



Joergen Bech @ post1.tele.dk> said:
Do you have a direct link to that sample?

To me, that looks like VB6 code. Not the way it would be
written in VB.Net.

/JB



On Sat, 31 Dec 2005 15:33:08 +0100, "m.posseth"

The code i showed i have also run ,,, and it worked as dices in my
situation

also nice thingy is that alter after the comments i searched the msdn
site
and saw that MS does it the same way

Dim MyValue As Integer
Randomize ' Initialize random-number generator.
MyValue = CInt(Int((6 * Rnd()) + 1)) ' Generate random value between 1
and
6.above is MS code

regards

Michel Posseth [MCP]


I have also noticed that instead of using Shared, if I rewrite the
New() constructor by adding the Randomize() command as below...

Public Sub New()
Randomize()
Me.Roll()
End Sub

and then consume 3 objects based on the Die Class,

Then use the same loop..

Dim One As New Die
Dim Two As New Die
Dim Three As New Die

Do

One.Display()
One.Roll()
Two.Display()
Two.Roll()
Three.Display()
Three.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

That the results are not random between the die at all!! The numbers
either are 3 of a the same number OR a pair of numbers an other other
number - no exceptions. How very odd!

Now I AM confused, lol.
 
i code always with option strict on ( and option explicit )
this is been set above every code module i write

but in this case it was a change to the TS`s code so it wasn`t there

but Zanna what is the problem with this code then ??

Option Explicit On

Option Strict On

Public Class Die

Private _value As Int16

Public Sub New()

Randomize()

Me.Roll()

End Sub

Public ReadOnly Property GetValue() As Int16

Get

Return _value

End Get

End Property

Public Sub Roll()

' Initialize the random-number generator.

Randomize()

' Generate random value between 1 and 6.

_value = CShort(CInt(Int((6 * Rnd()) + 1)))

End Sub

Public Sub Display()

Console.WriteLine(Me.GetValue)

End Sub

End Class



Module Module1

Sub Main()

Dim One As New Die

Dim Two As New Die

Do

One.Display()

One.Roll()

Two.Display()

Two.Roll()

Loop Until UCase(Console.ReadLine) = "Q"

End Sub

End Module

in my opinion this is perfectly valid code ,,,, correct me if i am wrong

regards

Michel Posseth [MCP]
 
The Microsoft.visualbasic namespace is standard availlable in a VB project
, Do we ask a C# coder to abandon the C# namespace ??

I don't think there *is* a C# namespace :)
so what is your point in saying Microsoft.VisualBasic.* is not "proper"
.Net

Sorry. Let me put this another way: I do not use functionality from
the Microsoft.VisualBasic.* namespace if similar functionality can be
found in mscorlib.
i have chosen to code mainly on the .Net platform in VB.Net so i can use
all of its main advantages faster , simpler coding just to call two of
them

If its name was Microsoft.VisualBasic6.* i would say you are right but in
this case it is just a valid namespace just like anny other

In this case, the choice between Rnd and Random is not just a choice
between two namespaces; it is a choice between two different
algorithms.

As I think I have mentioned in another thread, I consider
Microsoft.VisualBasic to be a wrapper around similar functionality
found elsewhere in the framework. A wrapper Microsoft made to
make the transition to .Net easier for the millions of classic VB
developers out there. I wish they would mark the whole namespace
obsolete.
And you were talking about the KIBS principle ?? ( Keep It Bloody Simple )

I think Random.Next(1, 7) is simpler than CInt(Int(Rnd*6)+1).

Actually, CInt(<somevalue>) is the same as CType(<somevalue>, Integer)
While the latter requires more typing, this is another case where I
try to get my head out of classic VB and stop using CInt, CLng, etc.
and start using the "new" way. Why have two ways of doing the
same thing? It can only lead to more of those code formatting wars
(like indentation, use of comments, curly braces, etc).
Are you sure you are not actually a C# progger trying to make VB just as
ineficient ? ;-)

I only look at C# code when I need to port something to VB.Net.
Sometimes I can only find a sample for something in C#.

I consider myself to be a .Net programmer who happens to use the
VB syntax due to having worked with classic VB for many years.

Remember: It is called CLR as in Common *Language* Runtime.

We are all friends here :)
i quit for now ,,, going to travel to my family to start celebrating the
new year with a nice dinner and champagne

Yep. Plenty of time for VB next year.

Regards,

Joergen Bech
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top