Matrix Multiplication in Java Without Using Imports - java

I'm trying to make a method that reads input from 2 files, each containing a matrix. The objective is to check if they can be multiplied (if the length of the rows of one is the same as the length of the columns in the other) then create a product matrix of the two inputted matrices. Here is what I have so far. It doesn't work, and I wasn't expecting it to. I just want to get some help on the logic part of the method.
public class MatrixOps {
public static double[][] multiply(double[][] matrix1, double[][] matrix2) {
int matrix1Cols = matrix1.length;
int matrix1Rows = matrix1[0].length;
int matrix2Cols = matrix2.length;
int matrix2Rows = matrix2[0].length;
double[][] productMatrix = new double[0][0];
if (matrix1Rows == matrix2Cols) {
productMatrix = new double[matrix1Cols][matrix2Rows];
for (int i = 0; i < matrix1Cols; i++) {
for (int j = 0; j < matrix1Rows; j++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][i];
for (int k = 0; k < matrix2Rows; k++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][k];
}
}
}
}
if (matrix1Cols == matrix2Rows) {
productMatrix = new double[matrix2Cols][matrix1Rows];
for (int i = 0; i < matrix2Rows; i++) {
for (int j = 0; j < matrix1Cols; j++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][i];
for (int k = 0; k < matrix2Rows; k++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][k];
}
}
}
}
return productMatrix;
}
}
Any ideas on how to get it to work?

First of all, not necessary to do product matrix on both loops:
for (int i = 0; i < matrix1Cols; i++) {
for (int j = 0; j < matrix1Rows; j++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][i];
for (int k = 0; k < matrix2Rows; k++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][k];
}
}
This is sufficient:
for (int i = 0; i < matrix1Cols; i++) {
for (int j = 0; j < matrix1Rows; j++) {
for (int k = 0; k < matrix2Rows; k++) {
productMatrix[i][j] += matrix1[i][j] * matrix2[j][k];
}
}
}
Also if you are changing the order of the matrix multiplications, then it must be reflected on your operation (seems you are just doing copy paste)
so on the second part:
if (matrix1Cols == matrix2Rows) {
The matrix multiplication has to change from:
productMatrix[i][j] += matrix1[i][j] * matrix2[j][k];
to:
productMatrix[i][j] += matrix2[i][j] * matrix1[j][k];

Related

how to print the below pattern

for the given input I need to print the pattern. For example for input = 6 I have to print:
MMMMMMSDDDDDD
MMMMMSSSDDDDD
MMMMSSSSSDDDD
MMMSSSSSSSDDD
MMSSSSSSSSSDD
MSSSSSSSSSSSD
CSSSSSSSSSSSK
CCSSSSSSSSSKK
CCCSSSSSSSKKK
CCCCSSSSSKKKK
CCCCCSSSKKKKK
CCCCCCSKKKKKK
I have tried but couldn't go further than this could anyone help
public class tgk {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int size = sc.nextInt();
int half = ((2*size)+1)/2;
for (int i = 0; i < size ; i++)
{
for (int j = size; j > i; j--)
{
System.out.print("M");
}
for (int k = half+1 ; k > half - i; k--)
{
System.out.print("S");
}
System.out.println();
}
for(int i = size; i > 0; i--)
{
for (int j = size; j >= i; j--) {
System.out.print("C");
}
for (int k = 0; k < (i * 2 - 1); k++) {
System.out.print("S");
}
System.out.println();
}
}
}
if input = 3 it should be
MMMSDDD
MMSSSDD
MSSSSSD
CSSSSSK
CCSSSKK
CCCSKKK
You can use two sets of for loops to print each half of the pattern. Assuming input variable holds the size of the problem
int input = 3;
for (int i = 0; i < input; i++) {
for (int j = 0; j < input - i; j++) {
System.out.print('M');
}
for (int j = 0; j < 2 * i + 1; j++) {
System.out.print('S');
}
for (int j = 0; j < input - i; j++) {
System.out.print('D');
}
System.out.println();
}
for (int i = input - 1; i >= 0; i--) {
for (int j = 0; j < input - i; j++) {
System.out.print('C');
}
for (int j = 0; j < 2 * i + 1; j++) {
System.out.print('S');
}
for (int j = 0; j < input - i; j++) {
System.out.print('K');
}
System.out.println();
}
will print for input = 3:
MMMSDDD
MMSSSDD
MSSSSSD
CSSSSSK
CCSSSKK
CCCSKKK
and for input = 6:
MMMMMMSDDDDDD
MMMMMSSSDDDDD
MMMMSSSSSDDDD
MMMSSSSSSSDDD
MMSSSSSSSSSDD
MSSSSSSSSSSSD
CSSSSSSSSSSSK
CCSSSSSSSSSKK
CCCSSSSSSSKKK
CCCCSSSSSKKKK
CCCCCSSSKKKKK
CCCCCCSKKKKKK
I don't know why, but I really wanted it to work with only one set of for-loops:
int number = 8;
for (int i = 0; i < number * 2; i++) {
for (int j = 0; j < (number * 2) + 1; j++) {
System.out.print(
i < number && j+i < number ? 'M' :
i < number && j-i > number ? 'D' :
i < number ? 'S' :
i >= number && i-j >= number ? 'C' :
i >= number && j+i >= number*3 ? 'K' :
'S'
);
}
System.out.println();
}
So for 8 (like in the code) it prints:
MMMMMMMMSDDDDDDDD
MMMMMMMSSSDDDDDDD
MMMMMMSSSSSDDDDDD
MMMMMSSSSSSSDDDDD
MMMMSSSSSSSSSDDDD
MMMSSSSSSSSSSSDDD
MMSSSSSSSSSSSSSDD
MSSSSSSSSSSSSSSSD
CSSSSSSSSSSSSSSSK
CCSSSSSSSSSSSSSKK
CCCSSSSSSSSSSSKKK
CCCCSSSSSSSSSKKKK
CCCCCSSSSSSSKKKKK
CCCCCCSSSSSKKKKKK
CCCCCCCSSSKKKKKKK
CCCCCCCCSKKKKKKKK
...or for 3:
MMMSDDD
MMSSSDD
MSSSSSD
CSSSSSK
CCSSSKK
CCCSKKK

