text:
<file_sep><!DOCTYPE html>

  <meta charset="utf-8">
  <title>User List</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="theme-color" content="#0070b7">
  <link rel="manifest" href="manifest.json">
  <link rel="import" href="./design/css/shared-styles.html">

  <my-app></my-app>
  Your browser does not support JavaScript!

<file_sep>export class ValidateUtil {
  static get phoneSettings() {
    return {
      DOMESTIC_LENGTH: 10,
      INTERNATIONAL_MAX_LENGTH: 13,
      DOMESTIC_SYMBOLS: [
        { name: 'dash', loc: 4, text: '-' },
        { name: 'spaceLoc', loc: 8, text: ' ' },
        { name: 'leftParLoc', loc: 9, text: ')' },
        { name: 'rightParLoc', loc: 13, text: '(' }
      ],
      INTERNATIONAL_SYMBOLS: [{ name: 'intSpace', loc: 14, text: ' ' }],
      _isInternational: length => {
        return length > this.phoneSettings.DOMESTIC_LENGTH;
      }
    };
  }

  static formatPhoneNumber(phoneNumber) {
    const reversedPhoneArray = phoneNumber.split('').reverse();
    const domFormatted = this.insertDomesticSymbols(reversedPhoneArray);

    let displayFormat;
    if (this.phoneSettings._isInternational(phoneNumber.length)) {
      const intFormatted = this.insertInternationalSymbols(domFormatted);
      displayFormat = intFormatted.reverse().join('');
    } else {
      displayFormat = domFormatted.reverse().join('');
    }

    return displayFormat;
  }

  static insertDomesticSymbols(reversedPhoneArray) {
    this.phoneSettings.DOMESTIC_SYMBOLS.forEach(symbol => {
      reversedPhoneArray.splice(symbol.loc, 0, symbol.text);
    });

    return reversedPhoneArray;
  }

  static insertInternationalSymbols(reversedPhoneArray) {
    this.phoneSettings.INTERNATIONAL_SYMBOLS.forEach(symbol => {
      reversedPhoneArray.splice(symbol.loc, 0, symbol.text);
    });

    return reversedPhoneArray;
  }

  static checkPhoneInput(phone) {
    const phoneDigits = phone.replace(/\D/g, '');

    const validNumberLength =
      phoneDigits.length >= this.phoneSettings.DOMESTIC_LENGTH &&
      phoneDigits.length <= this.phoneSettings.INTERNATIONAL_MAX_LENGTH;

    return validNumberLength;
  }

  static checkEmailInput(email) {
    const emailFormat = /^\S+@\S+\.\S+$/;
    return emailFormat.test(email);
  }
}
<file_sep># Banno Onboarding Project: Employee User Database Interface

## Description

This app allows you to create and edit user profiles. Each profile has an ID, Name, Number, Email Address, and Department.

Users are by default displayed as collapsed cards that can be expanded to show the user's information.

## To Start

1. Run `$ npm install` to add dependencies.
2. Run `$ yarn start` to fire up the server
3. Open `https://localhost:1820` in your browser.

## how to use

### Create User

- Clicking the add new user button at the top of user list expands the new user form.

- To cancel a new entry, click the same button you clicked to expand the user card. It will collapse it and clear the form.

- Appropriate warnings will notify the end user of improper email or phone formats.

  _Improper format will disable the save button._

### Edit/Delete User

- In case missing the obvious is one of your favorite hobbies, clicking the edit button on an expanded user card will allow you to in fact edit that user. Just as obvious is the fact that clicking the save button will save those changes.

- If you already have an edit in process, all edit and save buttons in other cards will be disabled.

- The save button in the current card will remain disabled while any fields are empty or formatted improperly.

### Sort Users

- Users can be sorted by Last Name, First Name and Department.
- Sort is prioritized by Last Name, First Name and Department, which is also the default.
  _So sorting by Department sorts Department, Last Name within Department, then by First Name Within Matching Last Names_
- Sort can also be done in alphabetical or reverse alphabetical order for each category

### Changing User Component In Code

#### Modes

User component has three mode settings:

1. **Display:** If you pass a `user` object with an `_id` property in to the `<user-component user-to-display="[[user]]"` attribute, then the card will default to a collapsed display mode that only shows the first and last name and the expand for details button.

```html
 <user-component user-to-display="[[userToDisplay]]"></user-component>
```

```js
  static get properties() {
    return {

      // Format of the user object to be passed in
      userToDisplay: {
        type: Object,
        value: {
          firstName: "Jane",
          lastName: "Doe",
          _id: "827104937",
          email: "<EMAIL>",
          phone: "(345) 435-9875"
        }
      }
    }
  }
```

2. **Edit:** Edit is available in an existing user display card when you click edit.
3. **Create New:** If there is not a `user` object with an `_id` property passed into the `<user-component>` the card will init as a 'create new user' button that expands to display the new user form when you click it.

#### Disable Functionality: edit-open attribute

When an edit button is clicked in a `user-component`, that `user-component` dispatches an event received by `user-list`. The dispatched event lets other components know there is an edit currently in progress. The `user-component` uses this boolean to enable/disable save and edit buttons when an edit is open in another card.

`<user-component edit-open="[true,false]"><user-component>`

#### Persistent User Card Display: is-expanded attribute

Because of polymer's dom-repeat information recycling, class changes are re-used on the index of the dom-repeat slot.

For this app, if a user card is added or deleted, other user cards are at a different index in the dom-repeat and take on whatever class that item in that index had before the list update. This causes cards to display as collapsed or expanded when they should not.

Because of the dom-recycling feature of Polymer, the display state of each component is checked and its state is set using its _id property.

`<user-component is-expanded="[[isUserCardDisplayExpanded(user.id, users)]]"><user-component>`

```js
 isUserCardDisplayExpanded(userId) {
    let isExpanded = false;
      this.expandedCardIds.forEach(id => {
        if (id === userId) {
          isExpanded = true;
        }
      });

      return isExpanded;
    }
```

### NPM Scripts

#### To serve the app (also runs ux-lint):

        `$ yarn start` then open https://localhost:1820 in the browser.

        This lints, builds, and serves the project using webpack-dev-server. It also includes settings defined in webpack.development.js, including the dev server settings and a source map for tracking code issues in dev-tools.

#### To see a list of available scripts [or a single script]:

        `$ yarn run help [filter scripts]`

#### To lint (UX-Lint)

        `$ yarn lint`

        `$ yarn start` also fires the linter in `pre-start` before serving the project.

#### To bundle the code for production. It gets built into the dist folder in the root.

        `$ yarn build` uses the normal webpack config, but also adds on webpack.production.js which minifies the code and generates a service worker with a list of files to be cached by the end-user.

## dependencies

### Live Database

The app database API uses a live database from `http://iop-db.herokuapp.com/users` that accepts:

    - POST: For saving a user in the database.
    - PUT: For updating a user in the database by _id.
    - GET: For getting an individual user by id or getting all of the users.
    - DELETE: For deleting a user by its _id.

### @Banno/Polymer

### Webpack

For building production code `$yarn build` bundles compressed into the dist folder.

### Webpack-Dev-Server

Webpack-dev-server wraps around webpack, bundling and serving the files for development purposes.

The bundling and serving happens together on `$ yarn start`

#### To Build File Structure Locally

You will not see the bundled folder locally when using `webpack-dev-server`.

