Home » Программы с матрицами на Java — примеры и использование
Программы с матрицами на Java — примеры и использование

Программы с матрицами на Java — примеры и использование

Матрицы в Java — это не просто абстракция из математики, которая пылится в университетских конспектах. Это мощный инструмент для решения целого спектра задач в серверном программировании: от обработки данных мониторинга до построения систем рекомендаций. Если вы админите серверы и пишете скрипты автоматизации, то работа с матрицами может существенно упростить задачи анализа логов, балансировки нагрузки и оптимизации ресурсов. В этой статье разберём, как правильно работать с матрицами в Java, какие подводные камни стоит учесть, и как применить это в реальных задачах администрирования.

Как работают матрицы в Java

В Java матрицы представляются как двумерные массивы. Звучит просто, но дьявол кроется в деталях. Основная особенность — это то, что Java хранит матрицы как массивы ссылок на массивы, что даёт гибкость, но может создать проблемы с производительностью при работе с большими объёмами данных.

Базовое создание матрицы выглядит так:

// Создание матрицы 3x3
int[][] matrix = new int[3][3];

// Инициализация с данными
int[][] serverLoad = {
    {80, 65, 90},  // CPU, Memory, Disk для сервера 1
    {45, 30, 60},  // CPU, Memory, Disk для сервера 2
    {95, 85, 40}   // CPU, Memory, Disk для сервера 3
};

// Динамическое создание
int rows = 5;
int cols = 3;
double[][] metrics = new double[rows][cols];

Ключевые особенности работы с матрицами:

  • Индексация начинается с 0 — классика Java
  • Размер фиксирован после создания (для стандартных массивов)
  • Автоматическая инициализация нулями для числовых типов
  • Возможность создания “рваных” массивов с разной длиной строк

Пошаговая настройка для практической работы

Давайте настроим рабочую среду для экспериментов с матрицами. Если у вас нет подходящего сервера для разработки, можете взять VPS с нужными характеристиками.

Шаг 1: Подготовка окружения

# Установка OpenJDK на Ubuntu/Debian
sudo apt update
sudo apt install openjdk-11-jdk

# Проверка установки
java -version
javac -version

# Создание рабочей директории
mkdir java-matrix-examples
cd java-matrix-examples

Шаг 2: Создание базового класса для работы с матрицами

// MatrixUtils.java
public class MatrixUtils {
    
    // Вывод матрицы в консоль
    public static void printMatrix(int[][] matrix) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.printf("%4d ", matrix[i][j]);
            }
            System.out.println();
        }
    }
    
    // Создание матрицы из данных мониторинга
    public static double[][] createMetricsMatrix(String[] servers, 
                                                 String[] metrics) {
        return new double[servers.length][metrics.length];
    }
    
    // Поиск максимального значения
    public static int findMax(int[][] matrix) {
        int max = matrix[0][0];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j] > max) {
                    max = matrix[i][j];
                }
            }
        }
        return max;
    }
}

Шаг 3: Компиляция и запуск

# Компиляция
javac MatrixUtils.java

# Создание тестового файла
cat > MatrixTest.java << 'EOF'
public class MatrixTest {
    public static void main(String[] args) {
        int[][] serverMetrics = {
            {80, 65, 90},
            {45, 30, 60},
            {95, 85, 40}
        };
        
        System.out.println("Server Metrics Matrix:");
        MatrixUtils.printMatrix(serverMetrics);
        
        System.out.println("Max value: " + MatrixUtils.findMax(serverMetrics));
    }
}
EOF

# Компиляция и запуск
javac MatrixTest.java
java MatrixTest

Практические примеры и кейсы

Кейс 1: Мониторинг серверной нагрузки

Представим, что у нас есть кластер из нескольких серверов, и мы хотим анализировать метрики производительности:

public class ServerMonitoring {
    
