matrices multiplication

matrices multiplication

Postby Antonio Linares » Sun Oct 11, 2020 11:53 am

Code: Select all  Expand view  RUN
function Main()

   ? MatrixMult( { { 1, 2 }, { 3, 4 } },;
                 { { 11, 12 }, { 13, 14 } } )
         
return nil

function MatrixMult( aMatrix1, aMatrix2 )

   local nRowsMatrix1 := Len( aMatrix1 )
   local nColsMatrix2 := Len( aMatrix2[ 1 ] )
   local aRowMatrix1, aRowMatrix2, nValRowMatrix1
   local aResult := Array( nRowsMatrix1, nColsMatrix2  ), nSum
   
   aMatrix2 = ArrTranspose( aMatrix2, .F. )
   
   for each aRowMatrix1 in aMatrix1
      for each aRowMatrix2 in aMatrix2
        nSum = 0
        for each nValRowMatrix1 in aRowMatrix1
          ? nValRowMatrix1, aRowMatrix2[ nValRowMatrix1:__enumIndex ]
          nSum += nValRowMatrix1 * aRowMatrix2[ nValRowMatrix1:__enumIndex ]
        next
        aResult[ aRowMatrix1:__enumIndex, aRowMatrix2:__enumIndex ] = nSum
      next        
   next      

return aResult

function ArrTranspose( aArray, lSquare )

   local nRows, nCols, nRow, nCol, nWidth
   local aNew

   // DEFAULT lSquare := .f.

   nRows          := Len( aArray )
   if lSquare
      nCols       := Len( aArray[ 1 ] )
   else
      nCols       := 1
      for nRow := 1 to nRows
         if ValType( aArray[ nRow ] ) == 'A'
            nCols    := Max( nCols, Len( aArray[ nRow ] ) )
         endif
      next
   endif

   aNew           := Array( nCols, nRows )
   for nRow := 1 to nRows
      if ValType( aArray[ nRow ] ) == 'A'
         nWidth  := Len( aArray[ nRow ] )
         for nCol := 1 to nWidth
            aNew[ nCol, nRow ]   := aArray[ nRow, nCol ]
         next
      else
         aNew[ 1, nRow ]      := aArray[ nRow ]
      endif
   next

return aNew

   
   
 


1 11
2 13
1 12
2 14
3 11
4 13
3 12
4 14
{{37, 40}, {85, 92}}
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby Antonio Linares » Mon Oct 12, 2020 10:14 am

Image
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby AntoninoP » Mon Oct 12, 2020 10:27 am

Nice, now some home work: :wink:
Image

I tryed:
Code: Select all  Expand view  RUN
function Main()
   LOCAL a := { { 1, 2, 3 }, { 4, 5, 6 } }
   LOCAL b := { { 11, 12 }, { 13, 14 }, { 15, 16 } }
   ? hb_ValToExp(MatrixMult( a,b ))
   ? hb_ValToExp(MatrixMult( b,a ))

return nil


and the result is
Image
AntoninoP
 
Posts: 375
Joined: Tue Feb 10, 2015 9:48 am
Location: Albenga, Italy

Re: matrices multiplication

Postby AntoninoP » Mon Oct 12, 2020 10:54 am

The fix is pretty simple, you swapped row and col creating the result, here the correct creation:

Code: Select all  Expand view  RUN
function MatrixMult( aMatrix1, aMatrix2 )

   local nRowsMatrix1 := Len( aMatrix1 )
   local nColsMatrix2 := Len( aMatrix2[ 1 ] )
   local aRowMatrix1, aRowMatrix2, nValRowMatrix1
   local aResult := Array( nRowsMatrix1, nColsMatrix2  ), nSum

in the original was
Code: Select all  Expand view  RUN
local aResult := Array( nColsMatrix1, nRowsMatrix2 )
AntoninoP
 
Posts: 375
Joined: Tue Feb 10, 2015 9:48 am
Location: Albenga, Italy

Re: matrices multiplication

Postby Antonio Linares » Mon Oct 12, 2020 11:46 am

Dear Antonino,

Fixed in my original post

many thanks!