If you want to bundle the files locally to see how they will be organized in webpack-dev-server, run `$ yarn build` which fires `webpack` and builds the file locally.

To view the bundled files from the server when using `webpack-dev-server`, enter `localhost:1820/webpack-dev-server` in your browser.

### Yarn

Used for running scripts instead of npm
<file_sep>const MinifyPlugin = require("babel-minify-webpack-plugin");
const WorkboxPlugin = require("workbox-webpack-plugin");

module.exports = () => {
  return {
    plugins: [
      new MinifyPlugin(),
      new WorkboxPlugin.GenerateSW()
    ]
  };
};
<file_sep>import { Element as PolymerElement } from '@banno/polymer/polymer-element.js'; // eslint-disable-line no-unused-vars
// import { ValidateUtil } from './../api/utils.js';
class WarningMessageElement extends PolymerElement {
  static get is() {
    return 'warning-message';
  }
}

customElements.define(WarningMessageElement.is, WarningMessageElement);
export default WarningMessageElement;
<file_sep>export {sortCategories, Database}

const databaseSettings = {
  DATABASE_URL: `http://iop-db.herokuapp.com/api`,
  HEADERS: {
    Authorization: 'Basic ' + btoa('intern:polymer'),
    'Content-Type': 'application/json'
  }
};

const sortSettings = {
  lastSort: 'lastName',
  // priority from right to left
  FIRST_NAME_SORT_ARRAY: ['lastName', 'firstName'],
  LAST_NAME_SORT_ARRAY: ['firstName', 'lastName'],
  DEPARTMENT_SORT_ARRAY: ['firstName', 'lastName', 'department'],
  isReversedSort: false
};

const sortCategories = {
  FIRST_NAME: 'firstName',
  LAST_NAME: 'lastName',
  DEPARTMENT: 'department'
};

let popupMessage = null;

const Database = class Database {
  static getUsers() {
    this.getUsersSortedBy(sortSettings.lastSort);
  }

  static editUser(user) {
    popupMessage = `Edits to ${user.firstName} ${user.lastName} saved.`;
    this.sendRequest('PUT', user);
  }

  static saveUser(user) {
    popupMessage = `${user.firstName} ${user.lastName} added.`;
    this.sendRequest('POST', user);
  }

  static deleteUser(user) {
    popupMessage = `${user.firstName} ${user.lastName} deleted.`;
    this.sendRequest('DELETE', user);
  }

  static async getUserById(userId) {
    const requestUrl = `${databaseSettings.DATABASE_URL}/${userId}`;
    const settings = {
      method: 'GET',
      headers: databaseSettings.HEADERS
    };

    const response = await fetch(requestUrl, settings);
    const user = await response.json();

    this.sendUserById(user);
  }

  static async getUsersSortedBy(sortBy, isReverse) {
    sortSettings.lastSort = sortBy;
    sortSettings.isReversedSort = Boolean(isReverse);

    const settings = { headers: databaseSettings.HEADERS };
    const response = await fetch(databaseSettings.DATABASE_URL, settings);

    if (response.status !== 200) {
      this.sendUserDatabaseError();
      return;
    }

    const users = await response.json();
    const sortFilterArray = this.getSortArray(sortBy);
    const sorted = this.sortUsers(sortFilterArray, users);

    this.sendUsers(sorted);
  }

  static getSortArray(primarySort) {
    let sortArray;

    Object.keys(sortCategories).forEach(category => {
      if (primarySort === sortCategories[category]) {
        sortArray = sortSettings[`${category}_SORT_ARRAY`];
        return;
      }
    });

    return sortArray;
  }

  static async sendRequest(method, user) {
    const dbUrl = databaseSettings.DATABASE_URL;
    const requestUrl = user._id ? `${dbUrl}/${user._id}` : `${dbUrl}`;
    const formattedForDatabase = this.formatUserData(user);

    const settings = {
      method: method,
      body: formattedForDatabase,
      headers: databaseSettings.HEADERS
    };

    await fetch(requestUrl, settings);
    this.getUsersSortedBy(sortSettings.lastSort, sortSettings.isReversedSort);
  }

  static sendUsers(users) {
    document.dispatchEvent(
      new CustomEvent('usersLoaded', {
        bubbles: true,
        composed: true,
        detail: {
          users: users,
          message: popupMessage
        }
      })
    );
    popupMessage = null;
  }

  static sendUserDatabaseError() {
    document.dispatchEvent(
      new CustomEvent('usersFailedToLoad', {
        bubbles: true,
        composed: true
      })
    );
  }

  // not currently connected to user-list
  static sendUserById(user) {
    document.dispatchEvent(
      new CustomEvent('userByIdLoaded', {
        bubbles: true,
        composed: true,
        detail: {
          user: user
        }
      })
    );
  }

  static sortUsers(sortFilterArray, users) {
    const sortFiltersArray = sortFilterArray || this.fallbackFilters();

    const sorted = users.sort((a, b) => {
      return this.compareTwoUsers(a, b, sortFiltersArray);
    });

    return sorted;
  }

  static fallbackFilters() {
    // priority from right to left
    return ['firstName', 'lastName'];
  }

  static compareTwoUsers(a, b, sortFilterArray) {
    let compared = 0;
    const primarySortFilter = sortFilterArray[sortFilterArray.length - 1];

    sortFilterArray.forEach(filter => {
      const categoryIsDifferent = a[filter] !== b[filter];

      if (categoryIsDifferent) {
        const aFirst = a[filter] > b[filter] ? 1 : -1;
        const bFirst = a[filter] < b[filter] ? 1 : -1;

        const reverseSort = sortSettings.isReversedSort && filter === primarySortFilter;

        compared = reverseSort ? bFirst : aFirst;
      }
    });

    return compared;
  }

  static formatUserData(user) {
    const formattedForDatabase = `{
      "firstName": "${this.titleCaseName(user.firstName)}",
      "lastName": "${this.titleCaseName(user.lastName)}",
      "phone": "${this.getOnlyPhoneDigits(user.phone)}",
      "email": "${user.email}",
      "department": "${user.department}"
    }`;

    return formattedForDatabase;
  }

  static getOnlyPhoneDigits(userPhoneInput) {
    return userPhoneInput.replace(/\D/g, '');
  }

  static titleCaseName(name) {
    const firstLetter = name.charAt(0).toUpperCase();
    const restOfName = name.slice(1).toLowerCase();

    return `${firstLetter}${restOfName}`;
  }
}
<file_sep>import { Element as PolymerElement } from '@banno/polymer/polymer-element.js'; // eslint-disable-line no-unused-vars