Inverse 2D FFT is outputting correct values in wrong order

My 2D FFT algorithm is outputting the correct values, but they are in the wrong order. For example, for input:
1050.0 1147.0 1061.0 1143.0
1046.0 1148.0 1118.0 1073.0
1072.0 1111.0 1154.0 1101.0
1078.0 1101.0 1106.0 1062.0
Taking the FFT, and then inverse FFT results in:
1050.0 1143.0 1061.0 1147.0
1078.0 1062.0 1106.0 1101.0
1072.0 1101.0 1154.0 1111.0
1046.0 1073.0 1118.0 1148.0
You can see that if you flip the last 3 columns horizontally, then the last 3 rows vertically, the data will be correct. As far as I can tell this is true for all input sizes so it's an easy (albeit hacky) fix. I am however worried about about computational time of the fix because I may have to perform this on 1024x1024 or even 2048x2048 images in the future.
I am fairly confident that my 1D FFT algorithm doFFT() is correct, and I am getting the expected values for the forward 2D FFT. It is just the inverse 2D FFT that is causing me trouble.
Does anyone see where my error is?
Code
private static double[] cose;
private static double[] sin;
public static void main(String[] args) {
float[][] img = new float[][]{
{ 1050.0f, 1147.0f, 1061.0f, 1143.0f},
{ 1046.0f, 1148.0f, 1118.0f, 1073.0f},
{ 1072.0f, 1111.0f, 1154.0f, 1101.0f},
{ 1078.0f, 1101.0f, 1106.0f, 1062.0f}
};
int size = img.length;
System.out.println("Image");
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
System.out.print(img[i][j] + "\t");
}
System.out.println();
}
Complex[][] fft = fft2D(toComplex(img), false);
Complex[][] inverse = fft2D(fft, true);
System.out.println("\nInverse");
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
System.out.print(inverse[i][j].getReal() + "\t");
}
System.out.println();
}
}
public static Complex[][] fft2D(Complex[][] pixels, boolean inverse){
int size = pixels.length;
computeCosSin(size);
Complex[][] data = transpose(pixels.clone());
Complex[] temp;
// FFT of rows
for (int i = 0; i < size; i++)
{
temp = doFFT(data[i], size);
data[i] = temp;
}
// FFT of columns
for (int i = 0; i < size; i++)
{
temp = new Complex[size];
for (int j = 0; j < size; j++)
{
temp[j] = data[j][i];
}
Complex[] temp2 = doFFT(temp, size);
for (int j = 0; j < size; j++)
{
data[j][i] = temp2[j];
}
}
if (!inverse)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
data[i][j] = data[i][j].divide(size*size);
}
}
}
return data;
}
public static Complex[] doFFT(Complex[] data, int size){
Complex[] temp = new Complex[size];
int j = 0;
for (int i = 0; i < size; i++) {
temp[i] = data[j];
int k = size / 2;
while ((j >= k) && (k > 0)) {
j -= k;
k /= 2;
}
j += k;
}
Complex n,m,h,f;
for(int i=0; i<size;i+=4){
n = temp[i].add(temp[i+1]);
m = temp[i+2].add(temp[i+3]);
h = temp[i].subtract(temp[i+1]);
f = temp[i+2].subtract(temp[i+3]);
Complex mult = h.add(f.multiply(Complex.I));
Complex sub = h.subtract(f.multiply(Complex.I));
temp[i] = n.add(m);
temp[i+2] = n.subtract(m);
temp[i+1] = sub;
temp[i+3] = mult;
}
int u;
for(int i=4; i< size;i<<=1){
int v = size/(i <<1);
for(int c=0; c< size;c +=i<<1){
for(int x=0; x < i; x++){
u = v*x;
double calc = temp[i+c+x].getReal()*cose[u] - temp[i+c+x].getImaginary()*sin[u];
double calc2 = temp[i+c+x].getReal()*sin[u] + temp[i+c+x].getImaginary()*cose[u];
Complex fftArray = new Complex(calc,calc2);
temp[(i+c+x)] =temp[(c+x)].subtract(fftArray);
temp[(c+x)] = temp[(c+x)].add(fftArray);
}
}
}
return temp;
}
public static Complex[][] toComplex(float[][] arr)
{
Complex[][] newArr = new Complex[arr.length][arr.length];
for (int i = 0; i < arr.length; i++)
{
for (int j = 0; j < arr.length; j++)
{
newArr[i][j] = new Complex(arr[i][j], 0.0);
}
}
return newArr;
}
public static Complex[][] transpose(Complex[][] array)
{
for (int i = 0; i < array.length; i++)
{
for (int j = i+1; j < array[i].length; j++)
{
Complex temp = array[i][j];
array[i][j] = array[j][i];
array[j][i] = temp;
}
}
return array;
}
public static void computeCosSin(int size){
double num = (2.0*Math.PI)/size;
double cos = Math.cos(num);
double sine = Math.sin(num);
cose = new double[size];
sin = new double[size];
cose[0] =1.0;
for(int i=1; i<size;i++){
cose[i] = cos*cose[i-1] + sine*sin[i-1];
sin[i] = cos*sin[i-1] - sine*cose[i-1];
}
}
}
This doesn't solve the root problem, but it does change the data I'm getting to the data I expect so it will serve my purpose for now. I do worry it will be incredibly slow on large arrays.
This function swaps row i with row N-i and then swaps every column i with column N-i, for 0 < i < N, (Assuming a square, power of 2 input array)
public Complex[][] inverseFix(Complex[][] array)
{
int size = array.length;
// Swap rows
Complex[] temp;
for (int i = 1; i < size/2; i++)
{
temp = array[i];
array[i] = array[size-i];
array[size-i] = temp;
}
// Swap columns
Complex temp2;
for (int i = 0; i < size; i++)
{
for (int j = 1; j < size/2; j++)
{
temp2 = array[i][j];
array[i][j] = array[i][size-j];
array[i][size-j] = temp2;
}
}
return array;
}