Mr. Rao has managed to implement an enhanced version that does not need the ArrTranspose() call.
It will be included in the next FWH build:
Code: Select all  Expand view  RUN
function MatrixMult( matrix1, matrix2 )

   local MatrixA, MatrixB, i, j, nRow, nCol, nRows, nCols, nSize, result, dot_product
   local cType

   // Check for scalar multiplication
   cType    := ValType( matrix1 ) + ValType( matrix2 )
   if cType != "AA"
      if cType == "NN"
         return matrix1 * matrix2
      elseif cType == "NA" .or. cType == "AN"
         if cType == "NA"
            i        := matrix1
            MatrixA  := matrix2
         else
            i        := matrix2
            MatrixA  := matrix1
         endif
         if !HB_ISARRAY( MatrixA[ 1 ] ); MatrixA := { MatrixA }; endif
         nSize       := Len( MatrixA[ 1 ] )
         for nRow := 1 to Len( MatrixA )
            for nCol := 1 to nSize
               MatrixA[ nRow, nCol ] *= i
            next
         next
         return MatrixA
      else
         ? "Invalid params"
         return nil
      endif
   endif

   if Empty( matrix1 ) .or. Empty( matrix2 )
      ? "Empty matrix"
      return nil
   endif

   // Check for single-dim arrays
   if !( HB_ISARRAY( matrix1[ 1 ] ) ); matrix1 := { matrix1 }; endif
   if !( HB_ISARRAY( matrix2[ 1 ] ) ); AEval( matrix2, { |x,i| matrix2[ i ] := { x } } ); endif

   MatrixA  := matrix1
   MatrixB  := matrix2

   // Actual Matrix Multiplication
   result   := Array( nRows := Len( MatrixA ), nCols := Len( MatrixB[ 1 ] ) )

   for nRow := 1 to nRows
      for nCol := 1 to nCols
         dot_product    := 0
         TRY
            AEval( MatrixA[ nRow ], { |n,i| dot_product += n * MatrixB[ i, nCol ] } )
         CATCH
         END
         result[ nRow, nCol ] := dot_product
      next
   next

return result
 
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby nageswaragunupudi » Mon Oct 12, 2020 11:50 am

All the new matrix functions are still under testing and may still need some fixes.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10656
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: matrices multiplication

Postby cnavarro » Mon Oct 12, 2020 3:13 pm

With ArrTranspose

Code: Select all  Expand view  RUN


function Main()

   // [ 2, 2 ] x [ 2, 2 ] = [ 2, 2 ]
   local aMatriz1 := { { 1, 2 }, { 3, 4 } }
   local aMatriz2 := { { 11, 12 }, { 13, 14 } }

   // [ 1, 3 ] x [ 3, 1 ] = [ 2, 1 ]
   //local aMatriz1 := { { 1, -2, 3 } }
   //local aMatriz2 := { { 4 }, { 5 }, { 6 } }

   // [ 2, 3 ] x [ 3, 1 ] = [ 2, 1 ]
   //local aMatriz1 := { { 1, -2, 3 }, { 1, 0, -1 } }
   //local aMatriz2 := { { 4 }, { 5 }, { 6 } }

   // [ 1, 3 ] x [ 3, 2 ] = [ 1, 2 ]
   //local aMatriz1 := { { 1, -2, 3 } }
   //local aMatriz2 := { { 4, 2 }, { 5, 0 }, { 6, 0 } }

   // [ 1, 2 ] x [ 2, 2 ] = [ 1, 2 ]
   //local aMatriz1 := { { 1, 5 } }
   //local aMatriz2 := { { 1, 2 }, { -2, 0 } }

   // [ 2, 2 ] x [ 2, 3 ] = [ 2, 3 ]
   //local aMatriz1 := { { 1, 2 }, { -2, 0 } }
   //local aMatriz2 := { { 1, 0, 2 }, { 0, 2, 0 } }

   // [ 1, 2 ] x [ 2, 1 ] = [ 1, 1 ]
   //local aMatriz1 := { { 1, -1 } }
   //local aMatriz2 := { { 5 }, { 5 } }

   // [ 3, 2 ] x [ 2, 3 ] = [ 3, 3 ]
   //local aMatriz1 := { { 1, 2 }, { 0, -1 }, { 0, 1 } }
   //local aMatriz2 := { { 0, 3, 0 }, { 3, 0, 3 } }

   // [ 3, 3 ] x [ 3, 1 ] = [ 3, 1 ]
   //local aMatriz1 := { { 1, 0, 2 }, { 0, 2, 0 }, { 0, 1, 3 } }
   //local aMatriz2 := { { 5 }, { -1 }, { 0 } }

   // [ 3, 3 ] x [ 3, 2 ] = [ 3, 2 ]
   //local aMatriz1 := { { 1, 2, 3 }, { 0, 1, 0 }, { 3, 2, 1 } }
   //local aMatriz2 := { { 1, -1 }, { 0, 2 }, { -2, 0 } }

   // [ 3, 3 ] x [ 3, 3 ] = [ 3, 3 ]
   //local aMatriz1 := { { 1, -1, 1 }, { 2, 2, 3 }, { -2, -3, -1 } }
   //local aMatriz2 := { { 1, 0, 4 }, { 0, 2, 5 }, { 1, 3, 0 } }


   // [ 2, 3 ] x [ 3, 2 ] = [ 2, 2 ]
   //local aMatriz1 := { { 1, 2, 6 }, { 3, 4, 9 } }
   //local aMatriz2 := { { 11, 12 }, { 13, 14 }, { 6, 9 } }

   if Len( aMatriz1[ 1 ] ) = Len( aMatriz2 )
      MatrixMult( aMatriz1, aMatriz2 )
   else
      ? "Only multiply matrix if number columns first matrix equal to number rows second matrix"
   endif
         