    public static void main(String[] args) {
        // Матрица: строки = серверы, столбцы = метрики (CPU, RAM, Disk, Network)
        double[][] metrics = {
            {78.5, 65.2, 45.1, 234.7},  // Server-01
            {45.3, 32.1, 67.8, 189.4},  // Server-02
            {89.7, 78.9, 34.5, 345.2},  // Server-03
            {34.2, 23.4, 56.7, 123.8}   // Server-04
        };
        
        String[] servers = {"Server-01", "Server-02", "Server-03", "Server-04"};
        String[] metricNames = {"CPU %", "RAM %", "Disk %", "Network MB/s"};
        
        // Поиск проблемных серверов
        findProblematicServers(metrics, servers, metricNames);
        
        // Расчёт средних значений по метрикам
        calculateAverages(metrics, metricNames);
    }
    
    public static void findProblematicServers(double[][] metrics, 
                                              String[] servers, 
                                              String[] metricNames) {
        double[] thresholds = {80.0, 70.0, 80.0, 300.0}; // Пороговые значения
        
        System.out.println("=== Проблемные серверы ===");
        for (int i = 0; i < metrics.length; i++) {
            boolean hasIssues = false;
            StringBuilder issues = new StringBuilder();
            
            for (int j = 0; j < metrics[i].length; j++) {
                if (metrics[i][j] > thresholds[j]) {
                    if (hasIssues) issues.append(", ");
                    issues.append(metricNames[j]).append(": ").append(metrics[i][j]);
                    hasIssues = true;
                }
            }
            
            if (hasIssues) {
                System.out.println(servers[i] + " - " + issues.toString());
            }
        }
    }
    
    public static void calculateAverages(double[][] metrics, String[] metricNames) {
        System.out.println("\n=== Средние значения по кластеру ===");
        
        for (int j = 0; j < metricNames.length; j++) {
            double sum = 0;
            for (int i = 0; i < metrics.length; i++) {
                sum += metrics[i][j];
            }
            double average = sum / metrics.length;
            System.out.printf("%s: %.2f\n", metricNames[j], average);
        }
    }
}

Кейс 2: Анализ логов доступа

Создадим матрицу для анализа трафика по часам и дням недели:

public class LogAnalysis {
    
    public static void main(String[] args) {
        // Матрица: строки = дни недели, столбцы = часы (0-23)
        int[][] trafficMatrix = generateTrafficData();
        
        // Поиск пикового времени
        findPeakHours(trafficMatrix);
        
        // Анализ по дням недели
        analyzeDailyPatterns(trafficMatrix);
    }
    
    public static int[][] generateTrafficData() {
        // Симуляция реальных данных трафика
        int[][] traffic = new int[7][24];
        
        // Заполняем матрицу симулированными данными
        for (int day = 0; day < 7; day++) {
            for (int hour = 0; hour < 24; hour++) {
                // Больше трафика в рабочие часы и дни
                int baseTraffic = (day < 5) ? 100 : 60; // Будни vs выходные
                int hourMultiplier = (hour >= 9 && hour <= 17) ? 2 : 1;
                traffic[day][hour] = baseTraffic * hourMultiplier + 
                                   (int)(Math.random() * 50);
            }
        }
        
        return traffic;
    }
    
    public static void findPeakHours(int[][] traffic) {
        String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
        
        System.out.println("=== Пиковые часы по дням ===");
        for (int day = 0; day < 7; day++) {
            int maxTraffic = 0;
            int peakHour = 0;
            
            for (int hour = 0; hour < 24; hour++) {
                if (traffic[day][hour] > maxTraffic) {
                    maxTraffic = traffic[day][hour];
                    peakHour = hour;
                }
            }
            
            System.out.printf("%s: %02d:00 (%d requests)\n", 
                            days[day], peakHour, maxTraffic);
        }
    }
    
    public static void analyzeDailyPatterns(int[][] traffic) {
        String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
        
        System.out.println("\n=== Общий трафик по дням ===");
        for (int day = 0; day < 7; day++) {
            int dailyTotal = 0;
            for (int hour = 0; hour < 24; hour++) {
                dailyTotal += traffic[day][hour];
            }
            System.out.printf("%s: %d requests\n", days[day], dailyTotal);
        }
    }
}

Сравнение различных подходов

