Learning Objectives

Upon completion of this short lesson, you will be able to:

  • appreciate role of views
  • create views
  • use views in queries
  • describe view materialization
  • use views for schema abstraction

Introduction

Views are an important mechanism in relational databases for queries. In a nutshell, a view is a virtual table that provides a way to present a subset of data from one or more tables. Unlike regular tables, a view does not store data itself but rather stores a SQL query that defines how the data is presented. The result of the query is generated on-the-fly whenever the view is accessed. Views are useful for several reasons, including simplifying complex queries, enhancing security by restricting access to certain columns or rows, and providing a layer of abstraction over the base tables.

While supported by virtually all databases and being part of the SQL-92 standard, there are some minor differences in restrictions around the use of views, so be sure to consult your database vendor’s documentation.

Why Views?

A well-designed schema-independent database only provides queries through an interface in the form of views and does not allow users or client applications to query the tables in the schema directly. Thus, views provide an abstraction of the actual schema that ensures that schema changes do not affect SQL queries. If the name of a table or the definitions of columns changes, then the database architect updates the view definition and client and user queries against the view continue to function without modification.

In addition, views also help enforce security as the data can only be accessed through views and the data in the actual tables is hidden from all users.

Views can also be used to present aggregate data, such as totals and averages, without exposing the underlying data.

Views vs Tables

A view is a virtual table defined by a SQL SELECT statement and behaves in queries exactly like a table. The content of the view is materialized generally only when the view is involved in a query, although databases may cache views to improve performance. Since a view is not stored, it do not consume storage; only the view definition is stored in the database.

Views for Secure Data Access

A view provides an abstraction of the data in one or more tables and can present the data in a different format, in redacted form, or only select rows and columns depending on access privileges. So, rather than exposes an entire table, different views can present different parts of the data in one or more tables to different users depending on their access privileges and needs.

Restrictions on Views

There are several restrictions on how views can be used:

  1. No Storage: Views do not store any data themselves. They are simply a stored query, and the data is fetched from the underlying tables when the view is queried.

  2. Non-Updatable Views: Not all views can be updated. Views that include JOIN operations, aggregate functions, or DISTINCT clauses are typically not updatable. In such cases, you will need to update the base tables directly.

  3. Performance: Since views are just stored queries, every time a view is queried, the underlying query is re-executed. This can have performance implications, especially if the view is based on complex queries or large tables.

  4. Schema Changes: If the structure of the underlying base tables changes (e.g., a column is dropped), the view may become invalid and need to be redefined.

Creating Views

A view is defined using the CREATE VIEW statement in SQL. This statement specifies the query that generates the view’s result set. Views can be based on one or more tables or other views. The structure of a view is identical to that of a table, in the sense that it has columns and rows, but it is dynamic and updates in real-time as the underlying data in the base tables changes.

Here’s the basic syntax to define a view:

CREATE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;

Let’s say that email addresses in the Author table of some marketing databases should not be accessible for non-marketing personnel but should at least be partially available for matching. So, rather than providing access to the data in the Author table, we define a view that the non-marketing personnel and applications use.

The view is defined below as the result of a SQL query. Notice the renaming of columns from the original table.

CREATE VIEW AuthorNM 
 (fullname, email)
 AS
 SELECT name,
        substr(email,1,2) || '****' || substr(email,-5)
   FROM Author;

Note that SQLite uses || for string concatenation while MySQL uses the concat() function.

So, in shortm views are used in much the same way as regular tables. You can run SELECT queries against views, join them with other tables or views, and even use them in INSERT, UPDATE, and DELETE operations under certain conditions.

As another example, suppose we have two tables in a SQLite database, employees and departments:

CREATE TABLE employees (
    emp_id INTEGER PRIMARY KEY,
    emp_name TEXT,
    department_id INTEGER,
    salary REAL
);

CREATE TABLE departments (
    department_id INTEGER PRIMARY KEY,
    department_name TEXT
);

We can define a view that presents the employees along with their department names:

CREATE VIEW employee_department_view AS
SELECT e.emp_name, d.department_name, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

Now, querying the view will display the employee details along with their department names:

SELECT * FROM employee_department_view;

Using a View in a Query

A view can be used anywhere a table can be used in a SQL SELECT statement.

SELECT * FROM AuthorNM;

Common Uses Cases for Views

  1. Define a view that contain select rows and columns and provide authorization for some users to access the view but not the underlying tables that define the view.

  2. Aggregate data from a complex query in a view to simplify common data access.

  3. Create views as alternatives to commonly used subqueries to make queries simpler and to provide a degree of reusability.

Updating Views

In SQLite, like Oracle, PostgreSQL, and SQL Server, views can be updatable, which means that you can perform INSERT, UPDATE, or DELETE operations on the view, and these changes will be reflected in the underlying base tables. However, this is not always the case, and there are some restrictions on when a view can be updated.

Views can generally only be used for data retrieval: SELECT but not in INSERT, UPDATE, or DELETE statements. Some views might be updatable if some specific criteria are met and if the database supports the feature.

A view is generally updatable if the following criteria are met:

  • The view must reference a single table.
  • The view must include all non-nullable columns from the base table
  • No aggregations (sum(), avg(), count(), etc.)
  • View does not contain a GROUP BY or HAVING clause
  • View definition does not use subqueries
  • View is not the result of a UNION, ALL, or DISTINCT query
  • The FROM clause in the view definition contains only updatable tables and views and only uses inner joins and no outer joins

Custom update logic on views can be provided using triggers which then update the underlying tables.

Here is an example of an updatable view:

CREATE VIEW employee_salary_view AS
SELECT emp_id, emp_name, salary
FROM employees;

Since this view only references a single table (employees) and does not contain any restrictions or aggregations, it can be updated. For example, we can update an employee’s salary through the view:

UPDATE employee_salary_view
SET salary = 75000
WHERE emp_id = 1;

This operation will directly modify the salary column in the underlying employees table.

However, if we try to update a view that involves a JOIN, SQLite will not allow it, because there’s ambiguity about which table should be updated. For example, trying to update the employee_department_view defined earlier would fail.

Let’s consider a view that is not updatable because it includes a JOIN:

CREATE VIEW employee_salary_department AS
SELECT e.emp_name, d.department_name, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

Trying to update this view would lead to an error, because it is not clear whether the update should apply to the employees table or the departments table:

UPDATE employee_salary_department
SET salary = 85000
WHERE emp_name = 'John Doe';

This will result in an error similar to:

Error: view employee_salary_department is not updatable

Dropping Views

If a view is no longer needed, it can be removed from the database using the DROP VIEW statement:

DROP VIEW IF EXISTS employee_department_view;

This statement removes the view from the database, but it does not affect the underlying tables or data.

View Materialization

View materialization is an optimization technique used by database management systems (DBMS) to improve the performance of queries that involve complex views. Under normal circumstances, a view is a virtual table, meaning that every time a view is queried, the DBMS must recompute the result by executing the underlying SQL query that defines the view. This is known as virtual view processing. For simple views or views that are rarely accessed, this is efficient. However, for complex views or views queried frequently, recomputing the result each time can be inefficient and slow.

Materialized views address this performance concern by precomputing and storing the results of the view. Instead of recalculating the view’s result every time it is accessed, the result is stored in a physical table (either as a physical table in storage or as a virtual table in memory), allowing much faster access. Essentially, materializing a view transforms it from a virtual view (which is dynamically generated on-demand) into a persistent, real table containing data whose contents is derived from other tables and views.

Materialization of views happens in specific scenarios, typically driven by the need to optimize query performance. There are two primary ways in which materialized views can be created and managed:

Manual Materialization (Materialized Views): In some databases, you explicitly define a materialized view. Unlike regular views, a materialized view is a view whose result is stored in the database as a physical table. The result of the query used to define the view is precomputed and saved, and future accesses to the materialized view retrieve the data from this stored result rather than recomputing it.

Example of creating a materialized view (not supported in SQLite but common in other databases like PostgreSQL and Oracle):

CREATE MATERIALIZED VIEW employee_summary AS
SELECT department_id, COUNT(emp_id) AS employee_count, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;

In this case, the view stores the number of employees and the average salary per department. Each time you query this view, the DBMS does not have to recompute the aggregate functions, but simply returns the precomputed data from storage.

Automatic Materialization (Internal Optimization): Some advanced database engines automatically materialize certain views or subqueries behind the scenes as part of query optimization. The DBMS may decide to materialize the result of a complex query or a frequently accessed view, especially if recomputation is expensive. This is transparent to the user but can provide significant performance benefits.

The decision to materialize a view in these cases is up to the database engine and is typically based on one or more of these considerations:

  • The complexity of the view (e.g., it involves multiple joins or aggregate functions).
  • The frequency with which the view is queried.
  • The cost of recalculating the view’s result each time.
  • Available system resources like memory and storage.

Once a view is materialized, the next concern is how to maintain the correctness of the materialized view, especially when the underlying data changes. There are two common approaches to this:

Lazy (On-demand) Refresh: In this approach, the materialized view is updated only when it is queried. If the underlying tables have been modified since the materialized view was last computed, the DBMS will refresh the materialized view just before returning the query result. This method can delay the retrieval of data but avoids unnecessary refreshes when the view is not accessed frequently.

For example, the employee_summary materialized view may be refreshed only when a query like SELECT * FROM employee_summary is run. If new employees have been added to the employees table since the last materialization, the view will be updated at that point.

