SAprioriSystem

SApriori Engine to Predict the seasonal consumption behavior of consumers based on Object Relational Mapping model and S-Apriori algorithm, load dataset from github/cloud or local, support export the rules into Excel

This Research from KMOU (Korea Maritime & Ocean University) – Data Science Lab – Room 407.

Authors: Duy Thanh Tran, Prof. Jun-Ho Huh

Any question, please free to contact me: thanhtd@uel.edu.vn

My full name: TRAN DUY THANH

Blog study coding: https://duythanhcse.wordpress.com/

Group support Student: https://www.facebook.com/groups/communityuni/

Install nuget package

Install-Package SAprioriModel -ProjectName YourProject

Model class diagram for Sales database:

alt text

Mapping JSon Data with Model class:

#DatasetModel classDescription
1Customer.jsonSAprioriCustomerList of Customer dataset
2Employee.jsonSAprioriEmployeeList of Employee dataset
3Category.jsonSAprioriCategoryList of Category dataset
4Product.jsonSAprioriProductList of Product dataset
5Order.jsonSAprioriOrderList of Order dataset
6OrderDetails.jsonSAprioriOrderDetailList of OrderDetails dataset

Example ORM mapping SAprioriEmployee with Employee.json: 

Model class diagram for SApriori engine:

alt text

SApriori algorithm processing center is located in class SAprioriEngine, All data is loaded into SAprioriDatabase object, data can be filtered by season (Spring, summer, Autumn, Winter, thanksgiving, Christmas…) or select query by time, according to the number of accesses. The model will provide many data query methods to run the algorithm. The SAprioriEngine object provides the runSAprioriModel function to find association rules, the result returned is the SAprioriResult object. The SAprioriResult object contains the set of rules stored in the SAprioriRule object, the SAprioriRule object stores the detailed results of each component after the SApriori algorithm completes, relying on this class to represent the data.

Create DemoSApriori .net application project C# code

Copy 2 dataset as below:

alt text

Example with smalldataset – C# code

Download https://github.com/thanhtd32/SAprioriSystem/tree/main/dataset/smalldataset and save it into local file.

smalldataset folder has:

  • Category.json
  • Customer.json
  • Employee.json
  • Order.json
  • OrderDetails.json
  • Product.json
#DatasetNumber of objectsDescription
1Customer.json3List of Customer data
2Employee.json2List of Employee data
3Category.json5List of Category data
4Product.json6List of Product data
5Order.json5List of Order data
6OrderDetails.json16List of OrderDetails data

You call LoadDatabase(“smalldataset”) like code is shown as below:

SAprioriDatabase database = new SAprioriDatabase();
database.LoadDatabase("smalldataset");
database.FilterOrders(100, true);
SAprioriEngine sApriori = new SAprioriEngine();
double minSupport = 40;
double minConfident = 50;
SAprioriResult result = sApriori.runSAprioriModel(database, minSupport, minConfident);

foreach (SAprioriRule arule in result.StrongRules)
{
    string s = "[" + arule.X_Results_Description + " --> " + arule.Y_Results_Description + " " + String.Format("{0:0.00}", (arule.Confidence * 100)) + "%] "+"\r\n";
    Console.WriteLine(s);
}

Result:

