Memverifikasi Isomorfisme Dua Grafik dan Pencarian untuk Subgraf Isomorfik: Suatu Pendekatan Berdasarkan Analisis NB-Paths

Halo semuanya.

Ada tugas semacam itu - untuk memeriksa apakah dua grafik saling isomorfik. Artinya, secara sederhana, untuk mengetahui apakah kedua grafik ini adalah grafik “sama”, tetapi dengan angka verteks yang berbeda dan, jika grafik ditentukan secara grafis, dengan lokasi spasial yang berbeda. Solusi untuk masalah ini tidak sejelas kelihatannya bagi seseorang pada pandangan pertama: bahkan untuk grafik kecil, melihat representasi grafis mereka tidak akan selalu memberikan jawaban yang jelas. Lihat, misalnya, gambar pada Wiki yang sama: en.wikipedia.org/wiki/Graph_isomorphism#Example .

Ya tentu saja?

Tetapi ada tugas yang lebih sulit: untuk mencari dalam grafik "besar" untuk semua subgraf isomorfik ke beberapa grafik "lebih kecil" lainnya. Ini bahkan lebih "hutan gelap." Tentu saja, itu tidak sepenuhnya gelap, tetapi tugasnya, Anda tahu, bukanlah yang termudah.

Jadi apa yang kita miliki?

1. Mengapa semua ini perlu?


Dan walaupun masalah isomorfik (sub) grafik, seperti yang telah kami sebutkan, rumit, itu cukup perlu dan berguna. Untuk apa? Dan kemudian, misalnya, untuk mencari basis data untuk senyawa kimia yang mirip dengan molekul yang diberikan oleh rumus strukturalnya. Lagi pula, itu bisa dibayangkan sebagai grafik, kan? Chemoinformatika melakukan ini - ada sains semacam itu. Tetapi ada juga tugas perbandingan dengan sampel, berbagai tugas bioinformatika, dan banyak hal menarik lainnya.

2. Bagaimana mengatasi masalah ini?


Pendekatan klasik untuk memecahkan masalah ini ditetapkan oleh J. Ullman pada tahun 1976 [3], kemudian ia secara substansial meningkatkan algoritme [4]. Juga, pendekatan ini dikembangkan lebih lanjut dalam karya banyak penulis lain, misalnya, Cordella dan rekan penulis [2] (algoritma VF2), Bonnitsi dan rekan penulis [1] (algoritma RI), dll.

Singkatnya, inti dari pendekatan ini adalah “pintar enumerating ”kemungkinan korespondensi dari simpul-simpul dari grafik sampel B dan grafik A di mana ia sedang dicari. Pencarian ini menjadikannya "pintar" untuk memasukkan berbagai kondisi dan batasan yang memungkinkan Anda untuk memotong opsi yang tidak cocok sedini mungkin. Juga, untuk tujuan ini, berbagai pemrosesan awal dari sumber data dapat dilakukan.

Kami tidak akan terlibat dalam menceritakan kembali karya-karya ini di sini, tetapi kami akan mengundang pembaca untuk berkenalan dengannya secara mandiri. Namun, "pertunjukan lebih baik daripada pesanan", dan harus dicatat bahwa ada contoh grafik yang baik dan penjelasan tentang algoritma ini di Web. Lihat, misalnya, berikut ini:
coderoad.ru/17480142/Is-atau-sederhana-contoh-untuk-explanation-algorithm-Ulman - penjelasan yang sangat bagus dan jelas dengan contoh,
issue.life/questions/8176298 - langkah-langkah algoritma VF2 dengan contoh .

Namun, muncul pertanyaan - apakah ada cara dan pendekatan lain yang mungkin untuk menyelesaikan masalah ini, meskipun untuk beberapa kasus khusus? Dan saya ingin memperkenalkan Anda pada salah satu jawaban yang mungkin untuk pertanyaan ini.

2. Isomorfisme grafik dan jalur NB


Mari kita segera setuju: di bawah NB-paths (dan bukan dari bahasa Inggris, tetapi hanya karena lebih pendek) kita akan memanggil jalur non-branching maksimal, yaitu. secara maksimal panjang jalur tidak bercabang beberapa grafik.

