Improve `ERC20FlashMint` documentation (#3719)

Co-authored-by: Hadrien Croubois <ed1fdb870694988da43d062485927c79190b889d@gmail.com>

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/extensions/ERC20FlashMint.sol)

pragma solidity ^0.8.0;

import "../../../interfaces/IERC3156FlashBorrower.sol";
import "../../../interfaces/IERC3156FlashLender.sol";
import "../ERC20.sol";

/**
 * @dev Implementation of the ERC3156 Flash loans extension, as defined in
 * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
 *
 * Adds the {flashLoan} method, which provides flash loan support at the token
 * level. By default there is no fee, but this can be changed by overriding {flashFee}.
 *
 * _Available since v4.1._
 */
abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");

    /**
     * @dev Returns the maximum amount of tokens available for loan.
     * @param token The address of the token that is requested.
     * @return The amount of token that can be loaned.
     */
    function maxFlashLoan(address token) public view virtual override returns (uint256) {
        return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
    }

    /**
     * @dev Returns the fee applied when doing flash loans. This function calls
     * the {_flashFee} function which returns the fee applied when doing flash
     * loans.
     * @param token The token to be flash loaned.
     * @param amount The amount of tokens to be loaned.
     * @return The fees applied to the corresponding flash loan.
     */
    function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
        require(token == address(this), "ERC20FlashMint: wrong token");
        return _flashFee(token, amount);
    }

    /**
     * @dev Returns the fee applied when doing flash loans. By default this
     * implementation has 0 fees. This function can be overloaded to make
     * the flash loan mechanism deflationary.
     * @param token The token to be flash loaned.
     * @param amount The amount of tokens to be loaned.
     * @return The fees applied to the corresponding flash loan.
     */
    function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
        // silence warning about unused variable without the addition of bytecode.
        token;
        amount;
        return 0;
    }

    /**
     * @dev Returns the receiver address of the flash fee. By default this
     * implementation returns the address(0) which means the fee amount will be burnt.
     * This function can be overloaded to change the fee receiver.
     * @return The address for which the flash fee will be sent to.
     */
    function _flashFeeReceiver() internal view virtual returns (address) {
        return address(0);
    }

    /**
     * @dev Performs a flash loan. New tokens are minted and sent to the
     * `receiver`, who is required to implement the {IERC3156FlashBorrower}
     * interface. By the end of the flash loan, the receiver is expected to own
     * amount + fee tokens and have them approved back to the token contract itself so
     * they can be burned.
     * @param receiver The receiver of the flash loan. Should implement the
     * {IERC3156FlashBorrower.onFlashLoan} interface.
     * @param token The token to be flash loaned. Only `address(this)` is
     * supported.
     * @param amount The amount of tokens to be loaned.
     * @param data An arbitrary datafield that is passed to the receiver.
     * @return `true` if the flash loan was successful.
     */
    // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
    // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
    // slither-disable-next-line reentrancy-no-eth
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) public virtual override returns (bool) {
        require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
        uint256 fee = flashFee(token, amount);
        _mint(address(receiver), amount);
        require(
            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
            "ERC20FlashMint: invalid return value"
        );
        address flashFeeReceiver = _flashFeeReceiver();
        _spendAllowance(address(receiver), address(this), amount + fee);
        if (fee == 0 || flashFeeReceiver == address(0)) {
            _burn(address(receiver), amount + fee);
        } else {
            _burn(address(receiver), amount);
            _transfer(address(receiver), flashFeeReceiver, fee);
        }
        return true;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/extensions/ERC20FlashMint.sol)

pragma solidity ^0.8.0;

import "../../../interfaces/IERC3156FlashBorrower.sol";
import "../../../interfaces/IERC3156FlashLender.sol";
import "../ERC20.sol";

/**
 * @dev Implementation of the ERC3156 Flash loans extension, as defined in
 * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
 *
 * Adds the {flashLoan} method, which provides flash loan support at the token
 * level. By default there is no fee, but this can be changed by overriding {flashFee}.
 *
 * _Available since v4.1._
 */
abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");

    /**
     * @dev Returns the maximum amount of tokens available for loan.
     * @param token The address of the token that is requested.
     * @return The amount of token that can be loaned.
     */
    function maxFlashLoan(address token) public view virtual override returns (uint256) {
        return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
    }

    /**
     * @dev Returns the fee applied when doing flash loans. By default this
     * implementation has 0 fees. This function can be overloaded to make
     * the flash loan mechanism deflationary.
     * @param token The token to be flash loaned.
     * @param amount The amount of tokens to be loaned.
     * @return The fees applied to the corresponding flash loan.
     */
    function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
        require(token == address(this), "ERC20FlashMint: wrong token");
        return _flashFee(token, amount);
    }

    /**
     * @dev Returns the fee applied when doing flash loans. This function calls the {flashFee} function which returns the fee applied when doing flash loans.
     * @param token The token to be flash loaned.
     * @param amount The amount of tokens to be loaned.
     * @return The fees applied to the corresponding flash loan.
     */
    function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
        // silence warning about unused variable without the addition of bytecode.
        token;
        amount;
        return 0;
    }

    /**
     * @dev Returns the receiver address of the flash fee. By default this
     * implementation returns the address(0) which means the fee amount will be burnt.
     * This function can be overloaded to change the fee receiver.
     * @return The address for which the flash fee will be sent to.
     */
    function _flashFeeReceiver() internal view virtual returns (address) {
        return address(0);
    }

    /**
     * @dev Performs a flash loan. New tokens are minted and sent to the
     * `receiver`, who is required to implement the {IERC3156FlashBorrower}
     * interface. By the end of the flash loan, the receiver is expected to own
     * amount + fee tokens and have them approved back to the token contract itself so
     * they can be burned.
     * @param receiver The receiver of the flash loan. Should implement the
     * {IERC3156FlashBorrower.onFlashLoan} interface.
     * @param token The token to be flash loaned. Only `address(this)` is
     * supported.
     * @param amount The amount of tokens to be loaned.
     * @param data An arbitrary datafield that is passed to the receiver.
     * @return `true` if the flash loan was successful.
     */
    // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
    // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
    // slither-disable-next-line reentrancy-no-eth
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) public virtual override returns (bool) {
        require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
        uint256 fee = flashFee(token, amount);
        _mint(address(receiver), amount);
        require(
            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
            "ERC20FlashMint: invalid return value"
        );
        address flashFeeReceiver = _flashFeeReceiver();
        _spendAllowance(address(receiver), address(this), amount + fee);
        if (fee == 0 || flashFeeReceiver == address(0)) {
            _burn(address(receiver), amount + fee);
        } else {
            _burn(address(receiver), amount);
            _transfer(address(receiver), flashFeeReceiver, fee);
        }
        return true;
    }
}


