--
-- Erstellung eines Beispielschemas mit generierten Testdaten
-- 
-- Zweck: (A) Test einer bestehenden Loesung und Vermessung von Statements
--
-- Voraussetzungen: 
--         - Beispielschema DEMO mit entsprechender Berechtigung (dbms_stats-Package)
--         - hinreichend grosser default Tablespace fuer die Objekte
--
-- Vorgehen:
--         - Objekte (Funktionen, Prozeduren, Tabellen im Beispielschema anlegen
--           (Teil 1)
--         - Objekte befuellen (Teil 2)
--         - Teststatements laufenlassen (Teil 3)
--
-- Anpassungen:
--         - verwendete Parallelitaetsgrade ggf. anpassen
--                   
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
-- =============================================================================
*/


/* =============================================================================
--
-- Teil 1: Erstellung aller Objekte im Beispielschema
--
-- =============================================================================
*/
SPOOL create_demo.log
SET linesize 200
SET pagesize 100
SET trimout ON
SET trimspool ON
SET serveroutput ON
SET echo ON
SET feedback ON
SET timing ON
SELECT 'Anlegen der Objekte fuer Beispielschema' FROM dual;

/* -----------------------------------------------------------------------------
-- Hilfsfunktionen zur Testdatengenerierung
-- -----------------------------------------------------------------------------
*/
CREATE OR REPLACE FUNCTION ran_m_n ( min_n    IN NUMBER
                                   , max_n    IN NUMBER
                                   , decimals IN NUMBER DEFAULT 0) 
                  RETURN NUMBER    
/* =============================================================================
-- FUNCTION RAN_N_M
-- 
-- Erzeugung von Zufallszahlen
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/

IS 
   random_number NUMBER;
   minimum_value NUMBER := 0;
   m             NUMBER := 0;
   digits        NUMBER := 1;

BEGIN
   IF max_n > min_n 
   THEN
      minimum_value := min_n; 
      -- the handling with default "Zero" prevent FROM a loop situation if the second parameter is lower
      -- than the first
   END IF;
   IF max_n < 10000000 
   THEN
      digits :=7;
   END IF;
   IF max_n < 1000000 
   THEN
      digits :=6;
   END IF;
   IF max_n < 100000 
   THEN
      digits :=5;
   END IF;
   IF max_n < 10000 
   THEN
      digits :=4;
   END IF;
   IF max_n < 1000 
   THEN
      digits :=3;
   END IF;
   IF max_n < 100 
   THEN
      digits :=2;
   END IF;
   LOOP
      random_number := TO_NUMBER(SUBSTR(TO_CHAR(DBMS_RANDOM.RANDOM), -digits, digits));
      IF  random_number < max_n 
      AND random_number > minimum_value 
      THEN 
         IF DECIMALS > 0 
         THEN 
            m := TO_NUMBER(SUBSTR(TO_CHAR(DBMS_RANDOM.RANDOM), -DECIMALS, DECIMALS));
            random_number :=  TO_NUMBER(TO_CHAR(random_number)||'.'||TO_CHAR(m));
         END IF;
         
         RETURN random_number;
      END IF;
   END LOOP;
END;
/

CREATE OR REPLACE FUNCTION ran_words ( word_delimiter IN VARCHAR2 DEFAULT 'default'
                                     , words          IN VARCHAR2) 
                  RETURN VARCHAR2    
/* =============================================================================
-- FUNCTION RAN_WORDS
-- 
-- Erzeugung von zufaelligen VARCHAR2 Werten aus einer Liste
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/
IS 
   word_counter  NUMBER :=0;
   random_number NUMBER := 0;
   n             NUMBER := 0;
   value_group   VARCHAR2(20);
   result_string VARCHAR2(256);

   TYPE t_value_table IS 
      TABLE OF VARCHAR2(20)
      INDEX BY BINARY_INTEGER;

   t_values t_value_table;

BEGIN
   t_values(1) := '';
   t_values(2) := '';
   t_values(3) := '';
   
   -- scan the values of parameter 'words'
   -- delimiter is the character in 'WORD_DELIMITER'
   LOOP
     n  := n+1;
     IF n = LENGTH(words)+1 
     THEN 
        EXIT; 
     END IF;
     IF SUBSTR(words, n, 1) = word_delimiter 
     THEN
         t_values(word_counter) := value_group;
         value_group := '';
         word_counter := word_counter+1;
     ELSE 
         value_group := value_group||SUBSTR(words,n,1);
     END IF;
   END LOOP;
   
   -- find a correct random number to fetch a record of PL/SQL table
   LOOP
      random_number := TO_NUMBER(SUBSTR(TO_CHAR(DBMS_RANDOM.RANDOM),-2,2));
      IF random_number < word_counter 
      THEN
         -- the correct random number is found
         EXIT;
      END IF;
   END LOOP;
   
   RETURN t_values(random_number);
END;
/

/* -----------------------------------------------------------------------------
-- Dimension Produkte
-- -----------------------------------------------------------------------------
*/
DROP TABLE produkte;
CREATE TABLE produkte ( produktnr     NUMBER(10)   NOT NULL
                      , produkt       VARCHAR2(20) NOT NULL
                      , produktgruppe VARCHAR2(20) NOT NULL
                      , produktsparte VARCHAR2(20) NOT NULL
                      );
 
CREATE OR REPLACE PROCEDURE produkt_gen (anz_produkte NUMBER) 
/* =============================================================================
-- PROCEDURE PRODUKT_GEN
-- 
-- Erzeugung von zufaelligen Testdaten fuer die Dimension PRODUKTE
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/
AS
   anz_prod NUMBER;
   recno    NUMBER;

BEGIN
   recno := 1;

   WHILE recno <= anz_produkte  LOOP
      INSERT INTO produkte ( produktnr
                           , produkt  
                           , produktgruppe
                           , produktsparte
                           )
      VALUES ( recno
             , ran_words('/','KT-MM/KT-LL/KT-AM/Schraube/Scheibe/Tisch/Messer/Haus/')
             , ran_words('/','Eisenwaren/Werzeug/Moebel/Bueroware')
             , ran_words('/','SP1/SP2/SP3/')
             );              

      recno := recno + 1;
   END LOOP;
   COMMIT;
END;
/

/* -----------------------------------------------------------------------------
-- Dimension Kunden
-- -----------------------------------------------------------------------------
*/
DROP TABLE kunden;
CREATE TABLE kunden ( kundennr     NUMBER(10)   NOT NULL
                    , kundenname   VARCHAR2(20) NOT NULL
                    , berufsgruppe VARCHAR2(20) NOT NULL
                    , segment      NUMBER(10)   NOT NULL
                    );

CREATE OR REPLACE PROCEDURE kunde_gen (anz_kunde NUMBER) 
/* =============================================================================
-- PROCEDURE KUNDE_GEN
-- 
-- Erzeugung von zufaelligen Testdaten fuer die Dimension KUNDEN
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/
AS
   recno NUMBER;

BEGIN
   recno := 1;

   WHILE recno <= anz_kunde  LOOP
      INSERT INTO kunden ( kundennr
                         , kundenname  
                         , berufsgruppe
                         , segment 
                         )         
      VALUES ( recno
             , ran_words('/','Schmidt/Meier/Mueller/Hartmann/Schuster/Schneider/Kraemer/Bauer/Meister/')
             , ran_words('/','Angestellte/Beamte/Freiberufler/Selbstaendige/Unbeschaeftigt/Student/Rentner/')        
             , ran_m_n(1,50)
             );              
      recno := recno + 1;
   END LOOP;
   COMMIT;
END;
/

/* -----------------------------------------------------------------------------
-- Dimension Tage
-- -----------------------------------------------------------------------------
*/
DROP TABLE tage;
CREATE TABLE tage ( datumnr          NUMBER(4)   NOT NULL
                  , datum            DATE        NOT NULL
                  , tag_des_monats   NUMBER(2)   NOT NULL
                  , tag_des_jahres   NUMBER(3)   NOT NULL
                  , woche_des_jahres NUMBER(2)   NOT NULL
                  , monats_nummer    NUMBER(2)   NOT NULL
                  , monat_desc       VARCHAR2(9) NOT NULL
                  , quartals_nummer  NUMBER(1)   NOT NULL
                  , jahr_nummer      NUMBER(4)   NOT NULL
                  );

CREATE OR REPLACE PROCEDURE time_gen ( startdate DATE
                                     , enddate   DATE) 
/* =============================================================================
-- PROCEDURE TIME_GEN
-- 
-- Erzeugung von zufaelligen Testdaten fuer die Dimension TAGE
--
-- Aufruf der Prozedur z.B.:
--   alter session set nls_date_format  = 'DDMMYYYY';
--   exec time_gen('01011990','31122015');
--   
--   alter session set nls_date_format  = 'DD-MON-YY';
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/
AS
   incdate DATE;
   recno   INTEGER;

BEGIN
   incdate := startdate;
   recno := 1;

   WHILE incdate <= enddate LOOP
      INSERT INTO tage ( datumnr 
                       , datum
                       , tag_des_monats
                       , tag_des_jahres
                       , woche_des_jahres
                       , monats_nummer
                       , monat_desc
                       , quartals_nummer
                       , jahr_nummer
                       )
      VALUES ( recno
             , incdate
             , TO_NUMBER(TO_CHAR(incdate,   'DD'))
             , TO_NUMBER(TO_CHAR(incdate,   'DDD'))          
             , TO_NUMBER(TO_CHAR(incdate,   'WW'))          
             , TO_NUMBER(TO_CHAR(incdate,   'MM'))      
             , TO_CHAR(incdate,   'Month')   
             , TO_NUMBER(TO_CHAR(incdate,   'Q'))          
             , TO_NUMBER(TO_CHAR(incdate,   'YYYY'))
             );              

      recno   := recno + 1;
      incdate := incdate + 1;
   END LOOP;
   COMMIT;
END;
/

/* -----------------------------------------------------------------------------
-- Dimension Orte
-- -----------------------------------------------------------------------------
*/
DROP TABLE orte;
CREATE TABLE orte ( ortnr  NUMBER(4) NOT NULL
                  , ort    VARCHAR2(20)
                  , land   VARCHAR2(20)
                  , region VARCHAR2(20)
                  );

-- Befuellung der Dimensionstabelle
INSERT INTO orte (ortnr,ort,land,region) VALUES (1,'Berlin','Berlin','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (2,'Braunschweig','Niedersachsen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (3,'Bremen','Bremen','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (4,'Dortmund','Nordrhein-Westfalen','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (5,'Elmshorn','Schleswig Holstein','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (6,'Essen','Nordrhein-Westfale','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (7,'Hannover','Niedersachsen','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (8,'Kaiserslautern','Rheinland Pfalz','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (9,'Karlsruhe','Baden Wuerttemberg','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (10,'Kiel','Schleswig Holstein','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (11,'Ludwigsburg','Bayern','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (12,'Mannheim','Baden Wuerttemberg','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (13,'Muenchen','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (15,'Muenster','Nordrhein-Westfalen','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (16,'Nuernberg','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (17,'Stuttgart','Baden Wuerttemberg','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (18,'Trier','Rheinland Pfalz','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (19,'Ulm','Baden Wuerttemberg','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (20,'Wuerzburg','Bayern','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (21,'Augsburg','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (22,'Saarbruecken','Saarland','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (23,'Leipzig','Sachsen','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (24,'Ludwigshafen','Rheinland Pfalz','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (25,'Dreieich','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (26,'Darmstadt','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (27,'Freiburg','Baden Wuerttemberg','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (28,'Regensburg','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (29,'Potsdam','Brandenburg','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (30,'Duisburg','Nordrhein-Westfale','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (31,'Koeln','Nordrhein-Westfale','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (32,'Koblenz','Rheinland Pfalz','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (33,'Worms','Rheinland Pfalz','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (34,'Heidelberg','Baden Wuerttemberg','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (35,'Coburg','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (36,'Frankfurt','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (37,'Flensburg','Schleswig Holstein','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (38,'Wiesbaden','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (39,'Mainz','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (40,'Fuerth','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (41,'Duesseldorf','Nordrhein-Westfalen','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (42,'Osnabrueck','Nordrhein-Westfalen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (43,'Bielefeld','Nordrhein-Westfalen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (44,'Aachen','Nordrhein-Westfalen','West');
INSERT INTO orte (ortnr,ort,land,region) VALUES (45,'Dresden','Sachsen','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (46,'Chemnitz','Sachsen','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (47,'Luebeck','Schleswig Holstein','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (48,'Wilhelmshaven','Nordrhein-Westfalen','Nord');
INSERT INTO orte (ortnr,ort,land,region) VALUES (49,'Kassel','Hessen','Mitte');
INSERT INTO orte (ortnr,ort,land,region) VALUES (50,'Schweinfurt','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (51,'Erfurt','Thueringen','Ost');
INSERT INTO orte (ortnr,ort,land,region) VALUES (52,'Bamberg','Bayern','Sued');
INSERT INTO orte (ortnr,ort,land,region) VALUES (53,'Hamburg','Hamburg','Nord');
COMMIT;

/* -----------------------------------------------------------------------------
-- Faktentabelle Umsaetze
-- -----------------------------------------------------------------------------
*/
DROP TABLE umsaetze;
CREATE TABLE umsaetze ( ortnr     NUMBER(4)  NOT NULL
                      , kundennr  NUMBER(10) NOT NULL
                      , datum     DATE       NOT NULL
                      , produktnr NUMBER(5)  NOT NULL
                      , summe     NUMBER(10) NOT NULL
                      , menge     NUMBER(10) NOT NULL
                      );
ALTER TABLE umsaetze NOLOGGING;

CREATE OR REPLACE PROCEDURE umsatz_gen ( anzahl NUMBER) 
/* =============================================================================
-- PROCEDURE UMSATZ_GEN
-- 
-- Erzeugung von zufaelligen Testdaten fuer die Faktentabelle UMSAETZE
--
-- Aufruf Tabelle Umsaetze
-- 1. Anzahl der einzeln eingefuegten Saetze durch connect by level <= XXXXXX
--    festgelegt (1.000.000 Saetze) 
--
-- 2. Parameter anzahl ergibt Iterationen (INSERT AS SELECT) und
--    damit Anzahl der Faktensaetze:
--     1  ->      2.000.000  Saetze      
--     2  ->      4.000.000  Saetze  
--     3  ->      8.000.000  Saetze  
--     4  ->     16.000.000  Saetze  
--     5  ->     32.000.000  Saetze  
--     6  ->     64.000.000  Saetze 
--     7  ->    128.000.000  Saetze
--     8  ->    256.000.000  Saetze
--     9  ->    512.000.000  Saetze  -> 15 GB Plattenplatz     
--    10  ->  1.024.000.000  Saetze  -> 30 GB Plattenplatz   
--    11  ->  2.048.000.000  Saetze
--    12  ->  4.096.000.000  Saetze   
--    13  ->  8.192.000.000  Saetze   
--    14  -> 16.384.000.000  Saetze   
--    15  -> 32.768.000.000  Saetze   
--    16  -> 65.536.000.000  Saetze   
--
-- Verfasser:
-- Frank Schneede,    ORACLE Deutschland B.V. & Co. KG 
--
-- Date     |Who | What
---------------------------------------------------------------
-- 22.02.10 | FS | Erstellung
--
  __  _   _   _     _
 (__)|-< /-\ (_ |_ (-_
-- =============================================================================
*/
AS
   recno INTEGER;

BEGIN
   -- 1. Einzelsaetze einfuegen
   INSERT /*+ append*/ INTO umsaetze
   SELECT ROUND(DBMS_RANDOM.VALUE(1, 52))                       ortnr
   ,      ROUND(DBMS_RANDOM.VALUE(1, 1000))                     kundennr
   ,      TO_DATE(SYSDATE-(ROUND(DBMS_RANDOM.VALUE(1, 5000))))  datum
   ,      ROUND(DBMS_RANDOM.VALUE(1, 1000))                     produktnr
   ,      ROUND(DBMS_RANDOM.VALUE(1, 200))                      summe
   ,      ROUND(DBMS_RANDOM.VALUE(1, 20))                       menge
   FROM dual
   CONNECT BY LEVEL <= 1000000;
   COMMIT;
   -- 2. Einzelsaetze vervielfachen
   recno := 1;
   WHILE recno <= anzahl LOOP
      INSERT /*+ APPEND */ INTO umsaetze 
      SELECT * FROM umsaetze;
      recno := recno + 1;
      COMMIT;
   END LOOP;
END;
/

SPOOL OFF;

/* =============================================================================
--
-- Teil 2: Befuellung der Schemaobjekte
--
-- =============================================================================
*/
pause
SPOOL fill_demo.log
SET linesize 200
SET pagesize 100
SET trimout ON
SET trimspool ON
SET serveroutput ON
SET echo ON
SET feedback ON
ALTER SESSION SET nls_date_format  = 'DDMMYYYY';
SET timing ON
SELECT 'Start der Verarbeitung' FROM dual;

/* -----------------------------------------------------------------------------
-- Fuellen Zeit-Dimension:  time_gen('von Datum','bis Datum')
-- -----------------------------------------------------------------------------
*/
SELECT 'Tage_Dimension' FROM dual;
EXEC time_gen('01011990','31122015');

/* -----------------------------------------------------------------------------
-- Fuellen Produkt-Dimension (10.000 Eintraege)
-- -----------------------------------------------------------------------------
*/
SELECT '--> Tabelle Produkte erstellen' Aktion FROM dual;
EXEC produkt_gen(10000);

SELECT 'Produkte hat: ',  count(*), ' Zeile' FROM produkte;

/* -----------------------------------------------------------------------------
-- Fuellen Kunde-Dimension (1.000 Eintraege)
-- -----------------------------------------------------------------------------
*/
SELECT '--> Tabelle Kunden erstellen' Aktion FROM dual;
EXEC kunde_gen(1000);

SELECT 'Kunde hat: ',  count(*), ' Zeilen' FROM kunden;

/* -----------------------------------------------------------------------------
-- Fuellen Orte-Dimension (1.000 Eintraege)
-- -----------------------------------------------------------------------------
*/
-- entfaellt, bereits mit Tabellenerstellung befuellt!

/* -----------------------------------------------------------------------------
-- Fuellen Umsatz-Fakten
-- -----------------------------------------------------------------------------
*/
SELECT '--> Tabelle Umsaetze erstellen' Aktion FROM dual;

ALTER TABLE umsaetze   PARALLEL NOLOGGING;

ALTER SESSION FORCE PARALLEL QUERY PARALLEL 4;
ALTER SESSION FORCE PARALLEL DML   PARALLEL 4;
ALTER SESSION FORCE PARALLEL DDL   PARALLEL 4;

-- Erzeugung von 64.000.000 Saetzen
EXEC umsatz_gen(6);

ALTER TABLE produkte PARALLEL;
ALTER TABLE kunden   PARALLEL;
ALTER TABLE tage     PARALLEL;
ALTER TABLE orte     PARALLEL;
ALTER TABLE umsaetze LOGGING;

/* -----------------------------------------------------------------------------
-- Statistiken erstellen und verifizieren
-- -----------------------------------------------------------------------------
*/
SELECT '--> Statistiken erstellen' Aktion FROM dual;
EXEC DBMS_STATS.GATHER_SCHEMA_STATS('DEMO', DEGREE=>4);

SELECT table_name
,      blocks 
FROM user_tables 
WHERE table_name LIKE 'UMSAETZE%'; 

SELECT ROUND((SELECT value FROM v$system_parameter WHERE name = 'db_block_size') * 
             (SELECT blocks FROM user_tables WHERE table_name = 'UMSAETZE')  / 
             (1024*1024*1024)
            ) Gigabytes
FROM dual;

SPOOL OFF;