Jadi, jika kita memiliki dua grafik yang isomorfik satu sama lain , maka untuk setiap representasi grafik pertama sebagai urutan jalur non-bercabang yang diperluas secara maksimal (mis., Jalur NB kami) selalu ada representasi yang sesuai dari grafik kedua yang sesuai dengannya, dan pada saat yang sama:

  • untuk grafik berarah, jalur yang sesuai satu sama lain akan disejajarkan,
  • derajat dari simpul yang bersesuaian satu sama lain untuk grafik tidak berarah (dan untuk grafik berorientasi, jumlah tepi yang masuk dan keluar, masing-masing) akan sama dengan
  • ketika "menggabungkan" representasi seperti itu sebagai hasilnya, kita akan memiliki korespondensi dari simpul dari grafik pertama dan kedua.

Contoh . Grafik A = {1-> 2, 1-> 6, 4-> 5, 5-> 1, 3-> 3}. Grafik B = {3-> 4, 3-> 5, 1-> 2, 2-> 3, 6-> 6}
PathsA ( jalur non-percabangan maksimal A ): 1-> 2, 1-> 6, 4-> 5-> 1, 3-> 3
PathsB ( jalur non-bercabang maksimal B ): 3-> 4, 3-> 5, 1-> 2-> 3, 6-> 6 Pencocokan Vertex
: 1 ( A): 3 (B), 2 (A): 4 (B), 6 (A): 5 (B), 4 (A): 1 (B), 5 (A): 2 (B), 3 ( A): 6 (B).

Dengan demikian, tugas untuk mengecek isomorfisma dari dua grafik dapat diselesaikan dengan menemukan jalur yang sesuai satu sama lain dan kemudian memeriksa kesetaraan matriks kedekatan yang dibangun berdasarkan pada konservasi mereka dari urutan simpul dalam urutan jalur yang diperoleh (setiap simpul ditambahkan ke urutan sekali, "sebutkan" pertama). Dalam contoh kami, urutan simpul berikut akan digunakan untuk membangun matriks adjacency:

A : 1, 2, 6, 4, 5, 3
B : 3, 4, 5, 1, 2, 6

Matriks penyesuaian untuk A untuk urutan simpul,

gambar

matriks Adjacency untuk B untuk urutan simpul tertentu:

gambar

Matriksnya sama, oleh karena itu, grafik A dan B adalah isomorfik.

Selain itu, pendekatan ini (1) berlaku untuk grafik berorientasi dan tidak-berorientasi, (2) berlaku untuk grafik yang mengandung lebih dari satu komponen yang terhubung / koneksi kuat, (3) itu berlaku untuk grafik yang mengandung banyak (banyak) tepi dan loop, namun (4) tidak memperhitungkan simpul akun yang tidak ada tepi insiden padanya.

3. Nah, periksa isomorfisme grafik. Tapi bagaimana dengan pencarian subgraph?


Dan di sini, terus terang, semuanya jauh lebih rumit. Di sini kita akan memiliki batasan berikut: berdasarkan pada esensi metode ini, Anda tidak dapat menemukan apa pun, tetapi hanya subgraph "tertulis" . Dan yang kami maksudkan adalah “subgraph” dari grafik A subgraph seperti itu yang dapat “dilem” ke bagian lain A hanya karena insiden edge hanya dengan simpul batas dari (subgraph) lintasan non-percabangannya dari panjang maksimum (dalam hal ini, A dapat mengandung komponen terhubung lainnya). Jangan khawatir, di bawah ini akan menjadi contoh, dan semuanya akan lebih jelas.

Selain itu, ketika menyelesaikan masalah ini, selain mencari korespondensi jalur NB dari grafik A dan B panjangnya (seperti halnya dengan tes isomorfisme di atas), juga perlu secara terpisah mencari korespondensi berikut yang mungkin di antaranya:

  • Korespondensi NB-paths - rantai sederhana dari grafik B ke NB-paths - rantai sederhana / sederhana dari siklus grafik A. Selain itu, awalnya jalur tersebut dalam grafik A dapat lebih panjang - dalam hal ini, segmennya diambil dengan panjang yang sama dengan jalur yang diinginkan dari B.
  • Korespondensi jalur NB- "akhir" dari grafik B ke setiap jalur NB-grafik A (dalam hal ini, jalur dari grafik A juga bisa lebih panjang - dalam hal ini, segmennya diambil dengan panjang yang sama dengan lintasan yang diinginkan dari grafik B).