return false if locked

return false if locked

pragma solidity ^0.4.0;

import "./Concept.sol";

/*
@type: contract
@name: ConceptRegistry
@purpose: To create concepts and store some concept data
*/
contract ConceptRegistry {
  mapping (address => bool) public conceptExists; //Maps concept addresses to a bool to confirm their existance
  address userRegistry; //The address of the userRegistry contract
  bool initialized = false; //Keeps track of whether or not the function to set the userRegistry variable is locked yet or not
  address public mewAddress;

  /*
  @occasion: When a concept is created
  @purpose: To help build a data store of concepts
  */
  event ConceptCreation (address _concept); //the address of the concept that was created

  /*
  @type: function
  @purpose: To set the userRegistry address
  @param: address userRegistry = the address of the userRegistry contract
  @returns: nothing
  */
  function init(address _userRegistry, address mew) returns(bool){
    if(initialized == false) { //Checks if the function has already been called
      userRegistry = _userRegistry; //Sets the userRegistry address
      mewAddress = mew;
      initialized = true; //Makes it so this function cannot be called again
    }
    else {
      return(false);
    }
  }

  /*
  @purpose: To make a concept
  @param: address[] parentList = an array of addresses containing the addresses of the concepts parents
  */
  function makeConcept(address[] parentList) {
    Concept newConcept = new Concept(parentList, userRegistry, address(this)); //Makes a new concept with the provided data
    address newConceptAddress = address(newConcept); //initializes an address variable and sets it equal to the address of the newly created concept
    conceptExists[newConceptAddress] = true; //Maps the concept address to true to show that it exists
    if(parentList.length == 0) {
      Concept(mewAddress).addChild(newConceptAddress);
    }
    for(uint j=0; j < parentList.length; j++) { //Iterates of the parents array in memory
      Concept(parentList[j]).addChild(newConceptAddress); //Adds the newly created concept to each of the parents as a child
    }
    ConceptCreation(newConceptAddress); //Makes ConceptCreation event with provided data
  }
}

pragma solidity ^0.4.0;

import "./Concept.sol";

/*
@type: contract
@name: ConceptRegistry
@purpose: To create concepts and store some concept data
*/
contract ConceptRegistry {
  mapping (address => bool) public conceptExists; //Maps concept addresses to a bool to confirm their existance
  address userRegistry; //The address of the userRegistry contract
  bool initialized = false; //Keeps track of whether or not the function to set the userRegistry variable is locked yet or not
  address public mewAddress;

  /*
  @occasion: When a concept is created
  @purpose: To help build a data store of concepts
  */
  event ConceptCreation (address _concept); //the address of the concept that was created

  /*
  @type: function
  @purpose: To set the userRegistry address
  @param: address userRegistry = the address of the userRegistry contract
  @returns: nothing
  */
  function init(address _userRegistry, address mew) {
    if(initialized == false) { //Checks if the function has already been called
      userRegistry = _userRegistry; //Sets the userRegistry address
      mewAddress = mew;
      initialized = true; //Makes it so this function cannot be called again
    }
  }

  /*
  @purpose: To make a concept
  @param: address[] parentList = an array of addresses containing the addresses of the concepts parents
  */
  function makeConcept(address[] parentList) {
    Concept newConcept = new Concept(parentList, userRegistry, address(this)); //Makes a new concept with the provided data
    address newConceptAddress = address(newConcept); //initializes an address variable and sets it equal to the address of the newly created concept
    conceptExists[newConceptAddress] = true; //Maps the concept address to true to show that it exists
    if(parentList.length == 0) {
      Concept(mewAddress).addChild(newConceptAddress);
    }
    for(uint j=0; j < parentList.length; j++) { //Iterates of the parents array in memory
      Concept(parentList[j]).addChild(newConceptAddress); //Adds the newly created concept to each of the parents as a child
    }
    ConceptCreation(newConceptAddress); //Makes ConceptCreation event with provided data
  }
}


updated comments

updated comments

pragma solidity ^0.4.9;

/*
    Open issues:
    - throw vs. return value?
*/

contract BancorEtherToken {
    string public standard = 'Token 0.1';
    string public name = "Bancor Network Ether";
    string public symbol = "BNE";
    uint256 public totalSupply = 0;
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function BancorEtherToken() {
    }

    // send coins
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[msg.sender] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;

        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
        Transfer(msg.sender, _to, _value);
        return true;
    }

    // allow another account/contract to spend some tokens on your behalf
    function approve(address _spender, uint256 _value) public returns (bool success) {
        // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
        if (_value != 0 && allowance[msg.sender][_spender] != 0)
            throw;

        allowance[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }

    // an account/contract attempts to get the coins
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[_from] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;
        if (_value > allowance[_from][msg.sender]) // allowance check
            throw;

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    }

    // deposit ether in the account
    function deposit() public payable returns (bool success) {
        if (balanceOf[msg.sender] + msg.value < balanceOf[msg.sender]) // overflow protection
            throw;

        balanceOf[msg.sender] += msg.value;
        return true;
    }

    // withdraw ether from the account
    function withdraw(uint256 _amount) public returns (bool success) {
        if (balanceOf[msg.sender] < _amount) // balance check
            throw;

        // deduct the amount from the account balance
        balanceOf[msg.sender] -= _amount;
        // send the amount
        if (!msg.sender.send(_amount))
            throw;

        return true;
    }

    // deposit ether in the account - identical to deposit function
    function() public payable {
        if (balanceOf[msg.sender] + msg.value < balanceOf[msg.sender]) // overflow protection
            throw;

        balanceOf[msg.sender] += msg.value;
    }
}

pragma solidity ^0.4.9;