Подход Преимущества Недостатки Применение
Стандартные массивы Простота, быстрый доступ Фиксированный размер Небольшие матрицы, известный размер
ArrayList<ArrayList> Динамический размер Больше памяти, медленнее Изменяемые размеры
Apache Commons Math Много готовых операций Дополнительная зависимость Сложные математические операции
EJML библиотека Оптимизация для больших матриц Сложность освоения Высокопроизводительные вычисления

Продвинутые техники и оптимизации

Оптимизация памяти для больших матриц

Для работы с большими объёмами данных мониторинга стоит рассмотреть использование одномерных массивов с математической адресацией:

public class OptimizedMatrix {
    private final int rows, cols;
    private final double[] data;
    
    public OptimizedMatrix(int rows, int cols) {
        this.rows = rows;
        this.cols = cols;
        this.data = new double[rows * cols];
    }
    
    public double get(int row, int col) {
        return data[row * cols + col];
    }
    
    public void set(int row, int col, double value) {
        data[row * cols + col] = value;
    }
    
    // Эффективное умножение матриц
    public OptimizedMatrix multiply(OptimizedMatrix other) {
        if (this.cols != other.rows) {
            throw new IllegalArgumentException("Incompatible matrix dimensions");
        }
        
        OptimizedMatrix result = new OptimizedMatrix(this.rows, other.cols);
        
        for (int i = 0; i < this.rows; i++) {
            for (int j = 0; j < other.cols; j++) {
                double sum = 0;
                for (int k = 0; k < this.cols; k++) {
                    sum += this.get(i, k) * other.get(k, j);
                }
                result.set(i, j, sum);
            }
        }
        
        return result;
    }
}

Параллельная обработка матриц

Для обработки больших объёмов данных мониторинга можно использовать параллельные потоки:

import java.util.stream.IntStream;

public class ParallelMatrixProcessor {
    
    public static double[][] processMetricsParallel(double[][] metrics) {
        int rows = metrics.length;
        int cols = metrics[0].length;
        double[][] result = new double[rows][cols];
        
        // Параллельная обработка по строкам
        IntStream.range(0, rows).parallel().forEach(i -> {
            for (int j = 0; j < cols; j++) {
                // Применяем сложную обработку к каждому элементу
                result[i][j] = processMetric(metrics[i][j]);
            }
        });
        
        return result;
    }
    
    private static double processMetric(double value) {
        // Имитация сложной обработки
        return Math.log(value + 1) * Math.sqrt(value);
    }
}

Интеграция с системами мониторинга

Вот пример интеграции с популярными системами мониторинга:

import java.io.*;
import java.nio.file.*;
import java.util.*;

public class MonitoringIntegration {
    
    // Парсинг данных от Prometheus
    public static double[][] parsePrometheusMetrics(String filename) throws IOException {
        List<String> lines = Files.readAllLines(Paths.get(filename));
        List<double[]> metrics = new ArrayList<>();
        
        for (String line : lines) {
            if (line.startsWith("node_cpu_seconds_total")) {
                // Парсинг метрик CPU
                String[] parts = line.split("\\s+");
                double[] values = new double[parts.length - 1];
                for (int i = 1; i < parts.length; i++) {
                    values[i-1] = Double.parseDouble(parts[i]);
                }
                metrics.add(values);
            }
        }
        
        return metrics.toArray(new double[0][]);
    }
    
    // Экспорт в формат для Grafana
    public static void exportToGrafana(double[][] matrix, String outputFile) 
            throws IOException {
        try (PrintWriter writer = new PrintWriter(outputFile)) {
            writer.println("timestamp,server,metric,value");
            
            long timestamp = System.currentTimeMillis();
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    writer.printf("%d,server-%d,metric-%d,%.2f\n", 
                                timestamp, i, j, matrix[i][j]);
                }
            }
        }
    }
}

Интересные факты и нестандартные применения

Несколько неочевидных способов использования матриц в админской практике:

  • Матрица связности серверов — можно представить сетевую топологию как матрицу смежности для анализа путей маршрутизации
  • Анализ зависимостей сервисов — матрица может показать, какие сервисы зависят друг от друга
  • Планирование резервного копирования — матрица времени/ресурсов для оптимизации расписания бэкапов
  • Балансировка нагрузки — матрица весов для различных алгоритмов балансировки