Mari kita lihat sebuah contoh:

gambar

Ketika mencari sebuah subgraf isomorfik "B tertulis" dari grafik B pada kolom A (lihat gambar di atas), korespondensi berikut akan ditemukan:

  • jalur dalam 2-> 3-> 4 kolom B: jalur dalam 2-> 3-> 4 kolom A,
  • jalur akhir 1-> 2 dan 10-> 2 kolom B: jalur akhir 0-> 2 kolom A dan sebuah fragmen dari jalur akhir 7-> 1-> 2 kolom A, yaitu 1-> 2,
  • 7->8 B: 9->10->11 A, 9->10 10->11, 12->13->14->12 A, 12->13, 13->14, 14->12.

Dengan demikian, sebagai subgraf "bertuliskan" dari grafik A yang isomorfik untuk grafik B, 5 opsi berikut dapat ditemukan:

A1 = {0-> 2, 1-> 2, 2-> 3, 3-> 4, 4-> 5, 4-> 6, 9-> 10}
A2 = {0-> 2, 1-> 2, 2-> 3, 3-> 4, 4-> 5, 4-> 6, 10-> 11}
A3 = {0-> 2, 1-> 2, 2-> 3, 3-> 4, 4-> 5, 4-> 6, 12-> 13}
A4 = {0-> 2, 1-> 2, 2 -> 3, 3-> 4, 4-> 5, 4-> 6, 13-> 14}
A5 = {0-> 2, 1-> 2, 2-> 3, 3-> 4, 4-> 5, 4-> 6, 14-> 12}

Namun, jika kita menambahkan tepi tambahan 3-> 8 ke grafik A, kita mendapatkan grafik A '(di bawah pada gambar yang sama). Dan dalam A 'tidak akan ada lagi "bertuliskan" subgraph isomorfik ke grafik B. Memang: tepi 3-> 8 "membagi" jalur 2-> 3-> 4 dari grafik A menjadi dua dan, oleh karena itu, jalur kandidat untuk jalur dalam 2 ->3 -> 4 kolom B tidak akan ditemukan.

4. Sekarang algoritma itu sendiri


Sekarang kita dapat beralih ke pertimbangan yang lebih terperinci dari algoritma pencarian untuk subgraph “dituliskan” dalam kolom A yang isomorfik untuk beberapa kolom B.

Jadi, algoritma akan terdiri dari 4 tahap:

  • Preprocessing
  • Sesuai
  • Penjarangan,
  • Kesimpulan

Tahap I. Preprocessing


Pada tahap ini, kami menemukan semua jalur NB untuk masing-masing grafik, serta mengevaluasi faktor-faktor yang membatasi ruang pilihan selama enumerasi. Itu kami melakukan hal berikut:

  1. Kami menemukan semua jalur-NB di kolom A dan menempatkannya ke dalam array dinamis (dalam C ++ - ke dalam vektor) PathsA
  2. Kami menemukan semua jalur-NB di kolom B dan menempatkannya di larik dinamis (vektor) PathsB
  3. A B. II-IV , 1. A- B B: - , B.
  4. A B ( ).
  5. A B: DA DB .
  6. – B00 – B , , .

Jadi, kami memiliki jalur NB pada grafik dan parameter batas dari p.p. 3-5

Tahap II. Pemetaan


Pada tahap ini, kami akan memilih jalur calon NB (selanjutnya hanya disebut sebagai "jalur calon") dari kolom A untuk masing-masing jalur NB kolom B. Menandai setiap jalur kandidat dari PathsA untuk setiap jalur jalan iH PathsB [i ] akan ditulis ke array dinamis dua dimensi (dalam C ++ - ke vektor vektor) NPath - secara berurutan ke vektor ke-i (garis ke-ke-1) - dalam bentuk tiga angka berurutan: jumlah jalur yang sesuai di PathsA - jumlah posisi awal di dalamnya - panjang jalur .

Misalnya, PathsB [2] = {1, 0, 3, 3, 1, 3} berarti bahwa jalur PathsB [2] dikaitkan dengan 2 jalur kandidat dari A: dari PathsA [1] - 3 elemen path pertama mulai dari nol ( awal), dan dari PathsA [3] - juga 3 elemen mulai dari yang pertama (di sebelah awal).