return nil

function MatrixMult( aMatrix1, aMatrix2 )

   local x
   local y
   local z
   local nRowsMatrix1 := Len( aMatrix1 )
   local nColsMatrix1 := Len( aMatrix1[ 1 ] )
   local nRowsMatrix2 := Len( aMatrix2 )
   local nColsMatrix2 := Len( aMatrix2[ 1 ] )
   local aResult := Array( nRowsMatrix1, nColsMatrix2  )
   local nSum    := 0
   local nVal    := 0
   local nIter   := 0
   local aRowMatrix1
   local aRowMatrix2
   local nValRowMatrix1

   // Show Matrix1, Matrix2   
   ?
   For x = 1 to Len( aMatrix1 )
      ?? "[ "
      For y = 1 to Len( aMatrix1[ x ] )
         ?? aMatrix1[ x ][ y ]
         ??  "   "
      Next y
      ?? " ]"
      ?
   Next x
   ?
   For x = 1 to Len( aMatrix2 )
      ?? "[ "
      For y = 1 to Len( aMatrix2[ x ] )
         ?? aMatrix2[ x ][ y ]
         ??  "   "
      Next y
      ?? " ]"
      ?
   Next x

   aMatrix2 = ArrTranspose( aMatrix2, .F. )
   ? "Transpose Matrix2"
   ?
   For x = 1 to Len( aMatrix2 )
      ?? "[ "
      For y = 1 to Len( aMatrix2[ x ] )
         ?? aMatrix2[ x ][ y ]
         ??  "   "
      Next y
      ?? " ]"
      ?
   Next x

   // Multiply matrix

   For x = 1 to Len( aMatrix1 )
      nSum   := 0
      aRowMatrix1 := aMatrix1[ x ]
      For y = 1 to nColsMatrix2
          aRowMatrix2 := aMatrix2[ y ]
          nSum   := 0
          For z = 1 to Len( aRowMatrix2 )
             nVal := aRowMatrix1[ z ] * aRowMatrix2[ z ]
             nSum += nVal
          Next z
          aResult[ x ][ y ] := nSum
      Next y
   Next x

   // Show Result
   ? "Result"
   ?
   For x = 1 to Len( aResult )
      ?? "[ "
      For y = 1 to Len( aResult[ x ] )
         ?? aResult[ x ][ y ]
         ??  "   "
      Next y
      ?? " ]"
      ?
   Next x

return aResult

function ArrTranspose( aArray, lSquare )

   local nRows, nCols, nRow, nCol, nWidth
   local aNew

   // DEFAULT lSquare := .f.

   nRows          := Len( aArray )
   if lSquare
      nCols       := Len( aArray[ 1 ] )
   else
      nCols       := 1
      for nRow := 1 to nRows
         if ValType( aArray[ nRow ] ) == 'A'
            nCols    := Max( nCols, Len( aArray[ nRow ] ) )
         endif
      next
   endif

   aNew           := Array( nCols, nRows )
   for nRow := 1 to nRows
      if ValType( aArray[ nRow ] ) == 'A'
         nWidth  := Len( aArray[ nRow ] )
         for nCol := 1 to nWidth
            aNew[ nCol, nRow ]   := aArray[ nRow, nCol ]
         next
      else
         aNew[ 1, nRow ]      := aArray[ nRow ]
      endif
   next

return aNew

 
Cristobal Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
User avatar
cnavarro
 
Posts: 6549
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: matrices multiplication

Postby cnavarro » Mon Oct 12, 2020 8:24 pm

Without ArrTranspose
Code: Select all  Expand view  RUN