Eager (Immediate) Refresh: In this case, the materialized view is updated immediately whenever the underlying base tables are modified. This ensures that the materialized view is always up-to-date but can incur additional overhead every time there’s an insert, update, or delete operation on the base tables.

For example, if an INSERT INTO employees statement adds a new employee, the system automatically updates the corresponding entry in the materialized view employee_summary. This ensures the view reflects the most recent data, but it adds overhead to every write operation.

Some systems support hybrid models where materialized views can be incrementally refreshed. This means instead of recalculating the entire view from scratch when the data changes, the system computes only the changes and applies them to the materialized view, making the update process more efficient.

Materialized Views in Practice

While SQLite, a lightweight relational database, does not natively support materialized views (only virtual views), larger DBMSs like Oracle, PostgreSQL, and Microsoft SQL Server have explicit support for materialized views. In these systems, materialized views are often used for:

  1. Data Warehousing: Materialized views are ideal for large-scale, complex queries over datasets that don’t change frequently. For instance, in a data warehouse, historical data is often queried for reporting purposes, and materialized views can provide pre-aggregated results for fast retrieval.

  2. Performance Optimization: When dealing with complex queries involving multiple joins, aggregations, or subqueries, materialized views allow the DBMS to store the precomputed result, significantly reducing the time required to execute these queries.

  3. Replication and Distributed Databases: Materialized views are sometimes used to replicate data across databases or distribute a consistent snapshot of data across systems. They allow for faster access to read-heavy workloads in distributed environments.

Example of Caching with Materialized Views

In a database that supports materialized views, the following example shows how caching might be used to improve query performance:

CREATE MATERIALIZED VIEW sales_summary AS
SELECT region_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region_id;

This materialized view stores the total sales per region. Normally, computing this aggregate query over a large sales table could be expensive. By materializing the view, the result is cached in the database, allowing fast access when the view is queried.

Now, if you run a query:

SELECT * FROM sales_summary WHERE region_id = 1;

Instead of scanning the entire sales table, the DBMS simply retrieves the precomputed result from the materialized view, which is much faster. The materialized view remains up-to-date according to its refresh policy (e.g., on-demand or automatically).

Advantages and Disadvantages of Materialized Views

Advantages:

  1. Performance: The primary benefit of materialized views is faster query performance. By caching the result of complex queries, materialized views avoid the overhead of recomputing the query every time it is executed.

  2. Efficient Aggregation: For queries that involve expensive operations like joins or aggregations (e.g., GROUP BY), materialized views store the precomputed results, reducing the load on the system when querying these views.

  3. Snapshot of Data: Materialized views provide a consistent snapshot of the data at the time they were last refreshed, which can be useful in data analysis or reporting applications where a “frozen” state of the data is required.

Disadvantages:

  1. Storage Overhead: Since materialized views physically store data, they require additional storage space. This can be significant for large datasets.

  2. Maintenance Overhead: Keeping materialized views up-to-date requires additional processing during INSERT, UPDATE, or DELETE operations on the base tables. In write-heavy environments, this overhead can be substantial, especially for views that need to be refreshed frequently.

  3. Staleness: If a materialized view is not refreshed frequently, its data can become stale, meaning it no longer reflects the current state of the base tables. This is particularly a concern for dynamic datasets with frequent changes.

To summarize, view materialization is an essential optimization technique in which the results of complex or frequently queried views are pre-materialized and cached. By storing the result of the view in a physical table, materialized views provide faster query execution at the cost of additional storage and maintenance overhead. The decision of when to materialize views depends on the complexity of the view, the frequency of query execution, and the balance between query performance and data freshness. While SQLite does not support materialized views, many advanced database systems leverage them to enhance performance, particularly in large-scale applications like data warehousing and reporting.

Summary

Views are an important element in the database architect’s toolkit for building scalable and maintainable databases as they allow abstraction and simplification of interactions with database data. They can significantly improve query readability, provide security by restricting access to certain data, and create a level of abstraction from the underlying table structures. However, views do generally not allow updates, and understanding when and how to use them appropriately is key to leveraging their full potential.

Tutorial

In this short kickstarter lesson, guest speaker Socratica provides an overview of views and how they can be used to abstract tables in a relational databases and provide schema independence for queries.

Files & Resources

All Files for Lesson 70.108

References

None.

Errata

Let us know.