Pada saat yang sama, kami akan mencari (memilih) jalur kandidat dalam 4 arah:

  1. Cari jalur kandidat untuk semua jalur NB internal grafik B dari PathsB, mis. mereka yang kedua simpul batasnya terhubung dalam grafik B dengan setidaknya 2 simpul lainnya (terlepas dari arah koneksi semacam itu) dan pada saat yang sama jalur ini bukan siklus sederhana (berorientasi - untuk grafik berorientasi).
  2. Cari jalur kandidat untuk mengikuti jalur NB dari PathsB.
  3. Cari jalur kandidat untuk jalur NB - loop sederhana dari PathsB.
  4. Cari jalur kandidat untuk jalur NB - jalur sederhana dari PathsB.

Ketika memilih kandidat path untuk setiap path ith dari PathsB, mereka dibandingkan (ini adalah di mana beberapa parameter limiter yang dihitung sebelumnya berguna):

  • panjang dan panjang jalur kandidat (harus sama untuk kasus 1 dan 3, dan untuk kasus 2 dan 4 jalur kandidat dari PathsA juga bisa lebih lama),
  • derajat dari simpul batasnya dan simpul yang sesuai dari jalur kandidat (untuk simpul dari jalur kandidat, nilai-nilai ini harus setidaknya jalur dari PathsB).

Jika, menurut hasil tahap II, tidak ada satu pun jalur kandidat dari PathsA yang ditemukan untuk salah satu jalur di PathsB, maka dianggap bahwa A tidak mengandung subgraph "tertulis" isomorfik ke kolom B.

Penjarangan


Sekarang mari kita coba "peras" grafik A. Kita hanya tinggal di tepian yang masuk ke jalur kandidat yang kita temukan. Jika pada saat yang sama, grafik A telah kehilangan setidaknya satu sisi dibandingkan dengan keadaan awalnya, kita kembali ke tahap I "Pra-pemrosesan": derajat simpul grafik A dan, karenanya, daftar jalur kandidat dapat dikurangi dan, karenanya, ruang pencarian dapat dikurangi.

Tahap III. Menipiskan daftar jalur kandidat


Tujuan dari langkah ini adalah untuk menghilangkan kandidat sebanyak mungkin yang tidak pantas. Untuk melakukan ini, kami melakukan langkah-langkah berikut.

  1. Pengecualian dari jalur kandidat yang jelas tidak sesuai berdasarkan jarak minimum antara simpul dalam kolom B. Jalur kandidat untuk setiap PathsB [i] yang setidaknya satu PathsB [j] tidak ada jalur kandidat ditemukan untuk yang terpendek jarak antara simpul batas mereka di kolom A kurang dari atau sama dengan jarak terpendek antara simpul batas yang sesuai dari lintasan PathsB [i] dan PathsB [j] dari grafik B.
  2. Pengecualian untuk semua siklus dari PathsB ke jalur kandidat non-siklik yang terkait dengannya dan sebaliknya.
  3. Pengecualian jalur kandidat mirip dengan Bagian 1, tetapi tidak dengan kriteria jarak terpendek antara simpul batas, tetapi oleh mereka (simpul batas) untuk kesetaraan atau ketidaksetaraan yang saling menguntungkan.

Misalnya, jika:

  • elemen awal PathsB [i] tidak sama dengan elemen awal PathsB [j], dan
  • elemen terakhir PathsB [i] tidak sama dengan elemen terakhir PathsB [j], dan
  • elemen awal PathsB [i] sama dengan elemen akhir PathsB [j], dan
  • elemen awal PathsB [j] tidak sama dengan elemen akhir PathsB [i],

jalur kandidat di sepanjang jalur PathsB [i] di mana setidaknya satu PathsB [j] tidak menemukan jalur kandidat yang memenuhi kondisi di atas mengenai kesetaraan / ketidaksetaraan (jalur kandidat) mereka awal dan akhir puncak.

Artinya, secara garis besar, kita membuang jalur kandidat yang jelas tidak akan dimasukkan dalam subgraph yang diinginkan - berdasarkan jarak ke semua jalur kandidat lainnya (jalur ini sangat jauh dari yang lain), berdasarkan pada siklus / non-siklus, dan juga didasarkan pada kesetaraan karena / ketidaksetaraan simpul batas mereka dengan simpul batas jalur kandidat lainnya.