/*
    Open issues:
    - throw vs. return value?
    - keep supply in gwei instead of wei to keep the supply relatively low (10^9 smaller)
*/

contract BancorEtherToken {
    string public standard = 'Token 0.1';
    string public name = "Bancor Network Ether";
    string public symbol = "BNE";
    uint256 public totalSupply = 0;
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    function BancorEtherToken() {
    }

    // send coins
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[msg.sender] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;

        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
        Transfer(msg.sender, _to, _value);
        return true;
    }

    // allow another account/contract to spend some tokens on your behalf
    function approve(address _spender, uint256 _value) public returns (bool success) {
        // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
        if (_value != 0 && allowance[msg.sender][_spender] != 0)
            throw;

        allowance[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }

    // an account/contract attempts to get the coins
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[_from] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;
        if (_value > allowance[_from][msg.sender]) // allowance check
            throw;

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    }

    // deposit ether in the account
    function deposit() public payable returns (bool success) {
        if (balanceOf[msg.sender] + msg.value < balanceOf[msg.sender]) // overflow protection
            throw;

        balanceOf[msg.sender] += msg.value;
        return true;
    }

    // withdraw ether from the account
    function withdraw(uint256 _amount) public returns (bool success) {
        if (balanceOf[msg.sender] < _amount) // balance check
            throw;

        // deduct the amount from the account balance
        balanceOf[msg.sender] -= _amount;
        // send the amount
        if (!msg.sender.send(_amount))
            throw;

        return true;
    }

    // deposit ether in the account - identical to deposit function
    function() public payable {
        if (balanceOf[msg.sender] + msg.value < balanceOf[msg.sender]) // overflow protection
            throw;

        balanceOf[msg.sender] += msg.value;
    }
}


removing unusable class

removing unusable class

/*
    Copyright 2017 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity 0.4.18;

import "./MarketContractOraclize.sol";
import "zeppelin-solidity/contracts/ownership/Ownable.sol";

/// @title MarketFactory that creates market contracts and collects white listed addresses of contracts it has
/// deployed.  Currently this idea is pretty limited by deployment gas, so leave this as place holder for future
/// ideas.
/// @author Phil Elsasser <phil@marketprotcol.io>
contract MarketFactoryOraclize is Ownable {

    address public MKT_TOKEN_ADDRESS;

    mapping(address => bool) isWhiteListed; // by default currently all contracts are whitelisted
    address[] deployedAddresses;            // record of all deployed addresses;

    event MarketContractDeployed(address indexed marketContractAddress);

    function MarketFactoryOraclize(address marketTokenAddress) public {
        MKT_TOKEN_ADDRESS = marketTokenAddress;
    }

    /// @notice deploys a market contract and adds the new address to the white list to allow trading
    /// @param contractName viewable name of this contract (BTC/ETH, LTC/ETH, etc)
    /// @param baseTokenAddress address of the ERC20 token that will be used for collateral and pricing
    /// @param contractSpecs array of unsigned integers including:
    /// floorPrice minimum tradeable price of this contract, contract enters settlement if breached
    /// capPrice maximum tradeable price of this contract, contract enters settlement if breached
    /// priceDecimalPlaces number of decimal places to convert our queried price from a floating point to
    /// an integer
    /// qtyDecimalPlaces decimal places to multiply traded qty by.
    /// expirationTimeStamp - seconds from epoch that this contract expires and enters settlement
    /// @param oracleDataSource a data-source such as "URL", "WolframAlpha", "IPFS"
    /// see http://docs.oraclize.it/#ethereum-quick-start-simple-query
    /// @param oracleQuery see http://docs.oraclize.it/#ethereum-quick-start-simple-query for examples
    /// @param oracleQueryRepeatSeconds how often to repeat this callback to check for settlement, more frequent
    /// queries require more gas and may not be needed.
    function deployMarketContractOraclize (
        string contractName,
        address baseTokenAddress,
        uint[5] contractSpecs,
        string oracleDataSource,
        string oracleQuery,
        uint oracleQueryRepeatSeconds
    ) external payable returns (address)
    {
        // create market contract, forwarding amount payed to the constructor
        // not that msg.sender in market contract will now be our address, so we need to
        // ammend constructor to accept the address of the caller of THIS function as the creator
        // of the contract.
        MarketContractOraclize marketContract = (new MarketContractOraclize).value(msg.value)(
            contractName,
            MKT_TOKEN_ADDRESS,
            baseTokenAddress,
            contractSpecs,
            oracleDataSource,
            oracleQuery,
            oracleQueryRepeatSeconds
        );

        isWhiteListed[marketContract] = true;
        deployedAddresses.push(marketContract);
        MarketContractDeployed(marketContract);
        return marketContract;
    }

    /// @notice determines if an address is a valid MarketContract
    /// @return false if the address has not been deployed by this factory, or is no longer white listed.
    function isAddressWhiteListed(address contractAddress) public view returns (bool) {
        return isWhiteListed[contractAddress] &&
            MarketContractOraclize(contractAddress).isCollateralPoolContractLinked();
    }

    /// @notice the current number of contracts that have been deployed by this factory.
    function getDeployedAddressesLength() external view returns (uint) {
        return deployedAddresses.length;
    }

    /// @notice allows user to get all addresses currently available from this factory
    /// @param index of the deployed contract to return the address
    /// @return address of a white listed contract, or if contract is no longer valid address(0) is returned.
    function getAddressByIndex(uint index) external view returns (address) {
        address deployedAddress = deployedAddresses[index];
        if (isAddressWhiteListed(deployedAddress)) {
            return deployedAddress;
        } else {
            return address(0);
        }
    }

    /// @dev allows for the owner to remove a white listed contract, eventually ownership could transition to
    /// a decentralized smart contract of community members to vote
    /// @param contractAddress contract to removed from white list
    function removeContractFromWhiteList(address contractAddress) external onlyOwner returns (bool) {
        isWhiteListed[contractAddress] = false;
    }
}


fix solidity test

fix solidity test

contract owned {
    function owned() { owner = msg.sender; }
    address owner;
}

// Use "is" to derive from another contract. Derived contracts can access all members
// including private functions and storage variables.
contract mortal is owned {
    function kill() { if (msg.sender == owner) suicide(owner); }
}

contract Config is owned, mortal {
    event ServiceChanged(uint indexed id);
    function register(uint id, address service) {
        if (tx.origin != owner)
            return;
        services[id] = service;
        ServiceChanged(id);
    }

    function unregister(uint id) {
        if (msg.sender != owner && services[id] != msg.sender)
            return;
        services[id] = address(0);
        ServiceChanged(id);
    }

    function lookup(uint service) constant returns(address a) {
        return services[service];
    }

    mapping (uint => address) services;
}

//sol Config
// Simple global configuration registrar.
// @authors:
//   Gav Wood <g@ethdev.com>
#require mortal
contract Config is mortal {
	function register(uint id, address service) {
		if (tx.origin != owner)
			return;
		services[id] = service;
		log1(0, id);
	}

	function unregister(uint id) {
		if (msg.sender != owner && services[id] != msg.sender)
			return;
		services[id] = address(0);
		log1(0, id);
	}

	function lookup(uint service) constant returns(address a) {
		return services[service];
	}

	mapping (uint => address) services;
}

/*

// Solidity Interface:
contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}

// Example Solidity use:
address addrConfig = 0xf025d81196b72fba60a1d4dddad12eeb8360d828;
address addrNameReg = Config(addrConfig).lookup(1);

// JS Interface:
var abiConfig = [{"constant":false,"inputs":[],"name":"kill","outputs":[]},{"constant":true,"inputs":[{"name":"service","type":"uint256"}],"name":"lookup","outputs":[{"name":"a","type":"address"}]},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"service","type":"address"}],"name":"register","outputs":[]},{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"unregister","outputs":[]}];

// Example JS use:
var addrConfig = "0x661005d2720d855f1d9976f88bb10c1a3398c77f";
var addrNameReg;
web3.eth.contract(addrConfig, abiConfig).lookup(1).call().then(function(r){ addrNameReg = r; })

*/