[Vinamilk --> String Strawberry 100.00%] 
[Vinamilk --> Hamburger 66.67%] 
[Vinamilk --> Hamburger,String Strawberry 66.67%] 
[Vinamilk,Hamburger --> String Strawberry 100.00%] 
[Vinamilk,String Strawberry --> Hamburger 66.67%] 
[Hamburger --> G7 Coffee,Bien Hoa Sugar 50.00%] 
[Hamburger --> Bien Hoa Sugar 50.00%] 
[Hamburger --> G7 Coffee,String Strawberry 50.00%] 
[Hamburger --> String Strawberry 75.00%] 
[Hamburger --> G7 Coffee 75.00%] 
[Hamburger --> Vinamilk,String Strawberry 50.00%] 
[Hamburger --> Vinamilk 50.00%] 
[Hamburger,G7 Coffee --> Bien Hoa Sugar 66.67%] 
[Hamburger,G7 Coffee --> String Strawberry 66.67%] 
[Hamburger,String Strawberry --> G7 Coffee 66.67%] 
[Hamburger,String Strawberry --> Vinamilk 66.67%] 
[Hamburger,Bien Hoa Sugar --> G7 Coffee 100.00%] 
[G7 Coffee --> Hamburger 100.00%] 
[G7 Coffee --> Hamburger,String Strawberry 66.67%] 
[G7 Coffee --> String Strawberry 66.67%] 
[G7 Coffee --> Hamburger,Bien Hoa Sugar 66.67%] 
[G7 Coffee --> Bien Hoa Sugar 66.67%] 
[G7 Coffee,String Strawberry --> Hamburger 100.00%] 
[G7 Coffee,Bien Hoa Sugar --> Hamburger 100.00%] 
[String Strawberry --> Vinamilk 75.00%] 
[String Strawberry --> Vinamilk,Hamburger 50.00%] 
[String Strawberry --> Hamburger 75.00%] 
[String Strawberry --> G7 Coffee 50.00%] 
[String Strawberry --> Hamburger,G7 Coffee 50.00%] 
[Bien Hoa Sugar --> Hamburger 100.00%] 
[Bien Hoa Sugar --> G7 Coffee 100.00%] 
[Bien Hoa Sugar --> Hamburger,G7 Coffee 100.00%] 

Example with largedataset – C# code

Download https://github.com/thanhtd32/SAprioriSystem/tree/main/dataset/largedataset and save it into local file.