Java - Selection Sort - only switching once

So the first switch definitely occurs between the lowest value 3, and 5, but it doesn't keep going after that. This makes me think there is something wrong with one of the for loops?
public class SelectionSort
{
public static void main (String[] args)
{
int [] list;
list = new int[5];
list[0] = 4;
list[1] = 5;
list[2] = 12;
list[3] = 9;
list[4] = 3;
for (int i = 0; i < list.length-1; ++i) {
int index = i;
for (int j = 1; j < list.length; ++j) {
if (list[j] < list[index]) {
int temp = list[j];
list[j] = list[index];
list[index] = temp;
}
}
}
for (int k = 0; k < list.length; ++k) {
System.out.print(list[k] + ", ");
}
}
}
Short versión:
for (int i = 0; i < list.length-1; i++)
for (int j = i+1; j < list.length; j++)
if (list[j] < list[i]) {
int temp = list[j];
list[j] = list[i];
list[i] = temp;
}
for (int k = 0; k < list.length; k++) {
System.out.print(list[k] + ", ");
}
after if (list[j] < list[index]) {, you have to update index if boolean statement gets satisfied so you need to index = j and do the swap after
see follwing :
public class MySelectionSort {
public static int[] doSelectionSort(int[] arr){
for (int i = 0; i < arr.length - 1; i++)
{
int index = i;
for (int j = i + 1; j < arr.length; j++)
if (arr[j] < arr[index])
index = j;
int smallerNumber = arr[index];
arr[index] = arr[i];
arr[i] = smallerNumber;
}
return arr;
}
for (int i = 0; i < list.length; ++i) {
int index = i;
for (int j = i + 1; j < list.length; ++j) {
if (list[j] < list[index]) {
index = j; }}
if (index != i) {
int temp = list[i];
list[i] = list[index];
list[index] = i; }}
To cut down on the number of swaps, the loop on j should only be looking the best item to swap with item i. There should be at most one swap for each value of i. (That's what makes it a Selection sort. If you do multiple swaps for each i, you might as well be doing Bubble Sort.
But that's for efficiency. The reason your code isn't working is that you start the loop on j at j = 1 instead of j = i + 1.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 8 [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Why do I get this exception? How to fix it?
public class NQueen_Pro {
// int[] Fitness;
/*
* int[] NQueens1; int[] NQueens2; int[] NQueens3; int[] NQueens4;
*/
int[][] NQueens;
int[][] CrossOver;
int[] Fitness;
int nFitSelected;
long startingTime ;
/*
* int[] CrossOver1; int[] CrossOver2; int[] CrossOver3; int[] CrossOver4;
*/
Random ran;
int length;
public NQueen_Pro(int n) {
Fitness = new int[n];
/*
* NQueens1 = new int[n]; NQueens2 = new int[n]; NQueens3 = new int[n];
* NQueens4 = new int[n];
*/
NQueens = new int[n][n];
CrossOver = new int[n][n];
nFitSelected = 40;
/*
* CrossOver1 = new int[n]; CrossOver2 = new int[n]; CrossOver3 = new
* int[n]; CrossOver4 = new int[n];
*/
length = n;
startingTime = System.currentTimeMillis();
ran = new Random();
while(true)
genetics();
}
public void fillValues() {
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++)
NQueens[i][j] = ran.nextInt(length);
}
}
private void genetics() {
fillValues();
for (int i = 0; i < length; i++) {
countFitness(i, NQueens[i]);
}
ArrangeFromFitness();
CrossOver();
Mutate();
IsDone();
}
private void IsDone() {
/*
* if (IsFit(CrossOver1)) print(CrossOver1); else if (IsFit(CrossOver2))
* print(CrossOver2); else if (IsFit(CrossOver3)) print(CrossOver3);
* else if (IsFit(CrossOver4)) print(CrossOver4); else{ System.gc();
* generics(); }
*/
int gotFit = 0;
for (int i = 0; i < nFitSelected; i++) {
if (IsFit(CrossOver[i])) {
print(CrossOver[i]);
gotFit = 1;
}
}
if (gotFit == 1) {
System.out.println("Total Time : \"" + ((System.currentTimeMillis()-startingTime)/(float)1000) + "\" Seconds.");
System.exit(1);
}
}
private void print(int[] Array) {
for (int i = 0; i < Array.length; i++) {
for (int j = 0; j < Array.length; j++) {
if (Array[i] == j)
System.out.print(" Q ");
else
System.out.print(" - ");
}
System.out.println();
}
}
private boolean IsFit(int[] crossOver) {
// Vertical Checking
for (int i = 0; i < crossOver.length; i++)
for (int j = i + 1; j < crossOver.length; j++)
if (crossOver[i] == crossOver[j])
return false;
// Diagonal Right Checking
for (int i = 0; i < crossOver.length; i++) {
int temp = 1;
for (int j = i + 1; j < crossOver.length; j++) {
if ((crossOver[i] + temp) == crossOver[j])
return false;
temp++;
}
}
// Diagonal Left Checking
/*
* for (int i = 0; i < crossOver.length; i++) { int temp = 1; for (int j
* = i; j < crossOver.length; j++) { if ((crossOver[i] - temp) ==
* crossOver[j]) return false; temp++; } }
*/
// Diagonal Left Checking
for (int i = 0; i < crossOver.length; i++) {
int temp = 1;
for (int j = i + 1; j < crossOver.length; j++) {
if (crossOver[i] - temp == crossOver[j])
return false;
temp++;
}
}
return true;
}
private void Mutate() {
for (int i = 0; i < nFitSelected; i++) {
int nMutations = ran.nextInt(length);
for (int j = 0; j < nMutations; j++)
CrossOver[i][ran.nextInt(length)] = ran.nextInt(length);
}
/*
* CrossOver1[mutateIndexBit] = ran.nextInt(length); mutateIndexBit =
* ran.nextInt(length); CrossOver2[mutateIndexBit] =
* ran.nextInt(length); mutateIndexBit = ran.nextInt(length);
* CrossOver3[mutateIndexBit] = ran.nextInt(length); mutateIndexBit =
* ran.nextInt(length); CrossOver4[mutateIndexBit] =
* ran.nextInt(length);
*/
}
private void CrossOver() {
int crossOvered = ran.nextInt(length);
for (int i = 0; i < nFitSelected; i += 2) {
for (int j = 0; j < crossOvered; j++) {
CrossOver[i][j] = NQueens[i][j];
CrossOver[i+1][j] = NQueens[i+1][j];
}
for (int j = crossOvered; j < length; j++) {
CrossOver[i+1][j] = NQueens[i][j];
CrossOver[i][j] = NQueens[i+1][j];
}
}
/*
* // FIRST GROUP
* for (int i = 0; i < halfLength; i++)
* CrossOver1[i] = NQueens1[i];
* for (int i = halfLength; i < NQueens1.length; i++)
* CrossOver1[i] = NQueens2[i];
*
* // SECOND GROUP for (int i = 0; i < halfLength; i++) CrossOver2[i] =
* NQueens2[i]; for (int i = halfLength; i < NQueens1.length; i++)
* CrossOver2[i] = NQueens1[i];
*
* // THIRD GROUP for (int i = 0; i < halfLength; i++) CrossOver3[i] =
* NQueens3[i]; for (int i = halfLength; i < NQueens1.length; i++)
* CrossOver3[i] = NQueens4[i];
*
* // FOURTH GROUP for (int i = 0; i < halfLength; i++) CrossOver4[i] =
* NQueens4[i]; for (int i = halfLength; i < NQueens1.length; i++)
* CrossOver4[i] = NQueens3[i];
*/
/*
* for (int i = 0; i < NQueens1.length; i++)
* System.out.print(CrossOver1[i]); System.out.println(); for (int i =
* 0; i < NQueens1.length; i++) System.out.print(CrossOver2[i]);
*/
}
private void ArrangeFromFitness() {
int[] index = new int[length];
for(int i=0;i<length;i++)
index[i] = i;
for (int i = 0; i < Fitness.length; i++) {
for (int j = 0; j < Fitness.length - 1; j++) {
if (Fitness[j] > Fitness[j + 1]) {
int temp = Fitness[j];
Fitness[j] = Fitness[j + 1];
Fitness[j + 1] = temp;
temp = index[j];
index[j] = index[j + 1];
index[j + 1] = temp;
}
}
}
int[][] tempQueens = NQueens;
/*NQueens[0] = tempQueens[index[0]];
NQueens[1] = tempQueens[index[1]];
NQueens[2] = tempQueens[index[0]];
NQueens[3] = tempQueens[index[2]];*/
int temp = 1;
for(int i=0;i<nFitSelected;i+=2){
NQueens[i] = tempQueens[index[0]];
NQueens[i+1] = tempQueens[index[temp]];
temp++;
}
}
/*private void printAll() {
for (int i = 0; i < NQueens1.length; i++)
System.out.print(NQueens1[i]);
System.out.println(" " + Fitness[0]);
for (int i = 0; i < NQueens1.length; i++)
System.out.print(NQueens2[i]);
System.out.println(" " + Fitness[1]);
for (int i = 0; i < NQueens1.length; i++)
System.out.print(NQueens3[i]);
System.out.println(" " + Fitness[2]);
for (int i = 0; i < NQueens1.length; i++)
System.out.print(NQueens4[i]);
System.out.println(" " + Fitness[3]);
}*/
private void countFitness(int nth, int[] Nqueen) {
for (int i = 0; i < Fitness.length; i++)
Fitness[i] = 0;
// Vertical Checking
for (int i = 0; i < Nqueen.length; i++)
for (int j = i + 1; j < Nqueen.length; j++)
if (Nqueen[i] == Nqueen[j])
Fitness[nth]++;
// Diagonal Right Checking
for (int i = 0; i < Nqueen.length; i++) {
int temp = 1;
for (int j = i + 1; j < Nqueen.length; j++) {
if ((Nqueen[i] + temp) == Nqueen[j])
Fitness[nth]++;
temp++;
}
}
// Diagonal Left Checking
for (int i = 0; i < Nqueen.length; i++) {
int temp = 1;
for (int j = i + 1; j < Nqueen.length; j++) {
if (Nqueen[i] - temp == Nqueen[j])
Fitness[nth]++;
temp++;
}
}
}
public static void main(String[] arg) {
new NQueen_Pro(8);
}
}
nFitSelected is 40. The size of NQueens is 8 by 8. You got your condition wrong in the for loop :
for(int i=0;i<nFitSelected;i+=2){
NQueens[i] = tempQueens[index[0]];
NQueens[i+1] = tempQueens[index[temp]];
temp++;
}
If you let i reach as high as 38, NQueens[i] would throw an exception when i reaches 8.
The definitions of the arrays :
public NQueen_Pro(int n) {
Fitness = new int[n];
NQueens = new int[n][n]; // 8 x 8
CrossOver = new int[n][n];
nFitSelected = 40;
...
public static void main(String[] arg)
{
new NQueen_Pro(8); // the value of n
}
Meta-notes: Your code needs better formatting - indentation and removal of commented-out-code.
anyway I like using command line to debug:
Follow what it saws, you need to import that Random class. Where is RAndom? A google search yields It is in Util. So add in:
import java.util.*;

find Prime numbers up to X - complexity

this algorithm is need to make an array with size X and then each number which isnt prime toput zero in his index.. can someone please tell me what is the complexity? and why?
// x is the number we want all the primes below him
int[] p = new int[x + 1];
// Initializes the array.
for (int i = 2; i < p.length; i++) {
p[i] = i;
}
// "Erases" the composite (non-prime) numbers.
for (int i = 2; i <= Math.sqrt(x); i++) {
for (int j = i * 2; j < p.length; j += i) {
p[j] = 0;
}
}
is the complexity is O(x*sqrt(x))?
If you are using the following code, the time complexity is O(x √x).
int[] p = new int[x];
for (int i = 0; i < p.length; i++) {
p[i] = i+1;
}
for (int i = 4; i <= p.length; i++) {
for(int j = 2; j <= Math.sqrt(i) ; j += 1) {
if(i%j==0) {
p[i-1] = 0;
}
}
}

Categories

Resources