set checkpoint after all confirmed

set checkpoint after all confirmed

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee;
  address[] assessors;
  address[] finalAssessors;
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters;
  State public assessmentStage;
  enum State {
    None,
    Called,
    Confirmed,
    Committed,
    Done,
    Burned
  }

  uint public assessorPoolLength;
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public checkpoint;
  uint public size;
  uint cost;
  mapping(address => string[]) public data;
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint public done; //counter how many assessors have committed/revealed their score
  mapping(address => int8) scores;
  mapping(int => bool) inRewardCluster;
  int public finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    checkpoint = block.number;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
    done = 0;
  }

  function cancelAssessment() onlyConceptAssessment() {
    Concept(concept).addBalance(assessee, cost*size);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called;
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
      if (block.number - checkpoint > 1000) {
          cancelAssessment();
          return;
      }
      if (assessorState[msg.sender] == State.Called &&
          assessors.length < size &&
          Concept(concept).subtractBalance(msg.sender, cost)
          ) {
          assessors.push(msg.sender);
          assessorState[msg.sender] = State.Confirmed;
          stake[msg.sender] = cost;
          UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
      }
      if (assessors.length == size) {
          notifyAssessors(uint(State.Confirmed), 4);
          checkpoint = block.number;
          UserRegistry(userRegistry).notification(assessee, 4);
          assessmentStage = State.Confirmed;
      }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data);
    DataSet(msg.sender, data[msg.sender].length - 1);
  }

  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 _hash) onlyInStage(State.Confirmed){
    if(done > size/2) {
        burnStakes();
    }

    if(done == size) {
      notifyAssessors(uint(State.Committed), 5);
      checkpoint = block.number; //Resets the startTime
      done = 0; //Resets the done counter
      assessmentStage = State.Committed;
    }

    if(assessorState[msg.sender] == State.Confirmed)
    {
        commits[msg.sender] = _hash;
        assessorState[msg.sender] = State.Committed;
        done++; //Increases done by 1 to help progress to the next assessment stage.

        if(done <= size/2) {
            checkpoint = block.number;
      }
    }
  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, string salt, address assessor) onlyInStage(State.Committed) {
      if(block.number - checkpoint > 1000) {
          for(uint i = 0; i < assessors.length; i++) {
              if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
                  stake[assessors[i]] = 0;
                  assessorState[assessors[i]] = State.Burned;
                  done++;
              }
          }
      }

      bytes32 hash = sha3(score,salt);
      if(commits[assessor] == hash) {
          if(msg.sender == assessor) {
              scores[msg.sender] = score;
              assessorState[assessor] = State.Done;
              done++; //done increases by 1 to help progress to the next assessment stage
          }
          else {
              Concept(concept).addBalance(msg.sender, stake[assessor]); // give the stake to the caller
              stake[assessor] = 0;
              assessorState[assessor] = State.Burned;
              done++; //done increases by 1 to help progress to the next assessment stage
          }
      }

      if(done == size) { //If all the assessors have revealed their scored or burned their stakes
          assessmentStage = State.Done;
          calculateResult(); //The final result is calculated
      }
  }

  function burnStakes() internal {
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) {
              uint r = 38; //Inverse burn rate
              stake[assessors[i]] = cost*2**(-(now-checkpoint)/r) - 1; //burns stake as a function of how much time has passed since half of the assessors commited
          }
          if(stake[assessors[i]] == 0) {
              assessorState[assessors[i]] = State.Burned;
              done++; //Increases done by 1 to help progress to the next assessment stage
          }
      }
  }

   function notifyAssessors(uint _state, uint _topic) private {
      for(uint i=0; i < assessors.length; i++) {
          if(uint(assessorState[assessors[i]]) == _state) {
              UserRegistry(userRegistry).notification(assessors[i], _topic);
          }
      }
  }

   function calculateResult() onlyInStage(State.Done) private {
    for(uint j = 0; j < size; j++) {
      if(assessorState[assessors[i]] == State.Done) {
        finalAssessors.push(assessors[i]); //Adds all the assessors that completed the assessment process to an array
      }
    }
    int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
    uint largestClusterIndex = 0; //store the index of the largest cluster
    int averageScore;
    for(uint i = 0; i < finalAssessors.length; i++) {
      score[i] = scores[finalAssessors[i]];
    }
    int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length));
    for(uint l = 0; l < score.length; l++) {
      for(uint m = 0; m < score.length; m++)
      {
        if(score[l] - score[m] <= meanAbsoluteDeviation) {
          clusters[l].push(score[m]);
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length) {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; o++) {
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length);
    finalScore = averageScore; //Sets the final score to the average score
    payout();
  }

   function payout() onlyInStage(State.Done) private { //TODO order functions according to styleguide
    for(uint i = 0; i < size; i++) {
      int score = scores[assessors[i]];
      int scoreDistance = ((score - finalScore)*100)/finalScore;
      if(scoreDistance < 0) {
        scoreDistance *= -1;
      }
      uint payoutValue = 1; //Initializes the payoutValue for the assessor
      if(inRewardCluster[score] == true) {
        uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
        payoutValue = (q*cost*((100 - uint(scoreDistance))/100)) + stake[assessors[i]]; //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
        Concept(concept).addBalance(assessors[i], payoutValue);
      }
      if(inRewardCluster[score] == false) {
        payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200); //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
        Concept(concept).addBalance(assessors[i], payoutValue);
      }
    Concept(concept).finishAssessment(finalScore, assessee, address(this)); //Sends assessment info to the concept so that it can update its records
    UserRegistry(userRegistry).notification(assessors[i], 6); //You Have Received Payment For Your Assessment
    }
  }
}

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee;
  address[] assessors;
  address[] finalAssessors;
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters;
  State public assessmentStage;
  enum State {
    None,
    Called,
    Confirmed,
    Committed,
    Done,
    Burned
  }

  uint public assessorPoolLength;
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public checkpoint;
  uint public size;
  uint cost;
  mapping(address => string[]) public data;
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint public done; //counter how many assessors have committed/revealed their score
  mapping(address => int8) scores;
  mapping(int => bool) inRewardCluster;
  int public finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    checkpoint = block.number;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
    done = 0;
  }

  function cancelAssessment() onlyConceptAssessment() {
    Concept(concept).addBalance(assessee, cost*size);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called;
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
      if (block.number - checkpoint > 1000) {
          cancelAssessment();
          return;
      }
      if (assessorState[msg.sender] == State.Called &&
          assessors.length < size &&
          Concept(concept).subtractBalance(msg.sender, cost)
          ) {
          assessors.push(msg.sender);
          assessorState[msg.sender] = State.Confirmed;
          stake[msg.sender] = cost;
          UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
      }
      if (assessors.length == size) {
          notifyAssessors(uint(State.Confirmed), 4);
          UserRegistry(userRegistry).notification(assessee, 4);
          assessmentStage = State.Confirmed;
      }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data);
    DataSet(msg.sender, data[msg.sender].length - 1);
  }

  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 _hash) onlyInStage(State.Confirmed){
    if(done > size/2) {
        burnStakes();
    }

    if(done == size) {
      notifyAssessors(uint(State.Committed), 5);
      checkpoint = block.number; //Resets the startTime
      done = 0; //Resets the done counter
      assessmentStage = State.Committed;
    }

    if(assessorState[msg.sender] == State.Confirmed)
    {
        commits[msg.sender] = _hash;
        assessorState[msg.sender] = State.Committed;
        done++; //Increases done by 1 to help progress to the next assessment stage.

        if(done <= size/2) {
            checkpoint = block.number;
      }
    }
  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, string salt, address assessor) onlyInStage(State.Committed) {
      if(block.number - checkpoint > 1000) {
          for(uint i = 0; i < assessors.length; i++) {
              if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
                  stake[assessors[i]] = 0;
                  assessorState[assessors[i]] = State.Burned;
                  done++;
              }
          }
      }

      bytes32 hash = sha3(score,salt);
      if(commits[assessor] == hash) {
          if(msg.sender == assessor) {
              scores[msg.sender] = score;
              assessorState[assessor] = State.Done;
              done++; //done increases by 1 to help progress to the next assessment stage
          }
          else {
              Concept(concept).addBalance(msg.sender, stake[assessor]); // give the stake to the caller
              stake[assessor] = 0;
              assessorState[assessor] = State.Burned;
              done++; //done increases by 1 to help progress to the next assessment stage
          }
      }

      if(done == size) { //If all the assessors have revealed their scored or burned their stakes
          assessmentStage = State.Done;
          calculateResult(); //The final result is calculated
      }
  }

  function burnStakes() internal {
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) {
              uint r = 38; //Inverse burn rate
              stake[assessors[i]] = cost*2**(-(now-checkpoint)/r) - 1; //burns stake as a function of how much time has passed since half of the assessors commited
          }
          if(stake[assessors[i]] == 0) {
              assessorState[assessors[i]] = State.Burned;
              done++; //Increases done by 1 to help progress to the next assessment stage
          }
      }
  }

   function notifyAssessors(uint _state, uint _topic) private {
      for(uint i=0; i < assessors.length; i++) {
          if(uint(assessorState[assessors[i]]) == _state) {
              UserRegistry(userRegistry).notification(assessors[i], _topic);
          }
      }
  }

   function calculateResult() onlyInStage(State.Done) private {
    for(uint j = 0; j < size; j++) {
      if(assessorState[assessors[i]] == State.Done) {
        finalAssessors.push(assessors[i]); //Adds all the assessors that completed the assessment process to an array
      }
    }
    int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
    uint largestClusterIndex = 0; //store the index of the largest cluster
    int averageScore;
    for(uint i = 0; i < finalAssessors.length; i++) {
      score[i] = scores[finalAssessors[i]];
    }
    int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length));
    for(uint l = 0; l < score.length; l++) {
      for(uint m = 0; m < score.length; m++)
      {
        if(score[l] - score[m] <= meanAbsoluteDeviation) {
          clusters[l].push(score[m]);
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length) {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; o++) {
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length);
    finalScore = averageScore; //Sets the final score to the average score
    payout();
  }

   function payout() onlyInStage(State.Done) private { //TODO order functions according to styleguide
    for(uint i = 0; i < size; i++) {
      int score = scores[assessors[i]];
      int scoreDistance = ((score - finalScore)*100)/finalScore;
      if(scoreDistance < 0) {
        scoreDistance *= -1;
      }
      uint payoutValue = 1; //Initializes the payoutValue for the assessor
      if(inRewardCluster[score] == true) {
        uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
        payoutValue = (q*cost*((100 - uint(scoreDistance))/100)) + stake[assessors[i]]; //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
        Concept(concept).addBalance(assessors[i], payoutValue);
      }
      if(inRewardCluster[score] == false) {
        payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200); //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
        Concept(concept).addBalance(assessors[i], payoutValue);
      }
    Concept(concept).finishAssessment(finalScore, assessee, address(this)); //Sends assessment info to the concept so that it can update its records
    UserRegistry(userRegistry).notification(assessors[i], 6); //You Have Received Payment For Your Assessment
    }
  }
}