class UserComponentElement extends PolymerElement {
  static get is() { return 'user-component'; }
  static get properties() {
    return {
      userToDisplay: {
        type: Object,
        observer: 'userUpdated'
      },
      user: {
        type: Object,
        value: () => {
          return {
            first: '',
            last: '',
            phone: '',
            email: '',
            department: '',
            id: ''
          };
        },
      },
      // edit, display
      mode: {
        type: String,
      },
      modes: {
        type: Object,
        value: () => {
          return {
            DISPLAY: 'display',
            EDIT_USER: 'edit',
            CREATE_NEW: 'create'
          };
        },
      },
      userCardClass: {
        type: String,
      },
      classes: {
        type: Object,
        value: () => {
          return {
            COLLAPSE_NEW: 'default-card collapse-new-user',
            ANIMATE_COLLAPSE_USER: 'default-card collapse-existing-user',
            IMMEDIATELY_COLLAPSE_USER: 'default-card immediately-collapse-existing-user',
            IMMEDIATELY_EXPAND_USER: 'default-card immediately-expand-existing-user',
            EXPAND_NEW: 'default-card expand-new-user',
            ANIMATE_EXPAND_USER: 'default-card expand-existing-user',
          };
        },
      },
      displayOpenEditMessage: {
        type: Boolean
      },
      displayPhoneWarning: {
        type: Boolean
      },
      displayEmailWarning: {
        type: Boolean
      },
      messages: {
        type: Object,
        value: () => {
          return {
            OPEN_EDIT: 'You are safe to save this once the other open edits are closed.',
            PHONE_FORMAT: 'Would you mind formatting your number as (xxx) xxx-xxxx?',
            EMAIL_FORMAT: 'Your email seems a little off. Would you mind formatting it as xxxx@xxxxx.xxx?'
          };
        },
      },
      disableSave: {
        type: Boolean,
        value: true,
      },
      editOpen: {
        type: Boolean,
        value: false,
        observer: 'onEditOpenChanged'
      },
      isExpanded: {
        type: Boolean,
        observer: 'setUserDetailsDisplay'
      },
      isFirstLoad: {
        type: Boolean,
        value: true
      }
    };
  }

  connectedCallback() {
    super.connectedCallback();
    this.initCardState();
  }

  initCardState() {
    let isExistingUser = Boolean(this.user._id);

    if (isExistingUser) {
      this.setProperties({
        userCardClass: this.classes.ANIMATE_COLLAPSE_USER,
        mode: this.modes.DISPLAY
      });
      this.toggleEditableInputs();
    } else {
      this.setProperties({
        userCardClass: this.classes.COLLAPSE_NEW,
        mode: this.modes.CREATE_NEW
      });
    }
  }

  ready() {
    super.ready();
    this.isFirstLoad = false;
    this.importClasses();
  }

  importClasses() {
    require.ensure(
      ['./../api/utils', './../api/user-database'],
      (require) => {
        window.ValidateUtil = require('./../api/utils').ValidateUtil;
        window.Database = require('./../api/user-database').Database;
      },
      (error) => {
        return error;
      },
      'Classes'
    ).then(() => {
      this.formatUserData();
    });
  }

  userUpdated(newUser) {
    this.formatUserData(newUser);
    this.user = newUser;
  }

  formatUserData(newUser) {
    const user = newUser ? newUser : Object.assign({}, this.user);
    const isExistingUser = this.user._id;
    const phoneNumHasSymbols = user.phone.match(/\D/);

    if (!phoneNumHasSymbols && isExistingUser) {
      user.phone = ValidateUtil.formatPhoneNumber(user.phone); // eslint-disable-line no-undef
    }

    this.user = user;
  }

  save(e) {
    e.preventDefault();

    if (this.hasInvalidFormInputs()) {
      this.updateWarningMessageDisplay();
      this.updateDisableSaveButton();
      return;
    }

    const user = Object.assign({}, this.user);

    if (this.isMode(this.modes.CREATE_NEW)) {
      Database.saveUser(user); // eslint-disable-line no-undef
    } else {
      this.toggleDetailsVisibility();
      Database.editUser(user); // eslint-disable-line no-undef
    }

    this.resetFormState();
  }

  delete(e) {
    e.preventDefault();

    Database.deleteUser(this.user); // eslint-disable-line no-undef
    this.toggleEditDisplayMode();
    this.toggleDetailsVisibility();
    this.clearInputFormatWarnings();
  }

  onInputChanged() {
    if (!this.isFirstLoad) {
      this.updateDisableSaveButton();
      this.updateWarningMessageDisplay();
    }
  }

  onEditOpenChanged() {
    if (!this.isFirstLoad) {
      this.updateDisableSaveButton();
      this.updateWarningMessageDisplay();
    }
  }

  hasInvalidFormInputs() {
    const editOpenAndNewUser =
      this.editOpen && this.isMode(this.modes.CREATE_NEW);
      const emptyFields = !this.areFieldsFilled();
      // eslint-disable-next-line no-undef
      const improperPhoneFormat = !ValidateUtil.checkPhoneInput(this.user.phone);
      // eslint-disable-next-line no-undef
      const improperEmailFormat = !ValidateUtil.checkEmailInput(this.user.email);

    return (
      editOpenAndNewUser ||
      emptyFields ||
      improperPhoneFormat ||
      improperEmailFormat
    );
  }

  updateDisableSaveButton() {
    if (!this.isFirstLoad) {
      this.disableSave = this.hasInvalidFormInputs();
    }
  }

  updateWarningMessageDisplay() {
    const isNewUserCard = this.isMode(this.modes.CREATE_NEW);
    const emailInProgress = this.user.email;
    const phoneInProgress = this.user.phone;

    const emailWarning = emailInProgress && !ValidateUtil.checkEmailInput(this.user.email);
    const phoneWarning = phoneInProgress && !ValidateUtil.checkPhoneInput(this.user.phone);
    const openEditMessage = isNewUserCard && this.editOpen;

    this.setProperties({
      displayEmailWarning: emailWarning,
      displayPhoneWarning: phoneWarning,
      displayOpenEditMessage: openEditMessage
    });

    const errorMessageDisplayed = this.displayEmailWarning || this.displayPhoneWarning || this.displayOpenEditMessage;

    if (errorMessageDisplayed) {
      this.resizeCardForWarningMessages(true);
    }
  }

  clearInputFormatWarnings() {
    this.setProperties({
      displayEmailWarning: false,
      displayPhoneWarning: false
    });

    this.resizeCardForWarningMessages(false);
  }

  areFieldsFilled() {
    const fields = this.shadowRoot.querySelectorAll('input');
    let fieldsFilled = true;

    fields.forEach(field => {
      if (!field.value.length) {
        fieldsFilled = false;
        return; // eslint-disable-line no-useless-return
      }
    });

    return fieldsFilled;
  }

  toggleDetailsVisibility() {
    this.isExpanded = !this.isExpanded;
    this.setUserDetailsDisplay();
    this.saveCardState();
  }

  saveCardState() {
    this.dispatchEvent(new CustomEvent('cardDetailDisplayChanged', {
      bubbles: true,
      composed: true,
      detail: { id: this.user._id, expanded: this.isExpanded }
    }));
  }

  setUserDetailsDisplay(event) {
    const isSort = typeof event === 'boolean';
    const animationStyle = isSort ? 'IMMEDIATELY' : 'ANIMATE';
    const displayStyle = this.isExpanded ? 'EXPAND' : 'COLLAPSE';

    this.userCardClass = this.classes[`${animationStyle}_${displayStyle}_USER`];
  }

  setDetailsArrowDirection() {
    const arrowIsDown = this.userCardClass.match(/collapse/g);
    return arrowIsDown;
  }

  editInProgress(bool) {
    this.dispatchEvent(new CustomEvent('editButtonClicked', {
      bubbles: true,
      composed: true,
      detail: {editInProgress: bool}
    }));
  }

  toggleEditDisplayMode(isButtonClick) {
    if (isButtonClick) {
      isButtonClick.preventDefault();
    }

    this.mode = this.isMode(this.modes.EDIT_USER) ? this.modes.DISPLAY : this.modes.EDIT_USER;

    this.editInProgress(this.isMode(this.modes.EDIT_USER));
    this.toggleEditableInputs();
  }