Пример: Анализ зависимостей сервисов

public class ServiceDependencyAnalyzer {
    
    public static void main(String[] args) {
        // Матрица зависимостей: 1 - есть зависимость, 0 - нет
        int[][] dependencies = {
            {0, 1, 1, 0},  // Web зависит от App и DB
            {0, 0, 1, 1},  // App зависит от DB и Cache
            {0, 0, 0, 0},  // DB независим
            {0, 0, 0, 0}   // Cache независим
        };
        
        String[] services = {"Web", "App", "DB", "Cache"};
        
        findCriticalServices(dependencies, services);
        suggestShutdownOrder(dependencies, services);
    }
    
    public static void findCriticalServices(int[][] deps, String[] services) {
        System.out.println("=== Критичные сервисы ===");
        
        for (int j = 0; j < services.length; j++) {
            int dependencyCount = 0;
            for (int i = 0; i < services.length; i++) {
                if (deps[i][j] == 1) dependencyCount++;
            }
            
            if (dependencyCount > 0) {
                System.out.printf("%s: %d зависимостей\n", 
                                services[j], dependencyCount);
            }
        }
    }
    
    public static void suggestShutdownOrder(int[][] deps, String[] services) {
        System.out.println("\n=== Порядок остановки сервисов ===");
        
        // Простой алгоритм: сначала сервисы без зависимостей
        boolean[] stopped = new boolean[services.length];
        int order = 1;
        
        while (order <= services.length) {
            for (int i = 0; i < services.length; i++) {
                if (stopped[i]) continue;
                
                boolean canStop = true;
                for (int j = 0; j < services.length; j++) {
                    if (deps[i][j] == 1 && !stopped[j]) {
                        canStop = false;
                        break;
                    }
                }
                
                if (canStop) {
                    System.out.printf("%d. %s\n", order++, services[i]);
                    stopped[i] = true;
                }
            }
        }
    }
}

Автоматизация и скрипты

Матрицы открывают широкие возможности для автоматизации рутинных задач. Вот несколько скриптов, которые могут пригодиться:

Скрипт анализа производительности

#!/bin/bash
# performance_analyzer.sh

# Сбор метрик системы
collect_metrics() {
    echo "$(date +%s),$(cat /proc/loadavg | cut -d' ' -f1),$(free | grep Mem | awk '{print $3/$2*100}'),$(df / | tail -1 | awk '{print $5}' | sed 's/%//')" >> metrics.csv
}

# Запуск Java-анализатора
analyze_metrics() {
    java -cp . MetricsAnalyzer metrics.csv
}

# Основной цикл
while true; do
    collect_metrics
    sleep 60
done &

# Анализ каждые 10 минут
while true; do
    sleep 600
    analyze_metrics
done

Java-класс для анализа собранных метрик

import java.io.*;
import java.util.*;

