Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • david/lipn-search
1 result
Show changes
Commits on Source (47)
Showing
with 1460 additions and 5 deletions
image: alpine
stages:
- test
lipn-search:
stage: test
script:
- echo "Installation des paquets"
- apk update
- apk add python3
- apk add py-pip
- pip install pytest
- echo "Début de la phase de test"
- echo "Début des tests sur l'index inversé"
- cd index_inverse/test/
- pytest test_hamming.py
- cd ../../
- echo "Fin des tests sur l'index inversé"
- echo "Fin de la phase de test"
\ No newline at end of file
File deleted
File deleted
# -*- coding: utf-8 -*-
# @param u : un mot de longueur n
# @param v : un mot de longueur n
# @contrainte Longueur de u et v dois être égale
# @return -1 si longueur de u et v ne sont pas égale
def hamming(u,v):
tmp=0
if(len(u)!=len(v)):
return -1
for i in range(len(u)):
if(u[i]!=v[i]):
tmp=tmp+1
return tmp
# -*- coding: utf-8 -*-
# la longueur d'une lettre avec accent en python 2.7.17 vaut 2 (ex:len('à')=2) donc il faut faire le test unitaire en python3 command: python3 -m unittest test_hamming.py
import sys
sys.path.append('../src')
from hamming import hamming
import unittest
class TestHamming(unittest.TestCase):
def test_hamming_1(self):
result=hamming("mais","mats")
self.assertEqual(result,1)
def test_hamming_2(self):
result=hamming("événement","avénement")
self.assertEqual(result,1)
def test_hamming_3(self):
result=hamming("lapin","poisson")
self.assertEqual(result,-1)
def test_hamming_4(self):
result=hamming("chat","chien")
self.assertEqual(result,-1)
CC= gcc
CXXFLAGS= -Wall --pedantic -O3
CPP_O_FILE = main-pagerank.o
LIB = -lm
all: $(CPP_O_FILE)
$(CC) $(CXXFLAGS) -o main-pagerank.exe main-pagerank.c ./src/pagerank.c ./src/graphe.c ./src/liste_url.c $(LIB)
clean:
rm -rf *.o *.exe
Le répertoire src contient les sources du programme qui crée un graphe
des pages web du lipn et en calcule le pagerank.
Le répertoire test cotient les tests unitaires qui vérifie que les fonctions
Le répertoire test contient les tests unitaires qui vérifie que les fonctions
dans contenues dans le répertoire src jouent correctement leur rôle
Les bibliothèques requises sont celles de base en C, stdlib, stdio, string, time et assert. (.h)
Pour compiler les fichiers de test, il faut se placer dans le répertoire test et entrer la commande make
Le programme final, quand il sera fait se compilera en faisant make dans le répertoire pagerank
#include <stdio.h>
#include <stdlib.h>
#include "graphe.h"
/** Fonction qui crée un graphe vide qui sera stocké à l'adresse g déjà allouée
* @param g un pointeur vers un graphe alloué en mémoire
* @param n le nombre strictement positif de cases des tableaux du graphe
* @requires n > 0
* @return 0 si le graphe a été créé et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation
* O(n)
*/
int creer_graphe(graphe* g, int n) {
int i;
if(n <= 0) {
printf("Erreur paramètre creer_graphe, n <= 0\n");
return -1;
}
if(!(g->ligne = (int*) malloc(sizeof(int) * n))) {
printf("Erreur allocation creer_graphe, g->ligne\n");
return -2;
}
if(!(g->col = (int*) malloc(sizeof(int) * n))) {
printf("Erreur allocation creer_graphe, g->col\n");
free(g->ligne);
return -2;
}
g->max = n;
g->premierVide = 0;
for(i = 0; i < n; i++) {
g->ligne[i] = -1;
g->col[i] = -1;
}
return 0;
}
/** Libère la mémoire allouée dans un graphe, mais pas le graphe lui même (le pointeur donné en paramètre reste à libérer
* @param g un pointeur vers un graphe alloué en mémoire
*/
void graphe_detruire(graphe* g) {
if(g) {
free(g->ligne);
free(g->col);
}
}
/** Renvoie la taille maximale actuelle des tableaux du graphe g
* @param g un pointeur vers un graphe alloué en mémoire
* @return g->max un entier
* O(1)
*/
int graphe_get_max(graphe* g) {
return g->max;
}
/** Renvoie l'indice du premier (-1 -1) dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return g->premierVide un entier
* O(1)
*/
int graphe_get_premier_vide(graphe* g) {
return g->premierVide;
}
/** Indique si un sommet est dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un entier, le sommet dont on veut savoir s'il est dans le graphe
* @return 1 si le sommet est dans le graphe et 0 sinon
* O(n)
*/
int graphe_contains(graphe* g, int u) {
return (graphe_find(g, u) != -1);
}
/** Donne l'indice de la première occurence d'un sommet dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un entier, le sommet dont on veut connaître l'indice dans le graphe
* @return l'indice de le sommet si il est dans le graphe et -1 sinon
* O(n)
*/
int graphe_find(graphe* g, int u) {
if(g && u >= 0 && u <= graphe_get_plus_grand_sommet(g)) {
int i;
for(i = 0; i < graphe_get_premier_vide(g); i++) {
if(g->ligne[i] == u || g->col[i] == u) {
return i;
}
}
}
return -1;
}
/** Renvoie le plus grand indice des sommets du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return un entier
* O(n)
*/
int graphe_get_plus_grand_sommet(graphe* g) {
int i, tmp, res = -1;
if(g) {
for(i = 0; i < graphe_get_max(g); i++) {
tmp = max(g->ligne[i], g->col[i]);
if(tmp > res) {
res = tmp;
}
}
}
return res;
}
/** Ajoute une arête entre les sommets i et j du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param i un des sommets de l'arête
* @param j un des sommets de l'arête
*/
void graphe_ajouter_arete(graphe* g, int i, int j) {
if(g) {
if(graphe_get_premier_vide(g) == graphe_get_max(g)) {
g->max *= 2;
g->ligne = realloc(g->ligne, g->max * sizeof(int));
g->col = realloc(g->col, g->max * sizeof(int));
for(int i = graphe_get_premier_vide(g); i < graphe_get_max(g); i++) {
g->ligne[i] = -1;
g->col[i] = -1;
}
}
g->ligne[graphe_get_premier_vide(g)] = i;
g->col[graphe_get_premier_vide(g)] = j;
g->premierVide++;
}
}
/** Supprime une arête entre les sommets i et j du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param i un des sommets de l'arête
* @param j un des sommets de l'arête
* O(n)
*/
void graphe_supprimer_arete(graphe* g, int i, int j) {
if(g) {
int cpt, flag = 0;
for(cpt = 0; cpt < graphe_get_premier_vide(g); cpt++) {
if(flag) { //On a trouvé l'arête et on l'a enlevée, donc maintenant il suffit de décaler le reste d'une case à gauche
g->ligne[cpt] = g->ligne[cpt+1];
g->col[cpt] = g->col[cpt+1];
}
else if(g->ligne[cpt] == i && g->col[cpt] == j) {
flag = 1;
g->ligne[cpt] = g->ligne[cpt+1];
g->col[cpt] = g->col[cpt+1];
}
}
if(flag) {
g->premierVide--;
}
}
}
/** Retasse la matrice creuse contenant des -1
* @param g un pointeur vers un graphe alloué en mémoire
* @param casesVides un tableau contenant les indices des cases vides de la matrice
* @param nbCasesVides le nombre de cases vides à retasser
* O(n)
*/
void graphe_retasser(graphe* g, int* casesVides, int nbCasesVides) {
int i;
for(i = graphe_get_max(g) - 1; i >= 0 && nbCasesVides; i--) {
if(g->ligne[i] != -1) { //Pas une case vide, donc on peut l'échanger avec une case vide
g->ligne[casesVides[nbCasesVides-1]] = g->ligne[i];
g->col[casesVides[nbCasesVides-1]] = g->col[i];
nbCasesVides--;
g->ligne[i] = -1;
g->col[i] = -1;
}
}
}
/** Retire les instances du sommet x dans le graphe et les remplace par -1 -1
* Indique dans un tableau quelles cases sont devenues vides, et le nombre de nouvelles cases vides
* Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc
* @param g un pointeur vers un graphe alloué en mémoire
* @param casesVides un tableau dans lequel on mettra les indices des cases vides de la matrice
* @param ptrNbCasesVides un pointeur vers le nombre de cases vides
* @param x le sommet à retirer
* O(n)
*/
void graphe_traitement_retirer_x(graphe* g, int* casesVides, int* ptrNbCasesVides, int x)
{
int i;
for(i=0; i < graphe_get_max(g); i++) {
if(g->ligne[i] == x || g->col[i] == x) { //Une arête part de x, ou arrive en x, on retire le sommet donc tout part
g->ligne[i] = -1;
g->col[i] = -1;
casesVides[(*ptrNbCasesVides)++] = i;
}
else { // Rien n'est supprimé, mais les indices des sommets > x sont décrémentés
if(g->ligne[i] > x) {
g->ligne[i]--;
}
if(g->col[i] > x) {
g->col[i]--;
}
}
}
g->premierVide -= (*ptrNbCasesVides);
}
/** Retire un sommet du graphe, assure qu'aucun sommet n'a d'arête allant vers ce dernier et qu'aucune arête ne part de celui ci.
* Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc
* @param g un pointeur vers un graphe alloué en mémoire
* @param x le sommet à retirer
* O(n)
*/
void graphe_retirer_sommet(graphe* g, int x) {
if(g) {
int n = graphe_get_plus_grand_sommet(g), i;
if(x >= 0 && x < n) {
int nbCasesVides = 0, *casesVides = (int*) calloc(sizeof(int), graphe_get_max(g));
int* ptrNbCasesVides = &nbCasesVides;
if(!casesVides) {
printf("Erreur allocation graphe_retirer_sommet (tableau casesVides)\n");
return;
}
//Le tableau casesVides stockant les indices des cases vides, on suppose au départ qu'aucune case n'est vide, donc on met -1 (pas un indice)
for(i=0; i < graphe_get_max(g); i++) {
casesVides[i]--;
}
graphe_traitement_retirer_x(g, casesVides, ptrNbCasesVides, x);
//Maintenant on a aucune trace du sommet x, mais plein de (-1 -1) où il était (ATTENTION ON PEUT ENCORE VOIR DES SOMMETS X, MAIS CE SONT LES ANCIENS SOMMETS X+1, CECI N'EST PAS UN BUG, NE PAS DÉBUGGER)
graphe_retasser(g, casesVides, nbCasesVides);
free(casesVides);
}
}
}
/** Indique si 2 sommets sont adjacents ou non, s'il existe une arête les reliant
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un des sommets de l'arête
* @param v un des sommets de l'arête
* @return 1 si les sommets sont adjacents et 0 sinon
* O(n)
*/
int graphe_voisins(graphe* g, int u, int v) {
if(g) {
int max = graphe_get_plus_grand_sommet(g);
if(u >= 0 && v >= 0 && u <= max && v <= max) {
int i;
for(i = 0; i < graphe_get_premier_vide(g); i++) {
if(g->ligne[i] == u && g->col[i] == v) {
return 1;
}
if(g->ligne[i] == v && g->col[i] == u) {
return 1;
}
}
}
}
return 0;
}
/** Indique si 1 sommet est successeur d'un autre ou non
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un des sommets de l'arête
* @param v un des sommets de l'arête
* @return 1 si v est un successeur de u et 0 sinon
* O(n)
*/
int graphe_est_succ(graphe* g, int u, int v) {
if(g) {
int max = graphe_get_plus_grand_sommet(g);
if(u >= 0 && v >= 0 && u <= max && v <= max) {
int i;
for(i = 0; i < graphe_get_premier_vide(g); i++) {
if(g->ligne[i] == u && g->col[i] == v) {
return 1;
}
}
}
}
return 0;
}
/** Crée la matrice d'adjacence d'un graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return une matrice d'entiers, la matrice d'adjacence du graphe
* O(n)
*/
int* graphe_to_Adj_Matrix(graphe* g) {
int lim = graphe_get_plus_grand_sommet(g)+1, i;
int* matrice = (int*) calloc(sizeof(int), lim * lim);
if(!matrice) {
printf("Erreur allocation graphe_to_Adj_Matrix \n");
return NULL;
}
for(i = 0; i < g->premierVide; i++) {
matrice[g->ligne[i] * lim + g->col[i]]++;
}
return matrice;
}
/** Affiche une matrice d'adjacence
* @param matrice un pointeur vers la matrice
* @param lim, un entier qui est la taille des côtés de la matrice
* O(n)
*/
void graphe_afficher_Matrice_Adj(int* matrice, int lim) {
int v, w;
/* ligne des indices des colonnes */
printf("\t\t");
for(w = 0 ; w < lim ; w++) {
printf("%d \t", w);
}
printf("\n");
printf("\t\t");
for(w = 0 ; w < lim ; w++) {
printf("_\t");
}
printf("\n");
/* lignes de la matrice */
for(v = 0 ; v < lim ; v++) {
printf("%d\t|\t", v);
for(w = 0 ; w < lim ; w++) {
printf("%d \t", matrice[v * lim + w]);
}
printf("|\n");
}
printf("\t\t");
for(w = 0 ; w < lim ; w++) {
printf("_\t");
}
printf("\n");
}
/** Affiche le graphe sous la forme d'une matrice d'adjacence
* @param g un pointeur vers un graphe alloué en mémoire
* O(n)
*/
void graphe_afficher(graphe* g) {
if(g) {
printf("Graphe à %d arêtes\n", graphe_get_premier_vide(g));
int lim = graphe_get_plus_grand_sommet(g)+1;
//Création de la matrice d'adjacence
int* matrice = graphe_to_Adj_Matrix(g);
if(!g) {
printf("Erreur matrice adjacence graphe_afficher\n");
return;
}
//Affichage
graphe_afficher_Matrice_Adj(matrice, lim);
free(matrice);
}
else {
printf("Erreur graphe_afficher Le graphe est NULL\n");
}
}
/** Affiche un tableau d'un graphe, celui des lignes si choix == 0 et celui des colonnes sinon
* @param g un pointeur vers un graphe alloué en mémoire
* @param choix un entier déterminant le tableau à afficher pour le graphe donné
* O(n)
*/
void graphe_afficher_Un_Des_Tableaux(int* t, int premierVide, int taille) {
int i;
for(i = 0; i < taille; i++) {
if(i < premierVide) {
printf("| %2d ", t[i]);
}
else { //Il ne devrait y avoir que des (-1)
printf("| (%d) ", t[i]);
}
}
}
/** Affiche le graphe sous la forme des 2 tableaux le composant
* @param g un pointeur vers un graphe alloué en mémoire
* O(n)
*/
void graphe_afficher_tableaux(graphe* g) {
int i;
printf("\n======================\nIndices\t\t");
for(i = 0; i < graphe_get_max(g); i++) {
printf("| %2d ", i);
}
printf("\n\nlignes :\t");
graphe_afficher_Un_Des_Tableaux(g->ligne, graphe_get_premier_vide(g), graphe_get_max(g));
printf("\ncolonnes :\t");
graphe_afficher_Un_Des_Tableaux(g->col, graphe_get_premier_vide(g), graphe_get_max(g));
printf("\n======================\n\n");
}
/** Donne la liste des sommets du graphe, sans répétitions dans le tableau listeSommets passé en argument
* @param g un graphe alloué en mémoire
* @param flagSommet un tableau qui indique pour chacun de ces indices, si le sommet correspondant est dans le graphe
* @param listeSommets la liste des sommets du graphe, au max il y en a 2n, où n = l'indice du premierVide du graphe
* @param ptrNbSommets un pointeur vers un entier qui est le nombre de sommets distincts du graphe
* O(n)
*/
void graphe_liste_sommets_dot(graphe* g, int* flagSommet, int* listeSommets, int* ptrNbSommets) {
/*
On utilise 2 tableaux pour avoir une liste des sommets dans le graphe.
Soit n = graphe_get_premier_vide(g), il y a au max 2n sommets différents
(Si tous les sommets dans le tableau des lignes sont distincts et pointent chacun vers un sommets distinct)
Donc c'est le nombre max de sommets dans le fichier DOT.
Et les valeurs des sommets sont entre 0 et graphe_get_plus_grand_sommet(g) inclus
*/
int i, n = graphe_get_premier_vide(g);
for(i = 0; i < 2*n; i++) {
listeSommets[i]--; //-1 partout
}
for(i = 0; i < n; i++) {
if(!flagSommet[g->ligne[i]]) { //g->ligne[i] valait 0, donc le sommet n'était pas découvert
flagSommet[g->ligne[i]] = 1;
listeSommets[(*ptrNbSommets)++] = g->ligne[i];
}
if(!flagSommet[g->col[i]]) { //g->col[i] valait 0, donc le sommet n'était pas découvert
flagSommet[g->col[i]] = 1;
listeSommets[(*ptrNbSommets)++] = g->col[i];
}
}
}
/** Convertit un graphe au format DOT dans un fichier
* dot -Tpdf fichier.dot -o fichier.pdf
* @param g un graphe alloué en mémoire
* @param nomFichier, le nom du fichier où l'on veut écrire le graphe
* @return 0 si le graphe a bien été écris au format DOT,
* -1 s'il y a eu une erreur d'ouverture du fichier, et -2 sinon
* O(n)
*/
int graphe_ecrire_dot(graphe* g, char* nomFichier) {
if(g) {
int i, nbSommets = 0, n = graphe_get_premier_vide(g), sommetMax = graphe_get_plus_grand_sommet(g);
int* ptrNbSommets = &nbSommets;
if(!n) {
printf("Erreur premier vide = 0, graphe vide graphe_ecrire_dot\n");
return -2;
}
int* flagSommet = (int *) calloc(sizeof(int), sommetMax + 1);
/*
flagSommet[i] =
0 : sommet pas dans le graphe
1 : sommet dans le graphe, il faudra l'ajouter dans le fichier dot
Toutes les valeurs entières entre 0 et sommetMax inclus peuvent être dans le graphe
*/
if(!flagSommet) {
printf("Erreur allocation flagSommet graphe_ecrire_dot\n");
return -2;
}
int* listeSommets = (int *) calloc(sizeof(int), 2*n);
/*
listeSommets[i] :
-1 : sommet pas dans le graphe
>= 0 : sommet dans le graphe, il faudra l'ajouter dans le fichier dot
Il y a au max 2n sommets différents
*/
if(!listeSommets) {
printf("Erreur allocation listeSommets graphe_ecrire_dot\n");
free(flagSommet);
return -2;
}
FILE *f = fopen(nomFichier, "w");
if (!f) {
free(flagSommet);
free(listeSommets);
printf("Erreur ouverture fichier \"%s\"graphe_ecrire_dot\n", nomFichier);
return -1;
}
graphe_liste_sommets_dot(g, flagSommet, listeSommets, ptrNbSommets);
//Dans flagSommet, on a tous les flags indiquant si un sommet est dans le graphe ou non
//Dans listeSommets on a la liste des sommets distincts
fputs("graph {\n", f);
for(i = 0; i < nbSommets; i++) {
fprintf(f, "\t%d;\n", listeSommets[i]);
}
fputs("\n", f);
for(i = 0; i < n; i++) {
fprintf(f, "\t%d -- %d;\n", g->ligne[i], g->col[i]);
}
fputs("}\n", f);
free(flagSommet);
free(listeSommets);
fclose(f);
return 0;
}
printf("Erreur pagerank NULL graphe_ecrire_dot\n");
return -2;
}
/** Renvoie le maximum entre 2 entiers
* @param x un entier
* @param y un entier
* @return un entier, le plus grand des 2 paramètres
* O(1)
*/
int max(int x, int y) {
return (x > y) ? x : y;
}
#ifndef GRAPHE_H
#define GRAPHE_H
struct s_graphe {
int max; // Longueur de chacun des tableaux
int premierVide; // Indice du premier (-1 -1)
int *ligne; // Indices i {-1 si non défini, entre 0 et n-1 sinon}
int *col; // Indices j {-1 si non défini, entre 0 et n-1 sinon}
};
typedef struct s_graphe graphe;
/** Fonction qui crée un graphe vide qui sera stocké à l'adresse g déjà allouée
* @param g un pointeur vers un graphe alloué en mémoire
* @param n le nombre strictement positif de cases des tableaux du graphe
* @requires n > 0
* @return 0 si le graphe a été créé et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation
* O(n)
*/
int creer_graphe(graphe* g, int n);
/** Libère la mémoire allouée dans un graphe, mais pas le graphe lui même (le pointeur donné en paramètre reste à libérer
* @param g un pointeur vers un graphe alloué en mémoire
* O(n);
*/
void graphe_detruire(graphe* g);
/** Renvoie la taille maximale actuelle des tableaux du graphe g
* @param g un pointeur vers un graphe alloué en mémoire
* @return g->max un entier
* O(1)
*/
int graphe_get_max(graphe* g);
/** Renvoie l'indice du premier (-1 -1) dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return g->premierVide un entier
* O(1)
*/
int graphe_get_premier_vide(graphe* g);
/** Indique si un sommet est dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un entier, le sommet dont on veut savoir s'il est dans le graphe
* @return 1 si le sommet est dans le graphe et 0 sinon
* O(n)
*/
int graphe_contains(graphe* g, int u);
/** Donne l'indice de la première occurence d'un sommet dans le graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un entier, le sommet dont on veut connaître l'indice dans le graphe
* @return l'indice de le sommet si il est dans le graphe et -1 sinon
* O(n)
*/
int graphe_find(graphe* g, int u);
/** Renvoie le plus grand indice des sommets du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return un entier
* O(n)
*/
int graphe_get_plus_grand_sommet(graphe* g);
/** Ajoute une arête entre les sommets i et j du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param i un des sommets de l'arête
* @param j un des sommets de l'arête
* O(1)
*/
void graphe_ajouter_arete(graphe* g, int i, int j);
/** Supprime une arête entre les sommets i et j du graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @param i un des sommets de l'arête
* @param j un des sommets de l'arête
* O(n)
*/
void graphe_supprimer_arete(graphe* g, int i, int j);
/** Retasse la matrice creuse contenant des -1
* @param g un pointeur vers un graphe alloué en mémoire
* @param casesVides un tableau contenant les indices des cases vides de la matrice
* @param nbCasesVides le nombre de cases vides à retasser
* O(n)
*/
void graphe_retasser(graphe* g, int* casesVides, int nbCasesVides);
/** Retire les instances du sommet x dans le graphe et les remplace par -1 -1
* Indique dans un tableau quelles cases sont devenues vides, et le nombre de nouvelles cases vides
* Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc
* @param g un pointeur vers un graphe alloué en mémoire
* @param casesVides un tableau dans lequel on mettra les indices des cases vides de la matrice
* @param nbCasesVides un pointeur vers le nombre de cases vides
* @param x le sommet à retirer
* O(n)
*/
void graphe_traitement_retirer_x(graphe* g, int* casesVides, int* ptrNbCasesVides, int x);
/** Retire un sommet du graphe, assure qu'aucun sommet n'a d'arête allant vers ce dernier et qu'aucune arête ne part de celui ci.
* Puis les sommets supérieurs sont renommés, ex : si x = 5, 6->5, 7->6 etc
* @param g un pointeur vers un graphe alloué en mémoire
* @param x le sommet à retirer
* O(n)
*/
void graphe_retirer_sommet(graphe* g, int x);
/** Indique si 2 sommets sont adjacents ou non, s'il existe une arête les reliant
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un des sommets de l'arête
* @param v un des sommets de l'arête
* @return 1 si les sommets sont adjacents et 0 sinon
* O(n)
*/
int graphe_voisins(graphe* g, int u, int v);
/** Indique si 1 sommet est successeur d'un autre ou non
* @param g un pointeur vers un graphe alloué en mémoire
* @param u un des sommets de l'arête
* @param v un des sommets de l'arête
* @return 1 si v est un successeur de u et 0 sinon
* O(n)
*/
int graphe_est_succ(graphe* g, int u, int v);
/** Crée la matrice d'adjacence d'un graphe
* @param g un pointeur vers un graphe alloué en mémoire
* @return une matrice d'entiers, la matrice d'adjacence du graphe
* O(n)
*/
int* graphe_to_Adj_Matrix(graphe* g);
/** Affiche une matrice d'adjacence
* @param matrice un pointeur vers la matrice
* @param lim, un entier qui est la taille des côtés de la matrice
* O(n)
*/
void graphe_afficher_Matrice_Adj(int* matrice, int lim);
/** Affiche le graphe sous la forme d'une matrice d'adjacence
* @param g un pointeur vers un graphe alloué en mémoire
* O(n)
*/
void graphe_afficher(graphe* g);
/** Affiche un tableau
* @param t un pointeur vers un tableau d'entiers
* @param premierVide un entier le nombre d'éléments non nuls
* @param taille un entier la taille du tableau
* O(n)
*/
void graphe_afficher_Un_Des_Tableaux(int* t, int premierVide, int taille);
/** Affiche le graphe sous la forme des 2 tableaux le composant
* @param g un pointeur vers un graphe alloué en mémoire
* O(n)
*/
void graphe_afficher_tableaux(graphe* g);
/** Donne la liste des sommets du graphe, sans répétitions dans le tableau listeSommets passé en argument pour les indiquer dans le fichier DOT
* @param g un graphe alloué en mémoire
* @param flagSommet un tableau qui indique pour chacun de ces indices, si le sommet correspondant est dans le graphe
* @param listeSommets la liste des sommets du graphe, au max il y en a 2n, où n = l'indice du premierVide du graphe
* @param ptrNbSommets un pointeur vers un entier qui est le nombre de sommets distincts du graphe
* O(n)
*/
void graphe_liste_sommets_dot(graphe* g, int* flagSommet, int* listeSommets, int* ptrNbSommets);
/** Convertit un graphe au format DOT dans un fichier
* @param g un graphe alloué en mémoire
* @param nomFichier, le nom du fichier où l'on veut écrire le graphe
* @return 0 si le graphe a bien été écris au format DOT,
* -1 s'il y a eu une erreur d'ouverture du fichier, et -2 sinon
* O(n)
*/
int graphe_ecrire_dot(graphe* g, char* nomFichier);
/** Renvoie le maximum entre 2 entiers
* @param x un entier
* @param y un entier
* @return un entier, le plus grand des 2 paramètres
* O(1)
*/
int max(int x, int y);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "liste_url.h"
/** Fonction qui crée une liste d'url qui est stockée à l'adresse donnée en argument, déjà allouée, la liste aura déjà n cases pour stocker des URLs (c'est un tableau dynamique, sa taille pourra être amenée à augmenter)
* @param l une liste d'url allouée en mémoire
* @param n un entier strictement positif, la taille de la liste
* @return 0 si la liste a été créée et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation
* O(n)
*/
int creer_liste(liste_url* l, int n) {
if(n <= 0) {
printf("Erreur paramètre creer_liste, n <= 0\n");
return -1;
}
if(!(l->urls = (char**) calloc(sizeof(char*), n))) {
printf("Erreur allocation creer_liste, l->urls\n");
return -2;
}
l->max = n;
l->premierNULL = 0;
return 0;
}
/** Renvoie la taille maximale actuelle de la liste l
* @param l un pointeur vers une liste allouée en mémoire
* @return g->max un entier
* O(1)
*/
int liste_get_max(liste_url* l) {
return l->max;
}
/** Renvoie l'indice du premier NULL dans le liste
* @param l un pointeur vers une liste allouée en mémoire
* @return l->premierNULL un entier
* O(1)
*/
int liste_get_premier_NULL(liste_url* l) {
return l->premierNULL;
}
/** Indique si une url est dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL dont on veut savoir si elle est dans la liste
* @return 1 si l'URL est dans la liste et 0 sinon
* O(n)
*/
int liste_contains(liste_url* l, char* s) {
return (liste_find(l, s) != -1);
}
/** Donne l'indice d'une url est dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL dont on veut connaître l'indice dans la liste
* @return l'indice de l'URL si elle est dans la liste et -1 sinon
* O(n)
*/
int liste_find(liste_url* l, char* s) {
if(l && s) {
int i;
for(i = 0; i < liste_get_premier_NULL(l); i++) {
if(!strcmp(s, liste_get_i(l, i))) {
return i;
}
}
}
return -1;
}
/** Renvoie l'url à l'indice i dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param i un entier
* @return l->urls[i] une chaîne de caractères si 0 <= i < liste_get_max(l), NULL sinon
* O(1)
*/
char* liste_get_i(liste_url* l, int i) {
if(0 <= i && i < liste_get_max(l)) {
return l->urls[i];
}
return NULL;
}
/** Ajoute une url à la première case vide dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL à ajouter
* O(1)
*/
void liste_add(liste_url* l, char* s) {
if(l) {
if(liste_get_premier_NULL(l) == liste_get_max(l)) { //Réallocation si nécessaire, complexité amortie en temps linéaire
l->max *= 2;
char** nouvelleListe = (char**) calloc(sizeof(char*), liste_get_max(l));
for (int i = 0; i < liste_get_premier_NULL(l); i++) {
nouvelleListe[i] = (char*) malloc(sizeof(char) * (strlen(l->urls[i]) + 1));
strcpy(nouvelleListe[i], l->urls[i]);
free(l->urls[i]);
}
nouvelleListe[l->premierNULL] = (char*) calloc(sizeof(char), strlen(s)+1);
strcpy(nouvelleListe[l->premierNULL++], s);
free(l->urls);
l->urls = nouvelleListe;
return;
}
l->urls[l->premierNULL] = (char*) calloc(sizeof(char), strlen(s)+1);
strcpy(l->urls[l->premierNULL++], s);
}
}
/** Retire une url de la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL à retirer
* O(n)
*/
void liste_remove(liste_url* l, char* s) {
if(l && s) {
int i, trouve = 0;
for(i = 0; i < liste_get_premier_NULL(l); i++) {
if(!trouve && (!strcmp(s, liste_get_i(l, i)))) {
free(liste_get_i(l, i));
if(i+1 < liste_get_max(l)) {
l->urls[i] = l->urls[i+1];
}
else {
l->urls[i] = NULL;
}
trouve = 1;
}
else if(trouve) {
if(i+1 < liste_get_max(l)) {
l->urls[i] = l->urls[i+1];
}
else {
l->urls[i] = NULL;
}
}
}
l->premierNULL--;
}
}
/** Libère la mémoire allouée dans une liste et toutes les chaînes contenues dedans
* @param l une liste d'url allouée en mémoire
* O(n)
*/
void liste_detruire(liste_url* l) {
if(l) {
int i;
for(i = 0; i < liste_get_premier_NULL(l); i++) {
free(l->urls[i]);
l->urls[i] = NULL;
}
free(l->urls);
}
}
/** Affiche la liste d'URLs
* @param l un pointeur vers une liste allouée en mémoire
* O(n)
*/
void liste_afficher(liste_url* l) {
if(l) {
int i, n = liste_get_max(l);
printf("i\t|\tURLs:\n");
for(i = 0; i < n; i++) {
if(i < liste_get_premier_NULL(l)) {
printf("%d \t|\t\"%s\"\n", i, liste_get_i(l, i));
}
else {
printf("%d \t|\tNULL\n", i);
}
}
}
else {
printf("La liste est NULL\n");
}
}
#ifndef LISTE_URL_H
#define LISTE_URL_H
struct s_liste_url {
int max; // Longueur de la liste allouée
int premierNULL; // Indice de la première chaîne NULL
char** urls; // Les urls de la liste
};
typedef struct s_liste_url liste_url;
/** Fonction qui crée une liste d'url qui est stockée à l'adresse donnée en argument, déjà allouée, la liste aura déjà n cases pour stocker des URLs (c'est un tableau dynamique, sa taille pourra être amenée à augmenter)
* @param l une liste d'url allouée en mémoire
* @param n un entier strictement positif, la taille de la liste
* @return 0 si la liste a été créée et -1 si n <= 0 et -2 s'il y a eu une erreur d'allocation
* O(n)
*/
int creer_liste(liste_url* l, int n);
/** Renvoie la taille maximale actuelle de la liste l
* @param l un pointeur vers une liste allouée en mémoire
* @return g->max un entier
* O(1)
*/
int liste_get_max(liste_url* l);
/** Renvoie l'indice du premier NULL dans le liste
* @param l un pointeur vers une liste allouée en mémoire
* @return l->premierNULL un entier
* O(1)
*/
int liste_get_premier_NULL(liste_url* l);
/** Indique si une url est dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL dont on veut savoir si elle est dans la liste
* @return 1 si l'URL est dans la liste et 0 sinon
* O(n)
*/
int liste_contains(liste_url* l, char* s);
/** Donne l'indice d'une url est dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL dont on veut connaître l'indice dans la liste
* @return l'indice de l'URL si elle est dans la liste et -1 sinon
* O(n)
*/
int liste_find(liste_url* l, char* s);
/** Renvoie l'url à l'indice i dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param i un entier
* @return l->urls[i] une chaîne de caractères si 0 <= i < liste_get_max(l), NULL sinon
* O(1)
*/
char* liste_get_i(liste_url* l, int i);
/** Ajoute une url à la première case vide dans la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL à ajouter
* O(1)
*/
void liste_add(liste_url* l, char* s);
/** Retire une url de la liste
* @param l un pointeur vers une liste allouée en mémoire
* @param s une chaîne, l'URL à retirer
* O(n)
*/
void liste_remove(liste_url* l, char* s);
/** Libère la mémoire allouée dans une liste et toutes les chaînes contenues dedans
* @param l une liste d'url allouée en mémoire
* O(n)
*/
void liste_detruire(liste_url* l);
/** Affiche la liste d'URLs
* @param l un pointeur vers une liste allouée en mémoire
* O(n)
*/
void liste_afficher(liste_url* l);
#endif
CC= gcc
CXXFLAGS= -Wall --pedantic -O3
CPP_O_FILE = test-graphe.o test-liste.o
LIB = -lm
all: $(CPP_O_FILE)
$(CC) $(CXXFLAGS) -o test-graphe.exe test-graphe.c ../src/graphe.c $(LIB)
$(CC) $(CXXFLAGS) -o test-liste.exe test-liste.c ../src/liste_url.c $(LIB)
clean:
rm -rf *.o *.exe
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "../src/graphe.h"
/*
dot -Tpdf graphe_1.dot -o graphe_1.pdf
dot -Tpdf graphe_2.dot -o graphe_2.pdf
dot -Tpdf graphe_3.dot -o graphe_3.pdf
*/
#define NB_SOMMETS 7
int main()
{
graphe g;
char* fichier1 = "graphe_1.dot";
assert(fichier1);
char* fichier2 = "graphe_2.dot";
assert(fichier2);
char* fichier3 = "graphe_3.dot";
assert(fichier3);
//Test création
creer_graphe(&g, NB_SOMMETS);
assert(g.max > 0);
assert(g.premierVide == 0);
assert(g.ligne);
assert(g.col);
//Test insertion
graphe_ajouter_arete(&g, 0, 1);
assert(graphe_est_succ(&g, 0, 1));
assert(!graphe_est_succ(&g, 1, 0));
assert(graphe_voisins(&g, 0, 1));
assert(g.ligne[0] == 0);
assert(g.col[0] == 1);
assert(g.premierVide == 1);
assert(graphe_get_plus_grand_sommet(&g) == 1);
graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 0, 9);
assert(graphe_est_succ(&g, 0, 9));
assert(graphe_voisins(&g, 0, 9));
assert(g.ligne[1] == 0);
assert(g.col[1] == 9);
assert(g.premierVide == 2);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 1, 3);
assert(graphe_est_succ(&g, 1, 3));
assert(graphe_voisins(&g, 1, 3));
assert(g.ligne[2] == 1);
assert(g.col[2] == 3);
assert(g.premierVide == 3);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 1, 2);
assert(graphe_est_succ(&g, 1, 2));
assert(graphe_voisins(&g, 1, 2));
assert(g.ligne[3] == 1);
assert(g.col[3] == 2);
assert(g.premierVide == 4);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 1, 4);
assert(graphe_est_succ(&g, 1, 4));
assert(graphe_voisins(&g, 1, 4));
assert(g.ligne[4] == 1);
assert(g.col[4] == 4);
assert(g.premierVide == 5);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 2, 4);
assert(graphe_est_succ(&g, 2, 4));
assert(graphe_voisins(&g, 2, 4));
assert(g.ligne[5] == 2);
assert(g.col[5] == 4);
assert(g.premierVide == 6);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 3, 4);
assert(graphe_est_succ(&g, 3, 4));
assert(graphe_voisins(&g, 3, 4));
assert(g.ligne[6] == 3);
assert(g.col[6] == 4);
assert(g.premierVide == 7);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 3, 5);
assert(graphe_est_succ(&g, 3, 5));
assert(graphe_voisins(&g, 3, 5));
assert(g.ligne[7] == 3);
assert(g.col[7] == 5);
assert(g.premierVide == 8);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 4, 5);
assert(graphe_est_succ(&g, 4, 5));
assert(graphe_voisins(&g, 4, 5));
assert(g.ligne[8] == 4);
assert(g.col[8] == 5);
assert(g.premierVide == 9);
assert(graphe_get_plus_grand_sommet(&g) == 9);
//graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 4, 6);
assert(graphe_est_succ(&g, 4, 6));
assert(graphe_voisins(&g, 4, 6));
assert(g.ligne[9] == 4);
assert(g.col[9] == 6);
assert(g.premierVide == 10);
assert(graphe_get_plus_grand_sommet(&g) == 9);
graphe_afficher_tableaux(&g);
graphe_ajouter_arete(&g, 5, 6);
assert(graphe_est_succ(&g, 5, 6));
assert(graphe_voisins(&g, 5, 6));
assert(g.ligne[10] == 5);
assert(g.col[10] == 6);
assert(g.premierVide == 11);
assert(graphe_get_plus_grand_sommet(&g) == 9);
graphe_afficher_tableaux(&g);
graphe_afficher(&g);
//Test contains
printf("Le graphe %s le sommet 9, indice : %d\n", (graphe_contains(&g, 9) ? "contient" : "ne contient pas"), graphe_find(&g, 9));
printf("On stocke le graphe tel qu'il est après tous les ajouts dans le fichier %s\n", fichier1);
graphe_ecrire_dot(&g, fichier1);
printf("Retirons le sommet 4 (un nouveau sommet 4 prendra sa place\n");
//Test retirer sommet
graphe_retirer_sommet(&g, 4);
assert(graphe_get_plus_grand_sommet(&g) == 8);
printf("On stocke le graphe tel qu'il est après la suppression du sommet 4 dans le fichier %s\n", fichier2);
graphe_ecrire_dot(&g, fichier2);
//Test retirer arêtes
printf("Retirons les arêtes 4-5, 1-3, et 5-4 (déjà retirée)\n");
graphe_supprimer_arete(&g, 4, 5);
graphe_afficher(&g);
graphe_supprimer_arete(&g, 1, 3);
graphe_afficher(&g);
graphe_supprimer_arete(&g, 5, 4);
graphe_afficher(&g);
//Test voisins et est_succ
printf("Les sommets 1 et 7 %s\n", (graphe_voisins(&g, 1, 7) ? "sont voisins" : "ne sont pas voisins"));
assert(!graphe_voisins(&g, 1, 7));
printf("Ajoutons une arête 1-7\n");
graphe_ajouter_arete(&g, 1, 7);
assert(graphe_est_succ(&g, 1, 7));
printf("Les sommets 1 et 7 %s\n", (graphe_voisins(&g, 1, 7) ? "sont voisins" : "ne sont pas voisins"));
assert(graphe_voisins(&g, 1, 7));
graphe_afficher(&g);
printf("Le graphe %s le sommet 9, indice : %d\n", (graphe_contains(&g, 9) ? "contient" : "ne contient pas"), graphe_find(&g, 9));
printf("On stocke le graphe tel qu'il est après des retraits et l'ajout du sommet 7 dans le fichier %s\n", fichier3);
graphe_ecrire_dot(&g, fichier3);
graphe_detruire(&g);
return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../src/liste_url.h"
int main()
{
liste_url l;
char* google = "www.google.com";
char* pasGoogle = "www.gogol.com";
char* fichierNico = "file://C:\\Users\\Nicolas\\Homework";
char* lipn = "lipn.univ-paris13.fr/";
char* lipnPresentation = "https://lipn.univ-paris13.fr/accueil/presentation/le-laboratoire/";
creer_liste(&l, 1);
liste_add(&l, google);
assert(!strcmp(l.urls[0], google));
liste_add(&l, pasGoogle);
assert(!strcmp(l.urls[1], pasGoogle));
liste_add(&l, fichierNico);
assert(!strcmp(l.urls[2], fichierNico));
liste_add(&l, lipn);
assert(!strcmp(l.urls[3], lipn));
liste_add(&l, lipnPresentation);
assert(!strcmp(l.urls[4], lipnPresentation));
liste_afficher(&l);
printf("La première URL est %s\n", liste_get_i(&l, 0));
printf("La liste %s l'URL \"www.google.com\", indice : %d\n", (liste_contains(&l, google) ? "contient" : "ne contient pas"), liste_find(&l, google));
assert(liste_contains(&l, google));
printf("Retirons la première URL, celle de google\n");
liste_remove(&l, google);
assert(strcmp(l.urls[0], google)); //strcmp != 0 => chaînes différentes (inférieures ou supérieures)
liste_afficher(&l);
printf("La liste %s l'URL \"www.google.com\", indice : %d\n", (liste_contains(&l, google) ? "contient" : "ne contient pas"), liste_find(&l, google));
assert(!liste_contains(&l, google));
liste_detruire(&l);
return EXIT_SUCCESS;
}
Le répertoire server contient le code source du serveur web
Le serveur web de notre projet,consiste la partie executable sur le web .
Pour executer le projet il suffit de suivre les etapes suivantes :
-Installer la version 3 de python ,
-Installer flask avec la commande suivante :
pip3 install flask
-Telecharger le projet du depot :
avec la commande : git clone https://depot.lipn.univ-paris13.fr/david/lipn-search.git
-Verifier que votre port 8011 est bien libre
-Se rendre sur le repertoire server et lancer votre serveur :
python3 severweb.py
VOus remarquerez que le server a demarer avec un message sur votre terminal
-Consulter votre navigateur avec l'url
https://localhost:8011
Et l'affichage de votre page html index serra afficher .
Enjoy
Le répertoire html contient les fichiers html du site
Le répertoire css contient les fichiers css du site
Le répertoire js contient les fichiers Javascript du site
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>lipn search</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="{{url_for('static',filename='../static/css/landing-page.min.css')}}">
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-light bg-light static-top">
<div class="container">
<a class="navbar-brand" href="#">Lipn Search</a>
</div>
</nav>
<!-- Masthead -->
<header class="masthead text-white text-center">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-xl-9 mx-auto">
<h1 class="mb-5">LIPN-SEARCH</h1>
</div>
<div class="col-md-10 col-lg-8 col-xl-7 mx-auto">
<form>
<div class="form-row">
<div class="col-12 col-md-25 mb-8 mb-md-0">
<input type="text" class="form-control form-control-lg" placeholder="Effectuez une recherche">
</div>
</div>
</form>
</div>
</div>
</div>
</header>
</body>
</html>
\ No newline at end of file
from flask import Flask, render_template
# Initialize the Flask application
app = Flask(__name__, template_folder='.')
@app.route('/')
def index():
return render_template('/index.html')
@app.route('/index')
def route_index():
return render_template('/index.html')
if __name__ == '__main__':
app.run(
host="localhost",
port=int("8011")
#,debug=True
)
body{font-family:Lato,'Helvetica Neue',Helvetica,Arial,sans-serif}h1,h2,h3,h4,h5,h6{font-family:Lato,'Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:700}header.masthead{position:relative;background-color:#343a40;background:url(../img/bg-masthead.jpg) no-repeat center center;background-size:cover;padding-top:8rem;padding-bottom:8rem}header.masthead .overlay{position:relative;background-color:#212529;height:100%;width:100%;top:0;left:0;opacity:.3}header.masthead h1{font-size:2rem}@media (min-width:768px){header.masthead{padding-top:12rem;padding-bottom:12rem}header.masthead h1{font-size:3rem}}.showcase .showcase-text{padding:3rem}.showcase .showcase-img{min-height:30rem;background-size:cover}@media (min-width:768px){.showcase .showcase-text{padding:7rem}}.features-icons{padding-top:7rem;padding-bottom:7rem}.features-icons .features-icons-item{max-width:20rem}.features-icons .features-icons-item .features-icons-icon{height:7rem}.features-icons .features-icons-item .features-icons-icon i{font-size:4.5rem}.features-icons .features-icons-item:hover .features-icons-icon i{font-size:5rem}.testimonials{padding-top:7rem;padding-bottom:7rem}.testimonials .testimonial-item{max-width:18rem}.testimonials .testimonial-item img{max-width:12rem;box-shadow:0 5px 5px 0 #adb5bd}.call-to-action{position:relative;background-color:#343a40;background:url(../img/bg-masthead.jpg) no-repeat center center;background-size:cover;padding-top:7rem;padding-bottom:7rem}.call-to-action .overlay{position:absolute;background-color:#212529;height:100%;width:100%;top:0;left:0;opacity:.3}footer.footer{padding-top:4rem;padding-bottom:4rem}
\ No newline at end of file
site_web/static/img/bg-masthead.jpg

939 KiB