  toggleEditableInputs() {
    this.shadowRoot.querySelectorAll('input')
      .forEach(input => {
        input.readOnly = this.isMode(this.modes.DISPLAY);
      });
  }

  resetFormState() {
    if (this.isMode(this.modes.EDIT_USER)) {
      this.toggleEditDisplayMode();
    } else {
      this.toggleNewUserFormDisplay();
    }

    this.clearInputFormatWarnings();
  }

  toggleNewUserFormDisplay() {
    const collapsed = this.classes.COLLAPSE_NEW;
    const expanded = this.classes.EXPAND_NEW;

    this.userCardClass = this.isClass(collapsed) ? expanded : collapsed;

    if (this.isClass(collapsed)) {
      this.resetNewUserForm();
    }
  }

  resetNewUserForm() {
    this.shadowRoot.querySelector('form').reset();
    this.clearInputFormatWarnings();
  }

  resizeCardForWarningMessages(expandForMessages) {
    if (expandForMessages) {
      this.$.userCard.style.height = 'auto';
    } else {
      this.$.userCard.style = '';
    }
  }

  isMode(expectedMode) {
    return expectedMode === this.mode;
  }

  isClass(expectedClass) {
    return expectedClass === this.userCardClass;
  }

}

customElements.define(UserComponentElement.is, UserComponentElement);
export default UserComponentElement;
<file_sep>const path = require("path");
const webpack = require("webpack");
const webpackMerge = require("webpack-merge");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
const manifestImport = require("./src/manifest.json");

const modeConfig = env => require(`./build-tools/webpack.${env}.js`)(env);

module.exports = env => {
  env = env || {};
  const mode = Object.keys(env)[0];

  return webpackMerge(
    {
      entry: "./src/index.js",
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "main.js"
      },
      module: {
        rules: [
          {
            test: /\.html$/,
            use: [
              { loader: "babel-loader" },
              {
                options: {
                  processStyleLinks: true
                },
                loader: "polymer-webpack-loader"
              }
            ]
          },
          {
            test: /\.js$/,
            use: [
              {
                loader: "babel-loader"
              }
            ]
          },
          {
            test: /\.css$/,
            use: [{ loader: "css-loader" }]
          }
        ]
      },
      plugins: [
        new webpack.NormalModuleReplacementPlugin(
          /\/node_modules\/@banno\/polymer\/polymer\.html$/,
          "@banno/polymer/polymer-element.js"
        ),
        new HtmlWebPackPlugin({
          template: path.resolve(__dirname, "./src/index.ejs"),
          alwaysWriteToDisk: true,
          inject: false,
          production: Boolean(env.production),
          filename: "index.html"
        }),
        new CopyWebpackPlugin([
          {
            from: path.resolve(
              __dirname,
              "./node_modules/@webcomponents/webcomponentsjs/*.js"
            ),
            to: "./webcomponentsjs/[name].[ext]"
          }
        ]),
        new ManifestPlugin({
          seed: manifestImport,
          writeToFileEmit: Boolean(env.production)
        }),
        new CleanWebpackPlugin(path.resolve("dist"))
      ]
    },
    modeConfig(mode)
  );
};
<file_sep>import { Element as PolymerElement } from '@banno/polymer/polymer-element.js'; // eslint-disable-line no-unused-vars
import { Database, sortCategories } from './../api/user-database.js';

class UserListElement extends PolymerElement {
  static get is() {
    return 'user-list';
  }
  static get properties() {
    return {
      users: {
        type: Array
      },
      noServerResponse: {
        type: Boolean,
        value: false
      },
      toastMessage: {
        type: String
      },
      editInProgress: {
        type: Boolean,
        value: false
      },
      expandedCardIds: {
        type: Array,
        value: []
      },
      sortCategoryOptions: {
        type: Array,
        value: () => ['<NAME>', '<NAME>', 'Department']
      },
      sortDirectionOptions: {
        type: Array,
        value: () => ['A-Z', 'Z-A']
      },
      currentSortCategory: {
        type: String,
        value: 'lastName'
      },
      sortDirectionIsReversed: {
        type: Boolean,
        value: false
      }
    };
  }

  connectedCallback() {
    super.connectedCallback();

    this.addEventListeners();

    Database.getUsersSortedBy(
      this.currentSortCategory,
      this.sortDirectionIsReversed
    );
  }

  addEventListeners() {
    document.addEventListener('usersLoaded', response => {
      this.noServerResponse = false;
      this.onUsersLoaded(response.detail);
    });

    document.addEventListener('usersFailedToLoad', response => {
      this.noServerResponse = true;
    });

    document.addEventListener('editButtonClicked', e => {
      this.editInProgress = e.detail.editInProgress;
    });

    document.addEventListener('cardDetailDisplayChanged', e => {
      let cardIsExpanded = e.detail.expanded;
      return cardIsExpanded
        ? this.addIdToExpandedList(e.detail.id)
        : this.removeIdFromExpandedList(e.detail.id);
    });
  }

  onUsersLoaded(response) {
    this.users = response.users;

    if (response.message) {
      this.sendToast(response.message);
    }
  }

  sendToast(message) {
    const toastReset = '';
    this.toastMessage = toastReset;
    this.toastMessage = message;
  }

  formatSortSelectionForDatabase(selected) {
    if (selected === 'Last Name') {
      return sortCategories.LAST_NAME;
    }
    if (selected === 'First Name') {
      return sortCategories.FIRST_NAME;
    }
    if (selected === 'Department') {
      return sortCategories.DEPARTMENT;
    }
  }

  dropdownSort(e) {
    if (!this.editInProgress) {
      this.currentSortCategory = this.formatSortSelectionForDatabase(
        e.target.selectedItem
      );

      Database.getUsersSortedBy(
        this.currentSortCategory,
        this.sortDirectionIsReversed
      );

      this.resetExpandedCardIds();
    }
  }

  sortByDirection(e) {
    if (!this.editInProgress) {
      this.sortDirectionIsReversed = e.target.selectedItem === 'Z-A';

      Database.getUsersSortedBy(
        this.currentSortCategory,
        this.sortDirectionIsReversed
      );

      this.resetExpandedCardIds();
    }
  }

  resetExpandedCardIds() {
    this.expandedCardIds = [];
  }

  addIdToExpandedList(id) {
    this.expandedCardIds.splice(0, 0, id);
  }

  removeIdFromExpandedList(idToDelete) {
    const idIndex = this.expandedCardIds.indexOf(idToDelete);

    this.expandedCardIds.splice(idIndex, 1);
  }

  noUsersInDatabase(users) {
    return !users.length;
  }

  isUserCardDisplayExpanded(userId) {
    return this.expandedCardIds.includes(userId);
  }

  setDisplayOfSortHeader(users, user, index, category) {
    let isFirstUser = index === 0
    let deletedLastUser = index === users.length;

    if (isFirstUser || deletedLastUser) {
      return true;
    }

    let currentUser = user[category];
    let previousUser = users[index - 1][category];

    if (this.dipslayFirstLetterOnly()) {
      return currentUser[0] !== previousUser[0];
    }

    const displayWholeCategory = currentUser !== previousUser;

    return displayWholeCategory;
  }

  dipslayFirstLetterOnly() {
    return (
      this.sortIs(sortCategories.FIRST_NAME, this.currentSortCategory) ||
      this.sortIs(sortCategories.LAST_NAME, this.currentSortCategory)
    );
  }

