This is an old revision of the document!
Responsabili:
Data publicării : 24 aprilie, ora: 04:20
Deadline hard: 21 mai, ora 23:55
Deadline soft: 18 mai, ora 23:59. Se acordă un bonus de 10% din punctajul obținut pe temă echipelor care încarcă tema pe vmchecker până la aceasta dată.
1 mai 2020 20:16: Adăugare schelet, teste şi checker
1 mai 2020 20:38: Prelungire deadline
1 mai 2020 23:37: Precizare gcc-multilib
2 mai 2020 00:00: Adăugare instanţă de upload pe vmchecker
3 mai 2020 15:09: Precizare despre modul în care trebuie încarcată arhiva
4 mai 2020 00:41: Completare condiție de egalitate get_oldest_influence
4 mai 2020 01:01: Precizare despre cross-citations în get_reading_order
4 mai 2020 01:08: Precizare limită distanță pentru find_best_coordinator
5 mai 2020 22:05: Plasare corectă a flagului -lm
în Makefile_tema3
8 mai 2020 19:31: Checkerul parsează fisierele .json
mai rapid.
15 mai 2020 05:41: Corectare teste şi încă o prelungire de deadline.
18 mai 2020 22:15: Corectare cerința 7. (cea discutată pe forum)
Se va implementa un sistem ce agregă informații despre artcole științifice și răspunde la query-uri folosind cele mai recente date salvate. În timpul execuției programului se vor primi update-uri și query-uri intercalate.
Tema va avea la bază articole științifice publicate la diferite conferințe. Un astfel de articol are urmatoarele proprietati:
În arhiva temei este prezentat un API (descris de fișierul header publications.h) ce va trebui implementat de voi și va fi apelat de către checker pentru a verifica corectitudinea. Acesta conține:
PublData
, declarată în publications.h
și pe care o veți implementa (îi veți declara membrii) în alt fișier (de exemplu publications.c
) astfel încât un cod care include publications.h
să nu aibă acces la membrii acestei structuri în mod direct. Vedeți exemplul de mai jos pentru detalii.PublData
în care aveți structurată informația pasată cu ajutorul comenzii add_paper
și trebuie doar să întoarcă outputul cerut. Nu va fi nevoie să faceți niciun fel de printuri. De altfel, se recomandă să nu le faceți decât pentru debug, după care să le eliminați, deoarece orice formă de I/O
încetineşte procesul.
În această structură va trebui să stocați toate datele pe care le veți folosi pentru rezolvarea cerințelor de mai jos. Structura este declarată în publications.h
. Voi va trebui să îi definiți membrii în publications.c
sau în orice alt fişier, cu condiția ca acești membri să nu fie vizibili din afara API-ului vostru (structura să fie “privată”).
Mai precis, trebuie să nu puteți accesa direct membrii structurii din afara API-ului. Presupunând că există în cadrul structurii voastre membrul int ceva, nu ar trebui să puteți compila codul următor, dacă acesta este scris în afara API-ului:
PublData* data = init_publ_data(); // functia este descrisa mai jos data->ceva = 1; // aici ar trebui sa apara eroarea de compilare
Pentru structura PublData
, va trebui să implementați două funcții care vor aloca, respectiv dealoca memoria utilizată pentru aceasta.
Semnătură:
PublData* init_publ_data(void)
Funcția va aloca o structura de tip PublData
și va întoarce un pointer la aceasta. Este la latitudinea voastră dacă faceți inițializările aici sau pe măsură ce se execută funcțiile din API.
Această funcție este apelată runner.c
înainte să se înceapă rularea funcțiilor propriu-zise ale API-ului.
Semnătură:
void destroy_publ_data(PublData* data)
Funcția dealoca toata memoria folosită de variabila data. În runner.c, aceasta functie se apelează după ce se apelează toate funcțiile din API.
runner.c
.
Semnătură:
void add_paper(PublData* data, const char* title, const char* venue, const int year, const char** author_names, const int64_t* author_ids, const char** institutions, const int num_authors, const char** fields, const int num_fields, const int64_t id, const int64_t* references, const int num_refs);
Funcția primește ca parametri toate datele referitoare la un articol (titlu, jurnalul la care a fost publicat, anul apariției, numele autorilor, instituțiile din care aceștia fac parte, domeniile de studiu în care se încadrează articolul, id-ul acestuia, precum și lista articolelor pe care acesta le citează) şi adaugă acest articol în logica de articole reținută în structura PublData
.
Semnătură:
char* get_oldest_influence(PublData* data, const int64_t paper);
Funcția primește ca parametru id-ul unui articol și se dorește găsirea celui mai vechi (cu cel mai mic an al aparitiei) articol de care acesta “depinde”. Spunem că articolul X depinde de articolul Y dacă una din condițiile de mai jos este îndeplinită:
Observăm că avem o definiție recursivă.
add_paper
. Dacă pentru un articol dat nu se găsește niciun alt articol care să indeplinească cerințele, funcția va întoarce string-ul “None”
.
Semnătură:
float get_venue_impact_factor(PublData* data, const char* venue);
Funcția întoarce factorul de impact al jurnalului primit ca parametru. Factorul de impact se defineşte ca fiind numărul mediu de citări ale tuturor articolelor publicate în jurnalul dat până în momentul în care se primește query-ul.
Rezultatul poate să difere de cel din fișierele de referință cu maximum 0.001
.
Semnătură:
int get_number_of_influenced_papers(PublData* data, const int64_t id_paper, const int distance);
Funcția primește id-ul unui articol și o anumită distanță și folosește aceeași definiție pentru X este influențat de Y ca la Cerința 1. Se cere numărul total de articole care au fost influențate de articolul dat. Practic, Y a influențat X și Z. X și Z au influențat la rândul lor alte articole, etc. Toate acestea formează mulțimea articolele influențate de Y și trebuie să fie numărate.
Parametrul “distance” are rolul de a limita distanța de la Y la un articol care depinde de el. Mai exact, numărăm doar articolele care depind de Y și se află la o distanță cel mult “distance” în graful de citări.
Semnătură:
int get_erdos_distance(PublData* data, const int64_t id1, const int64_t id2);
Se primesc id-urile a doi autori și se dorește calcularea distanței Erdos dintre cei doi. Definim distanța Erdos în felul următor:
Dacă autorul A a publicat un articol împreună cu autorul B, înseamnă că distanța dintre ei este minimă, adică 1. Dacă la rândul lui B a publicat un articol cu C (iar A nu a publicat cu C) distanța Erdos dintre A și C este 2, pentru că se află la 2 muchii distanță în graful de autori, unde o muchie între doi autori înseamnă că au publicat un articol împreună. Analog pentru distanțe mai mari.
-1
Semnătură:
char** get_most_cited_papers_by_field(PublData* data, const char* field, const int* num_papers);
Se primesc numele unui domeniu și numărul de articole dorit. Se vor întoarce titlurile celor mai citate num_papers
care studiază acel domeniu, adică field
se află în lista lor de domenii studiate. Un articol X numără câte o citare pentru fiecare articol Y care în lista lui de references conține articolul X.
char**
. Reprezintă un array de string-uri ce va fi alocat dinamic în funcție și în care se vor copia titlurile așteptate. Runnerul va citi acest răspuns, apoi se va ocupa de eliberarea memoriei.
*num_papers
are un rol dublu: inițial indică numarul de artcole ce se doreste intors. In cazul in care lista intoarsa contine mai putine elemente, acest numar se va suprascrie cu dimensiunea listei intoarse.
Semnătură:
int get_number_of_papers_between_dates(PublData* data, const int early_date, const int late_date);
Funcția va calcula si va returna câte articole au fost publicate în total între cei doi ani primiți ca parametru.
Semnătură:
int get_number_of_authors_with_field(PublData* data, const char* institution, const char* field);
Se primesc numele unei instituții și un domeniu de studiu. Să se determine numărul total de autori care au publicat articole despre acel domeniu în timp ce erau asociați cu instituția dată.
Semnătură:
int* get_histogram_of_citations(PublData* data const int64_t id_author, int* num_years);
Se dorește calcularea numărului de citări ale tuturor articolelor publicate de autorul primit ca parametru. Funcția returnează aceasta histograma sub forma unui vector de lungime num_years
(valoare pe care tot funcția o va seta). Acest vector contine pe poziția i
, numărul de noi citări ale articolelor autorului cu i
ani în urma fata de anul curent (2020). Astfel, pe poziția 0 va fi numărul de citări din 2020, pe poziția 1, cel din 2019 s.a.m.d.
int**
. Reprezintă un array de intregi ce va fi alocat dinamic în funcție în care se vor scrie valorile așteptate. Runnerul va citi acest răspuns, apoi se va ocupa de eliberarea memoriei. Parametrul int* num_years
este de tip “output” - la adresa indicată se va scrie dimensiunea histogramei întoarse
Semnătură:
char** get_reading_order(PublData* data, const int64_t id_paper, const int distance, int* num_papers);
Se primește un articol care se dorește a fi citit și o anumită distantă.
Considerăm că pentru a înțelege mai bine un articol, trebuie să citim întâi articolele pe care le-a citat, analog pentru acestea. Practic, înainte de a citi un articol, dorim să citim toate articolele de care acesta depinde / de care este influențat.
Să se găsească o ordine în care aceste articole să fie citite astfel încât să se respecte criteriile de mai sus.
Pentru o distanță finită specificată, se consideră doar articolele de care depinde articolul curent și se află la o distanță mai mică sau egală cu distance
față de acesta.
În cazul în care există mai multe soluții posibile vom aplica și următoarele criterii, în ordine:
Respectând aceste două criterii adiționale, output-ul va fi unic determinat.
char**
. Reprezintă un array de string-uri ce va fi alocat dinamic în funcție și în care se vor copia titlurile așteptate. Runnerul va citi acest răspuns, apoi se va ocupa de eliberarea memoriei. Parametrul int* num_papers
este de tip “output” - la adresa indicată se va scrie dimensiunea listei întoarse.
Semnătură:
char* find_best_coordinator(PublData* data, int64_t id_author);
Un student a publicat câteva articole și vrea să aplice la doctorat. Pentru a face acest lucru, trebuie să găsească un profesor coordonator de doctorat cât mai potrivit. Pentru fiecare autor din sistem (altul decât studentul dat), vom defini scorul următor:
$$score_i = e^{-erdos(a, a_i)} \sum_{j=1}^{n_i}{num\_citations_j * impact\_factor_j}$$
Unde:
Să se întoarcă numele autorului care maximizeaza acest scor.
Sarcina voastră este să implementați API-ul descris astfel încât la rularea comenzii make build
să fie creat un fişier obiect, numit publications.o
. Pentru aceasta, aveți pus la dispoziție makefile-ul Makefile
. Aveți libertatea să-l modificați cum doriți, cu mențiunea că acesta trebuie să creeze fișierul publications.o
la rularea comenzii make build și să aibă și o regulă clean care va șterge publications.o
și orice alte fișiere binare care rezultă în urma compilării obiectului publications.o
. Regula clean
nu trebuie să șteargă și fișierele binare create de restul scheletului, care vor fi detailate mai jos.
Infrastructura de testare este destul de amplă, pe de o parte pentru ca modulul Parson
ce se ocupă cu parsarea fișierelor .json
este destul de stufos, dar și pentru a vă obișnui cu ideea de a lucra într-un proiect mai mare, la care voi trebuie sa adăugați o funcționalitate anume (în acest caz, API-ul descris în enunț).
În runner.c
se parsează câte un fişier de test și se apelează funcțiile din acesta, împreuna cu parametrii fiecăreia. Observați folosirea keywordului static
pentru acele funcții care nu trebuie să fie vizibile din afara runner.c
. Se recomandă folosirea acestui tip de funcții și în implementarea API-ului, acolo unde acest lucru este posibil (oriunde este vorba de o funcție care nu se apelează decât din interiorul API-ului, adică, în general orice funcție auxiliară vă definiții).
Fiecare funcție statică din runner
își va apela corespondenta din API și va compara outputul acesteia cu cel citit din fişierul de referință (în afară de run_add_paper
, care apelează add_paper
, care nu intoarce nimic).
Makefile_tema3
conține regulile build
şi clean
. Prima dintre acestea va crea executabilul tema3
, iar clean va șterge acest executabil, precum și toate fișierele binare rezultate în urma compilării.
Puteți folosi regulile din Makefile_tema3
astfel:
make -f Makefile_tema3 <regula>
tema3
va linka publications.o
, pe care Makefile_tema3
îl creează folosind make build
.
-m32
. Pentru a putea să-l folosiți și voi, va trebui să vă instalați biblioteca gcc-multilib
astfel:
sudo apt-get install gcc-multilib
Pentru a putea parsa un fișier de tip .json
, așa cum sunt fișierele ce conțin articolele cu care se va opera, se folosește parserul Parson
. În general, orice parser va citi tot fișierul și va crea o structură de date ce va conține toate datele din acel fișier. Această structura de date poate fi asemănată cu un hashtable, mapând numele câmpurilor din fișierul .json
, ca stringuri, la valorile acestora. Acesta este și cazul parserului Parson:
JSON_Object
. Acesta poate conține câmpuri (stringuri, numere etc.), array-uri (JSON_Array
), precum și alte JSON_Object
-uri.json_array_get_<tip_de_date>(JSON_Array* array, const char* key)
fie din obiecte
json_object_get_<tip_de_date>(JSON_Object* object, const char* key)
JSON_Array
sau JSON_Object
. Din acest motiv, în funcția run_add_paper
nu se dealocă şi parametrii pasați funcției add_paper
, ci doar parserul.
Trebuie sa încărcați pe vmchecker o arhivă .zip
care să conțină fișierul Makefile
care creează publications.o
, sursele din care acest fişier obiect este creat, precum și README
-ul care prezintă implementarea API-ului.
Puteți să modificați orice în cele doua fișiere publications.c
și publications.h
date, cu excepția semnăturilor funcțiilor din publications.h
și a macro-ului DIE
(pe care chiar vă recomandăm sa îl folosiți în implementarea temei).
checker
sau Makefile_tema3
) în arhivă, pentru că vor fi suprascrise!
make -f Makefile_tema3 clean
înainte să creați arhiva! O arhivă care conține fișiere binare va primi o depunctare de 10p.
README
/README.txt
/README.md
(formatul pe care vi-l recomandăm). Se pot aplica depunctari pentru README
-uri care nu respectă cerințele.cpplint.py
şi este binar: ori e bine şi primiți punctele, ori nu e bine şi nu primiți nimic
De aceea, vă sfătuim să nu vă lăsați rezolvări ale temelor pe calculatoare partajate pe mail/liste de discuții/grupuri etc.
Q: Putem implementa tema în C++?
A: Din păcate, nu :(
Q: Putem include în publications.h biblioteca <blablabla>.h
?
A: <blablabla>.h
e un header, NU o bibliotecă. Biblioteca se cheamă libc şi este declarată în mai multe headere, printre care şi <blablabla>.h
, pe care, da, aveți voie să-l includeți.
Q: Putem să “spargem” implementarea API-ului în mai multe fişiere sursă şi, eventual, mai multe headere?
A: Da.
Q: Putem folosi flaguri de optimizare în Makefile
?
A: Nu.
Q: Putem folosi variabile globale?
A: Nu. Folosiți structura PublData
sau, dacă din orice motiv nu vi se pare potrivit, folosiţi variabile statice.
Q: Am trimis tema cu x secunde după deadline. Mi se poate lua în considerare?
A: Nu.
https://www.aminer.org/citation - dataset-ul din care au fost extrase articolele din teste.