// шпаргалка по массивам, указателям и динамическому выделению памяти
// http://coolaf.com/run/snippets/enzt95gz29
// что еще не доделано
// - проверка на компланарность. я хз как реализовать проверку на компланарность
// системы из n векторов
// - проверка образует ли n векторов замкнутую фигуру - зависит от проверки на
// компланарность
// - я не уверен насчет корректности работы векторного произведения в
// размерностях отличных от 3
// про что можно написать в курсаче:
// - динамическое выделение памяти (которое необходимо для реализации структуры
// вектора)
// https://ru.wikipedia.org/wiki/Динамическое_распределение_памяти
// http://www.amse.ru/courses/cpp1/2009.10.19.html
//
// - про структуру вектора и функцию v_create
//
// - про функцию v_input, там есть, что скопировать в курсач
//
// - операции над векторами и системами векторов
//
// - векторное умножение в n-мерном пространстве, иллюстрация
// https://mathoverflow.net/questions/94312/n-dimensional-cross-product-reference-request
// w1 - wn = вектора
// e1 - en = базисные вектора
// det = детерминатор матрицы
//
// - функции реализованы так, что программа не будет вылетать при возникновении
// серьезной ошибки, а попытается её проигнорировать или исправить.
// для примера, посмотри на v_add, или v_input. можешь просто скопировать то,
// что я там написал, и написать об этов в разделе "обработка ошибок"
//
// - функции разделены на две группы, и имеют свои префиксы в названиях:
// * v_ - для работы векторами
// * va_ - для работы с массивом векторов
// ПОДКЛЮЧЕНИЕ ВНЕШНИХ ФАЙЛОВ --------------------------------------------------
#include <stdio.h> // ввод-вывод (printf, fgets, sscanf, fopen)
#include <stdlib.h> // работа с паматью (malloc, realloc, free)
// ОПРЕДЕЛЕНИЯ -----------------------------------------------------------------
// т.к. в си нету типа bool (true / false), заменяем их на 1 и 0
#define TRUE 1 // компилятор заменит все слова TRUE на 1
#define FALSE 0 // компилятор заменит все слова FALSE на 0
// длина буфера для хранения обрабатываемых строк
#define BUFFER_LENGTH 80 // компилятор заменит эту строку на число 80
// СТРУКТУРЫ -------------------------------------------------------------------
struct Vector { // арефметический вектор
int size; // размерность вектора (т.е. кол-во компонентов)
double* data; // указатель на массив компонент вектора
};
typedef struct Vector Vector; // позволяет писать Vector вместо struct Vector
// можно было и сразу написать
// typedef struct { ... } Vector
// но так проще для понимания
// ПРОТОТИПЫ ФУНКЦИЙ ----------------------------------------------------------
// компилятору лучше заранее указать прототипы функций. это позволит писать
// их в любом порядке, не волнуясь о том, что одна функция вызывает другую,
// которая может зависеть от третьей, и т.д.
Vector v_create(int size, double* source); // создает новый вектор
Vector v_input(); // новый вектор из консоли
Vector v_load(char* path); // новый вектор из файла
void v_save(Vector v, char* path); // записать вектор в файл
void v_print(Vector v); // вывести вектор на экран
void v_destroy(Vector v); // уничтожить вектор
int v_equal(Vector a, Vector b); // два вектора равны
void v_add(Vector v1, Vector v2); // прибавить v2 к v1
void v_sub(Vector v1, Vector v2); // вычесть v2 из v1
double v_dot(Vector v1, Vector v2); // скалярное произведение
// двух векторов
Vector va_cross(Vector* vectors, int count); // векторное произведение
// массива векторов
int v_collinear(Vector v1, Vector v2); // два вектора коллинеарны
int va_collinear(Vector* vectors, int count); // массив векторов коллинеарен
// т.е. линейно зависим
int va_complanar(Vector* vectors, int count); // массив векторов компланарен
// РЕАЛИЗАЦИЯ ФУНКЦИЙ ----------------------------------------------------------
/*
возвращает новый вектор указанной размерности
если в качестве source передать NULL, все компоненты вектора будут равны нулю
если в качестве source передать массив, функция скопирует из него значения
*/
Vector v_create(int size, double* source) {
// если вдруг передали отрицательную размерность, делаем её равной нулю
if (size < 0) {
size = 0;
}
// создаем экземпляр вектора
Vector v;
// прописываем размерность
v.size = size;
// Vector хранит поле data, которое должно содержать адрес массива чисел
// типа double. изначально этот указатель пустой, да и массива нету. нужно
// вручную выделить область в RAM под массив чисел, и дать этому указателю
// адрес на него. используем malloc для такой операции.
// кстати, если попытаться выделить 0 байт, malloc вернет NULL
// каждое число double занимает sizeof(double) байт. нам нужно size чисел,
// значит нужно умножить sizeof(double) на size, чтобы выделить нужное
// количество байт
v.data = malloc(size * sizeof(double));
// заполняем массив чисел нулями (иначе он будет заполнен мусором из RAM)
for (int i = 0; i < v.size; i++) {
v.data[i] = 0;
}
// если при вызове функции, в качестве source мы передали массив, то функция
// скопирует из него значения. если передать NULL - ничего не произойдет.
if (source != NULL) {
for (int i = 0; i < v.size; i++) { // копируем size чисел из source
v.data[i] = source[i]; // в v.data
}
}
// возвращаем созданный вектор
return v;
}
/*
возвращает новый вектор из данных, введенных в консоль
*/
Vector v_input() {
// размерность будущего вектора
int size;
// если просто использовать scanf для ввода всех данных из консоли, можно
// наткнуться на кучу неочевидных ошибок.
// поэтому для обработки ввода из консоли, лучше сначала считать строку,
// которую ввел пользователь в буфер с помощью fgets (file get string),
// а затем парсить сам буфер с помощью sscanf (string scan formatted, не
// путать с обычной scanf!)
// создаем буфер (читай массив) из BUFFER_LENGTH символов типа char
// BUFFER_LENGTH определена в начале файла с помощью #define
char buffer[BUFFER_LENGTH];
printf("Input Vector size (int): ");
// вместо того, чтобы вылетать с ошибкой при неправильном вводе, можно
// сделать так, чтобы программа распознавала ошибку и просила пользователя
// ввести данные снова и снова, пока он не сделает это правильно.
// если в условие цикла while прописать TRUE или 1, он будет выполняться
// бесконечно, пока мы не остановим его принудительно.
// если что, закрыть консольную программу можно по нажатию CTRL + C
while (TRUE) {
// считываем строку из stdin (стандартный ввод консоли) в буфер
// fgets считает до BUFFER_LENGTH символов, пока не наткнется на перенос
// строки (enter)
fgets(buffer, BUFFER_LENGTH, stdin);
// когда мы вызываем sscanf (или обычную scanf), она всегда возвращает
// количество число корректно отпарсенных переменных. это число можно
// использовать для проверки ошибок.
// мы пытаемся считать одну переменную (size), соответственно sscanf_s
// должна вернуть число 1.
// проверяем это с помощью if
if (sscanf(buffer, "%i", &size) == 1) {
// если число, которое вернула функция sscanf_s равно 1, значит все
// правильно. принудительно выходим из цикла, чтобы он не начался заново
break;
}
// если мы дошли до этой строки, значит предыдущее условие не сработало.
// сообщаем об ошибке, и просим пользователя ввести данные еще раз
printf("Error while reading input.\nTry again with correct number (int): ");
}
// создаем пустой вектор указанной размерности
Vector v = v_create(size, NULL);
// просим пользователя ввести size чисел
printf("Input %i values separated by new line (double): \n", size);
// начинаем цикл
for (int i = 0; i < size; i++) {
// считываем строку, введенную пользователем
fgets(buffer, BUFFER_LENGTH, stdin);
// сканируем одно число, заодно проверяем, что нам вернет sscanf_s
if (sscanf(buffer, "%lf", &v.data[i]) != 1) {
// если sscanf_s вернула не 1, сообщаем об ошибке
printf("Error while reading input.\nStarting over from #[%i] (double): ", i);
// и делаем шаг назад в цикле. пользователю придется ввести это число
// снова
i--;
}
}
// возвращаем заполненый вектор
return v;
}
/*
выводит содержание вектора
*/
void v_print(Vector v) {
printf("{ ");
// проходимся по компонентам вектора в цикле
for (int i = 0; i < v.size; i++) {
// %lf означает long float, используется для отображения данных типа double
printf("%lf ", v.data[i]);
}
printf("}");
}
/*
возвращает вектор из текстового файла
path - строка, путь к файлу
формат файла такой:
3 // первая строка - размерность вектора
1.2345 // остальные строки - компоненты вектора
5.678
123.45
*/
Vector v_load(char* path) {
int size;
Vector v;
// FILE - это специальная структура из stdio.h, хранящая в себе информацию об
// открытом файле и данные из него.
// fopen создает структуру FILE, выделяет под неё память и возвращает адрес
// этой структуры. адрес этот мы будем хранить в указателе на FILE, т.е. FILE*
// открываем файл (с помощью флага "r")
FILE* file = fopen(path, "r");
// буфер для хранения строк из файла (по сути просто строка, массив символов)
// длинной в BUFFER_LENGTH символов
char buffer[BUFFER_LENGTH];
// если произошла ошибка при создании FILE, fopen возвращает NULL
if (file == NULL) {
// в таком случае сообщаем об ошибке при открытии файла
printf("Error while opening file [%s]. Returning empty vector...\n", path);
// и возвращаем пустой вектор нулевой размерности
return v_create(0, NULL);
}
// считываем до BUFFER_LENGTH символов из файла в буфер, пока не наткнемся
// на новую строку
fgets(buffer, BUFFER_LENGTH, file);
// ищем в буфере целое число (размерность нового вектора). Если sscanf вернет
// число отличное, от числа переменных, которые мы пытаемся считать (1),
// расцениваем это как ошибку.
if (sscanf(buffer, "%i", &size) != 1) {
// сообщаем об ошибке при чтении размерности вектора
printf("Error while reading vector size [%s]. Setting size to 0...\n", path);
// устанавливаем нулевую размерность
size = 0;
}
// если все ок, создаем пустой вектор заданной размерности
v = v_create(size, NULL);
// пытаемся считать компоненты вектора из файла
for (int i = 0; i < size; i++) {
// читаем следующую строку из файла в буфер
fgets(buffer, BUFFER_LENGTH, file);
// сканируем 1 число за раз, одновременно проверяя, что вернет sscanf
if (sscanf(buffer, "%lf", &v.data[i]) != 1) {
// если она вернула не 1, сообщаем об ошибке
printf("Error while reading vector data [%s]. Returning empty vector...\n", path);
// и уничтожаем содержимое вектора (высвобождаем память)
v_destroy(v);
}
}
// возвращаем получившийся вектор в любом случае, даже если была ошибка чтения
return v;
}
/*
сохраняет вектор в текстовый файл
*/
void v_save(Vector v, char* path) {
// создаем файл (с помощью флага "w")
FILE* file = fopen(path, "w");
// если файл не открылся для записи, сообщаем об ошибке
if (file == NULL) {
printf("Error while saving vector to file [%s]\n", path);
// и возвращаемся из функции
return;
}
// если все ок, пишем в файл размерность вектора с помощью fprintf
// (file print formatted). используем %i для целого числа.
fprintf(file, "%i\n", v.size);
// и записываем в файл компоненты вектора
for (int i = 0; i < v.size; i++) {
fprintf(file, "%lf\n", v.data[i]);
}
printf("Vector saved [%s]\n", path);
}
/*
высвобождает память, выделенную под вектор и делает его размерность нулевой.
это важно делать, чтобы не засорять память ненужными векторами, или если
нужно пересоздать вектор под той же переменной.
*/
void v_destroy(Vector v) {
v.size = 0;
free(v.data);
}
/*
возвращает TRUE если векторы равны, FALSE в обратном случае
*/
int v_equal(Vector v1, Vector v2) {
// если их размерность равна
if (v1.size == v2.size) {
// тогда проходимся по компонентам вектора в цикле
for (int i = 0; i < v1.size; i++) {
// и если наткнемся хоть на одно несовпадение, сразу возвращаем FALSE
if (v1.data[i] != v2.data[i]) {
return FALSE;
}
}
// а если несовпадений не было, то векторы равны. тогда возвращаем TRUE
return TRUE;
}
// если их размерность не равна, возвращаем FALSE.
// else писать нет смысла мы в любом случае дойдем до сюда, если первое
// условие не сработает
return FALSE;
}
/*
прибавляет v2 к v1 (не возращает сумму векторов, а прибавляет один к другому!)
создать функцию sum, которая просто возвращает новый вектор, сложенный из двух
других неполучится. вернее получится, но в подобном выражении:
v = sum(v1, sum(v2, v3)) будет утечка памяти, т.к. память под создание
вектора sum(v2, v3) выделится, но этот вектор является промежуточным
результатом и мы его никуда не сохраняем.
таким образом мы просто прое**м участок памяти, не сохранив ссылку на него
в переменной, и программа больше не сможет его использовать. и освободить
мы его не сможем тоже. то же самое с вычитанием.
поэтому просто избегаем подобного функционала.
*/
void v_add(Vector v1, Vector v2) {
// вообще, эта операция определена только на векторах одинаковой размерности
// но мы сделаем немного удобнее
// если размерность v2 больше, чем размерность v1, мы просто увеличим
// размерность v1 так, чтобы она была такой же, как и у v2
// пример:
// v1 = {1, 2, 3}
// v2 = {1, 1, 1, 1, 1}
// v_add(v1, v2) -> {1, 2, 3, 1, 1} <- теперь v1 не 3D, а 4D вектор, как и v2
// делается это с помощью realloc (memory reallocate). мы указываем ей какую
// область памяти нужно перераспределить, и сколько байт потребуется (в нашем
// случае кол-во байт будет равно v2.size * sizeof(double).
// все элементы, что в области до перераспределения, сохранятся.
// кстати, если с помощью realloc уменьшить размер выделенной области,
// то все, что не влезет будет отброшено.
if (v2.size > v1.size) {
// участок памяти, который нужно перераспределить
// v
v1.data = realloc(v1.data, v2.size * sizeof(double));
// ^ ^
// кол-во элементов сколько байт занимает каждый элемент
// заполняем новые ячейки нулями, начиная с ячейки v1.size, иначе там будет
// мусор из RAM. пример:
// v1 = {1, 2, 3}
// v1 = {1, 2, 3, ?, ?}
// i = 0 1 2 3 4
// ^
// начинаем отсюда
for (int i = v1.size; i < v2.size; i++) {
v1.data[i] = 0;
}
v1.size = v2.size; // и не забываем скопировать значение size из v2
}
// пробегаемся по компонентам вектора
for (int i = 0; i < v1.size; i++) {
// и прибавляем элементы вектора v2 к элементам вектора v1
// операция (a += b) равносильна (a = a + b)
v1.data[i] += v2.data[i];
}
}
/*
вычитает v2 из v1
*/
void v_sub(Vector v1, Vector v2) {
// эта операция тоже определена только на векторах одинаковой размерности
// и мы опять сделаем немного удобнее
// делаем то же самое, что делали в v_add. если размер v2 больше, чем v1, то
// мы увеличим размер v1, чтобы из него можно было вычесть все элементы v2
// пример:
// v1 = {1, 2, 3}
// v2 = {1, 1, 1, 1, 1}
// v_sub(v1, v2) -> {0, 1, 2, -1, -1}
if (v2.size > v1.size) {
v1.data = realloc(v1.data, v2.size * sizeof(double));
for (int i = v1.size; i < v2.size; i++) {
v1.data[i] = 0;
}
v1.size = v2.size;
}
// если все ок, пробегаемся по компонентам вектора
for (int i = 0; i < v1.size; i++) {
// вычитаем элементы вектора v2 из элементов вектора v1
// операция (a -= b) равносильна (a = a - b)
v1.data[i] -= v2.data[i];
}
}
/*
умножает все компоненты вектора на k
*/
void v_mul(Vector v, double k) {
for (int i = 0; i < v.size; i++) {
v.data[i] *= k;
}
}
/*
возвращает TRUE если векторы коллинеарны (пропорциональны)
FALSE в обратном случае
два вектора коллинеарны, если их векторное произведение равно нулевому вектору
(у которого все компоненты равны нулю)
*/
int v_collinear(Vector v1, Vector v2) {
// создаем массив из двух векторов
Vector vectors[2] = {v1, v2};
// пихаем этот массив в va_cross, чтобы получить векторное произведение
Vector cross = va_cross(vectors, 2);
// смотрим на координаты получившегося вектора
for (int i = 0; i < cross.size; i++) {
if (cross.data[i] != 0) {
// если хоть одна компонента вектора не равна нулю, возвращаем false
return FALSE;
}
}
// если все ок, возвращаем true
return TRUE;
}
/*
проверяет массив векторов (vector array) на коллинеарность
в качестве аргументов указывается массив векторов (vectors)
и количество векторов в нем (count)
*/
int va_collinear(Vector* vectors, int count) {
// эта операция определена только на массиве векторов одинаковой размерности
// мы будем использовать функцию v_collinear, которая отбрасывает векторы
// разной размерности (возвращает FALSE)
// начиная со второго вектора
for (int i = 1; i < count; i++) {
// проверяем на не коллинеарность первого вектора со всеми остальными
// используя оператор "!" (логическое "не") и функцию v_collinear
if (!v_collinear(vectors[i], vectors[0])) {
// если хоть один из векторов не коллинеарен с первым, возвращаем FALSE
return FALSE;
}
}
// если все ок, возвращаем TRUE
return TRUE;
}
/*
проверяет массив векторов (vector array) на компланарность
операция определена для двух и более векторов
2 вектора всегда компланарны
3 вектора компланарны, если их смешанное произведение равно нулю
n векторов компланарны, если ???
*/
int va_complanar(Vector* vectors, int count) {
// НЕ ДОДЕЛАНА
// два или один вектор всегда лежат в одной плоскости
if (count <= 2) {
return TRUE;
}
// три вектора
if (count == 3) {
// получаем векторное произведение b и c
Vector bc[2] = {vectors[1], vectors[2]};
Vector cross = va_cross(bc, 2);
// получаем скалярное произведение a и (b cross c)
double dot = v_dot(vectors[0], cross);
// cross уже не нужен, уничтожаем его, чтобы не прое**ть выделенную память
v_destroy(cross);
// если результат равен нулю, значит три вектора компланарны
if (dot == 0) {
return TRUE;
}
}
return FALSE;
}
/*
скалярное произведение векторов (англ. dot product)
пример:
v1 = {1, 2, 3}
v2 = {4, 5, 6}
v_dot(v1, v2) = 1*4 + 2*5 + 3*6
*/
double v_dot(Vector v1, Vector v2) {
// вообще, эта операция определена только на векторах одинаковой размерности
// но если они отличаются, то мы просто перемножим те пары компонент, которые
// есть у обоих векторов, а все остальное проигнорируем.
// пример:
// {5, 5}
// {1, 2, 3, 4}
// ^ ^
// игнорируем
// вычислим, у какого из векторов размерность меньше
int min_size = v1.size;
if (v2.size < v1.size) {
min_size = v2.size;
}
// в цикле сложим произведения только существующих пар компонентов
double sum = 0;
for (int i = 0; i < min_size; i++) {
sum += v1.data[i] * v2.data[i];
}
return sum;
}
/*
векторное произведение векторов (англ. cross product)
на входе принимает массив из векторов vectors
и кол-во векторов count
на выходе возвращает count-мерный вектор, перпендикулярный заданным
в случае ошибки, возвращает 0-мерный вектор
векторное произведение count векторов определено на count+1 мерном
пространстве. для выполнения операции в 2D пространстве, тебе понадобится 1
вектор. в 3D, тебе понадобится 2 вектора, в 4D понадобится 3 вектора и т.д.
в 2D пространстве ты находишь вектор, перпендикулярный заданной линии.
в 3D ты находишь вектор, перпендикулярный заданной плоскости.
в 4D, я предполагаю, что ты находишь вектор, перпендикулярный заданному объему
в четырехмерном пространстве.
теперь о том, как эта функция работает:
по сути тут просто идет вычисление через детерминанты (det) суб-матриц, как и
с обычным векторным произведением, только для пространства любой размерности.
на всякий случай загляни в гугл картинки с запросом "определитель матрицы",
так будет понятнее.
пример:
a = {a1, a2, a3, a4}
b = {b1, b2, b3, b4}
c = {c1, c2, c3, c4}
e1 = {1, 0, 0, 0}
e2 = {0, 1, 0, 0}
e3 = {0, 0, 1, 0}
e4 = {0, 0, 0, 1
|e1 e2 e3 e4|
a x b x c = det|a1 a2 a3 a4|
|b1 b2 b3 b4|
|c1 c2 c3 c4|
|a2 a3 a4| |a3 a4 a1| |a4 a1 a2| |a1 a2 a3|
= e1*det|b2 b3 b4| + e2*det|b3 b4 b1| + e3*det|b4 b1 b2| + e4*det|b1 b2 b3|
|c2 c3 c4| |c3 c4 c1| |c4 c1 c2| |c1 c2 c3|
= e1*(a2*b3*c4 + a3*b4*c2 + a4*b2*c3 - a4*b3*c2 - a3*b2*c4 - a2*b4*c3) +
e2* ... +
e3* ... +
e4* ...
= {1, 0, 0, 0} * (a2*b3*c4 + a3*b4*c2 + a4*b2*c3 - a4*b3*c2 - a3*b2*c4 ...) +
{0, 1, 0, 0} * (...) +
{0, 0, 1, 0} * (...) +
{0, 0, 0, 1} * (...)
= {r1, r2, r3, r4} <= сложили 4 вектора и получили 1 вектор
т.е. это обычный детерминант, только в качестве элементов первого ряда стоят
так называемые "базис вектора" e1, e2, и т.д., и на них умножается крест-
накрест компоненты векторов.
*/
Vector va_cross(Vector* vectors, int count) {
// чтобы не вычислять постоянно размерность (count + 1), запишем в переменную
int size = count + 1;
// проверяем, у всех ли count векторов размерность равна count+1
for (int i = 0; i < count; i++) {
if (vectors[i].size != size) {
// ошибка, возвращаем пустой вектор нулевой размерности (0-мерный вектор)
return v_create(0, NULL);
}
}
// если все ок, то создаем (count+1)-мерный вектор. передаем NULL вместо
// массива чисел, чтобы все компоненты заполнились нулями (см. v_create)
Vector result = v_create(size, NULL);
// сначала небольшой костыль для работы с 2D векторами:
// vector = {x, y}
// result = {y, -x} <- провто разворачиваем вектор на 90 градусов по часовой
if (size == 2) {
result.data[0] = vectors[0].data[1];
result.data[1] = -vectors[0].data[0];
return result;
}
// далее идет алгоритм который рассчитан на работу с размерностями начиная от
// 3 и более.
// вычисляем компоненты нового вектора
for (int r = 0; r < size; r++) {
// суб-детерминант. сюда будем прибавлять перемноженные накрест ячейки.
// все переменные, объявленные внутри цикла, создаются заново каждый раз.
double sub_det = 0;
// вычисляем суб-детерминант для result[r]
for (int offset = 0; offset < count; offset++) {
// e0, e1, e2, ... en всегда равны 1.
// мы перемножаем диагонали. основную диагональ (сверху-вниз-вправо) затем
// прибавляем к суб-детерминанту, а побочную (снизу-вверх-вправо) вычитаем.
// для каждой диагонали будет своя переменная, изначально равная 1,
// которая потом умножается на числа ячейках.
double positive_mul = 1; // основная
double negative_mul = 1; // побочная
// проходимся по массиву vectors
for (int w = 0; w < count; w++) {
// индекс вычислялся путем сдвига в зависимости от следующих параметров:
// r - текущая компонента вектора result
// w - текущий вектор из массива vectors
// offset - сдвиг диагонали, по которой мы умножаем, вправо
//
// r0 r1 r2
// |1 1 1 |
// w0 > | |
// w1 > | |
// ^ ^ ^
// offset
//
// при вычислении индекса необходимой ячейки используется оператор
// остатка от целочисленного деления "%"
// с помощью него мы "ходим по кругу" по ячейкам от 0 до size-1
// пример:
// 0 % 3 = 0
// 1 % 3 = 1
// 2 % 3 = 2
// 3 % 3 = 0
// 4 % 3 = 1
// 5 % 3 = 2
// ...
int index = (r + w + offset + 1) % size;
// если мы наткнулись на ячейку столбца, который вычеркнут в данный
// момент (для r = 0 вычеркнут весь столбец 0), то сдвигаем index на 2
// в правую сторону
if (index == r) {
index = (index + 2) % size;
}
// сверху-вниз вправо
// v v
positive_mul *= vectors[w].data[index];
negative_mul *= vectors[count - 1 - w].data[index];
// ^ ^
// снизу-вверх вправо
}
sub_det += positive_mul;
sub_det -= negative_mul;
}
result.data[r] = sub_det;
}
return result;
}
/*
main
*/
int main() {
Vector a = v_create(3, (double[]) {
1, 2, 3
});
Vector b = v_create(3, (double[]) {
4, 5, 6
});
Vector vectors[] = {a, b};
Vector cross = va_cross(vectors, 2);
printf("\nCross result:\n");
v_print(cross);
/*
// создаем пустой 2D вектор
Vector a;
a = v_create(3, NULL); // можно и сразу написать Vector a = v_create(2, NULL)
// задаем координаты вручную
a.data[0] = 1.0;
a.data[1] = 2.0;
a.data[3] = 3.0;
// выводим его на экран
v_print(a);
printf("\n");
// уничтожаем вектор, освобождая выделенную под его компоненты память
v_destroy(a); // теперь a.size == 0, a.data == NULL
// теперь попробуем создать 2D вектор, скопировав значения из массива чисел
double array[3] = {3.0, 1.0, 1.0};
a = v_create(3, array); // снова используем переменную a
// так можно делать только после v_destroy(a), иначе
// прое**м кусок памяти, выделенный ранее под a.data
Vector b = v_create(3, (double[]) {2.0, 2.0, 3.0});
Vector vectors[2] = {a, b};
printf("Cross product\n");
Vector cross = va_cross(vectors, 2);
printf("\nResult\n");
v_print(cross);
printf("Dot product\n");
printf("%lf\n", v_dot(a, b));
*/
/*
// можно создать вектор, копируя значения из заранее созданного массива
double numbers[3] = { 1, 2.5, 3.14 };
Vector a = v_create(3, numbers);
// а можно задать массив чисел прямо в вызове функции
Vector b = v_create(3, (double[]) { 2, 5.0, 6.28 });
// ^
// нужно заранее сказать компилятору, что это массив
// double, иначе будет ошибка
// выводим эти векторы
printf("Vectors:\n");
v_print(a);
printf("\n");
v_print(b);
// првоерка на равность
if (v_equal(a, b)) {
printf("Vectors are equal!\n");
}
// и на неравность (добавив "!", т.е. "не")
if (!v_equal(a, b)) {
printf("Vectors are not equal!\n");
}
// пробуем считать вектор из файла и вывести его на экран:
Vector v1 = v_load("v1.txt");
printf("v1 = ");
v_print(v1);
// прибавляем b к v1 и выводим результат на экран
v_add(v1, b);
printf("v1 + b = ");
v_print(v1);
// сохраняем v1 в файл result.txt
v_save(v1, "result.txt");
// теперь попробуем создать вектор введя его из консоли
printf("Reading from console:\n");
Vector v2 = v_input(); // <- эта функция запустит ввод из консоли
// выводим получившийся вектор
printf("v2 = ");
v_print(v2);
*/
return 0;
}