  sortIs(expectedSortCategory, currentSortCategory) {
    return expectedSortCategory === currentSortCategory;
  }

  setNewGroupHeaderText(user, category) {
    const firstLetter = `${user[category][0]}`;
    const wholeCategory = `${user[category]}`;

    return this.dipslayFirstLetterOnly() ? firstLetter : wholeCategory;
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    document.removeEventListener('usersLoaded');
    document.removeEventListener('editInProgress');
    document.removeEventListener('cardDetailDisplayChanged');
  }
}

customElements.define(UserListElement.is, UserListElement);
export default UserListElement;
<file_sep>import './my-app.html';

languages: JavaScript Markdown EJS

repo_name: Jropp/jason-ropp-js-iobp


text:
<file_sep># altoque4
altoque4

branch_name: refs/heads/master

repo_language: Markdown


text:
<repo_name>dtbinh/Consensus-Problem<file_sep>/fault-tolerant rendezvous:Tverberg Point/query_adjustment.m
function [adjustment] = query_adjustment(n,ntotal,nf,p,ptotal,fp,r,Neighbor,delta,t)
    proposalTarget = query_proposalNextMove(n,ntotal,nf,p,ptotal,fp,r,Neighbor,delta,t);
    adjustment = [];
    for i = 1:n
        W = zeros(size(Neighbor{i},2),1);
        for j = 1: size(Neighbor{i},2)
            %if j <= n
                d = sqrt((proposalTarget(i,1)-proposalTarget(Neighbor{i}(j),1)).^2+(proposalTarget(i,2)-proposalTarget(Neighbor{i}(j),2)).^2);
                if d <= r
                    W(j) = 1;
                end
            %end
        end
        if W == ones(size(Neighbor{i},2),1)
            adjustment(i,:) = proposalTarget(i,:);
        else
            adjustment(i,:) = p(i,:)+1./2*(proposalTarget(i,:)-p(i,:));
        end
    end
end<file_sep>/fault-tolerant rendezvous:Tverberg Point/query_proposalNextMove.m
function [proposalTarget] = query_proposalNextMove(n,ntotal,nf,p,ptotal,fp,r,Neighbor,delta,t)
    nextMove = query_nextMove(n,ntotal,p,ptotal,fp,r);
    argminDist = [];
    proposalTarget = [];
    if t>=1.2
        delta_1 = delta;
    else
        delta_1 = 0.01;
    end
    for i=1:n
        argminDist(i) = sqrt((nextMove(i,1)-p(i,1)).^2+(nextMove(i,2)-p(i,2)).^2);
        proposalTarget(i,:) = p(i,:);
        for x = 0:delta_1:1
            for y = 0:delta_1:1
                W = zeros(size(Neighbor{i},2),1);
                for j = 1:size(Neighbor{i},2)
                    d = sqrt((x-ptotal(Neighbor{i}(j),1)).^2+(y-ptotal(Neighbor{i}(j),2)).^2);
                    if d <= r
                        W(j)=1;
                    end
                end
                if W==ones(size(Neighbor{i},2),1)
                    dist = sqrt((nextMove(i,1)-x).^2+(nextMove(i,2)-y).^2);
                    if dist <= argminDist(i)
                        argminDist(i) = dist;
                        proposalTarget(i,:) = [x,y];
                    end
                end
            end
        end
    end
    proposalTarget = [proposalTarget;fp];
end<file_sep>/fault-tolerant rendezvous:Tverberg Point/firstAttackModel.m
function [fp,fp1,fp2,fp3,fp4]= firstAttackModel(fp1,fp2,fp3,fp4,iter)
    for i = 1:size(fp1,1)
        if fp1(i,2)<=0.9 && fp1(i,1)>=0.1
            fp1(i,1) = fp1(i,1) - 0.1;
            fp1(i,2) = fp1(i,2) + 0.1;
        end
    end
    for i = size(fp1,1)+1:size([fp1;fp2],1)
        if fp2(i-size(fp1,1),2)>=0.1 && fp2(i-size(fp1,1),1)>=0.1
            fp2(i-size(fp1,1),2) = fp2(i-size(fp1,1),2) - 0.1;
            fp2(i-size(fp1,1),1) = fp2(i-size(fp1,1),1) - 0.1;
        end
     end
     for i = size([fp1;fp2],1)+1:size([fp1;fp2;fp3],1)
        if fp3(i-size([fp1;fp2],1),2)>=0.1 && fp3(i-size([fp1;fp2],1),1)<=0.9
            fp3(i-size([fp1;fp2],1),2) = fp3(i-size([fp1;fp2],1),2) - 0.1;
            fp3(i-size([fp1;fp2],1),1) = fp3(i-size([fp1;fp2],1),1) + 0.1;
        end
     end
     for i = size([fp1;fp2;fp3],1)+1:size([fp1;fp2;fp3;fp4],1)
        if fp4(i-size([fp1;fp2;fp3],1),1)<=0.9 && fp4(i-size([fp1;fp2;fp3],1),2)<=0.9
            fp4(i-size([fp1;fp2;fp3],1),2) = fp4(i-size([fp1;fp2;fp3],1),2) + 0.1;
            fp4(i-size([fp1;fp2;fp3],1),1) = fp4(i-size([fp1;fp2;fp3],1),1) + 0.1;
        end
     end
    fp = [fp1;fp2;fp3;fp4];<file_sep>/randomized gossip/plot_graph.m
function plot_graph(x_total,n,n_total)
    hold off;
    plot(x_total(1:n,1),x_total(1:n,2),'s','LineWidth',1,...
            'MarkerEdgeColor','k',...
            'MarkerFaceColor','g',...
            'MarkerSize',8);

    hold on;
    plot(x_total(n+1:n_total,1),x_total(n+1:n_total,2),'o','LineWidth',1,...
                'MarkerEdgeColor','k',...
                'MarkerFaceColor','r',...
                'MarkerSize',7);

    axis('equal')
    axis([0 1 0 1]);
    set(gca,'xtick',[0 0.5 1]);
    set(gca,'ytick',[0 0.5 1]);
    set(gca,'FontSize',12);
    drawnow;
    <file_sep>/fault-tolerant rendezvous:Tverberg Point/query_adjacency.m
function[A]=query_adjacency(ptotal,ntotal,r)

    %% Computes the adjacency matrix for the disk graph
    A=zeros(ntotal,ntotal);
    for i=1:ntotal
      for j = 1:ntotal
        d = sqrt((ptotal(i,1)-ptotal(j,1)).^2+(ptotal(i,2)-ptotal(j,2)).^2);
        if (i~=j && d <= r)   % Check for proximity
          A(i,j)=1;
        end
      end
    end
end
<file_sep>/README.md
# Consensus-Problem
Distributed agents consensus problem, matlab code
<file_sep>/randomized gossip/getAdjacency.m
function neighbor = getAdjacency(x_total,n_total,r,i)
    neighbor = zeros(1,n_total);
    for j = 1:n_total
        if norm(x_total(i,:)-x_total(j,:))<=r
            neighbor(1,j) = 1;
        end
    end