public class MetricsAnalyzer {
    
    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.err.println("Usage: java MetricsAnalyzer ");
            return;
        }
        
        List<double[]> metrics = readMetrics(args[0]);
        double[][] matrix = metrics.toArray(new double[0][]);
        
        // Анализ трендов
        analyzeTrends(matrix);
        
        // Поиск аномалий
        detectAnomalies(matrix);
        
        // Прогноз на основе простой линейной регрессии
        predictTrends(matrix);
    }
    
    private static List<double[]> readMetrics(String filename) throws IOException {
        List<double[]> metrics = new ArrayList<>();
        
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(",");
                double[] values = new double[parts.length - 1]; // Пропускаем timestamp
                for (int i = 1; i < parts.length; i++) {
                    values[i-1] = Double.parseDouble(parts[i]);
                }
                metrics.add(values);
            }
        }
        
        return metrics;
    }
    
    private static void analyzeTrends(double[][] matrix) {
        String[] metricNames = {"Load Average", "Memory %", "Disk %"};
        
        System.out.println("=== Анализ трендов ===");
        for (int col = 0; col < matrix[0].length; col++) {
            double sum = 0;
            double min = Double.MAX_VALUE;
            double max = Double.MIN_VALUE;
            
            for (int row = 0; row < matrix.length; row++) {
                double value = matrix[row][col];
                sum += value;
                min = Math.min(min, value);
                max = Math.max(max, value);
            }
            
            double average = sum / matrix.length;
            System.out.printf("%s: Avg=%.2f, Min=%.2f, Max=%.2f\n", 
                            metricNames[col], average, min, max);
        }
    }
    
    private static void detectAnomalies(double[][] matrix) {
        System.out.println("\n=== Обнаружение аномалий ===");
        
        for (int col = 0; col < matrix[0].length; col++) {
            double[] values = new double[matrix.length];
            for (int row = 0; row < matrix.length; row++) {
                values[row] = matrix[row][col];
            }
            
            double mean = Arrays.stream(values).average().orElse(0.0);
            double stdDev = calculateStdDev(values, mean);
            
            // Поиск значений за пределами 2 стандартных отклонений
            for (int row = 0; row < matrix.length; row++) {
                double value = matrix[row][col];
                if (Math.abs(value - mean) > 2 * stdDev) {
                    System.out.printf("Аномалия в метрике %d, строка %d: %.2f\n", 
                                    col, row, value);
                }
            }
        }
    }
    
    private static double calculateStdDev(double[] values, double mean) {
        double sum = 0;
        for (double value : values) {
            sum += Math.pow(value - mean, 2);
        }
        return Math.sqrt(sum / values.length);
    }
    
    private static void predictTrends(double[][] matrix) {
        System.out.println("\n=== Прогноз трендов ===");
        
        for (int col = 0; col < matrix[0].length; col++) {
            // Простая линейная регрессия
            double[] x = new double[matrix.length];
            double[] y = new double[matrix.length];
            
            for (int i = 0; i < matrix.length; i++) {
                x[i] = i;
                y[i] = matrix[i][col];
            }
            
            double slope = calculateSlope(x, y);
            double intercept = calculateIntercept(x, y, slope);
            
            // Прогноз на следующие 10 точек
            double nextValue = slope * matrix.length + intercept;
            System.out.printf("Метрика %d: прогноз = %.2f (тренд: %s)\n", 
                            col, nextValue, slope > 0 ? "↑" : "↓");
        }
    }
    
    private static double calculateSlope(double[] x, double[] y) {
        double n = x.length;
        double sumX = Arrays.stream(x).sum();
        double sumY = Arrays.stream(y).sum();
        double sumXY = 0;
        double sumX2 = 0;
        
        for (int i = 0; i < n; i++) {
            sumXY += x[i] * y[i];
            sumX2 += x[i] * x[i];
        }
        
        return (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
    }
    
    private static double calculateIntercept(double[] x, double[] y, double slope) {
        double meanX = Arrays.stream(x).average().orElse(0.0);
        double meanY = Arrays.stream(y).average().orElse(0.0);
        return meanY - slope * meanX;
    }
}

Похожие решения и альтернативы