function Main()

   // [ 2, 2 ] x [ 2, 2 ] = [ 2, 2 ]
   local aMatriz1 := { { 1, 2 }, { 3, 4 } }
   local aMatriz2 := { { 11, 12 }, { 13, 14 } }

   // [ 1, 3 ] x [ 3, 1 ] = [ 2, 1 ]
   //local aMatriz1 := { { 1, -2, 3 } }
   //local aMatriz2 := { { 4 }, { 5 }, { 6 } }

   // [ 2, 3 ] x [ 3, 1 ] = [ 2, 1 ]
   //local aMatriz1 := { { 1, -2, 3 }, { 1, 0, -1 } }
   //local aMatriz2 := { { 4 }, { 5 }, { 6 } }

   // [ 1, 3 ] x [ 3, 2 ] = [ 1, 2 ]
   //local aMatriz1 := { { 1, -2, 3 } }
   //local aMatriz2 := { { 4, 2 }, { 5, 0 }, { 6, 0 } }

   // [ 1, 2 ] x [ 2, 2 ] = [ 1, 2 ]
   //local aMatriz1 := { { 1, 5 } }
   //local aMatriz2 := { { 1, 2 }, { -2, 0 } }

   // [ 2, 2 ] x [ 2, 3 ] = [ 2, 3 ]
   //local aMatriz1 := { { 1, 2 }, { -2, 0 } }
   //local aMatriz2 := { { 1, 0, 2 }, { 0, 2, 0 } }

   // [ 1, 2 ] x [ 2, 1 ] = [ 1, 1 ]
   //local aMatriz1 := { { 1, -1 } }
   //local aMatriz2 := { { 5 }, { 5 } }

   // [ 3, 2 ] x [ 2, 3 ] = [ 3, 3 ]
   //local aMatriz1 := { { 1, 2 }, { 0, -1 }, { 0, 1 } }
   //local aMatriz2 := { { 0, 3, 0 }, { 3, 0, 3 } }

   // [ 3, 3 ] x [ 3, 1 ] = [ 3, 1 ]
   //local aMatriz1 := { { 1, 0, 2 }, { 0, 2, 0 }, { 0, 1, 3 } }
   //local aMatriz2 := { { 5 }, { -1 }, { 0 } }

   // [ 3, 3 ] x [ 3, 2 ] = [ 3, 2 ]
   //local aMatriz1 := { { 1, 2, 3 }, { 0, 1, 0 }, { 3, 2, 1 } }
   //local aMatriz2 := { { 1, -1 }, { 0, 2 }, { -2, 0 } }

   // [ 3, 3 ] x [ 3, 3 ] = [ 3, 3 ]
   //local aMatriz1 := { { 1, -1, 1 }, { 2, 2, 3 }, { -2, -3, -1 } }
   //local aMatriz2 := { { 1, 0, 4 }, { 0, 2, 5 }, { 1, 3, 0 } }


   // [ 2, 3 ] x [ 3, 2 ] = [ 2, 2 ]
   //local aMatriz1 := { { 1, 2, 6 }, { 3, 4, 9 } }
   //local aMatriz2 := { { 11, 12 }, { 13, 14 }, { 6, 9 } }

   if Len( aMatriz1[ 1 ] ) = Len( aMatriz2 )
      MatrixMult( aMatriz1, aMatriz2 )
   else
      ? "Only multiply matrix if number columns first matrix equal to number rows second matrix"
   endif
         
return nil

function ShowMatrix( aMatrix )

   local x
   local y

   ?
   For x = 1 to Len( aMatrix )
      ?? "[ "
      For y = 1 to Len( aMatrix[ x ] )
         ?? aMatrix[ x ][ y ]
         ??  "   "
      Next y
      ?? " ]"
      ?
   Next x

Return nil


function MatrixMult( aMatrix1, aMatrix2 )

   local x
   local y
   local z
   local nRowsMatrix1 := Len( aMatrix1 )
   local nColsMatrix1 := Len( aMatrix1[ 1 ] )
   local nRowsMatrix2 := Len( aMatrix2 )
   local nColsMatrix2 := Len( aMatrix2[ 1 ] )
   local aResult := Array( nRowsMatrix1, nColsMatrix2  )
   local nSum    := 0
   local nVal0   := 0
   local nVal1   := 0
   local nIter   := 0

   ShowMatrix( aMatrix1 )
   ShowMatrix( aMatrix2 )

   For x = 1 to Len( aMatrix1 )
      For y = 1 to nColsMatrix2
         nSum   := 0
         For z = 1 to nRowsMatrix2
            nVal0  := aMatrix1[ x ][ z ]
            nVal1 := nVal0 * aMatrix2[ z ][ y ]
            nSum  += nVal1
         Next z
         aResult[ x ][ y ] := nSum
      Next y
   Next x

   ? "Resultado"
   ShowMatrix( aResult )

