diff --git a/AngularApp/prototype/angular.json b/AngularApp/prototype/angular.json index 0a028fdda6efbf3ffbe7e20d6dc595cf2d18e8ac..964ccabb969ea643221be52cd7ddbf2e469b2e91 100644 --- a/AngularApp/prototype/angular.json +++ b/AngularApp/prototype/angular.json @@ -20,7 +20,6 @@ "tsConfig": "tsconfig.app.json", "aot": true, "assets": [ - "src/favicon.ico", "src/assets" ], "styles": [ @@ -124,4 +123,4 @@ } }, "defaultProject": "prototype" -} \ No newline at end of file +} diff --git a/AngularApp/prototype/src/app/api.service.ts b/AngularApp/prototype/src/app/api.service.ts index 4245ac76640a0a70851b4035d221b56ff6c7986a..be5fff874225d3a75b6dea62c1d1aedd15cdc26f 100644 --- a/AngularApp/prototype/src/app/api.service.ts +++ b/AngularApp/prototype/src/app/api.service.ts @@ -22,8 +22,8 @@ export class ApiService { } // Split data into windows and normalize - async createWindows(values, parameters): Promise { - const postData = {values, parameters}; + async createWindows(parameters): Promise { + const postData = {parameters}; const response = await fetch('http://127.0.0.1:5000/create-windows', { method: 'POST', headers: { @@ -36,10 +36,9 @@ export class ApiService { } // Generate LSH-tables by hashing each window - async createTables(windows, parameters): Promise { + async createTables(parameters): Promise { console.log("creating tables"); - const postData = {windows, parameters}; - console.log("ahh"); + const postData = {parameters}; const response = await fetch('http://127.0.0.1:5000/create-tables', { method: 'POST', headers: { @@ -52,20 +51,32 @@ export class ApiService { } // Return similar windows based on an input query - async getSimilarWindows(window, tables) { + async getSimilarWindows(query, tables) { const response = await fetch('http://127.0.0.1:5000/query', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, - body: JSON.stringify({window, tables}) + body: JSON.stringify({query, tables}) }); return await response.json(); } - async updateTables(windows, labelData, tables, parameters): Promise { - const postData = {windows, labelData, tables, parameters}; + async getAverageWindow(windows): Promise { + const response = await fetch('http://127.0.0.1:5000/average', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({windows}) + }); + return await response.json(); + } + + async updateTables(labelData, tables, parameters): Promise { + const postData = {labelData, tables, parameters}; const response = await fetch('http://127.0.0.1:5000/update', { method: 'POST', headers: { diff --git a/AngularApp/prototype/src/app/app.component.html b/AngularApp/prototype/src/app/app.component.html index b7c16c2d287af0241d390d1e7b229b519a4489b3..d6d0515c3a1a3dc408e87e5ee367d2a5fecead11 100644 --- a/AngularApp/prototype/src/app/app.component.html +++ b/AngularApp/prototype/src/app/app.component.html @@ -1,22 +1,24 @@
-
+
+ + + + + + + + + + + + + +
+
- - - - - - - - - - - - - - + diff --git a/AngularApp/prototype/src/app/app.module.ts b/AngularApp/prototype/src/app/app.module.ts index 810ac8004450405bafb8e891b8cf12945a658468..3aa4f91778c066c80e481d00cc9d8a9f9edffc88 100644 --- a/AngularApp/prototype/src/app/app.module.ts +++ b/AngularApp/prototype/src/app/app.module.ts @@ -15,6 +15,7 @@ import {MatTabsModule} from '@angular/material/tabs'; import { LabelsComponent } from './labels/labels.component'; import { QueryWindowComponent } from './query-window/query-window.component'; import {ZingchartAngularModule} from 'zingchart-angular'; +import { ProgressViewComponent } from './progress-view/progress-view.component'; PlotlyModule.plotlyjs = PlotlyJS; @@ -27,6 +28,7 @@ PlotlyModule.plotlyjs = PlotlyJS; TableOverviewComponent, LabelsComponent, QueryWindowComponent, + ProgressViewComponent, ], imports: [ BrowserModule, diff --git a/AngularApp/prototype/src/app/cache.service.ts b/AngularApp/prototype/src/app/cache.service.ts index bd27a7bd24a41f812983a554dd75743759306196..19375342fa8c4e75b0d48a45407d326e71525e37 100644 --- a/AngularApp/prototype/src/app/cache.service.ts +++ b/AngularApp/prototype/src/app/cache.service.ts @@ -9,17 +9,17 @@ export class CacheService { public rawIndices: string[]; private _currentTab: number; - private _windows: number[][]; private _query = undefined; private _labels = {}; private _tables; private _windowSimilarity; - public windowSize = 200; + public windowSize = 500; public nrOfTables = 10; public hashSize = 5; public stepSize = 1; + public onNewData: EventEmitter = new EventEmitter(); public onNewSimilarity: EventEmitter = new EventEmitter(); public onNewLabels: EventEmitter = new EventEmitter(); public onNewQuery: EventEmitter = new EventEmitter(); @@ -36,40 +36,43 @@ export class CacheService { async initialize(): Promise { await this.getRawData(); - await this.getWindows(); + await this.createWindows(); await this.createTables(); } async reset(): Promise { this.windowSimilarity = undefined; - await this.getWindows(); + await this.createWindows(); await this.createTables(); } async getRawData(): Promise { const rawData: RawData = await this.api.readFile(); - console.log(rawData); this.rawIndices = rawData.index; this.rawValues = rawData.values; + this.onNewData.emit(); } - async getWindows(): Promise { - this.windows = await this.api.createWindows(this.rawValues, this.parameters); - console.log(this.windows); + async createWindows(): Promise { + await this.api.createWindows(this.parameters); + this.onNewWindows.emit(); } async createTables(): Promise { - this.tables = await this.api.createTables(this.windows, this.parameters); - console.log(this.tables); + this.tables = await this.api.createTables(this.parameters); } - async getSimilarWindows(window): Promise { - this.windowSimilarity = await this.api.getSimilarWindows(window, this.tables); + async getSimilarWindows(query): Promise { + this.windowSimilarity = await this.api.getSimilarWindows(query, this.tables); return this.windowSimilarity; } + async getAverageWindow(windows): Promise { + return await this.api.getAverageWindow(windows); + } + async updateTables(): Promise { - this.tables = await this.api.updateTables(this.windows, this.labels, this.tables, this.parameters); + this.tables = await this.api.updateTables(this.labels, this.tables, this.parameters); } public set query(v) { @@ -81,16 +84,12 @@ export class CacheService { return this._query; } - public set windows(v) { - this._windows = v; - this.onNewWindows.emit(); - } - public get windows(): number[][] { - return this._windows; + return []; } public set tables(v) { + console.log(v); this._tables = v; this.onNewTables.emit(); } @@ -110,6 +109,7 @@ export class CacheService { public set windowSimilarity(v) { this._windowSimilarity = v; + console.log(v); this.onNewSimilarity.emit(); } @@ -119,7 +119,6 @@ export class CacheService { public set currentTab(v) { this._currentTab = v; - console.log(this.currentTab); this.onNewTab.emit(); } diff --git a/AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts b/AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts index dadc8d8bca449881589ff1439e3e9fde068d4f6e..13012b852bfedc814532f5b40193ce51396b7ea2 100644 --- a/AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts +++ b/AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts @@ -41,6 +41,15 @@ export class LabelingWindowComponent implements OnInit { this.labels[index] = false; } + shuffleArray(array) { + for (var i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } + public getTopKSimilar() { this.labels = []; let abort = false; @@ -50,22 +59,23 @@ export class LabelingWindowComponent implements OnInit { let topk = []; let k = this.k; const keys = Object.keys(this.windowSimilarity).map(a => Number(a)).sort((a, b) => b - a); - for (const key of keys) { - const windows = this.windowSimilarity[key]; - for (const index of windows) { - if (this.service.labels[index] !== undefined) { - continue; + for (let i = 0; i < this.k; i++) { + const windows = this.windowSimilarity[keys[i]]; + this.shuffleArray(windows); + for (const index of windows) { + if (this.service.labels[index] !== undefined) { + continue; + } + topk.push({index, frequency: 100 * keys[i] / this.service.nrOfTables}); + k -= 1; + if (k < 1) { + abort = true; + } + break; } - topk.push({index, frequency: 100 * key / this.service.nrOfTables}); - k -= 1; - if (k < 1) { - abort = true; + if (abort) { break; } - } - if (abort) { - break; - } } this.topk = topk; diff --git a/AngularApp/prototype/src/app/labels/labels.component.ts b/AngularApp/prototype/src/app/labels/labels.component.ts index d47e780e1d166876a363ea589dfc1ff4c1db3afd..0d0411667c78bf8c3a348c31a9a6f873c9ce44a0 100644 --- a/AngularApp/prototype/src/app/labels/labels.component.ts +++ b/AngularApp/prototype/src/app/labels/labels.component.ts @@ -19,7 +19,6 @@ export class LabelsComponent implements OnInit { createSubplots(): void { this.goodLabels = []; this.badLabels = []; - console.log(this.service.labels); for (const key of Object.keys(this.service.labels)) { const index = Number(key); const plot = diff --git a/AngularApp/prototype/src/app/overview-window/overview-window.component.html b/AngularApp/prototype/src/app/overview-window/overview-window.component.html index 2f13d32074abfee736bd0e5d2703f474e5b9dbd2..30873b26300aa961ac33f853c31d85fbe087a570 100644 --- a/AngularApp/prototype/src/app/overview-window/overview-window.component.html +++ b/AngularApp/prototype/src/app/overview-window/overview-window.component.html @@ -2,4 +2,4 @@
- + diff --git a/AngularApp/prototype/src/app/overview-window/overview-window.component.ts b/AngularApp/prototype/src/app/overview-window/overview-window.component.ts index 1dcd8d6c8eddfad23e13183484d8ccf9ec2a72cc..5c2c5bb736b03f80f10ac735c5a1957c4ef1d976 100644 --- a/AngularApp/prototype/src/app/overview-window/overview-window.component.ts +++ b/AngularApp/prototype/src/app/overview-window/overview-window.component.ts @@ -18,7 +18,8 @@ export class OverviewWindowComponent { } async ngOnInit(): Promise { - this.service.onNewWindows.subscribe(() => { this.initializePlot(); }); + this.service.onNewData.subscribe(() => this.initializePlot()); + this.service.onNewWindows.subscribe(() => { }); this.service.onNewTables.subscribe(() => { if (this.service.query) { this.updatePlot(); @@ -38,13 +39,7 @@ export class OverviewWindowComponent { this.config = { type: "mixed", preview: { - 'auto-fit': true, - handleTop: { - alpha : 0.0 - }, - handleBottom: { - alpha : 0.0 - } + // 'auto-fit': true }, 'scale-x': { zooming: true, @@ -104,8 +99,7 @@ export class OverviewWindowComponent { const opacity: number[] = []; // Similarity - const windowSimilarity = await this.service.getSimilarWindows(this.service.windows[this.service.query]); - console.log(windowSimilarity); + const windowSimilarity = await this.service.getSimilarWindows(this.service.query); // for (const frequency in windowSimilarity){ // for (const index of windowSimilarity[frequency]) { // colors[index] = this.getColor(Number(frequency) / this.service.nrOfTables); @@ -116,15 +110,12 @@ export class OverviewWindowComponent { let goodLabels = []; let badLabels = []; for (const index in this.service.labels) { - console.log(index); if (this.service.labels[index]) { goodLabels.push(this.data[index]); } else { badLabels.push(this.data[index]); } } - console.log(goodLabels); - console.log(badLabels); this.series = [ { type: 'line', diff --git a/Flaskserver/venv/Lib/site-packages/attr/py.typed b/AngularApp/prototype/src/app/progress-view/progress-view.component.css similarity index 100% rename from Flaskserver/venv/Lib/site-packages/attr/py.typed rename to AngularApp/prototype/src/app/progress-view/progress-view.component.css diff --git a/AngularApp/prototype/src/app/progress-view/progress-view.component.html b/AngularApp/prototype/src/app/progress-view/progress-view.component.html new file mode 100644 index 0000000000000000000000000000000000000000..129481907fd38843c3e1ad9bea7fd93344a5c459 --- /dev/null +++ b/AngularApp/prototype/src/app/progress-view/progress-view.component.html @@ -0,0 +1,7 @@ +
+
+
+ +
+
+
diff --git a/AngularApp/prototype/src/app/progress-view/progress-view.component.ts b/AngularApp/prototype/src/app/progress-view/progress-view.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2b1ff7a41207ba3ed09c7b8e09842a8c1803803 --- /dev/null +++ b/AngularApp/prototype/src/app/progress-view/progress-view.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit } from '@angular/core'; +import {CacheService} from '../cache.service'; + +@Component({ + selector: 'app-progress-view', + templateUrl: './progress-view.component.html', + styleUrls: ['./progress-view.component.css'] +}) +export class ProgressViewComponent implements OnInit { + public plots; + + constructor(private cache: CacheService) { } + + ngOnInit(): void { + this.cache.onNewSimilarity.subscribe(() => { this.initializeInfo() }); + } + + averagePlot(average) { + return { + data: [{ + x: [...Array(average.length).keys()], + y: average, + type: 'line', + }], + layout: { + hovermode: 'closest', + autosize: true, + margin: { + l: 10, + r: 10, + t: 10, + b: 10, + pad: 4 + }, + xaxis: { + showticklabels: false + }, + yaxis: { + showticklabels: false + }, + height: 100, + width: screen.width * 0.1, + } + }; + } + + async initializeInfo(): Promise { + this.plots = []; + const keys = Object.keys(this.similarity).map(a => Number(a)).sort((a, b) => b - a); + const allWindows = []; + for (const key of keys) { + allWindows.push(this.similarity[key]); + } + const averages = await this.cache.getAverageWindow(allWindows); + this.plots = averages.map(x => this.averagePlot(x)); + } + + public get similarity() { + return this.cache.windowSimilarity; + } +} diff --git a/AngularApp/prototype/src/app/table-overview/table-overview.component.css b/AngularApp/prototype/src/app/table-overview/table-overview.component.css index 0995276c536359a4fec220286cdbcedb95707128..212ff557cbf4022c33016079d2ac26de1904abb6 100644 --- a/AngularApp/prototype/src/app/table-overview/table-overview.component.css +++ b/AngularApp/prototype/src/app/table-overview/table-overview.component.css @@ -1,7 +1,10 @@ .window { width: 100%; + overflow: scroll; +} + +.plots { display: flex; - flex-wrap: wrap; } .subplot:hover { diff --git a/AngularApp/prototype/src/app/table-overview/table-overview.component.html b/AngularApp/prototype/src/app/table-overview/table-overview.component.html index ea7abaeb10511d12698ddd7b5e85d87f175a3a09..82561d7ef79f79540cc54434337e33de8643b12b 100644 --- a/AngularApp/prototype/src/app/table-overview/table-overview.component.html +++ b/AngularApp/prototype/src/app/table-overview/table-overview.component.html @@ -1,5 +1,12 @@
-
- +
+
+ +
+
+
+
+ +
diff --git a/AngularApp/prototype/src/app/table-overview/table-overview.component.ts b/AngularApp/prototype/src/app/table-overview/table-overview.component.ts index 13a00a4f72503e8ccb6bd63fcb3a4114628a7194..7eb78a5a75b89215a040cb603f7424ae32014ac0 100644 --- a/AngularApp/prototype/src/app/table-overview/table-overview.component.ts +++ b/AngularApp/prototype/src/app/table-overview/table-overview.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import {CacheService} from '../cache.service'; +import {element} from 'protractor'; @Component({ selector: 'app-table-overview', @@ -8,20 +9,59 @@ import {CacheService} from '../cache.service'; }) export class TableOverviewComponent implements OnInit { public subplots; + public averages; constructor(private service: CacheService) { } ngOnInit(): void { this.service.onNewTables.subscribe(() => this.createPlots()); + this.service.onNewQuery.subscribe(() => this.createPlots()); } public get tables() { return this.service.tables; } - createPlots() { + averagePlot(average): object { + return { + data: [{ + x: [...Array(average.length).keys()], + y: average, + type: 'line', + }], + layout: { + hovermode: 'closest', + autosize: true, + margin: { + l: 10, + r: 10, + t: 10, + b: 10, + pad: 4 + }, + xaxis: { + showticklabels: false + }, + yaxis: { + showticklabels: false + }, + height: 100, + width: screen.width * 0.1, + } + }; + } + + async createPlots() { this.subplots = []; + this.averages = []; + const listOfWindows = []; for (const tableIndex in this.tables) { const table = this.tables[tableIndex]; + if (this.service.query) { + const queryEntry = Object.keys(table.entries).filter((hash: string) => { + return table.entries[hash].indexOf(this.service.query) > -1; + })[0]; + listOfWindows.push(table.entries[queryEntry]); + } this.subplots.push( { data: [{ @@ -29,8 +69,16 @@ export class TableOverviewComponent implements OnInit { return Number('0b' + hash); } ), - y: Object.values(table.entries).map((values: number[]) => values.length / this.service.windows.length), - type: 'bar' + y: Object.values(table.entries).map((values: number[]) => values.length / (this.service.rawValues.length - this.service.windowSize)), + type: 'bar', + marker: { + color: Object.values(table.entries).map((values: number[]) => { + if (values.indexOf(this.service.query) > -1) { + return 'red'; + } + return 'blue'; + }) + } }], layout: { hovermode: 'closest', @@ -54,5 +102,10 @@ export class TableOverviewComponent implements OnInit { } ); } + if (this.service.query) { + const temp = await this.service.getAverageWindow(listOfWindows); + this.averages = temp.map(x => this.averagePlot(x)); + console.log(this.averages); + } } } diff --git a/AngularApp/prototype/src/assets/favicon.ico b/AngularApp/prototype/src/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3ad2e31ba00eed675f72698dffcaea32f3c1f711 Binary files /dev/null and b/AngularApp/prototype/src/assets/favicon.ico differ diff --git a/AngularApp/prototype/src/favicon.ico b/AngularApp/prototype/src/favicon.ico deleted file mode 100644 index 997406ad22c29aae95893fb3d666c30258a09537..0000000000000000000000000000000000000000 Binary files a/AngularApp/prototype/src/favicon.ico and /dev/null differ diff --git a/AngularApp/prototype/src/index.html b/AngularApp/prototype/src/index.html index fb64115e352137ad497a428785f2a5c656bf3125..0c55c3961fa99d78b24130fc0d06aa19afdc4329 100644 --- a/AngularApp/prototype/src/index.html +++ b/AngularApp/prototype/src/index.html @@ -2,10 +2,10 @@ - Prototype + PSEUDo - + diff --git a/Flaskserver/.idea/workspace.xml b/Flaskserver/.idea/workspace.xml index 0b972b4e63903821a35695b455a7fc7f0510f147..366066f23af702fd66c39910940262ab4d8762ee 100644 --- a/Flaskserver/.idea/workspace.xml +++ b/Flaskserver/.idea/workspace.xml @@ -2,116 +2,4012 @@ + + + + + - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -204,11 +4080,6 @@ - - - - -