LS0tCnRpdGxlOiAiUXVlcnkgYW5kIFNjaGVtYSBBYnN0cmFjdGlvbiB3aXRoIFZpZXdzIgpwYXJhbXM6CiAgY2F0ZWdvcnk6IDcwCiAgbnVtYmVyOiAxMDgKICB0aW1lOiAzMAogIGxldmVsOiBiZWdpbm5lcgogIHRhZ3M6ICJzcWwsU0VMRUNULEFTLExJTUlULE9SREVSIEJZLERJU1RJTkNUIgogIGRlc2NyaXB0aW9uOiAiRXhwbGFpbnMgaG93IHRvIGRlZmluZSB2aXJ0dWFsIHRhYmxlcyB1c2luZyB2aWV3cy4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgTGVhcm5pbmcgT2JqZWN0aXZlcwoKVXBvbiBjb21wbGV0aW9uIG9mIHRoaXMgc2hvcnQgbGVzc29uLCB5b3Ugd2lsbCBiZSBhYmxlIHRvOgoKLSAgIGFwcHJlY2lhdGUgcm9sZSBvZiB2aWV3cwotICAgY3JlYXRlIHZpZXdzCi0gICB1c2Ugdmlld3MgaW4gcXVlcmllcwotICAgZGVzY3JpYmUgdmlldyBtYXRlcmlhbGl6YXRpb24KLSAgIHVzZSB2aWV3cyBmb3Igc2NoZW1hIGFic3RyYWN0aW9uCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEludHJvZHVjdGlvbgoKVmlld3MgYXJlIGFuIGltcG9ydGFudCBtZWNoYW5pc20gaW4gcmVsYXRpb25hbCBkYXRhYmFzZXMgZm9yIHF1ZXJpZXMuIEluIGEgbnV0c2hlbGwsIGEgKip2aWV3KiogaXMgYSB2aXJ0dWFsIHRhYmxlIHRoYXQgcHJvdmlkZXMgYSB3YXkgdG8gcHJlc2VudCBhIHN1YnNldCBvZiBkYXRhIGZyb20gb25lIG9yIG1vcmUgdGFibGVzLiBVbmxpa2UgcmVndWxhciB0YWJsZXMsIGEgdmlldyBkb2VzIG5vdCBzdG9yZSBkYXRhIGl0c2VsZiBidXQgcmF0aGVyIHN0b3JlcyBhIFNRTCBxdWVyeSB0aGF0IGRlZmluZXMgaG93IHRoZSBkYXRhIGlzIHByZXNlbnRlZC4gVGhlIHJlc3VsdCBvZiB0aGUgcXVlcnkgaXMgZ2VuZXJhdGVkIG9uLXRoZS1mbHkgd2hlbmV2ZXIgdGhlIHZpZXcgaXMgYWNjZXNzZWQuIFZpZXdzIGFyZSB1c2VmdWwgZm9yIHNldmVyYWwgcmVhc29ucywgaW5jbHVkaW5nIHNpbXBsaWZ5aW5nIGNvbXBsZXggcXVlcmllcywgZW5oYW5jaW5nIHNlY3VyaXR5IGJ5IHJlc3RyaWN0aW5nIGFjY2VzcyB0byBjZXJ0YWluIGNvbHVtbnMgb3Igcm93cywgYW5kIHByb3ZpZGluZyBhIGxheWVyIG9mIGFic3RyYWN0aW9uIG92ZXIgdGhlIGJhc2UgdGFibGVzLgoKV2hpbGUgc3VwcG9ydGVkIGJ5IHZpcnR1YWxseSBhbGwgZGF0YWJhc2VzIGFuZCBiZWluZyBwYXJ0IG9mIHRoZSBTUUwtOTIgc3RhbmRhcmQsIHRoZXJlIGFyZSBzb21lIG1pbm9yIGRpZmZlcmVuY2VzIGluIHJlc3RyaWN0aW9ucyBhcm91bmQgdGhlIHVzZSBvZiB2aWV3cywgc28gYmUgc3VyZSB0byBjb25zdWx0IHlvdXIgZGF0YWJhc2UgdmVuZG9yJ3MgZG9jdW1lbnRhdGlvbi4KCiMjIFdoeSBWaWV3cz8KCkEgd2VsbC1kZXNpZ25lZCBzY2hlbWEtaW5kZXBlbmRlbnQgZGF0YWJhc2Ugb25seSBwcm92aWRlcyBxdWVyaWVzIHRocm91Z2ggYW4gaW50ZXJmYWNlIGluIHRoZSBmb3JtIG9mIHZpZXdzIGFuZCBkb2VzIG5vdCBhbGxvdyB1c2VycyBvciBjbGllbnQgYXBwbGljYXRpb25zIHRvIHF1ZXJ5IHRoZSB0YWJsZXMgaW4gdGhlIHNjaGVtYSBkaXJlY3RseS4gVGh1cywgdmlld3MgcHJvdmlkZSBhbiBhYnN0cmFjdGlvbiBvZiB0aGUgYWN0dWFsIHNjaGVtYSB0aGF0IGVuc3VyZXMgdGhhdCBzY2hlbWEgY2hhbmdlcyBkbyBub3QgYWZmZWN0IFNRTCBxdWVyaWVzLiBJZiB0aGUgbmFtZSBvZiBhIHRhYmxlIG9yIHRoZSBkZWZpbml0aW9ucyBvZiBjb2x1bW5zIGNoYW5nZXMsIHRoZW4gdGhlIGRhdGFiYXNlIGFyY2hpdGVjdCB1cGRhdGVzIHRoZSB2aWV3IGRlZmluaXRpb24gYW5kIGNsaWVudCBhbmQgdXNlciBxdWVyaWVzIGFnYWluc3QgdGhlIHZpZXcgY29udGludWUgdG8gZnVuY3Rpb24gd2l0aG91dCBtb2RpZmljYXRpb24uCgpJbiBhZGRpdGlvbiwgdmlld3MgYWxzbyBoZWxwIGVuZm9yY2Ugc2VjdXJpdHkgYXMgdGhlIGRhdGEgY2FuIG9ubHkgYmUgYWNjZXNzZWQgdGhyb3VnaCB2aWV3cyBhbmQgdGhlIGRhdGEgaW4gdGhlIGFjdHVhbCB0YWJsZXMgaXMgaGlkZGVuIGZyb20gYWxsIHVzZXJzLgoKVmlld3MgY2FuIGFsc28gYmUgdXNlZCB0byBwcmVzZW50IGFnZ3JlZ2F0ZSBkYXRhLCBzdWNoIGFzIHRvdGFscyBhbmQgYXZlcmFnZXMsIHdpdGhvdXQgZXhwb3NpbmcgdGhlIHVuZGVybHlpbmcgZGF0YS4KCiMjIFZpZXdzIHZzIFRhYmxlcwoKQSB2aWV3IGlzIGEgdmlydHVhbCB0YWJsZSBkZWZpbmVkIGJ5IGEgU1FMICpTRUxFQ1QqIHN0YXRlbWVudCBhbmQgYmVoYXZlcyBpbiBxdWVyaWVzIGV4YWN0bHkgbGlrZSBhIHRhYmxlLiBUaGUgY29udGVudCBvZiB0aGUgdmlldyBpcyBtYXRlcmlhbGl6ZWQgZ2VuZXJhbGx5IG9ubHkgd2hlbiB0aGUgdmlldyBpcyBpbnZvbHZlZCBpbiBhIHF1ZXJ5LCBhbHRob3VnaCBkYXRhYmFzZXMgbWF5IGNhY2hlIHZpZXdzIHRvIGltcHJvdmUgcGVyZm9ybWFuY2UuIFNpbmNlIGEgdmlldyBpcyBub3Qgc3RvcmVkLCBpdCBkbyBub3QgY29uc3VtZSBzdG9yYWdlOyBvbmx5IHRoZSB2aWV3IGRlZmluaXRpb24gaXMgc3RvcmVkIGluIHRoZSBkYXRhYmFzZS4KCiMjIFZpZXdzIGZvciBTZWN1cmUgRGF0YSBBY2Nlc3MKCkEgdmlldyBwcm92aWRlcyBhbiBhYnN0cmFjdGlvbiBvZiB0aGUgZGF0YSBpbiBvbmUgb3IgbW9yZSB0YWJsZXMgYW5kIGNhbiBwcmVzZW50IHRoZSBkYXRhIGluIGEgZGlmZmVyZW50IGZvcm1hdCwgaW4gcmVkYWN0ZWQgZm9ybSwgb3Igb25seSBzZWxlY3Qgcm93cyBhbmQgY29sdW1ucyBkZXBlbmRpbmcgb24gYWNjZXNzIHByaXZpbGVnZXMuIFNvLCByYXRoZXIgdGhhbiBleHBvc2VzIGFuIGVudGlyZSB0YWJsZSwgZGlmZmVyZW50IHZpZXdzIGNhbiBwcmVzZW50IGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgZGF0YSBpbiBvbmUgb3IgbW9yZSB0YWJsZXMgdG8gZGlmZmVyZW50IHVzZXJzIGRlcGVuZGluZyBvbiB0aGVpciBhY2Nlc3MgcHJpdmlsZWdlcyBhbmQgbmVlZHMuCgojIyBSZXN0cmljdGlvbnMgb24gVmlld3MKClRoZXJlIGFyZSBzZXZlcmFsIHJlc3RyaWN0aW9ucyBvbiBob3cgdmlld3MgY2FuIGJlIHVzZWQ6CgoxLiAgKipObyBTdG9yYWdlKio6IFZpZXdzIGRvIG5vdCBzdG9yZSBhbnkgZGF0YSB0aGVtc2VsdmVzLiBUaGV5IGFyZSBzaW1wbHkgYSBzdG9yZWQgcXVlcnksIGFuZCB0aGUgZGF0YSBpcyBmZXRjaGVkIGZyb20gdGhlIHVuZGVybHlpbmcgdGFibGVzIHdoZW4gdGhlIHZpZXcgaXMgcXVlcmllZC4KCjIuICAqKk5vbi1VcGRhdGFibGUgVmlld3MqKjogTm90IGFsbCB2aWV3cyBjYW4gYmUgdXBkYXRlZC4gVmlld3MgdGhhdCBpbmNsdWRlIGBKT0lOYCBvcGVyYXRpb25zLCBhZ2dyZWdhdGUgZnVuY3Rpb25zLCBvciBgRElTVElOQ1RgIGNsYXVzZXMgYXJlIHR5cGljYWxseSBub3QgdXBkYXRhYmxlLiBJbiBzdWNoIGNhc2VzLCB5b3Ugd2lsbCBuZWVkIHRvIHVwZGF0ZSB0aGUgYmFzZSB0YWJsZXMgZGlyZWN0bHkuCgozLiAgKipQZXJmb3JtYW5jZSoqOiBTaW5jZSB2aWV3cyBhcmUganVzdCBzdG9yZWQgcXVlcmllcywgZXZlcnkgdGltZSBhIHZpZXcgaXMgcXVlcmllZCwgdGhlIHVuZGVybHlpbmcgcXVlcnkgaXMgcmUtZXhlY3V0ZWQuIFRoaXMgY2FuIGhhdmUgcGVyZm9ybWFuY2UgaW1wbGljYXRpb25zLCBlc3BlY2lhbGx5IGlmIHRoZSB2aWV3IGlzIGJhc2VkIG9uIGNvbXBsZXggcXVlcmllcyBvciBsYXJnZSB0YWJsZXMuCgo0LiAgKipTY2hlbWEgQ2hhbmdlcyoqOiBJZiB0aGUgc3RydWN0dXJlIG9mIHRoZSB1bmRlcmx5aW5nIGJhc2UgdGFibGVzIGNoYW5nZXMgKGUuZy4sIGEgY29sdW1uIGlzIGRyb3BwZWQpLCB0aGUgdmlldyBtYXkgYmVjb21lIGludmFsaWQgYW5kIG5lZWQgdG8gYmUgcmVkZWZpbmVkLgoKIyMgQ3JlYXRpbmcgVmlld3MKCkEgKip2aWV3KiogaXMgZGVmaW5lZCB1c2luZyB0aGUgYENSRUFURSBWSUVXYCBzdGF0ZW1lbnQgaW4gU1FMLiBUaGlzIHN0YXRlbWVudCBzcGVjaWZpZXMgdGhlIHF1ZXJ5IHRoYXQgZ2VuZXJhdGVzIHRoZSB2aWV3J3MgcmVzdWx0IHNldC4gVmlld3MgY2FuIGJlIGJhc2VkIG9uIG9uZSBvciBtb3JlIHRhYmxlcyBvciBvdGhlciB2aWV3cy4gVGhlIHN0cnVjdHVyZSBvZiBhIHZpZXcgaXMgaWRlbnRpY2FsIHRvIHRoYXQgb2YgYSB0YWJsZSwgaW4gdGhlIHNlbnNlIHRoYXQgaXQgaGFzIGNvbHVtbnMgYW5kIHJvd3MsIGJ1dCBpdCBpcyBkeW5hbWljIGFuZCB1cGRhdGVzIGluIHJlYWwtdGltZSBhcyB0aGUgdW5kZXJseWluZyBkYXRhIGluIHRoZSBiYXNlIHRhYmxlcyBjaGFuZ2VzLgoKSGVyZeKAmXMgdGhlIGJhc2ljIHN5bnRheCB0byBkZWZpbmUgYSB2aWV3OgoKYGBgIHNxbApDUkVBVEUgVklFVyB2aWV3X25hbWUgQVMKU0VMRUNUIGNvbHVtbjEsIGNvbHVtbjIsIC4uLgpGUk9NIHRhYmxlX25hbWUKV0hFUkUgY29uZGl0aW9uOwpgYGAKCkxldCdzIHNheSB0aGF0IGVtYWlsIGFkZHJlc3NlcyBpbiB0aGUgKkF1dGhvciogdGFibGUgb2Ygc29tZSBtYXJrZXRpbmcgZGF0YWJhc2VzIHNob3VsZCBub3QgYmUgYWNjZXNzaWJsZSBmb3Igbm9uLW1hcmtldGluZyBwZXJzb25uZWwgYnV0IHNob3VsZCBhdCBsZWFzdCBiZSBwYXJ0aWFsbHkgYXZhaWxhYmxlIGZvciBtYXRjaGluZy4gU28sIHJhdGhlciB0aGFuIHByb3ZpZGluZyBhY2Nlc3MgdG8gdGhlIGRhdGEgaW4gdGhlIEF1dGhvciB0YWJsZSwgd2UgZGVmaW5lIGEgdmlldyB0aGF0IHRoZSBub24tbWFya2V0aW5nIHBlcnNvbm5lbCBhbmQgYXBwbGljYXRpb25zIHVzZS4KClRoZSB2aWV3IGlzIGRlZmluZWQgYmVsb3cgYXMgdGhlIHJlc3VsdCBvZiBhIFNRTCBxdWVyeS4gTm90aWNlIHRoZSByZW5hbWluZyBvZiBjb2x1bW5zIGZyb20gdGhlIG9yaWdpbmFsIHRhYmxlLgoKYGBgIHNxbApDUkVBVEUgVklFVyBBdXRob3JOTSAKIChmdWxsbmFtZSwgZW1haWwpCiBBUwogU0VMRUNUIG5hbWUsCiAgICAgICAgc3Vic3RyKGVtYWlsLDEsMikgfHwgJyoqKionIHx8IHN1YnN0cihlbWFpbCwtNSkKICAgRlJPTSBBdXRob3I7CmBgYAoKTm90ZSB0aGF0IFNRTGl0ZSB1c2VzIFx8XHwgZm9yIHN0cmluZyBjb25jYXRlbmF0aW9uIHdoaWxlIE15U1FMIHVzZXMgdGhlIGBjb25jYXQoKWAgZnVuY3Rpb24uCgpTbywgaW4gc2hvcnRtIHZpZXdzIGFyZSB1c2VkIGluIG11Y2ggdGhlIHNhbWUgd2F5IGFzIHJlZ3VsYXIgdGFibGVzLiBZb3UgY2FuIHJ1biBgU0VMRUNUYCBxdWVyaWVzIGFnYWluc3Qgdmlld3MsIGpvaW4gdGhlbSB3aXRoIG90aGVyIHRhYmxlcyBvciB2aWV3cywgYW5kIGV2ZW4gdXNlIHRoZW0gaW4gYElOU0VSVGAsIGBVUERBVEVgLCBhbmQgYERFTEVURWAgb3BlcmF0aW9ucyB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnMuCgpBcyBhbm90aGVyIGV4YW1wbGUsIHN1cHBvc2Ugd2UgaGF2ZSB0d28gdGFibGVzIGluIGEgU1FMaXRlIGRhdGFiYXNlLCBgZW1wbG95ZWVzYCBhbmQgYGRlcGFydG1lbnRzYDoKCmBgYCBzcWwKQ1JFQVRFIFRBQkxFIGVtcGxveWVlcyAoCiAgICBlbXBfaWQgSU5URUdFUiBQUklNQVJZIEtFWSwKICAgIGVtcF9uYW1lIFRFWFQsCiAgICBkZXBhcnRtZW50X2lkIElOVEVHRVIsCiAgICBzYWxhcnkgUkVBTAopOwoKQ1JFQVRFIFRBQkxFIGRlcGFydG1lbnRzICgKICAgIGRlcGFydG1lbnRfaWQgSU5URUdFUiBQUklNQVJZIEtFWSwKICAgIGRlcGFydG1lbnRfbmFtZSBURVhUCik7CmBgYAoKV2UgY2FuIGRlZmluZSBhIHZpZXcgdGhhdCBwcmVzZW50cyB0aGUgZW1wbG95ZWVzIGFsb25nIHdpdGggdGhlaXIgZGVwYXJ0bWVudCBuYW1lczoKCmBgYCBzcWwKQ1JFQVRFIFZJRVcgZW1wbG95ZWVfZGVwYXJ0bWVudF92aWV3IEFTClNFTEVDVCBlLmVtcF9uYW1lLCBkLmRlcGFydG1lbnRfbmFtZSwgZS5zYWxhcnkKRlJPTSBlbXBsb3llZXMgZQpKT0lOIGRlcGFydG1lbnRzIGQgT04gZS5kZXBhcnRtZW50X2lkID0gZC5kZXBhcnRtZW50X2lkOwpgYGAKCk5vdywgcXVlcnlpbmcgdGhlIHZpZXcgd2lsbCBkaXNwbGF5IHRoZSBlbXBsb3llZSBkZXRhaWxzIGFsb25nIHdpdGggdGhlaXIgZGVwYXJ0bWVudCBuYW1lczoKCmBgYCBzcWwKU0VMRUNUICogRlJPTSBlbXBsb3llZV9kZXBhcnRtZW50X3ZpZXc7CmBgYAoKIyMgVXNpbmcgYSBWaWV3IGluIGEgUXVlcnkKCkEgdmlldyBjYW4gYmUgdXNlZCBhbnl3aGVyZSBhIHRhYmxlIGNhbiBiZSB1c2VkIGluIGEgU1FMIFNFTEVDVCBzdGF0ZW1lbnQuCgpgYGAgc3FsClNFTEVDVCAqIEZST00gQXV0aG9yTk07CmBgYAoKIyMgQ29tbW9uIFVzZXMgQ2FzZXMgZm9yIFZpZXdzCgoxLiAgRGVmaW5lIGEgdmlldyB0aGF0IGNvbnRhaW4gc2VsZWN0IHJvd3MgYW5kIGNvbHVtbnMgYW5kIHByb3ZpZGUgYXV0aG9yaXphdGlvbiBmb3Igc29tZSB1c2VycyB0byBhY2Nlc3MgdGhlIHZpZXcgYnV0IG5vdCB0aGUgdW5kZXJseWluZyB0YWJsZXMgdGhhdCBkZWZpbmUgdGhlIHZpZXcuCgoyLiAgQWdncmVnYXRlIGRhdGEgZnJvbSBhIGNvbXBsZXggcXVlcnkgaW4gYSB2aWV3IHRvIHNpbXBsaWZ5IGNvbW1vbiBkYXRhIGFjY2Vzcy4KCjMuICBDcmVhdGUgdmlld3MgYXMgYWx0ZXJuYXRpdmVzIHRvIGNvbW1vbmx5IHVzZWQgc3VicXVlcmllcyB0byBtYWtlIHF1ZXJpZXMgc2ltcGxlciBhbmQgdG8gcHJvdmlkZSBhIGRlZ3JlZSBvZiByZXVzYWJpbGl0eS4KCiMjIFVwZGF0aW5nIFZpZXdzCgpJbiBTUUxpdGUsIGxpa2UgT3JhY2xlLCBQb3N0Z3JlU1FMLCBhbmQgU1FMIFNlcnZlciwgdmlld3MgY2FuIGJlICoqdXBkYXRhYmxlKiosIHdoaWNoIG1lYW5zIHRoYXQgeW91IGNhbiBwZXJmb3JtIGBJTlNFUlRgLCBgVVBEQVRFYCwgb3IgYERFTEVURWAgb3BlcmF0aW9ucyBvbiB0aGUgdmlldywgYW5kIHRoZXNlIGNoYW5nZXMgd2lsbCBiZSByZWZsZWN0ZWQgaW4gdGhlIHVuZGVybHlpbmcgYmFzZSB0YWJsZXMuIEhvd2V2ZXIsIHRoaXMgaXMgbm90IGFsd2F5cyB0aGUgY2FzZSwgYW5kIHRoZXJlIGFyZSBzb21lIHJlc3RyaWN0aW9ucyBvbiB3aGVuIGEgdmlldyBjYW4gYmUgdXBkYXRlZC4KClZpZXdzIGNhbiBnZW5lcmFsbHkgb25seSBiZSB1c2VkIGZvciBkYXRhIHJldHJpZXZhbDogU0VMRUNUIGJ1dCBub3QgaW4gSU5TRVJULCBVUERBVEUsIG9yIERFTEVURSBzdGF0ZW1lbnRzLiBTb21lIHZpZXdzIG1pZ2h0IGJlIHVwZGF0YWJsZSBpZiBzb21lIHNwZWNpZmljIGNyaXRlcmlhIGFyZSBtZXQgYW5kIGlmIHRoZSBkYXRhYmFzZSBzdXBwb3J0cyB0aGUgZmVhdHVyZS4KCkEgdmlldyBpcyBnZW5lcmFsbHkgdXBkYXRhYmxlIGlmIHRoZSBmb2xsb3dpbmcgY3JpdGVyaWEgYXJlIG1ldDoKCi0gICBUaGUgdmlldyBtdXN0IHJlZmVyZW5jZSBhIHNpbmdsZSB0YWJsZS4KLSAgIFRoZSB2aWV3IG11c3QgaW5jbHVkZSBhbGwgbm9uLW51bGxhYmxlIGNvbHVtbnMgZnJvbSB0aGUgYmFzZSB0YWJsZQotICAgTm8gYWdncmVnYXRpb25zIChzdW0oKSwgYXZnKCksIGNvdW50KCksIGV0Yy4pCi0gICBWaWV3IGRvZXMgbm90IGNvbnRhaW4gYSBHUk9VUCBCWSBvciBIQVZJTkcgY2xhdXNlCi0gICBWaWV3IGRlZmluaXRpb24gZG9lcyBub3QgdXNlIHN1YnF1ZXJpZXMKLSAgIFZpZXcgaXMgbm90IHRoZSByZXN1bHQgb2YgYSBVTklPTiwgQUxMLCBvciBESVNUSU5DVCBxdWVyeQotICAgVGhlIEZST00gY2xhdXNlIGluIHRoZSB2aWV3IGRlZmluaXRpb24gY29udGFpbnMgb25seSB1cGRhdGFibGUgdGFibGVzIGFuZCB2aWV3cyBhbmQgb25seSB1c2VzIGlubmVyIGpvaW5zIGFuZCBubyBvdXRlciBqb2lucwoKQ3VzdG9tIHVwZGF0ZSBsb2dpYyBvbiB2aWV3cyBjYW4gYmUgcHJvdmlkZWQgdXNpbmcgdHJpZ2dlcnMgd2hpY2ggdGhlbiB1cGRhdGUgdGhlIHVuZGVybHlpbmcgdGFibGVzLgoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIGFuIHVwZGF0YWJsZSB2aWV3OgoKYGBgIHNxbApDUkVBVEUgVklFVyBlbXBsb3llZV9zYWxhcnlfdmlldyBBUwpTRUxFQ1QgZW1wX2lkLCBlbXBfbmFtZSwgc2FsYXJ5CkZST00gZW1wbG95ZWVzOwpgYGAKClNpbmNlIHRoaXMgdmlldyBvbmx5IHJlZmVyZW5jZXMgYSBzaW5nbGUgdGFibGUgKGBlbXBsb3llZXNgKSBhbmQgZG9lcyBub3QgY29udGFpbiBhbnkgcmVzdHJpY3Rpb25zIG9yIGFnZ3JlZ2F0aW9ucywgaXQgY2FuIGJlIHVwZGF0ZWQuIEZvciBleGFtcGxlLCB3ZSBjYW4gdXBkYXRlIGFuIGVtcGxveWVlJ3Mgc2FsYXJ5IHRocm91Z2ggdGhlIHZpZXc6CgpgYGAgc3FsClVQREFURSBlbXBsb3llZV9zYWxhcnlfdmlldwpTRVQgc2FsYXJ5ID0gNzUwMDAKV0hFUkUgZW1wX2lkID0gMTsKYGBgCgpUaGlzIG9wZXJhdGlvbiB3aWxsIGRpcmVjdGx5IG1vZGlmeSB0aGUgYHNhbGFyeWAgY29sdW1uIGluIHRoZSB1bmRlcmx5aW5nIGBlbXBsb3llZXNgIHRhYmxlLgoKSG93ZXZlciwgaWYgd2UgdHJ5IHRvIHVwZGF0ZSBhIHZpZXcgdGhhdCBpbnZvbHZlcyBhIGBKT0lOYCwgU1FMaXRlIHdpbGwgbm90IGFsbG93IGl0LCBiZWNhdXNlIHRoZXJl4oCZcyBhbWJpZ3VpdHkgYWJvdXQgd2hpY2ggdGFibGUgc2hvdWxkIGJlIHVwZGF0ZWQuIEZvciBleGFtcGxlLCB0cnlpbmcgdG8gdXBkYXRlIHRoZSBgZW1wbG95ZWVfZGVwYXJ0bWVudF92aWV3YCBkZWZpbmVkIGVhcmxpZXIgd291bGQgZmFpbC4KCkxldOKAmXMgY29uc2lkZXIgYSB2aWV3IHRoYXQgaXMgbm90IHVwZGF0YWJsZSBiZWNhdXNlIGl0IGluY2x1ZGVzIGEgYEpPSU5gOgoKYGBgIHNxbApDUkVBVEUgVklFVyBlbXBsb3llZV9zYWxhcnlfZGVwYXJ0bWVudCBBUwpTRUxFQ1QgZS5lbXBfbmFtZSwgZC5kZXBhcnRtZW50X25hbWUsIGUuc2FsYXJ5CkZST00gZW1wbG95ZWVzIGUKSk9JTiBkZXBhcnRtZW50cyBkIE9OIGUuZGVwYXJ0bWVudF9pZCA9IGQuZGVwYXJ0bWVudF9pZDsKYGBgCgpUcnlpbmcgdG8gdXBkYXRlIHRoaXMgdmlldyB3b3VsZCBsZWFkIHRvIGFuIGVycm9yLCBiZWNhdXNlIGl0IGlzIG5vdCBjbGVhciB3aGV0aGVyIHRoZSB1cGRhdGUgc2hvdWxkIGFwcGx5IHRvIHRoZSBgZW1wbG95ZWVzYCB0YWJsZSBvciB0aGUgYGRlcGFydG1lbnRzYCB0YWJsZToKCmBgYCBzcWwKVVBEQVRFIGVtcGxveWVlX3NhbGFyeV9kZXBhcnRtZW50ClNFVCBzYWxhcnkgPSA4NTAwMApXSEVSRSBlbXBfbmFtZSA9ICdKb2huIERvZSc7CmBgYAoKVGhpcyB3aWxsIHJlc3VsdCBpbiBhbiBlcnJvciBzaW1pbGFyIHRvOgoKYGBgICAgICAgICAgCkVycm9yOiB2aWV3IGVtcGxveWVlX3NhbGFyeV9kZXBhcnRtZW50IGlzIG5vdCB1cGRhdGFibGUKYGBgCgojIyBEcm9wcGluZyBWaWV3cwoKSWYgYSB2aWV3IGlzIG5vIGxvbmdlciBuZWVkZWQsIGl0IGNhbiBiZSByZW1vdmVkIGZyb20gdGhlIGRhdGFiYXNlIHVzaW5nIHRoZSBgRFJPUCBWSUVXYCBzdGF0ZW1lbnQ6CgpgYGAgc3FsCkRST1AgVklFVyBJRiBFWElTVFMgZW1wbG95ZWVfZGVwYXJ0bWVudF92aWV3OwpgYGAKClRoaXMgc3RhdGVtZW50IHJlbW92ZXMgdGhlIHZpZXcgZnJvbSB0aGUgZGF0YWJhc2UsIGJ1dCBpdCBkb2VzIG5vdCBhZmZlY3QgdGhlIHVuZGVybHlpbmcgdGFibGVzIG9yIGRhdGEuCgojIyBWaWV3IE1hdGVyaWFsaXphdGlvbgoKVmlldyBtYXRlcmlhbGl6YXRpb24gaXMgYW4gb3B0aW1pemF0aW9uIHRlY2huaXF1ZSB1c2VkIGJ5IGRhdGFiYXNlIG1hbmFnZW1lbnQgc3lzdGVtcyAoREJNUykgdG8gaW1wcm92ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgcXVlcmllcyB0aGF0IGludm9sdmUgY29tcGxleCB2aWV3cy4gVW5kZXIgbm9ybWFsIGNpcmN1bXN0YW5jZXMsIGEgdmlldyBpcyBhIHZpcnR1YWwgdGFibGUsIG1lYW5pbmcgdGhhdCBldmVyeSB0aW1lIGEgdmlldyBpcyBxdWVyaWVkLCB0aGUgREJNUyBtdXN0IHJlY29tcHV0ZSB0aGUgcmVzdWx0IGJ5IGV4ZWN1dGluZyB0aGUgdW5kZXJseWluZyBTUUwgcXVlcnkgdGhhdCBkZWZpbmVzIHRoZSB2aWV3LiBUaGlzIGlzIGtub3duIGFzICoqdmlydHVhbCB2aWV3KiogcHJvY2Vzc2luZy4gRm9yIHNpbXBsZSB2aWV3cyBvciB2aWV3cyB0aGF0IGFyZSByYXJlbHkgYWNjZXNzZWQsIHRoaXMgaXMgZWZmaWNpZW50LiBIb3dldmVyLCBmb3IgY29tcGxleCB2aWV3cyBvciB2aWV3cyBxdWVyaWVkIGZyZXF1ZW50bHksIHJlY29tcHV0aW5nIHRoZSByZXN1bHQgZWFjaCB0aW1lIGNhbiBiZSBpbmVmZmljaWVudCBhbmQgc2xvdy4KCioqTWF0ZXJpYWxpemVkIHZpZXdzKiogYWRkcmVzcyB0aGlzIHBlcmZvcm1hbmNlIGNvbmNlcm4gYnkgKipwcmVjb21wdXRpbmcqKiBhbmQgKipzdG9yaW5nKiogdGhlIHJlc3VsdHMgb2YgdGhlIHZpZXcuIEluc3RlYWQgb2YgcmVjYWxjdWxhdGluZyB0aGUgdmlldydzIHJlc3VsdCBldmVyeSB0aW1lIGl0IGlzIGFjY2Vzc2VkLCB0aGUgcmVzdWx0IGlzIHN0b3JlZCBpbiBhIHBoeXNpY2FsIHRhYmxlIChlaXRoZXIgYXMgYSBwaHlzaWNhbCB0YWJsZSBpbiBzdG9yYWdlIG9yIGFzIGEgdmlydHVhbCB0YWJsZSBpbiBtZW1vcnkpLCBhbGxvd2luZyBtdWNoIGZhc3RlciBhY2Nlc3MuIEVzc2VudGlhbGx5LCBtYXRlcmlhbGl6aW5nIGEgdmlldyB0cmFuc2Zvcm1zIGl0IGZyb20gYSB2aXJ0dWFsIHZpZXcgKHdoaWNoIGlzIGR5bmFtaWNhbGx5IGdlbmVyYXRlZCBvbi1kZW1hbmQpIGludG8gYSBwZXJzaXN0ZW50LCByZWFsIHRhYmxlIGNvbnRhaW5pbmcgZGF0YSB3aG9zZSBjb250ZW50cyBpcyBkZXJpdmVkIGZyb20gb3RoZXIgdGFibGVzIGFuZCB2aWV3cy4KCk1hdGVyaWFsaXphdGlvbiBvZiB2aWV3cyBoYXBwZW5zIGluIHNwZWNpZmljIHNjZW5hcmlvcywgdHlwaWNhbGx5IGRyaXZlbiBieSB0aGUgbmVlZCB0byBvcHRpbWl6ZSBxdWVyeSBwZXJmb3JtYW5jZS4gVGhlcmUgYXJlIHR3byBwcmltYXJ5IHdheXMgaW4gd2hpY2ggbWF0ZXJpYWxpemVkIHZpZXdzIGNhbiBiZSBjcmVhdGVkIGFuZCBtYW5hZ2VkOgoKKipNYW51YWwgTWF0ZXJpYWxpemF0aW9uIChNYXRlcmlhbGl6ZWQgVmlld3MpOioqIEluIHNvbWUgZGF0YWJhc2VzLCB5b3UgZXhwbGljaXRseSBkZWZpbmUgYSBtYXRlcmlhbGl6ZWQgdmlldy4gVW5saWtlIHJlZ3VsYXIgdmlld3MsIGEgKiptYXRlcmlhbGl6ZWQgdmlldyoqIGlzIGEgdmlldyB3aG9zZSByZXN1bHQgaXMgc3RvcmVkIGluIHRoZSBkYXRhYmFzZSBhcyBhIHBoeXNpY2FsIHRhYmxlLiBUaGUgcmVzdWx0IG9mIHRoZSBxdWVyeSB1c2VkIHRvIGRlZmluZSB0aGUgdmlldyBpcyBwcmVjb21wdXRlZCBhbmQgc2F2ZWQsIGFuZCBmdXR1cmUgYWNjZXNzZXMgdG8gdGhlIG1hdGVyaWFsaXplZCB2aWV3IHJldHJpZXZlIHRoZSBkYXRhIGZyb20gdGhpcyBzdG9yZWQgcmVzdWx0IHJhdGhlciB0aGFuIHJlY29tcHV0aW5nIGl0LgoKRXhhbXBsZSBvZiBjcmVhdGluZyBhIG1hdGVyaWFsaXplZCB2aWV3IChub3Qgc3VwcG9ydGVkIGluIFNRTGl0ZSBidXQgY29tbW9uIGluIG90aGVyIGRhdGFiYXNlcyBsaWtlIFBvc3RncmVTUUwgYW5kIE9yYWNsZSk6CgpgYGAgc3FsCkNSRUFURSBNQVRFUklBTElaRUQgVklFVyBlbXBsb3llZV9zdW1tYXJ5IEFTClNFTEVDVCBkZXBhcnRtZW50X2lkLCBDT1VOVChlbXBfaWQpIEFTIGVtcGxveWVlX2NvdW50LCBBVkcoc2FsYXJ5KSBBUyBhdmdfc2FsYXJ5CkZST00gZW1wbG95ZWVzCkdST1VQIEJZIGRlcGFydG1lbnRfaWQ7CmBgYAoKSW4gdGhpcyBjYXNlLCB0aGUgdmlldyBzdG9yZXMgdGhlIG51bWJlciBvZiBlbXBsb3llZXMgYW5kIHRoZSBhdmVyYWdlIHNhbGFyeSBwZXIgZGVwYXJ0bWVudC4gRWFjaCB0aW1lIHlvdSBxdWVyeSB0aGlzIHZpZXcsIHRoZSBEQk1TIGRvZXMgbm90IGhhdmUgdG8gcmVjb21wdXRlIHRoZSBhZ2dyZWdhdGUgZnVuY3Rpb25zLCBidXQgc2ltcGx5IHJldHVybnMgdGhlIHByZWNvbXB1dGVkIGRhdGEgZnJvbSBzdG9yYWdlLgoKKipBdXRvbWF0aWMgTWF0ZXJpYWxpemF0aW9uIChJbnRlcm5hbCBPcHRpbWl6YXRpb24pOioqIFNvbWUgYWR2YW5jZWQgZGF0YWJhc2UgZW5naW5lcyBhdXRvbWF0aWNhbGx5IG1hdGVyaWFsaXplIGNlcnRhaW4gdmlld3Mgb3Igc3VicXVlcmllcyBiZWhpbmQgdGhlIHNjZW5lcyBhcyBwYXJ0IG9mIHF1ZXJ5IG9wdGltaXphdGlvbi4gVGhlIERCTVMgbWF5IGRlY2lkZSB0byBtYXRlcmlhbGl6ZSB0aGUgcmVzdWx0IG9mIGEgY29tcGxleCBxdWVyeSBvciBhIGZyZXF1ZW50bHkgYWNjZXNzZWQgdmlldywgZXNwZWNpYWxseSBpZiByZWNvbXB1dGF0aW9uIGlzIGV4cGVuc2l2ZS4gVGhpcyBpcyB0cmFuc3BhcmVudCB0byB0aGUgdXNlciBidXQgY2FuIHByb3ZpZGUgc2lnbmlmaWNhbnQgcGVyZm9ybWFuY2UgYmVuZWZpdHMuCgpUaGUgZGVjaXNpb24gdG8gbWF0ZXJpYWxpemUgYSB2aWV3IGluIHRoZXNlIGNhc2VzIGlzIHVwIHRvIHRoZSBkYXRhYmFzZSBlbmdpbmUgYW5kIGlzIHR5cGljYWxseSBiYXNlZCBvbiBvbmUgb3IgbW9yZSBvZiB0aGVzZSBjb25zaWRlcmF0aW9uczoKCi0gICBUaGUgY29tcGxleGl0eSBvZiB0aGUgdmlldyAoKmUuZy4qLCBpdCBpbnZvbHZlcyBtdWx0aXBsZSBqb2lucyBvciBhZ2dyZWdhdGUgZnVuY3Rpb25zKS4KLSAgIFRoZSBmcmVxdWVuY3kgd2l0aCB3aGljaCB0aGUgdmlldyBpcyBxdWVyaWVkLgotICAgVGhlIGNvc3Qgb2YgcmVjYWxjdWxhdGluZyB0aGUgdmlld+KAmXMgcmVzdWx0IGVhY2ggdGltZS4KLSAgIEF2YWlsYWJsZSBzeXN0ZW0gcmVzb3VyY2VzIGxpa2UgbWVtb3J5IGFuZCBzdG9yYWdlLgoKT25jZSBhIHZpZXcgaXMgbWF0ZXJpYWxpemVkLCB0aGUgbmV4dCBjb25jZXJuIGlzIGhvdyB0byAqKm1haW50YWluKiogdGhlIGNvcnJlY3RuZXNzIG9mIHRoZSBtYXRlcmlhbGl6ZWQgdmlldywgZXNwZWNpYWxseSB3aGVuIHRoZSB1bmRlcmx5aW5nIGRhdGEgY2hhbmdlcy4gVGhlcmUgYXJlIHR3byBjb21tb24gYXBwcm9hY2hlcyB0byB0aGlzOgoKKipMYXp5IChPbi1kZW1hbmQpIFJlZnJlc2gqKjogSW4gdGhpcyBhcHByb2FjaCwgdGhlIG1hdGVyaWFsaXplZCB2aWV3IGlzIHVwZGF0ZWQgb25seSB3aGVuIGl0IGlzIHF1ZXJpZWQuIElmIHRoZSB1bmRlcmx5aW5nIHRhYmxlcyBoYXZlIGJlZW4gbW9kaWZpZWQgc2luY2UgdGhlIG1hdGVyaWFsaXplZCB2aWV3IHdhcyBsYXN0IGNvbXB1dGVkLCB0aGUgREJNUyB3aWxsIHJlZnJlc2ggdGhlIG1hdGVyaWFsaXplZCB2aWV3IGp1c3QgYmVmb3JlIHJldHVybmluZyB0aGUgcXVlcnkgcmVzdWx0LiBUaGlzIG1ldGhvZCBjYW4gZGVsYXkgdGhlIHJldHJpZXZhbCBvZiBkYXRhIGJ1dCBhdm9pZHMgdW5uZWNlc3NhcnkgcmVmcmVzaGVzIHdoZW4gdGhlIHZpZXcgaXMgbm90IGFjY2Vzc2VkIGZyZXF1ZW50bHkuCgpGb3IgZXhhbXBsZSwgdGhlIGBlbXBsb3llZV9zdW1tYXJ5YCBtYXRlcmlhbGl6ZWQgdmlldyBtYXkgYmUgcmVmcmVzaGVkIG9ubHkgd2hlbiBhIHF1ZXJ5IGxpa2UgYFNFTEVDVCAqIEZST00gZW1wbG95ZWVfc3VtbWFyeWAgaXMgcnVuLiBJZiBuZXcgZW1wbG95ZWVzIGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgYGVtcGxveWVlc2AgdGFibGUgc2luY2UgdGhlIGxhc3QgbWF0ZXJpYWxpemF0aW9uLCB0aGUgdmlldyB3aWxsIGJlIHVwZGF0ZWQgYXQgdGhhdCBwb2ludC4KCioqRWFnZXIgKEltbWVkaWF0ZSkgUmVmcmVzaCoqOiBJbiB0aGlzIGNhc2UsIHRoZSBtYXRlcmlhbGl6ZWQgdmlldyBpcyB1cGRhdGVkIGltbWVkaWF0ZWx5IHdoZW5ldmVyIHRoZSB1bmRlcmx5aW5nIGJhc2UgdGFibGVzIGFyZSBtb2RpZmllZC4gVGhpcyBlbnN1cmVzIHRoYXQgdGhlIG1hdGVyaWFsaXplZCB2aWV3IGlzIGFsd2F5cyB1cC10by1kYXRlIGJ1dCBjYW4gaW5jdXIgYWRkaXRpb25hbCBvdmVyaGVhZCBldmVyeSB0aW1lIHRoZXJl4oCZcyBhbiBpbnNlcnQsIHVwZGF0ZSwgb3IgZGVsZXRlIG9wZXJhdGlvbiBvbiB0aGUgYmFzZSB0YWJsZXMuCgpGb3IgZXhhbXBsZSwgaWYgYW4gYElOU0VSVCBJTlRPIGVtcGxveWVlc2Agc3RhdGVtZW50IGFkZHMgYSBuZXcgZW1wbG95ZWUsIHRoZSBzeXN0ZW0gYXV0b21hdGljYWxseSB1cGRhdGVzIHRoZSBjb3JyZXNwb25kaW5nIGVudHJ5IGluIHRoZSBtYXRlcmlhbGl6ZWQgdmlldyBgZW1wbG95ZWVfc3VtbWFyeWAuIFRoaXMgZW5zdXJlcyB0aGUgdmlldyByZWZsZWN0cyB0aGUgbW9zdCByZWNlbnQgZGF0YSwgYnV0IGl0IGFkZHMgb3ZlcmhlYWQgdG8gZXZlcnkgd3JpdGUgb3BlcmF0aW9uLgoKU29tZSBzeXN0ZW1zIHN1cHBvcnQgaHlicmlkIG1vZGVscyB3aGVyZSBtYXRlcmlhbGl6ZWQgdmlld3MgY2FuIGJlICoqaW5jcmVtZW50YWxseSByZWZyZXNoZWQqKi4gVGhpcyBtZWFucyBpbnN0ZWFkIG9mIHJlY2FsY3VsYXRpbmcgdGhlIGVudGlyZSB2aWV3IGZyb20gc2NyYXRjaCB3aGVuIHRoZSBkYXRhIGNoYW5nZXMsIHRoZSBzeXN0ZW0gY29tcHV0ZXMgb25seSB0aGUgY2hhbmdlcyBhbmQgYXBwbGllcyB0aGVtIHRvIHRoZSBtYXRlcmlhbGl6ZWQgdmlldywgbWFraW5nIHRoZSB1cGRhdGUgcHJvY2VzcyBtb3JlIGVmZmljaWVudC4KCiMjIyBNYXRlcmlhbGl6ZWQgVmlld3MgaW4gUHJhY3RpY2UKCldoaWxlIFNRTGl0ZSwgYSBsaWdodHdlaWdodCByZWxhdGlvbmFsIGRhdGFiYXNlLCBkb2VzIG5vdCBuYXRpdmVseSBzdXBwb3J0IG1hdGVyaWFsaXplZCB2aWV3cyAob25seSB2aXJ0dWFsIHZpZXdzKSwgbGFyZ2VyIERCTVNzIGxpa2UgT3JhY2xlLCBQb3N0Z3JlU1FMLCBhbmQgTWljcm9zb2Z0IFNRTCBTZXJ2ZXIgaGF2ZSBleHBsaWNpdCBzdXBwb3J0IGZvciBtYXRlcmlhbGl6ZWQgdmlld3MuIEluIHRoZXNlIHN5c3RlbXMsIG1hdGVyaWFsaXplZCB2aWV3cyBhcmUgb2Z0ZW4gdXNlZCBmb3I6CgoxLiAgKipEYXRhIFdhcmVob3VzaW5nKio6IE1hdGVyaWFsaXplZCB2aWV3cyBhcmUgaWRlYWwgZm9yIGxhcmdlLXNjYWxlLCBjb21wbGV4IHF1ZXJpZXMgb3ZlciBkYXRhc2V0cyB0aGF0IGRvbuKAmXQgY2hhbmdlIGZyZXF1ZW50bHkuIEZvciBpbnN0YW5jZSwgaW4gYSBkYXRhIHdhcmVob3VzZSwgaGlzdG9yaWNhbCBkYXRhIGlzIG9mdGVuIHF1ZXJpZWQgZm9yIHJlcG9ydGluZyBwdXJwb3NlcywgYW5kIG1hdGVyaWFsaXplZCB2aWV3cyBjYW4gcHJvdmlkZSBwcmUtYWdncmVnYXRlZCByZXN1bHRzIGZvciBmYXN0IHJldHJpZXZhbC4KCjIuICAqKlBlcmZvcm1hbmNlIE9wdGltaXphdGlvbioqOiBXaGVuIGRlYWxpbmcgd2l0aCBjb21wbGV4IHF1ZXJpZXMgaW52b2x2aW5nIG11bHRpcGxlIGpvaW5zLCBhZ2dyZWdhdGlvbnMsIG9yIHN1YnF1ZXJpZXMsIG1hdGVyaWFsaXplZCB2aWV3cyBhbGxvdyB0aGUgREJNUyB0byBzdG9yZSB0aGUgcHJlY29tcHV0ZWQgcmVzdWx0LCBzaWduaWZpY2FudGx5IHJlZHVjaW5nIHRoZSB0aW1lIHJlcXVpcmVkIHRvIGV4ZWN1dGUgdGhlc2UgcXVlcmllcy4KCjMuICAqKlJlcGxpY2F0aW9uIGFuZCBEaXN0cmlidXRlZCBEYXRhYmFzZXMqKjogTWF0ZXJpYWxpemVkIHZpZXdzIGFyZSBzb21ldGltZXMgdXNlZCB0byByZXBsaWNhdGUgZGF0YSBhY3Jvc3MgZGF0YWJhc2VzIG9yIGRpc3RyaWJ1dGUgYSBjb25zaXN0ZW50IHNuYXBzaG90IG9mIGRhdGEgYWNyb3NzIHN5c3RlbXMuIFRoZXkgYWxsb3cgZm9yIGZhc3RlciBhY2Nlc3MgdG8gcmVhZC1oZWF2eSB3b3JrbG9hZHMgaW4gZGlzdHJpYnV0ZWQgZW52aXJvbm1lbnRzLgoKIyMjIEV4YW1wbGUgb2YgQ2FjaGluZyB3aXRoIE1hdGVyaWFsaXplZCBWaWV3cwoKSW4gYSBkYXRhYmFzZSB0aGF0IHN1cHBvcnRzIG1hdGVyaWFsaXplZCB2aWV3cywgdGhlIGZvbGxvd2luZyBleGFtcGxlIHNob3dzIGhvdyBjYWNoaW5nIG1pZ2h0IGJlIHVzZWQgdG8gaW1wcm92ZSBxdWVyeSBwZXJmb3JtYW5jZToKCmBgYCBzcWwKQ1JFQVRFIE1BVEVSSUFMSVpFRCBWSUVXIHNhbGVzX3N1bW1hcnkgQVMKU0VMRUNUIHJlZ2lvbl9pZCwgU1VNKHNhbGVzX2Ftb3VudCkgQVMgdG90YWxfc2FsZXMKRlJPTSBzYWxlcwpHUk9VUCBCWSByZWdpb25faWQ7CmBgYAoKVGhpcyBtYXRlcmlhbGl6ZWQgdmlldyBzdG9yZXMgdGhlIHRvdGFsIHNhbGVzIHBlciByZWdpb24uIE5vcm1hbGx5LCBjb21wdXRpbmcgdGhpcyBhZ2dyZWdhdGUgcXVlcnkgb3ZlciBhIGxhcmdlIHNhbGVzIHRhYmxlIGNvdWxkIGJlIGV4cGVuc2l2ZS4gQnkgbWF0ZXJpYWxpemluZyB0aGUgdmlldywgdGhlIHJlc3VsdCBpcyBjYWNoZWQgaW4gdGhlIGRhdGFiYXNlLCBhbGxvd2luZyBmYXN0IGFjY2VzcyB3aGVuIHRoZSB2aWV3IGlzIHF1ZXJpZWQuCgpOb3csIGlmIHlvdSBydW4gYSBxdWVyeToKCmBgYCBzcWwKU0VMRUNUICogRlJPTSBzYWxlc19zdW1tYXJ5IFdIRVJFIHJlZ2lvbl9pZCA9IDE7CmBgYAoKSW5zdGVhZCBvZiBzY2FubmluZyB0aGUgZW50aXJlIGBzYWxlc2AgdGFibGUsIHRoZSBEQk1TIHNpbXBseSByZXRyaWV2ZXMgdGhlIHByZWNvbXB1dGVkIHJlc3VsdCBmcm9tIHRoZSBtYXRlcmlhbGl6ZWQgdmlldywgd2hpY2ggaXMgbXVjaCBmYXN0ZXIuIFRoZSBtYXRlcmlhbGl6ZWQgdmlldyByZW1haW5zIHVwLXRvLWRhdGUgYWNjb3JkaW5nIHRvIGl0cyByZWZyZXNoIHBvbGljeSAoZS5nLiwgb24tZGVtYW5kIG9yIGF1dG9tYXRpY2FsbHkpLgoKIyMjIEFkdmFudGFnZXMgYW5kIERpc2FkdmFudGFnZXMgb2YgTWF0ZXJpYWxpemVkIFZpZXdzCgojIyMjIEFkdmFudGFnZXM6CgoxLiAgKipQZXJmb3JtYW5jZSoqOiBUaGUgcHJpbWFyeSBiZW5lZml0IG9mIG1hdGVyaWFsaXplZCB2aWV3cyBpcyBmYXN0ZXIgcXVlcnkgcGVyZm9ybWFuY2UuIEJ5IGNhY2hpbmcgdGhlIHJlc3VsdCBvZiBjb21wbGV4IHF1ZXJpZXMsIG1hdGVyaWFsaXplZCB2aWV3cyBhdm9pZCB0aGUgb3ZlcmhlYWQgb2YgcmVjb21wdXRpbmcgdGhlIHF1ZXJ5IGV2ZXJ5IHRpbWUgaXQgaXMgZXhlY3V0ZWQuCgoyLiAgKipFZmZpY2llbnQgQWdncmVnYXRpb24qKjogRm9yIHF1ZXJpZXMgdGhhdCBpbnZvbHZlIGV4cGVuc2l2ZSBvcGVyYXRpb25zIGxpa2Ugam9pbnMgb3IgYWdncmVnYXRpb25zIChlLmcuLCBgR1JPVVAgQllgKSwgbWF0ZXJpYWxpemVkIHZpZXdzIHN0b3JlIHRoZSBwcmVjb21wdXRlZCByZXN1bHRzLCByZWR1Y2luZyB0aGUgbG9hZCBvbiB0aGUgc3lzdGVtIHdoZW4gcXVlcnlpbmcgdGhlc2Ugdmlld3MuCgozLiAgKipTbmFwc2hvdCBvZiBEYXRhKio6IE1hdGVyaWFsaXplZCB2aWV3cyBwcm92aWRlIGEgY29uc2lzdGVudCBzbmFwc2hvdCBvZiB0aGUgZGF0YSBhdCB0aGUgdGltZSB0aGV5IHdlcmUgbGFzdCByZWZyZXNoZWQsIHdoaWNoIGNhbiBiZSB1c2VmdWwgaW4gZGF0YSBhbmFseXNpcyBvciByZXBvcnRpbmcgYXBwbGljYXRpb25zIHdoZXJlIGEgImZyb3plbiIgc3RhdGUgb2YgdGhlIGRhdGEgaXMgcmVxdWlyZWQuCgojIyMjIERpc2FkdmFudGFnZXM6CgoxLiAgKipTdG9yYWdlIE92ZXJoZWFkKio6IFNpbmNlIG1hdGVyaWFsaXplZCB2aWV3cyBwaHlzaWNhbGx5IHN0b3JlIGRhdGEsIHRoZXkgcmVxdWlyZSBhZGRpdGlvbmFsIHN0b3JhZ2Ugc3BhY2UuIFRoaXMgY2FuIGJlIHNpZ25pZmljYW50IGZvciBsYXJnZSBkYXRhc2V0cy4KCjIuICAqKk1haW50ZW5hbmNlIE92ZXJoZWFkKio6IEtlZXBpbmcgbWF0ZXJpYWxpemVkIHZpZXdzIHVwLXRvLWRhdGUgcmVxdWlyZXMgYWRkaXRpb25hbCBwcm9jZXNzaW5nIGR1cmluZyBgSU5TRVJUYCwgYFVQREFURWAsIG9yIGBERUxFVEVgIG9wZXJhdGlvbnMgb24gdGhlIGJhc2UgdGFibGVzLiBJbiB3cml0ZS1oZWF2eSBlbnZpcm9ubWVudHMsIHRoaXMgb3ZlcmhlYWQgY2FuIGJlIHN1YnN0YW50aWFsLCBlc3BlY2lhbGx5IGZvciB2aWV3cyB0aGF0IG5lZWQgdG8gYmUgcmVmcmVzaGVkIGZyZXF1ZW50bHkuCgozLiAgKipTdGFsZW5lc3MqKjogSWYgYSBtYXRlcmlhbGl6ZWQgdmlldyBpcyBub3QgcmVmcmVzaGVkIGZyZXF1ZW50bHksIGl0cyBkYXRhIGNhbiBiZWNvbWUgc3RhbGUsIG1lYW5pbmcgaXQgbm8gbG9uZ2VyIHJlZmxlY3RzIHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBiYXNlIHRhYmxlcy4gVGhpcyBpcyBwYXJ0aWN1bGFybHkgYSBjb25jZXJuIGZvciBkeW5hbWljIGRhdGFzZXRzIHdpdGggZnJlcXVlbnQgY2hhbmdlcy4KClRvIHN1bW1hcml6ZSwgdmlldyBtYXRlcmlhbGl6YXRpb24gaXMgYW4gZXNzZW50aWFsIG9wdGltaXphdGlvbiB0ZWNobmlxdWUgaW4gd2hpY2ggdGhlIHJlc3VsdHMgb2YgY29tcGxleCBvciBmcmVxdWVudGx5IHF1ZXJpZWQgdmlld3MgYXJlIHByZS1tYXRlcmlhbGl6ZWQgYW5kIGNhY2hlZC4gQnkgc3RvcmluZyB0aGUgcmVzdWx0IG9mIHRoZSB2aWV3IGluIGEgcGh5c2ljYWwgdGFibGUsIG1hdGVyaWFsaXplZCB2aWV3cyBwcm92aWRlIGZhc3RlciBxdWVyeSBleGVjdXRpb24gYXQgdGhlIGNvc3Qgb2YgYWRkaXRpb25hbCBzdG9yYWdlIGFuZCBtYWludGVuYW5jZSBvdmVyaGVhZC4gVGhlIGRlY2lzaW9uIG9mIHdoZW4gdG8gbWF0ZXJpYWxpemUgdmlld3MgZGVwZW5kcyBvbiB0aGUgY29tcGxleGl0eSBvZiB0aGUgdmlldywgdGhlIGZyZXF1ZW5jeSBvZiBxdWVyeSBleGVjdXRpb24sIGFuZCB0aGUgYmFsYW5jZSBiZXR3ZWVuIHF1ZXJ5IHBlcmZvcm1hbmNlIGFuZCBkYXRhIGZyZXNobmVzcy4gV2hpbGUgU1FMaXRlIGRvZXMgbm90IHN1cHBvcnQgbWF0ZXJpYWxpemVkIHZpZXdzLCBtYW55IGFkdmFuY2VkIGRhdGFiYXNlIHN5c3RlbXMgbGV2ZXJhZ2UgdGhlbSB0byBlbmhhbmNlIHBlcmZvcm1hbmNlLCBwYXJ0aWN1bGFybHkgaW4gbGFyZ2Utc2NhbGUgYXBwbGljYXRpb25zIGxpa2UgZGF0YSB3YXJlaG91c2luZyBhbmQgcmVwb3J0aW5nLgoKIyMgU3VtbWFyeQoKVmlld3MgYXJlIGFuIGltcG9ydGFudCBlbGVtZW50IGluIHRoZSBkYXRhYmFzZSBhcmNoaXRlY3QncyB0b29sa2l0IGZvciBidWlsZGluZyBzY2FsYWJsZSBhbmQgbWFpbnRhaW5hYmxlIGRhdGFiYXNlcyBhcyB0aGV5IGFsbG93IGFic3RyYWN0aW9uIGFuZCBzaW1wbGlmaWNhdGlvbiBvZiBpbnRlcmFjdGlvbnMgd2l0aCBkYXRhYmFzZSBkYXRhLiBUaGV5IGNhbiBzaWduaWZpY2FudGx5IGltcHJvdmUgcXVlcnkgcmVhZGFiaWxpdHksIHByb3ZpZGUgc2VjdXJpdHkgYnkgcmVzdHJpY3RpbmcgYWNjZXNzIHRvIGNlcnRhaW4gZGF0YSwgYW5kIGNyZWF0ZSBhIGxldmVsIG9mIGFic3RyYWN0aW9uIGZyb20gdGhlIHVuZGVybHlpbmcgdGFibGUgc3RydWN0dXJlcy4gSG93ZXZlciwgdmlld3MgZG8gZ2VuZXJhbGx5IG5vdCBhbGxvdyB1cGRhdGVzLCBhbmQgdW5kZXJzdGFuZGluZyB3aGVuIGFuZCBob3cgdG8gdXNlIHRoZW0gYXBwcm9wcmlhdGVseSBpcyBrZXkgdG8gbGV2ZXJhZ2luZyB0aGVpciBmdWxsIHBvdGVudGlhbC4KCiMjIFR1dG9yaWFsCgpJbiB0aGlzIHNob3J0IGtpY2tzdGFydGVyIGxlc3NvbiwgZ3Vlc3Qgc3BlYWtlciBTb2NyYXRpY2EgcHJvdmlkZXMgYW4gb3ZlcnZpZXcgb2Ygdmlld3MgYW5kIGhvdyB0aGV5IGNhbiBiZSB1c2VkIHRvIGFic3RyYWN0IHRhYmxlcyBpbiBhIHJlbGF0aW9uYWwgZGF0YWJhc2VzIGFuZCBwcm92aWRlIHNjaGVtYSBpbmRlcGVuZGVuY2UgZm9yIHF1ZXJpZXMuCgo8aWZyYW1lIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkLzhqVThTckFQbjljP3NpPVBfUEFLcndsdjZiVVZ0S3UiIHRpdGxlPSJTUUwgVmlld3MiIGZyYW1lYm9yZGVyPSIxIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGNsaXBib2FyZC13cml0ZTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZTsgd2ViLXNoYXJlIiBhbGxvd2Z1bGxzY3JlZW4gZGF0YS1leHRlcm5hbD0iMSI+Cgo8L2lmcmFtZT4KCiMjIFNlZSBBbHNvCgotICAgWzYuMzAxIFdvcmtpbmcgd2l0aCBEYXRhYmFzZXMgaW4gUl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0zMDEtc3FsaXRlLWZyb20tci9sLTYtMzAxLmh0bWwpCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZWZlcmVuY2VzCgpOb25lLgoKIyMgRXJyYXRhCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==