Если стандартных средств Java недостаточно, стоит рассмотреть специализированные библиотеки:

  • Apache Commons Math (https://commons.apache.org/proper/commons-math/) — полнофункциональная математическая библиотека
  • EJML (http://ejml.org/) — оптимизированная для производительности библиотека
  • Colt — высокопроизводительная научная библиотека
  • ND4J — библиотека для работы с N-мерными массивами

Пример использования Apache Commons Math

import org.apache.commons.math3.linear.*;

public class CommonsMatrixExample {
    
    public static void main(String[] args) {
        // Создание матрицы
        double[][] data = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        RealMatrix matrix = MatrixUtils.createRealMatrix(data);
        
        // Вычисление определителя
        double det = new LUDecomposition(matrix).getDeterminant();
        System.out.println("Определитель: " + det);
        
        // Умножение матриц
        RealMatrix result = matrix.multiply(matrix.transpose());
        System.out.println("Результат умножения:");
        System.out.println(result);
    }
}

Новые возможности для автоматизации

Работа с матрицами в Java открывает множество возможностей для автоматизации системного администрирования:

  • Intelligent Load Balancing — анализ матрицы производительности для динамического распределения нагрузки
  • Predictive Maintenance — предсказание сбоев на основе анализа исторических данных
  • Resource Optimization — оптимизация использования ресурсов на основе матричных вычислений
  • Security Analysis — анализ логов доступа для выявления подозрительных паттернов
  • Capacity Planning — планирование мощностей на основе трендов нагрузки

Пример системы предупреждения

public class AlertSystem {
    
    private static final double[][] ALERT_THRESHOLDS = {
        {80.0, 85.0, 90.0},  // CPU: Warning, Critical, Emergency
        {70.0, 80.0, 90.0},  // Memory: Warning, Critical, Emergency
        {80.0, 90.0, 95.0}   // Disk: Warning, Critical, Emergency
    };
    
    public static void checkAlerts(double[][] currentMetrics) {
        String[] servers = {"Web-01", "Web-02", "DB-01", "Cache-01"};
        String[] metrics = {"CPU", "Memory", "Disk"};
        String[] levels = {"WARNING", "CRITICAL", "EMERGENCY"};
        
        for (int server = 0; server < currentMetrics.length; server++) {
            for (int metric = 0; metric < currentMetrics[server].length; metric++) {
                double value = currentMetrics[server][metric];
                
                for (int level = 0; level < ALERT_THRESHOLDS[metric].length; level++) {
                    if (value >= ALERT_THRESHOLDS[metric][level]) {
                        System.out.printf("[%s] %s %s: %.2f%%\n", 
                                        levels[level], servers[server], 
                                        metrics[metric], value);
                        
                        // Отправка уведомления
                        sendNotification(servers[server], metrics[metric], 
                                       value, levels[level]);
                        break;
                    }
                }
            }
        }
    }
    
    private static void sendNotification(String server, String metric, 
                                       double value, String level) {
        // Здесь может быть отправка email, Slack, Telegram и т.д.
        System.out.printf("📧 Notification sent: %s %s %.2f%% [%s]\n", 
                        server, metric, value, level);
    }
}

Заключение и рекомендации

Матрицы в Java — это мощный инструмент для системного администратора, который выходит далеко за рамки академической математики. Они позволяют эффективно обрабатывать большие объёмы данных мониторинга, строить системы предупреждений и автоматизировать рутинные задачи.

Когда использовать стандартные массивы:

  • Небольшие объёмы данных (до 1000x1000 элементов)
  • Простые операции (поиск, сортировка, базовая арифметика)
  • Критична производительность
  • Нет сложных математических операций

Когда стоит использовать специализированные библиотеки:

  • Большие объёмы данных (миллионы элементов)
  • Сложные математические операции
  • Машинное обучение и анализ данных
  • Научные вычисления

Практические советы:

  • Всегда проверяйте границы массивов
  • Используйте try-with-resources для работы с файлами
  • Рассмотрите использование параллельных потоков для больших данных
  • Не забывайте об оптимизации памяти для продакшена

Для разработки и тестирования ваших матричных алгоритмов можете использовать облачный VPS с достаточным объёмом RAM, а для высоконагруженных систем анализа данных стоит рассмотреть выделенные серверы с мощными процессорами.

Матрицы в Java — это не просто структура данных, а основа для создания интеллектуальных систем мониторинга и автоматизации. Начните с простых примеров, постепенно усложняя задачи, и вскоре вы сможете создавать собственные системы анализа данных, которые будут работать на уровне enterprise-решений.


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

Данная статья предназначена исключительно для ознакомительных и образовательных целей и не ущемляет права правообладателей. Если какой-либо материал, защищенный авторским правом, был использован без должного упоминания или с нарушением законов об авторском праве, это непреднамеренно, и мы исправим это незамедлительно после уведомления. Обратите внимание, что переиздание, распространение или воспроизведение части или всего содержимого в любой форме запрещено без письменного разрешения автора и владельца веб-сайта. Для получения разрешений или дополнительных запросов, пожалуйста, свяжитесь с нами.

Leave a reply

Your email address will not be published. Required fields are marked