This Large Dataset is from Microsoft AdventureWorks2017 (https://docs.microsoft.com/en-us/sql/samples/adventureworks-install-configure?view=sql-server-ver15&tabs=ssms), I converted the large dataset to json, so any researcher can use this dataset without installing Microsoft SQL Server.

largedataset folder has:

  • Category.json
  • Customer.json
  • Employee.json
  • Order.json
  • OrderDetails.json
  • Product.json
#DatasetNumber of objectsDescription
1Customer.json19119List of Customer data
2Employee.json17List of Employee data
3Category.json37List of Category data
4Product.json504List of Product data
5Order.json31465List of Order data
6OrderDetails.json121317List of OrderDetails data

You call LoadDatabase(“largedataset”) like code is shown as below:

SAprioriDatabase database = new SAprioriDatabase();
database.LoadDatabase("largedataset");
DateTime from = new DateTime(2011, 5, 1);
DateTime to= new DateTime(2011, 5, 31);
database.FilterOrders(from,to, true);
SAprioriEngine sApriori = new SAprioriEngine();
double minSupport = 20;
double minConfident = 80;
SAprioriResult result = sApriori.runSAprioriModel(database, minSupport, minConfident);

foreach (SAprioriRule arule in result.StrongRules)
{
    string s = "[" + arule.X_Results_Description + " --> " + arule.Y_Results_Description + " " + String.Format("{0:0.00}", (arule.Confidence * 100)) + "%] " + "\r\n";
    Console.WriteLine(s);
}

Result:

[Sport-100 Helmet, Red --> Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Black,AWC Logo Cap,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Red --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Black,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Red --> AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Blue,AWC Logo Cap,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Red --> Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Blue 81.82%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Blue,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Red --> Sport-100 Helmet, Black 90.91%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black --> AWC Logo Cap 90.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black --> Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Blue 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Black 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black 90.00%] 
[Sport-100 Helmet, Red,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black,AWC Logo Cap 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black,AWC Logo Cap 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue,AWC Logo Cap 90.00%] 
[Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue,AWC Logo Cap 90.00%] 
[Sport-100 Helmet, Black --> AWC Logo Cap 84.62%] 
[Sport-100 Helmet, Black --> Sport-100 Helmet, Blue 84.62%] 
[Sport-100 Helmet, Black --> Long-Sleeve Logo Jersey, L 84.62%] 
[Sport-100 Helmet, Black --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 84.62%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue --> AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue --> Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Black,Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Red,Long-Sleeve Logo Jersey, L 81.82%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Blue 90.91%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 100.00%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Red 81.82%] 
[Sport-100 Helmet, Black,AWC Logo Cap --> Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L 90.91%] 
[Sport-100 Helmet, Black,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue 90.91%] 
[Sport-100 Helmet, Black,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red 81.82%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue,AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red 81.82%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue 90.91%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue,AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Blue --> Long-Sleeve Logo Jersey, L 84.62%] 
[Sport-100 Helmet, Blue --> Sport-100 Helmet, Black 84.62%] 
[Sport-100 Helmet, Blue --> AWC Logo Cap,Long-Sleeve Logo Jersey, L 84.62%] 
[Sport-100 Helmet, Blue --> AWC Logo Cap 92.31%] 
[Sport-100 Helmet, Blue,AWC Logo Cap --> Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L 83.33%] 
[Sport-100 Helmet, Blue,AWC Logo Cap --> Sport-100 Helmet, Black 83.33%] 
[Sport-100 Helmet, Blue,AWC Logo Cap --> Sport-100 Helmet, Black,Long-Sleeve Logo Jersey, L 83.33%] 
[Sport-100 Helmet, Blue,AWC Logo Cap --> Long-Sleeve Logo Jersey, L 91.67%] 
[Sport-100 Helmet, Blue,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black 90.91%] 
[Sport-100 Helmet, Blue,AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red 81.82%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> AWC Logo Cap 100.00%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red,AWC Logo Cap 81.82%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black,AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Red 81.82%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black,AWC Logo Cap 90.91%] 
[Sport-100 Helmet, Blue,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black 90.91%] 
[AWC Logo Cap --> Sport-100 Helmet, Blue 85.71%] 
[AWC Logo Cap --> Long-Sleeve Logo Jersey, L 92.86%] 
[AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Black 84.62%] 
[AWC Logo Cap,Long-Sleeve Logo Jersey, L --> Sport-100 Helmet, Blue 84.62%] 
[Long-Sleeve Logo Jersey, L --> AWC Logo Cap 86.67%] 
[LL Road Frame - Red, 60 --> Road-650 Black, 52 90.00%] 
[LL Road Frame - Red, 60 --> Road-650 Red, 60 90.00%] 
[LL Road Frame - Red, 60 --> Road-450 Red, 52 90.00%] 
[LL Road Frame - Red, 60 --> LL Road Frame - Black, 52 90.00%] 
[LL Road Frame - Red, 60 --> Road-450 Red, 52,Road-650 Red, 60 90.00%] 
[LL Road Frame - Red, 60,Road-450 Red, 52 --> Road-650 Red, 60 100.00%] 
[LL Road Frame - Red, 60,Road-650 Red, 60 --> Road-450 Red, 52 100.00%] 
[LL Road Frame - Black, 52 --> Road-450 Red, 52 90.00%] 
[LL Road Frame - Black, 52 --> LL Road Frame - Red, 60 90.00%] 
[LL Road Frame - Black, 52 --> Road-650 Red, 44 90.00%] 
[Road-450 Red, 58 --> Road-650 Black, 52 81.82%] 
[Road-450 Red, 58 --> Road-650 Red, 44 81.82%] 
[Road-450 Red, 58 --> Road-450 Red, 52 81.82%] 
[Road-450 Red, 52,Road-650 Red, 60 --> Road-650 Red, 44 81.82%] 
[Road-450 Red, 52,Road-650 Red, 60 --> Road-650 Red, 44,Road-650 Black, 52 81.82%] 
[Road-450 Red, 52,Road-650 Red, 60 --> Road-650 Red, 44,Road-650 Black, 52 81.82%] 
[Road-450 Red, 52,Road-650 Red, 60 --> Road-650 Black, 52 90.91%] 
[Road-450 Red, 52,Road-650 Red, 60 --> LL Road Frame - Red, 60 81.82%] 
[Road-450 Red, 52,Road-650 Red, 60,Road-650 Red, 44 --> Road-650 Black, 52 100.00%] 
[Road-450 Red, 52,Road-650 Red, 60,Road-650 Black, 52 --> Road-650 Red, 44 90.00%] 
[Road-450 Red, 52,Road-650 Red, 44 --> Road-650 Black, 52 83.33%] 
[Road-450 Red, 52,Road-650 Red, 44,Road-650 Black, 52 --> Road-650 Red, 60 90.00%] 
[Road-450 Red, 52,Road-650 Black, 52 --> Road-650 Red, 60 90.91%] 
[Road-450 Red, 52,Road-650 Black, 52 --> Road-650 Red, 60,Road-650 Red, 44 81.82%] 
[Road-450 Red, 52,Road-650 Black, 52 --> Road-650 Red, 60,Road-650 Red, 44 81.82%] 
[Road-450 Red, 52,Road-650 Black, 52 --> Road-650 Red, 44 90.91%] 
[Road-650 Red, 60 --> Road-650 Black, 52 85.71%] 
[Road-650 Red, 60,Road-650 Red, 44 --> Road-450 Red, 52 100.00%] 
[Road-650 Red, 60,Road-650 Red, 44 --> Road-450 Red, 52,Road-650 Black, 52 100.00%] 
[Road-650 Red, 60,Road-650 Red, 44 --> Road-650 Black, 52 100.00%] 
[Road-650 Red, 60,Road-650 Red, 44 --> Road-450 Red, 52,Road-650 Black, 52 100.00%] 
[Road-650 Red, 60,Road-650 Red, 44,Road-650 Black, 52 --> Road-450 Red, 52 100.00%] 
[Road-650 Red, 60,Road-650 Black, 52 --> Road-450 Red, 52 83.33%] 
[Road-650 Red, 44 --> Road-450 Red, 52 85.71%] 
[Road-650 Red, 44,Road-650 Black, 52 --> Road-450 Red, 52 90.91%] 
[Road-650 Red, 44,Road-650 Black, 52 --> Road-450 Red, 52,Road-650 Red, 60 81.82%] 
[Road-650 Red, 44,Road-650 Black, 52 --> Road-450 Red, 52,Road-650 Red, 60 81.82%] 
[Road-650 Red, 44,Road-650 Black, 52 --> Road-650 Red, 60 81.82%] 
[Road-650 Black, 52 --> Road-650 Red, 60 85.71%] 

Example with largedataset – C# code – filter season

static void Main(string[] args)
        {
            //create SAprioriDatabase object
            SAprioriDatabase database = new SAprioriDatabase();
            //define season, it depends on the region of dataset collecting
            database.addSeason(SAprioriSeason.Spring, new List<int>() { 3, 4, 5 });
            database.addSeason(SAprioriSeason.Summer, new List<int>() { 6, 7, 8 });
            database.addSeason(SAprioriSeason.Autumn, new List<int>() { 9, 10, 11 });
            database.addSeason(SAprioriSeason.Winter, new List<int>() { 12, 1, 2 });
            //call LoadDatabase method, largedataset is folder stores 6 json file
            database.LoadDatabase("largedataset");
            int year = 2011;
            //filter dataset by sesaon
            database.FilterOrders(SAprioriSeason.Spring, year, true);

            //run SApriori
            SAprioriEngine sApriori = new SAprioriEngine();
            double minSupport = 20;
            double minConfident = 80;
            SAprioriResult result = sApriori.runSAprioriModel(database, minSupport, minConfident);

            foreach (SAprioriRule arule in result.StrongRules)
            {
                string s = "[" + arule.X_Results_Description + " --> " + arule.Y_Results_Description + " " + String.Format("{0:0.00}", (arule.Confidence * 100)) + "%] " + "\r\n";
                Console.WriteLine(s);
            }
            Console.ReadLine();
        }

Some example GUI for filtering data and SAprior:

GUI for filtering season

alt text

GUI for SApriori

alt text

Export Excel function

SAprioriExcel aprioriExcel = new SAprioriExcel();
saveFileDialog1.Filter = "Excel 2007-2015|*.xlsx|Excel 2003(*.xls)|*.xls";
if (saveFileDialog1.ShowDialog()==DialogResult.OK)
{
    bool x= aprioriExcel.ExportStrongRulesToExcel(sApriori, saveFileDialog1.FileName);
    if(x==true)
    {
        //Export excel sucessfully
    }
}               
alt text

Leave a Reply