fix iterator variable names

fix iterator variable names

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee;
  address[] assessors;
  address[] finalAssessors;
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters;
  State public assessmentStage;
  enum State {
    None,
    Called,
    Confirmed,
    Committed,
    Done,
    Burned
  }

  uint public assessorPoolLength;
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public checkpoint; //marks creation of assessment / start of commit phase / start of reveal phase (time in seconds)
  uint burnRate;
  uint public size;
  uint cost;
  mapping(address => string[]) public data;
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint public done; //counter how many assessors have committed/revealed their score
  mapping(address => int8) scores;
  mapping(int => bool) inRewardCluster;
  int public finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    checkpoint = now;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
    done = 0;
  }

  function cancelAssessment() onlyConceptAssessment() {
    Concept(concept).addBalance(assessee, cost*assessors.length);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called;
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
      // cancel if the assessment is older than 12 hours
      if (now - checkpoint > 12 hours) {
          cancelAssessment();
          return;
      }
      if (assessorState[msg.sender] == State.Called &&
          assessors.length < size &&
          Concept(concept).subtractBalance(msg.sender, cost)
          ) {
          assessors.push(msg.sender);
          assessorState[msg.sender] = State.Confirmed;
          stake[msg.sender] = cost;
          UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
      }
      if (assessors.length == size) {
          notifyAssessors(uint(State.Confirmed), 4);
          checkpoint = now; // reset checkpoint to mark start of actual assessment phase
          UserRegistry(userRegistry).notification(assessee, 4);
          assessmentStage = State.Confirmed;
      }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data);
    DataSet(msg.sender, data[msg.sender].length - 1);
  }
  event fb(uint x);
  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 _hash) onlyInStage(State.Confirmed){
    if(done > size/2) {
        burnStakes();
    }

    if(assessorState[msg.sender] == State.Confirmed)
    {
        commits[msg.sender] = _hash;
        assessorState[msg.sender] = State.Committed;
        done++; //Increases done by 1 to help progress to the next assessment stage.

        // if the boundary assessor
        if( done*10 >= 10*size/2 &&
            (done - 1)*10 < 10*size/2) {
            burnRate = cost / (now - checkpoint);
            checkpoint = now + (now - checkpoint);
        }
    }
    if(done == size) {
        notifyAssessors(uint(State.Committed), 5);
        checkpoint = now; //reset checkpoint to mark start of reveal phase
        done = 0; //Resets the done counter
        assessmentStage = State.Committed;
    }

  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, string salt, address assessor) onlyInStage(State.Committed) {
      if(now - checkpoint > 12 hours) {
          for(uint i = 0; i < assessors.length; i++) {
              if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
                  stake[assessors[i]] = 0;
                  assessorState[assessors[i]] = State.Burned;
                  size--;
              }
          }
          return;
      }

      bytes32 hash = sha3(score,salt);
      if (assessorState[assessor] == State.Committed) {
              if(commits[assessor] == hash){
          // log in score and increase done
          if(msg.sender == assessor) {
              scores[msg.sender] = score;
              assessorState[assessor] = State.Done;
              done++;
          }
          else {
            // give the stake to the caller and burn assessor
            Concept(concept).addBalance(msg.sender, stake[assessor]);
            stake[assessor] = 0;
            assessorState[assessor] = State.Burned;
            size--;
          }
      }
      }
      //If all the assessors have revealed their scored or burned their stakes
      if(done == size) {
          assessmentStage = State.Done;
          calculateResult(); //The final result is calculated
      }
  }
  event fb(uint x);
  event b(string s);
  //@purpose: burns stakes as a function of how much time has passed since half of the assessors commited
  function burnStakes() internal {
      if(now-checkpoint > now) { //-> underflows if now before the end of the grace period
          return;
      }
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) {
              stake[assessors[i]] = cost - (burnRate * (now - checkpoint));
              if (stake[assessors[i]] == 0 ||
                  stake[assessors[i]] > cost){
                    assessorState[assessors[i]] = State.Burned;
                    stake[assessors[i]] = 0;
                    size--; //decrease size to help progress to the next assessment stage
            }
              }
          }
      }

   function notifyAssessors(uint _state, uint _topic) private {
      for(uint i=0; i < assessors.length; i++) {
          if(uint(assessorState[assessors[i]]) == _state) {
              UserRegistry(userRegistry).notification(assessors[i], _topic);
          }
      }
  }

   function calculateResult() onlyInStage(State.Done) private {
       for(uint j = 0; j < assessors.length; j++) {
           if(assessorState[assessors[j]] == State.Done) {
               finalAssessors.push(assessors[j]); //Adds all the assessors that completed the assessment process to an array}
           }
       }
       int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
       uint largestClusterIndex = 0; //store the index of the largest cluster
       int averageScore;
       for(uint i = 0; i < finalAssessors.length; i++) {
           score[i] = scores[finalAssessors[i]];
       }
       int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length));
       for(uint l = 0; l < score.length; l++) {
           for(uint m = 0; m < score.length; m++) {
               if(score[l] - score[m] <= meanAbsoluteDeviation) {
                   clusters[l].push(score[m]);
               }
           }
           if(clusters[l].length > clusters[largestClusterIndex].length) {
               largestClusterIndex = l;
           }
       }
       for (uint o = 0; o < clusters[largestClusterIndex].length; o++) {
           averageScore += clusters[largestClusterIndex][o];
           inRewardCluster[clusters[largestClusterIndex][o]] = true;
       }
       averageScore /= int(clusters[largestClusterIndex].length);
       finalScore = averageScore; //Sets the final score to the average score
       payout();
   }

   function payout() onlyInStage(State.Done) private { //TODO order functions according to styleguide
       for(uint i = 0; i < assessors.length; i++) {
           if (assessorState[assessors[i]] == State.Done){
               int score = scores[assessors[i]];
               int scoreDistance = ((score - finalScore)*100)/finalScore;
               if(scoreDistance < 0) {
                   scoreDistance *= -1;
               }
               uint payoutValue = 1; //Initializes the payoutValue for the assessor
               if(inRewardCluster[score] == true) {
                   uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
                   //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
                   payoutValue = (q*cost*((100 - uint(scoreDistance))/100)) + stake[assessors[i]];
                   Concept(concept).addBalance(assessors[i], payoutValue);
               }
               if(inRewardCluster[score] == false) {
                   //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
                   payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200);
                   Concept(concept).addBalance(assessors[i], payoutValue);
               }
               //Sends assessment info to the concept so that it can update its records
               Concept(concept).finishAssessment(finalScore, assessee, address(this));
               UserRegistry(userRegistry).notification(assessors[i], 6); //You  got paid!
           }
       }
   }
}