return aResult


 
Cristobal Navarro
Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo
El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
User avatar
cnavarro
 
Posts: 6549
Joined: Wed Feb 15, 2012 8:25 pm
Location: España

Re: matrices multiplication

Postby Antonio Linares » Tue Oct 13, 2020 7:36 am

Shortest version ? :-)

Code: Select all  Expand view  RUN
function Main()

   ? MatrixMult( { { 1, 2 }, { 3, 4 } },;
                 { { 11, 12 }, { 13, 14 } } )

return nil

function MatrixMult( aMatrix1, aMatrix2 )

   local aRowMatrix1, nColMatrix2, nRowMatrix2
   local aResult := Array( Len( aMatrix1 ), Len( aMatrix2[ 1 ] ) )

   for each aRowMatrix1 in aMatrix1
      for nColMatrix2 = 1 to Len( aMatrix2[ 1 ] )
         nSum = 0
         for nRowMatrix2 = 1 to Len( aMatrix2 )
            nSum += aRowMatrix1[ nRowMatrix2 ] * aMatrix2[ nRowMatrix2 ][ nColMatrix2 ]
         next
         aResult[ aRowMatrix1:__enumIndex ][ nColMatrix2 ] := nSum
      next
   next    
   
return aResult  


{{37, 40}, {85, 92}}
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby Antonio Linares » Tue Oct 13, 2020 8:22 am

Shortest version based on Mr. Rao version:

Code: Select all  Expand view  RUN
function Main()

   ? MatrixMult( { { 1, 2 }, { 3, 4 } },;
                 { { 11, 12 }, { 13, 14 } } )

return nil

function MatrixMult( aMatrix1, aMatrix2 )

   local aRowMatrix1, nSum, nCols := Len( aMatrix2[ 1 ] )
   local aResult := Array( Len( aMatrix1 ), nCols )

   for each aRowMatrix1 in aMatrix1
      for nCol := 1 to nCols
         nSum = 0
         AEval( aRowMatrix1, { |n,i| nSum += n * aMatrix2[ i, nCol ] } )
         aResult[ aRowMatrix1:__enumIndex, nCol ] = nSum
      next
   next

return aResult
 


{{37, 40}, {85, 92}}
{ { 1*11 + 2*13, 1*12 + 2*14 }, { 3*11 + 4*13, 3*12 + 4*14 }}
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby Silvio.Falconi » Tue Oct 13, 2020 5:58 pm

I am very intrigued, I did not understand what you are trying to do, I want to participate too if it comes to things to eat (cakes, sweets, ..) even if I have to go on a diet I know !!!
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour November 2023 - January 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
User avatar
Silvio.Falconi
 
Posts: 7075
Joined: Thu Oct 18, 2012 7:17 pm

Re: matrices multiplication

Postby Antonio Linares » Tue Oct 13, 2020 6:09 pm

regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: matrices multiplication

Postby Silvio.Falconi » Tue Oct 13, 2020 6:54 pm

Antonio Linares wrote:Dear Silvio,

viewtopic.php?f=44&t=39467


Dear antonio

I know that Python

it is a multi-paradigm language, which supports both procedural programming (which makes use of functions) and object-oriented programming (including features such as single and multiple inheritance, but Harbor also supports these two things. and then I don't understand what is needed to convert programs made on python to harbour that is, I do not see the need, if you have something else in mind that I do not know I raise my hands
a colleague of mine ( school) builds programs with python but uses additional tools for graphics and more (TkInter which also includes the graphical interface management system, which is what you see when you use the program. Other tools to mention are PYQt and wxPython.), he use pyton to create games (PyGame, PyKyra and the 3D rendering engines)

There are things that Python can't do well, or at all.

For example it is not a low-level language and therefore not good for creating components such as drivers.

It is not suitable for creating cross-platform executables and it is not even the ideal choice if the code must be as fast as possible.

Historically it has been mainly used for scripting and automation elements in fact my colleague uses it with Arduino
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour November 2023 - January 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
User avatar
Silvio.Falconi
 
Posts: 7075
Joined: Thu Oct 18, 2012 7:17 pm

Re: matrices multiplication

Postby Antonio Linares » Tue Oct 13, 2020 9:21 pm

Dear Silvio,

My interest in porting those 9 lines from Python to Harbour is in building a small and simple neural network to be used from Harbour and mainly for learning purposes :-)
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain


Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 69 guests