<file_sep>/fault-tolerant rendezvous:Tverberg Point/save_figure.m
function save_figure(t)
    if t == 0
        saveas(1,'fig_t=0.png');
    elseif t>=0.1 && t<= 0.2
        saveas(1,'fig_t=0.4.png');
    elseif t>=0.3 && t<= 0.4
        saveas(1,'fig_t=0.6.png');
    elseif t>=0.5 && t<= 0.7
        saveas(1,'fig_t=1.0.png');
    elseif t>=1.3 && t<= 1.4
        saveas(1,'fig_t=1.4.png');
    end
end<file_sep>/randomized gossip/main_faultTolerant.m
clear; close all; clc
K = 50; % No. of consensus instance
iter = 500; % No. of iterations
n = 8;
nf = 1;
n_total = n + nf;
Adjacency = ...
  [1 1 1 1 0 0 1 0 0;
   1 1 1 0 1 0 0 1 0;
   1 1 1 0 0 1 0 0 1;
   1 0 0 1 1 1 1 0 0;
   0 1 0 1 1 1 0 1 0;
   0 0 1 1 1 1 0 0 1;
   1 0 0 1 0 0 1 1 1;
   0 1 0 0 1 0 1 1 1;
   0 0 1 0 0 1 1 1 1;];
Adjacency = Adjacency - eye(9);
createBiograph(Adjacency,n,nf)
rng('default');
x_f = zeros(K,nf);
x = zeros(K,n);
x_total = zeros(K,n_total);
x_start = x_total;

for k = 1:K
    decay = -1;
    x_f(k,:) = 0.1*rand(nf,1);
    x(k,:) = randn(n,1);
    x_total(k,:) = [x(k,:) x_f(k,:)];
    x_start(k,:) = x_total(k,:);
    for t = 1:iter
        i = randi([1,n_total],1);
        neighbor = find(Adjacency(i,:)~=0);
        j = neighbor(randi([1,size(neighbor,2)],1));
        temp = (x_total(k,i)+x_total(k,j))/2;
        x_total(k,i) = temp;
        if j > n
            x_total(k,j) = 0.1*rand*exp(decay);
            decay = decay - 1;
        else
            x_total(k,j) = temp;
        end

%         if mod(t,20)==0
%             plot_graph(reshape(x_total(k,:,:),n_total,2),n,n_total)

%         end
    end
end

%% Neighborhood Detection Task
e = 1/K * sum(abs(x_total - x_start))';
e_bar = zeros(n,1);
H1 = zeros(n,1);
for i = 1:n
    e_bar(i) = 1/size(find(Adjacency(i,:)~=0),2) * Adjacency(i,:)*e;
    for j = 1:n_total
        if Adjacency(i,j)~=0
            H1(i) = H1(i) + abs(e(j)-e_bar(i));
        end
    end
end
fault_neighbor = find(H1>0.6);
fault = [];

%% Localization Task
for i=1:length(fault_neighbor)
    for j = 1:n_total
        if Adjacency(fault_neighbor(i),j)~=0
            if e(j) < 0.25
                fault = [fault j];
                Adjacency(fault_neighbor(i),j) = 0;
                Adjacency(j,fault_neighbor(i)) = 0;
            end
        end
    end
end

createBiograph(Adjacency,n,nf)

    <file_sep>/randomized gossip/createBiograph.m