pragma solidity ^0.4.0;

import "./Math.sol";
import "./Concept.sol";
import "./ConceptRegistry.sol";
import "./UserRegistry.sol";

contract Assessment {
  address assessee;
  address[] assessors;
  address[] finalAssessors;
  mapping (address => State) assessorState;
  mapping(uint => int[]) clusters;
  State public assessmentStage;
  enum State {
    None,
    Called,
    Confirmed,
    Committed,
    Done,
    Burned
  }

  uint public assessorPoolLength;
  address concept;
  address userRegistry;
  address conceptRegistry;
  uint public checkpoint; //marks creation of assessment / start of commit phase / start of reveal phase (time in seconds)
  uint burnRate;
  uint public size;
  uint cost;
  mapping(address => string[]) public data;
  mapping(address => bytes32) commits;
  mapping(address => uint) stake;
  uint public done; //counter how many assessors have committed/revealed their score
  mapping(address => int8) scores;
  mapping(int => bool) inRewardCluster;
  int public finalScore;
  event DataSet(address _dataSetter, uint _index);

  modifier onlyAssessorAssessee() {
    if(msg.sender != assessee && uint(assessorState[msg.sender]) == 0) {
      throw;
    }
    _;
  }

  modifier onlyConceptAssessment() {
    if(msg.sender != address(this) && msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyConcept() {
    if(msg.sender != concept) {
      throw;
    }
    _;
  }

  modifier onlyInStage(State _stage) {
      if(assessmentStage != _stage){
          throw;
      }
      _;
  }

  function Assessment(address _assessee, address _userRegistry, address _conceptRegistry, uint _size, uint _cost) {
    assessee = _assessee;
    concept = msg.sender;
    userRegistry = _userRegistry;
    conceptRegistry = _conceptRegistry;
    checkpoint = now;
    size = _size;
    cost = _cost;
    UserRegistry(userRegistry).notification(assessee, 0); // assesse has started an assessment
    assessorPoolLength = 0;
    done = 0;
  }

  function cancelAssessment() onlyConceptAssessment() {
    Concept(concept).addBalance(assessee, cost*assessors.length);
    UserRegistry(userRegistry).notification(assessee, 3); //Assessment Cancled and you have been refunded
    for(uint i = 0; i < assessors.length; i++) {
      Concept(concept).addBalance(assessors[i], cost);
      UserRegistry(userRegistry).notification(assessors[i], 3); //Assessment Cancled and you have been refunded
    }
    suicide(conceptRegistry);
  }

  //@purpose: adds a user to the pool eligible to accept an assessment
  function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
    if(assessorState[assessor] == State.None) {
      UserRegistry(userRegistry).notification(assessor, 1); //Called As A Potential Assessor
      assessorState[assessor] = State.Called;
      assessorPoolLength++;
      return true;
    }
    else {
      return false;
    }
  }

  /*
  @purpose: To recursively set the pool to draw assessors from in the assessment
  stops when the pool of potential assessors is 20 times the size of the assessment2
  @param: uint seed = the seed number for random number generation
  @param: address _concept = the concept being called from
  @param: uint num = the number of assessors to be called
  */
  function setAssessorPool(uint seed, address _concept, uint num) onlyConceptAssessment() {
    uint numCalled = 0;
    for (uint k=0; k < num; k++) {
      if (assessorPoolLength == size*20) { return; } //
      address randomUser = Concept(_concept).getRandomMember(seed + k);
      if (addAssessorToPool(randomUser)) {
        numCalled++;
      }
    }
    uint remaining = num - numCalled;
    if (remaining > 0) {
      for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
        setAssessorPool(seed + assessorPoolLength, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
      }
    }
    assessmentStage = State.Called;
  }

  /*
    @purpose: To set the pool of assessors when there are so few users in the system that
    assembling them at random is not meaningful. This will use all members of mew instead
  */
  function setAssessorPoolFromMew() onlyConcept() {
    Concept mew = Concept(ConceptRegistry(conceptRegistry).mewAddress()); //TODO: can this be more elegant and/or in one line?
      for (uint i=0; i<mew.getMemberLength(); i++) {
        addAssessorToPool(mew.members(i));
      }
      size = mew.getMemberLength();
      assessmentStage = State.Called;
  }

  //@purpose: called by an assessor to confirm and stake
  function confirmAssessor() onlyInStage(State.Called){
      // cancel if the assessment is older than 12 hours
      if (now - checkpoint > 12 hours) {
          cancelAssessment();
          return;
      }
      if (assessorState[msg.sender] == State.Called &&
          assessors.length < size &&
          Concept(concept).subtractBalance(msg.sender, cost)
          ) {
          assessors.push(msg.sender);
          assessorState[msg.sender] = State.Confirmed;
          stake[msg.sender] = cost;
          UserRegistry(userRegistry).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
      }
      if (assessors.length == size) {
          notifyAssessors(uint(State.Confirmed), 4);
          checkpoint = now; // reset checkpoint to mark start of actual assessment phase
          UserRegistry(userRegistry).notification(assessee, 4);
          assessmentStage = State.Confirmed;
      }
  }

  function setData(string _data) onlyAssessorAssessee() {
    data[msg.sender].push(_data);
    DataSet(msg.sender, data[msg.sender].length - 1);
  }
  event fb(uint x);
  //@purpose: called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
  function commit(bytes32 _hash) onlyInStage(State.Confirmed){
    if(done > size/2) {
        burnStakes();
    }

    if(assessorState[msg.sender] == State.Confirmed)
    {
        commits[msg.sender] = _hash;
        assessorState[msg.sender] = State.Committed;
        done++; //Increases done by 1 to help progress to the next assessment stage.

        // if the boundary assessor
        if( done*10 >= 10*size/2 &&
            (done - 1)*10 < 10*size/2) {
            burnRate = cost / (now - checkpoint);
            checkpoint = now + (now - checkpoint);
        }
    }
    if(done == size) {
        notifyAssessors(uint(State.Committed), 5);
        checkpoint = now; //reset checkpoint to mark start of reveal phase
        done = 0; //Resets the done counter
        assessmentStage = State.Committed;
    }

  }

  //@purpose: called by assessors to reveal their own commits or others
  function reveal(int8 score, string salt, address assessor) onlyInStage(State.Committed) {
      if(now - checkpoint > 12 hours) {
          for(uint i = 0; i < assessors.length; i++) {
              if(assessorState[assessors[i]] == State.Committed) { //If the assessor has not revealed their score
                  stake[assessors[i]] = 0;
                  assessorState[assessors[i]] = State.Burned;
                  size--;
              }
          }
          return;
      }

      bytes32 hash = sha3(score,salt);
      if (assessorState[assessor] == State.Committed) {
              if(commits[assessor] == hash){
          // log in score and increase done
          if(msg.sender == assessor) {
              scores[msg.sender] = score;
              assessorState[assessor] = State.Done;
              done++;
          }
          else {
            // give the stake to the caller and burn assessor
            Concept(concept).addBalance(msg.sender, stake[assessor]);
            stake[assessor] = 0;
            assessorState[assessor] = State.Burned;
            size--;
          }
      }
      }
      //If all the assessors have revealed their scored or burned their stakes
      if(done == size) {
          assessmentStage = State.Done;
          calculateResult(); //The final result is calculated
      }
  }
  event fb(uint x);
  event b(string s);
  //@purpose: burns stakes as a function of how much time has passed since half of the assessors commited
  function burnStakes() internal {
      if(now-checkpoint > now) { //-> underflows if now before the end of the grace period
          return;
      }
      for(uint i = 0; i < assessors.length; i++) {
          if(assessorState[assessors[i]] == State.Confirmed) {
              stake[assessors[i]] = cost - (burnRate * (now - checkpoint));
              if (stake[assessors[i]] == 0 ||
                  stake[assessors[i]] > cost){
                    assessorState[assessors[i]] = State.Burned;
                    stake[assessors[i]] = 0;
                    size--; //decrease size to help progress to the next assessment stage
            }
              }
          }
      }

   function notifyAssessors(uint _state, uint _topic) private {
      for(uint i=0; i < assessors.length; i++) {
          if(uint(assessorState[assessors[i]]) == _state) {
              UserRegistry(userRegistry).notification(assessors[i], _topic);
          }
      }
  }

   function calculateResult() onlyInStage(State.Done) private {
       for(uint j = 0; j < assessors.length; j++) {
           if(assessorState[assessors[i]] == State.Done) {
               finalAssessors.push(assessors[i]); //Adds all the assessors that completed the assessment process to an array}
           }
       }
       int[] memory score = new int[] (finalAssessors.length); //Initializes an array to store scores that is the same length as the number of finalAssessors
       uint largestClusterIndex = 0; //store the index of the largest cluster
       int averageScore;
       for(uint i = 0; i < finalAssessors.length; i++) {
           score[i] = scores[finalAssessors[i]];
       }
       /* int meanAbsoluteDeviation = Math.calculateMAD(score,int(finalAssessors.length)); */
       /* for(uint l = 0; l < score.length; l++) { */
       /*     for(uint m = 0; m < score.length; m++) { */
       /*         if(score[l] - score[m] <= meanAbsoluteDeviation) { */
       /*             clusters[l].push(score[m]); */
       /*         } */
       /*     } */
       /*     if(clusters[l].length > clusters[largestClusterIndex].length) { */
       /*         largestClusterIndex = l; */
       /*     } */
       /* } */
       /* for (uint o = 0; o < clusters[largestClusterIndex].length; o++) { */
       /*     averageScore += clusters[largestClusterIndex][o]; */
       /*     inRewardCluster[clusters[largestClusterIndex][o]] = true; */
       /* } */
       /* averageScore /= int(clusters[largestClusterIndex].length); */
       /* finalScore = averageScore; //Sets the final score to the average score */
       /* payout(); */
   }

   function payout() onlyInStage(State.Done) private { //TODO order functions according to styleguide
       for(uint i = 0; i < assessors.length; i++) {
           if (assessorState[assessors[i]] == State.Done){
               int score = scores[assessors[i]];
               int scoreDistance = ((score - finalScore)*100)/finalScore;
               if(scoreDistance < 0) {
                   scoreDistance *= -1;
               }
               uint payoutValue = 1; //Initializes the payoutValue for the assessor
               if(inRewardCluster[score] == true) {
                   uint q = 1; //Inflation rate factor, WE NEED TO FIGURE THIS OUT AT SOME POINT
                   //The assessor's payout will be some constant times a propotion of their original stake determined by how close to the final score they were
                   payoutValue = (q*cost*((100 - uint(scoreDistance))/100)) + stake[assessors[i]];
                   Concept(concept).addBalance(assessors[i], payoutValue);
               }
               if(inRewardCluster[score] == false) {
                   //The assessor's payout will be a propotion of their remaining stake determined by their distance from the final score
                   payoutValue = stake[assessors[i]]*((200 - uint(scoreDistance))/200);
                   Concept(concept).addBalance(assessors[i], payoutValue);
               }
               //Sends assessment info to the concept so that it can update its records
               Concept(concept).finishAssessment(finalScore, assessee, address(this));
               UserRegistry(userRegistry).notification(assessors[i], 6); //You  got paid!
           }
       }
   }
}