Jika, menurut hasil tahap III, setidaknya 1 jalur kandidat telah dihapus dari PathsA, maka - lagi - kolom A diperbarui - hanya tepi yang termasuk dalam jalur kandidat yang tersisa tetap di dalamnya. Dan, sekali lagi, jika pada saat yang sama, grafik A "kehilangan berat" dengan setidaknya satu tepi, kita kembali lagi ke tahap I "Preprocessing": derajat simpul grafik A dan, karenanya, daftar jalur kandidat dapat dikurangi dan, karenanya, dapat dikurangi ruang pencarian.

Tahap IV. Kesimpulan


Setiap kemungkinan kombinasi dari semua jalur kandidat yang tersisa mendefinisikan subgraf kolom A. Untuk setiap subgraf tersebut, sebuah matriks adjacency dibangun dengan mempertimbangkan urutan jalur kandidat dengan cara yang sama seperti matriks adjacency B00 dibangun untuk grafik B, dan kemudian matriks adjacency ini dibandingkan.

Jika mereka sama, maka multiplisitas sisi dibandingkan (lihat Bagian 3 dari Tahap I Preprocessing). Jika semua kondisi ini dipenuhi, subgraf yang ditemukan dianggap isomorfik untuk grafik B dan ditambahkan ke set hasil yang ditemukan.

Selama tahap IV "Kesimpulan", berbagai pemeriksaan tambahan dapat digunakan untuk mempercepat identifikasi dan penolakan subgraf yang tidak sesuai.

5. Bagaimana semuanya bingung ... Pertimbangkan contoh algoritma


Saya tidak tahu tentang Anda, tetapi itu tidak akan bisa dipahami oleh saya tanpa contoh. Mari kita lihat sebuah contoh.

Dalam gbr. grafik di bawah ini adalah A = {1-> 2, 2-> 5, 5-> 15, 16-> 15, 5-> 5, 5-> 5, 5-> 4, 5-> 6, 7-> 6 , 6-> 8, 6-> 6, 6-> 9, 9-> 10, 9-> 11, 12-> 0, 0-> 12, 4-> 13, 13-> 14, 14-> 4 } dan B = {1-> 1, 4-> 5, 5-> 1, 1-> 3, 3-> 3, 3-> 2, 2-> 7, 2-> 8, 9-> 10, 10-> 9, 1-> 6, 6-> 11, 11-> 12, 12-> 6}. Tugas kita adalah untuk menemukan dalam graf A semua "bertuliskan" subgraph isomorfik ke grafik B. Hasilnya juga ditunjukkan pada gambar: simpul yang ditemukan dan tepi grafik A disorot oleh garis tebal, dan jumlah simpul yang sesuai dari grafik B ditunjukkan dalam tanda kurung (jika ada beberapa pilihan, mereka ditunjukkan oleh gambar pecahan).

gambar

Saat memecahkan masalah pencarian di kolom A dari subgraph “bertulis” isomorfik ke kolom B,kami memiliki hasil algoritma berikut.

Tahap I: Semua tindakan dan perhitungan dilakukan sesuai dengan p.p. 1-5 dari langkah ini, berikut ini adalah PathsA dan PathsB yang dihasilkan.

PathsA:

1-> 2-> 5
4-> 13-> 14 4
5-> 4
5-> 5
5-> 6
5-> 15
6-> 6
6-> 8
6-> 9
7-> 6
9 -> 10
9-> 11
16-> 15
0-> 12-> 0

JalurB:

1-> 1
1-> 3
1-> 6
2-> 7
2-> 8
3-> 2
3-> 3
4- > 5-> 1
6-> 11-> 12-> 6
9-> 10-> 9

Tahapan II-III. Perbandingan menunjukkan bahwa untuk setiap jalur dari PathsA ada setidaknya satu jalur kandidat dari PathsB, dan PathsA disingkat oleh hasil penjarangan awal. Pada tahap penipisan utama (III), pengurangan lebih lanjut dari PathsA tidak terjadi. Sebagai hasilnya, PathsA dan PathsB mengambil bentuk:

PathsA:

1-> 2-> 5
4-> 13-> 14-> 4
5-> 5
5-> 5
5-> 6
6-> 6
6-> 9
9-> 10
9-> 11
0-> 12-> 0

PathsB:

1-> 1
1-> 3
1-> 6
2-> 7
2-> 8
3-> 2
3-> 3
4-> 5-> 1
6 -> 11-> 12-> 6
9-> 10->9

Perbandingan terakhir dari NPath adalah:
NPath:

i = 0: 3 0 2
i = 1: 4 0 2
i = 2: 2 0 2
i = 3: 7 0 2 8 0 2
i = 4: 7 0 2 8 0 2
i = 5: 6 0 2
i = 6: 5 0 2
i = 7: 0 0 3
i = 8: 1 0 4
i = 9: 9 0 3

Ini adalah waktu untuk mengingat bahwa setiap triplet angka yang dipesan dalam NPaths [i] mendefinisikan PathsB [i] jalur kandidat yang sesuai dari A dalam format: jumlah jalur yang sesuai dari PathsA - posisi awal segmen dari jalur ini - panjang segmen. Dengan demikian, mudah untuk memverifikasi bahwa pathsB [0] = {1-> 1} (i = 0: 3 0 2) sesuai dengan satu-satunya path dari A, yaitu, segmen dari PathsA [3], mulai dari awal dan memiliki panjang 2.

Tahap IV. Akibatnya, subgraph A1 yang unik ditemukan di kolom A, isomorfik ke B. A1 = {0-> 12, 1-> 2, 2-> 5, 4-> 13, 5-> 4, 5-> 5, 5- > 6, 6-> 6, 6-> 9, 9-> 10, 9-> 11, 12-> 0, 13-> 14, 14-> 4}.

5. Apa selanjutnya?


Dan kemudian, pada prinsipnya, itu saja. Meskipun tidak cukup: penulis harus mengakui bahwa algoritma masih dapat diselesaikan dan diselesaikan. Apa yang bisa ditambahkan?

  1. Ketika fitur tambahan dari simpul kolom A dan B diperkenalkan (misalnya, ketika senyawa ditentukan oleh grafik senyawa kimia, kode numerik yang sesuai dengan satu dan hanya satu atom (isotop) dapat dikaitkan dengan setiap titik), proses pencarian dapat dipercepat karena akurasi perbandingan yang lebih besar sesuai dengan Tahap II: tambahan syarat untuk memilih jalur kandidat adalah korespondensi dari label titik.
  2. . , «», «» - B.
  3. , , , «» :
    (1) «» , ,
    (2) .
    «» « - », , , .
  4. , - , .


Umum tentang masalah:
1. Bonnici, V., Giugno, R., Pulvirenti, A. et al. Algoritma isomorfisma subgraph dan aplikasinya untuk data biokimia. BMC Bioinformatics 14, S13 (2013). doi.org/10.1186/1471-2105-14-S7-S13 .
2. Cordella L, Foggia P, Sansone C, Vento M: A (Sub) Grafik Algoritma Isomorfisma untuk Mencocokkan Grafik Besar. Transaksi IEEE pada Analisis Pola dan Kecerdasan Mesin. 2004, 26 (10): 1367-1372. 10.1109 / TPAMI.2004.75.
3. Ullmann Julian R.: Algoritma untuk Subgraph Isomorphism. Jurnal Asosiasi untuk Mesin Komputer. 1976, 23: 31-42. 10.1145 / 321921.321925.
4. Ullmann Julian R.: Algoritma bit-vektor untuk kepuasan kendala biner dan isomorfisme subgraph. Algoritma J Exp. 2011, 15 (1.6): 1.1-1.6. 1.64

Pracetak penulis yang ditulis dalam bahasa yang lebih resmi tentang ini adalah yang paling "algoritma berbasis NB-Paths", yang juga berisi informasi tentang upaya untuk mengimplementasikannya dalam C ++:
5. Chernoukhov SA 2020. Preprints.RU. Memeriksa isomorfisme dari dua grafik dan mencari subgraph "bertulis" isomorfik: suatu pendekatan yang didasarkan pada analisis pendekatan jalur non-percabangan yang diperluas secara maksimal untuk memecahkan (sub) grafik masalah isomorfisme. DOI: dx.doi.org/10.24108/preprints-3111977

Sumber internet yang berguna tentang topik:
6. coderoad.ru/17480142/Is-li- contoh-sederhana-untuk-penjelasan-algoritma-Ulman
7. isu.life/questions / 8176298 .

All Articles