% Create a biograph object.
function createBiograph(AdjacencyMatrix,n,nf)
% AdjacencyMatrix = ...
%   [1 1 1 1 0 0 1 0 0;
%    1 1 1 0 1 0 0 1 0;
%    1 1 1 0 0 1 0 0 1;
%    1 0 0 1 1 1 1 0 0;
%    0 1 0 1 1 1 0 1 0;
%    0 0 1 1 1 1 0 0 1;
%    1 0 0 1 0 0 1 1 1;
%    0 1 0 0 1 0 1 1 1;
%    0 0 1 0 0 1 1 1 1;];
AdjacencyMatrix = AdjacencyMatrix - eye(9);
B = tril(AdjacencyMatrix');

IDS={'1','2','3','4','5','6','7','8','9'};
graph = biograph(B,IDS,'ShowArrows','off');
x1 = [];
x2 = [];
for i = 1:n
    x1 = [x1 i];
end
for i = n+1:n+nf
    x2 = [x2 i];
end
set(graph.nodes(x2),'Color',[240 128 128]/255,'LineColor',[0 0 0]);
set(graph.nodes(x1),'Color',[1 1 1],'LineColor',[0 0 0]);
set(graph.nodes,'shape','circle');
set(graph.edges,'LineColor',[0 0 0]);
set(graph.edges,'LineWidth',1);

dolayout(graph);
graph.nodes(1).Position = [0 400]/5;
graph.nodes(2).Position = [200 400]/5;
graph.nodes(3).Position = [400 400]/5;
graph.nodes(4).Position = [0 200]/5;
graph.nodes(5).Position = [200 200]/5;
graph.nodes(6).Position = [400 200]/5;
graph.nodes(7).Position = [0 0]/5;
graph.nodes(8).Position = [200 0]/5;
graph.nodes(9).Position = [400 0]/5;
dolayout(graph, 'Pathsonly', true);

view(graph);
<file_sep>/fault-tolerant rendezvous:Tverberg Point/tvb.m
function [tvb_pnt] = tvb(p)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Obtain Tverberg point Tverberg partition of maximum depth w/ 1D/2D/3D points
% tvb.m
% Coded by <NAME> (<EMAIL>, <EMAIL>)
% Date: Apr 15, 2015
% Based upon the paper W.Mulzer and D. Werner, titled
% "Approximating Tverberg points in Linear Time for Any Fixed Dimension"
% Input: set of n points in 1D/2D/3D  (n times d matrix)
% format:
%       p = [x1 y1 z1;
%            x2 y2 z2;
%            ...
%            xn yn zn]
% Output: Tverberg point/partition of depth ceil(n/2^d)
%       out1: a Tverberg point
%       out2: Tverberg partition
% 0.75:     initial release
% 0.95 Sep, 2, 2015 : bugs are fixed

d = size(p,2);

if size(p,1)==1
    tvb_pnt = [p(1,1) p(1,2)];
elseif size(p,2)==2
    tvb_pnt = [(p(1,1)+p(2,1))/2 (p(1,2)+p(2,2))/2];
elseif size(p,2)==3
    tvb_pntx = (p(1,1)+p(2,1)+p(3,1))/3;
    tvb_pnty = (p(1,2)+p(2,2)+p(3,2))/3;
    tvb_pnt = [tvb_pntx tvb_pnty];
else
    switch(d)

        case 1  % d = 1, this is simply a median of a set of points
    %%
            [B,IX] = sort(p(:,1)); % sort
            sB = size(B,1); % size of the array
            size1 = floor(sB/2); % floor
            % Obtain Tverberg partition (1D) with depth ceil(n / 2)
            for i=1:size1
                tvb_prt{i} = p([IX(i) IX(sB-i+1)],:); % a pair in each group
            end
            if mod(sB,2) == 1       % if the number of points is ODD
            % there is a group with a single point
                tvb_prt{i+1} = p(IX(i+1),:); % singleton
            end
            % Obtain Tverberg point (1D) with depth ceil(n / 2) == median
            tvb_pnt = median(B); % obtain median -----> Tverberg point

        case 2  % d = 2
    %%
            sumX=0;
            sumY=0;
            [B,IX] = sort(p(:,1)); % sort
            sB = size(B,1); % size of the array
            if sB < 4
                for i=1:sB
                    sumX = sumX + p(i,1);
                    sumY = sumY + p(i,2);
                end
                mean_X = sumX ./ sB;
                mean_Y = sumY ./ sB;
                tvb_pnt = [mean_X, mean_Y];
            end

            size1 = floor(sB/2); % floor
            % Obtain Tverberg partition (1D) with depth ceil(n / 2)
            for i=1:size1
                tver{i} = [IX(i) IX(sB-i+1)]; % a pair in each group
            end
            if mod(sB,2) == 1
                tver{i+1} = IX(i+1); % if the number of points is ODD
            % there is a group with a single point
            end
            % Obtain Tverberg point (1D) with depth ceil(n / 2) == median
            tverp = median(B); % obtain median -----> Tverberg point: X coordinate
            sT = size(tver,2); % size of partition (1D)
            % lifting to 2D
            for i = 1:sT
               tst_pnt{i} =p(tver{i},1:2); % for each group with up to 2 points
               if size(tst_pnt{i},1) == 1
                   tver2(i) = tst_pnt{i}(1,2); % if it is a singleton take the value
               else
                   if tst_pnt{i}(1,1) == tst_pnt{i}(2,1) % exclude slope at infinite
                       tver2(i) = mean(tst_pnt{i}(:,2));
                   else
                       lineq = polyfit(tst_pnt{i}(:,1),tst_pnt{i}(:,2),1);
                       tver2(i) = lineq(1)*tverp + lineq(2);
                       % find the intersection with the horizontal line
                   end
               end
            end
            [~,IX2] = sort(tver2);
            sB2 = size(IX2,2);
            size2 = floor(sB2/2);
            for i=1:size2
                tver3{i} = [IX2(i) IX2(sB2-i+1)];
            end
            if mod(sB2,2) == 1
                tver3{i+1} = IX2(i+1);
            end
            tverp2 = median(tver2); % tverberg point: Y coordinate
            tvb_pnt = [tverp tverp2]; % tverberg point
            for i = 1:size(tver3,2)
                if size(tver3{i},2) == 1
                    tvb_prt{i} = tst_pnt{tver3{i}};
                else
                    tvb_prt{i} = [tst_pnt{tver3{i}(1)};tst_pnt{tver3{i}(2)}];
                end
            end

        case 3  % d = 3
    %%
            [B,IX] = sort(p(:,1)); % sort
            sB = size(B,1); % size of the array
            size1 = floor(sB/2); % floor
            % Obtain Tverberg partition (1D) with depth ceil(n / 2)
            for i=1:size1
                tver{i} = [IX(i) IX(sB-i+1)]; % two elements
            end
            if mod(sB,2) == 1
                tver{i+1} = IX(i+1); % singleton
            end
            % Obtain Tverberg point (1D) with depth ceil(n / 2) == median
            tverp = median(B); % obtain median
            sT = size(tver,2); % size of partition (1D)

            % lifting to 2D
            for i = 1:sT
               tst_pnt{i} =p(tver{i},1:2);
    %            tst_pnt{i}
               if size(tst_pnt{i},1) == 1
                   tver2(i) = tst_pnt{i}(1,2);
               else
                   if tst_pnt{i}(1,1) == tst_pnt{i}(2,1)
                       tver2(i) = mean(tst_pnt{i}(:,2));
                   else
                       lineq = polyfit(tst_pnt{i}(:,1),tst_pnt{i}(:,2),1);
                       tver2(i) = lineq(1)*tverp + lineq(2);
                   end
               end

            %    clear tst_pnt;
            end
            [~,IX2] = sort(tver2);
            sB2 = size(IX2,2);
            size2 = floor(sB2/2);
            for i=1:size2
                tver3{i} = [IX2(i) IX2(sB2-i+1)];
                tver4{i} = [tver{IX2(i)} tver{IX2(sB2-i+1)}];
            end
            if mod(sB2,2) == 1
                tver3{i+1} = IX2(i+1);
                tver4{i+1} = tver{IX2(i+1)};
            end
            tverp2 = median(tver2);
            tver_pnt = [tverp tverp2];
            for i = 1:size(tver3,2)
                if size(tver3{i},2) ==1
                    tverf{i} = tst_pnt{tver3{i}};
                else
                    tverf{i} = [tst_pnt{tver3{i}(1)};tst_pnt{tver3{i}(2)}];
                end
            end
            sT2 = size(tver4,2); % size of Tverberg partition (2D)
            % lifting to 3D
            for i = 1:sT2
    %             tver4{i}
                pl1 = p(tver4{i},:);
    %             pl1
    %             rank(pl1(:,1:2))
                [~,ia,ic] = unique(pl1(:,1:2),'rows');
    %             unique(pl1(:,1:2),'rows')
                tst_pnt2{i} = pl1(ia,:);
    %             ia
    %             tst_pnt2{i}
                for q1 = 1:size(tst_pnt2{i},1)
                   tmp3 = find(ic == q1);
                   if size(tmp3,1) >=2
                       tst_pnt2{i}(q1,:) = mean(pl1(tmp3,:),1);
                   end
                end
    %             tst_pnt2{i} = unique(p(tver4{i},:),'rows');
                if size(tst_pnt2{i},1) == 1 || rank(pl1(ia,1:2)) ==1
                    tver5(i) = tst_pnt2{i}(1,3);
                    tver7(i) = tst_pnt2{i}(1,3);
                elseif size(tst_pnt2{i},1) == 2 || rank(pl1(ia,1:2)) ==2

                tver5(i) = (tver_pnt(2)-tst_pnt2{i}(1,2))/(tst_pnt2{i}(2,2)-tst_pnt2{i}(1,2)) *...
                    (tst_pnt2{i}(2,3)-tst_pnt2{i}(1,3))+tst_pnt2{i}(1,3);
                tver7(i) = (tver_pnt(2)-tst_pnt2{i}(1,2))/(tst_pnt2{i}(2,2)-tst_pnt2{i}(1,2)) *...
                    (tst_pnt2{i}(2,3)-tst_pnt2{i}(1,3))+tst_pnt2{i}(1,3);
                else

                   [A,b,~,~]=vert2lcon(tst_pnt2{i});
                        b1 = b- A(:,1:2)*tver_pnt';
                        g = inline('x');
                        g1 = inline('-x');
                        options = optimset('Algorithm','interior-point','Display','off');
                        tver5(i) = fmincon(g,0,A(:,3),b1,[],[],[],[],[],options);
                        tver7(i) = fmincon(g1,0,A(:,3),b1,[],[],[],[],[],options);
                end
            % linear programming problem
            end
            tver6 = mean([tver5;tver7]);
            tverp3 = median(tver6);
            tvb_pnt = [tverp tverp2 tverp3];
            [~,IX4] = sort(tver6);
            sB3 = size(IX4,2);
            size3 = floor(sB3/2);

            for i=1:size3
                tverB1(i,:) = [IX4(i) IX4(sB3-i+1)];
                tverB2{i} = [tver4{IX4(i)} tver4{IX4(sB3-i+1)}];
            end
            if mod(sB3,2) == 1
                tverB1(i+1,:) = IX4(i+1);
                tverB2{i+1} = tver4{IX4(i+1)};
            end
            sT3 = size(tverB2,2); % size of partition (2D)
            % lifting to 3D
            for i = 1:sT3
                tvb_prt{i} = p(tverB2{i},:);
            end

    end
end<file_sep>/randomized gossip/main.m
clear; close all; clc
n = 10;
nf = 10;
n_total = n + nf;
r = 0.5;
decay = -1;
rng('default');
x_f = 0.1*randn(nf,2); %when attackers choose an coordinated point, attack sometimes succeeds, sometimes fails.
% when nf larger enough, attack succeeds.(n = 50, nf = 10 succeeds)
%x_f = rand(nf,2); %when attackers choose an arbitrary next move, the attack succeeds
x = rand(n,2);
x_total = [x;x_f];
iter = 1;
plot_graph(x_total,n,n_total)

Adjacency = zeros(n,n_total);
for i = 1:n
    for j = 1:n_total
        if norm(x(i,:)-x_total(j,:))<=r
            Adjacency(i,j) = 1;
        end
    end
end

while iter < 3000
    i = randi([1,n],1);
    neighbor = find(Adjacency(i,:)~=0);
    j = neighbor(randi([1,size(neighbor,2)],1));
    temp = (x_total(i,:)+x_total(j,:))/2;
    x_total(i,:) = temp;
    if j > n
        x_total(j,:) = 0.1*randn(1,2)*exp(decay);
        %x_total(j,:) = rand(1,2);
        decay = decay - 1;
    else
        x_total(j,:) = temp;
    end
    Adjacency(i,:) = getAdjacency(x_total,n_total,r,i);
    if j <= n
        Adjacency(j,:) = getAdjacency(x_total,n_total,r,j);
    end

    iter = iter + 1;
    if mod(iter,100)==0
        plot_graph(x_total,n,n_total)
        pause(0.2)
    end
end

    <file_sep>/fault-tolerant rendezvous:Tverberg Point/query_nextMove.m
function [nextMove] = query_nextMove(n,ntotal,p,ptotal,fp,r)
    % find the neighbors of each robot, and get the Tverberg Point for each
    % fault-free robot.
    nextMove = [];
    for i = 1:n
        N_i = [];
        for j = 1:ntotal
            d = sqrt((p(i,1)-ptotal(j,1)).^2+(p(i,2)-ptotal(j,2)).^2);
            if d <= r
               N_i = [N_i;ptotal(j,:)];
            end
        end
        [tvb_pnt] = tvb(N_i);
        nextMove = [nextMove; p(i,:) + 0.5*(tvb_pnt-p(i,:))];
    end
    nextMove = [nextMove;fp];

end<file_sep>/fault-tolerant rendezvous:Tverberg Point/query_neighbors.m
function [Neighbor] = query_neighbors(n,ntotal,p,ptotal,r)
    Neighbor = cell(n,1);
    for i = 1:n
        neighb = [];
        for j = 1:ntotal
            d = sqrt((p(i,1)-ptotal(j,1)).^2+(p(i,2)-ptotal(j,2)).^2);
            if d <= r
                neighb = [neighb,j];
            end
        end
        Neighbor{i} = neighb;
    end
end<file_sep>/fault-tolerant rendezvous:Tverberg Point/mainfunction.m

%% system parameters
clear;close;clc
n = 30; % number of fault-free robots
nf = 16; % number of faulty robots
ntotal = n + nf; % total number of robots
d = 2; % dimension
p = rand(n,d); % generate n fault-free robots in R^d
r=0.71; % sensing range
%fp = rand(nf,d); % generate nf faulty robots
%fp = [0.3+0.4*rand(nf,1),0.3+0.4*rand(nf,1)];  %在图中间产生敌方的attack model

% first attack model
%fp1 = [0.3+0.2*rand(nf/4,1),0.5+0.2*rand(nf/4,1)];fp2 = [0.3+0.2*rand(nf/4,1),0.3+0.2*rand(nf/4,1)];fp3 = [0.5+0.2*rand(nf/4,1),0.3+0.2*rand(nf/4,1)];fp4 = [0.5+0.2*rand(nf/4,1),0.5+0.2*rand(nf/4,1)];
% second attack model
fp1 = [0.1*rand(nf/4,1),rand(nf/4,1)];fp2 = [0.9+0.1*rand(nf/4,1),rand(nf/4,1)];fp3 = [0.1+0.8*rand(nf/4,1),0.1*rand(nf/4,1)];fp4 = [0.1+0.8*rand(nf/4,1),0.9+0.1*rand(nf/4,1)];

fp = [fp1;fp2;fp3;fp4];
%fp = [0.8+0.2*rand(nf,1),0.8+0.2*rand(nf,1)];
%fp = [0.1,0.1; 0.9,0.9];
ptotal = [p;fp]; % total robots matrix
delta = 0.01; % compact degree of the convergence

%% simulation parameters
dt=0.1; % numerical steplength
Tf=2; % final time
t=0;
iter=1;

while (t<=Tf);
%% Plot the solution every 10 iterations
  if (mod(iter,1)==0);
    show_graph(p,n,nf,fp,ptotal,ntotal,r);
  end
  grid on;

  M(iter) = getframe;
  if iter==1
    [I,map]=rgb2ind(M(iter).cdata,256);
    imwrite(I,map,'out.gif','DelayTime',1)
  else
    imwrite(rgb2ind(M(iter).cdata,map),map,'out.gif','WriteMode','append','DelayTime',1)
  end

  iter=iter+1;
  Neighbor = query_neighbors(n,ntotal,p,ptotal,r);

%% get each fault-free robot's next move (after collection,proposal and adjustment phase)
  adjustment = query_adjustment(n,ntotal,nf,p,ptotal,fp,r,Neighbor,delta,t);
  %adjustment = query_nextMove(n,ntotal,p,ptotal,fp,r);

  for i=1:n
      p(i,:) = adjustment(i,:);
  end
  % update Faulty Robots
  %[fp,fp1,fp2,fp3,fp4] = firstAttackModel(fp1,fp2,fp3,fp4,iter);
  [fp,fp1,fp2,fp3,fp4] = secondAttackModel(fp1,fp2,fp3,fp4);
  ptotal = [p;fp];

%% Update time
  %save_figure(t);
    t=t+dt;
end
movie2avi(M,'out2.avi')

        <file_sep>/fault-tolerant rendezvous:Tverberg Point/show_graph.m
function[]=show_graph(p,n,nf,fp,ptotal,ntotal,r)
    % A is the adjacency matrix associated with the system using a disk graph.
    A = query_adjacency(ptotal,ntotal,r);
    figure(1);
    hold off;

    for i=1:n
      for j=1:ntotal
        if (A(i,j)==1);
          plot([ptotal(i,1),ptotal(j,1)],[ptotal(i,2),ptotal(j,2)],'color',[0.5 0.5 0.5]);
          hold on;
        end
      end
    end
    for i = 1:n
      plot(p(i,1),p(i,2),'o','LineWidth',1,...
                'MarkerEdgeColor','k',...
                'MarkerFaceColor','g',...
                'MarkerSize',10);

      hold on;
    end

    for i=1:nf
      plot(fp(i,1),fp(i,2),'o','LineWidth',1,...
                'MarkerEdgeColor','k',...
                'MarkerFaceColor','r',...
                'MarkerSize',10);

      hold on;
    end

    axis('equal')
    axis([0 1 0 1]);
    set(gca,'xtick',[0 1]);
    set(gca,'ytick',[0 1]);
    set(gca,'FontSize',16);
    xlabel('X');ylabel('Y');
    drawnow;
end

languages: MATLAB Markdown

repo_language: MATLAB

repo_name: dtbinh/Consensus-Problem