register returns true

register returns true

pragma solidity ^0.4.11;

import "./owned.sol";
import "./tokenRecipient.sol";

contract UnConf is owned, tokenRecipient {

  /* Contract Variables and events */
  string name;
  Topic[] public topics;
  uint public numProposals;
  mapping (address => uint) public memberId;
  Member[] public members;

  event TopicAdded(uint topicID, address proposer, string description);
  event Voted(uint topicID, address voter);
  event MembershipChanged(address member, bool isMember);

  struct Topic {
    address proposer;
    uint amount;
    string description;
    uint numberOfVotes;
    Vote[] votes;
    mapping (address => bool) voted;
  }

  struct Member {
    address member;
    string name;
    uint memberSince;
  }

  struct Vote {
    bool inSupport;
    address voter;
  }

  /* modifier that allows only shareholders to vote and create new proposals */
  modifier onlyMembers {
    if (memberId[msg.sender] == 0) throw;
    _;
  }

  /* First time setup */
  function UnConf(string _name) payable {
    name = _name;
    // It’s necessary to add an empty first member
    addMember(0, '');
    // and let's add the founder, to save a step later
    addMember(owner, 'founder');
  }

  /*make member*/
  function addMember(address targetMember, string memberName) onlyOwner returns (bool success){
    if (memberId[targetMember] != 0) return;
    uint id;
    memberId[targetMember] = members.length;
    id = members.length++;
    members[id] = Member({
      member: targetMember,
      memberSince: now,
      name: memberName
    });
    MembershipChanged(targetMember, true);
    return true;
  }

  function removeMember(address targetMember) onlyOwner {
    if (memberId[targetMember] == 0) throw;

    for (uint i = memberId[targetMember]; i < members.length - 1; i++) {
      members[i] = members[i + 1];
    }

    delete members[members.length - 1];
    members.length--;
  }

  /* Function to create a new proposal */
  function newProposal(string topicDescription)
    onlyMembers
    returns (uint topicID)
  {
    topicID = topics.length++;
    Topic p = topics[topicID];
    p.proposer = msg.sender;
    p.description = topicDescription;
    p.numberOfVotes = 0;
    TopicAdded(topicID, msg.sender, topicDescription);
    numProposals = topicID + 1;

    return topicID;
  }

  function vote(uint topicID) onlyMembers returns (uint voteID) {
    Topic p = topics[topicID];               // Get the proposal
    if (p.voted[msg.sender] == true) throw;  // If has already voted, cancel
    p.voted[msg.sender] = true;              // Set this voter as having voted
    p.numberOfVotes++;                       // Increase score
    // Create a log of this event
    Voted(topicID, msg.sender);
    return p.numberOfVotes;
  }
}

pragma solidity ^0.4.11;

import "./owned.sol";
import "./tokenRecipient.sol";

contract UnConf is owned, tokenRecipient {

  /* Contract Variables and events */
  string name;
  Topic[] public topics;
  uint public numProposals;
  mapping (address => uint) public memberId;
  Member[] public members;

  event TopicAdded(uint topicID, address proposer, string description);
  event Voted(uint topicID, address voter);
  event MembershipChanged(address member, bool isMember);

  struct Topic {
    address proposer;
    uint amount;
    string description;
    uint numberOfVotes;
    Vote[] votes;
    mapping (address => bool) voted;
  }

  struct Member {
    address member;
    string name;
    uint memberSince;
  }

  struct Vote {
    bool inSupport;
    address voter;
  }

  /* modifier that allows only shareholders to vote and create new proposals */
  modifier onlyMembers {
    if (memberId[msg.sender] == 0) throw;
    _;
  }

  /* First time setup */
  function UnConf(string _name) payable {
    name = _name;
    // It’s necessary to add an empty first member
    addMember(0, '');
    // and let's add the founder, to save a step later
    addMember(owner, 'founder');
  }

  /*make member*/
  function addMember(address targetMember, string memberName) onlyOwner {
    if (memberId[targetMember] != 0) return;
    uint id;
    memberId[targetMember] = members.length;
    id = members.length++;
    members[id] = Member({
      member: targetMember,
      memberSince: now,
      name: memberName
    });
    MembershipChanged(targetMember, true);
  }

  function removeMember(address targetMember) onlyOwner {
    if (memberId[targetMember] == 0) throw;

    for (uint i = memberId[targetMember]; i < members.length - 1; i++) {
      members[i] = members[i + 1];
    }

    delete members[members.length - 1];
    members.length--;
  }

  /* Function to create a new proposal */
  function newProposal(string topicDescription)
    onlyMembers
    returns (uint topicID)
  {
    topicID = topics.length++;
    Topic p = topics[topicID];
    p.proposer = msg.sender;
    p.description = topicDescription;
    p.numberOfVotes = 0;
    TopicAdded(topicID, msg.sender, topicDescription);
    numProposals = topicID + 1;

    return topicID;
  }

  function vote(uint topicID) onlyMembers returns (uint voteID) {
    Topic p = topics[topicID];               // Get the proposal
    if (p.voted[msg.sender] == true) throw;  // If has already voted, cancel
    p.voted[msg.sender] = true;              // Set this voter as having voted
    p.numberOfVotes++;                       // Increase score
    // Create a log of this event
    Voted(topicID, msg.sender);
    return p.numberOfVotes;
  }
}


Delete TokenTraderFactory.sol

Delete TokenTraderFactory.sol

pragma solidity ^0.4.4;

// ------------------------------------------------------------------------
// TokenTraderFactory
//
// Decentralised trustless ERC20-compliant token to ETH exchange contract
// on the Ethereum blockchain.
//
// Note that this TokenTrader cannot be used with the Golem Network Token
// directly as the token does not implement the ERC20
// transferFrom(...), approve(...) and allowance(...) methods
//
// Enjoy. (c) JonnyLatte & BokkyPooBah 2016. The MIT licence.
// ------------------------------------------------------------------------

// https://github.com/ethereum/EIPs/issues/20
contract ERC20 {
    function totalSupply() constant returns (uint totalSupply);
    function balanceOf(address _owner) constant returns (uint balance);
    function transfer(address _to, uint _value) returns (bool success);
    function transferFrom(address _from, address _to, uint _value) returns (bool success);
    function approve(address _spender, uint _value) returns (bool success);
    function allowance(address _owner, address _spender) constant returns (uint remaining);
    event Transfer(address indexed _from, address indexed _to, uint _value);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}

contract Owned {
    address public owner;
    event OwnershipTransferred(address indexed _from, address indexed _to);

    function Owned() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _;
    }

    function transferOwnership(address newOwner) onlyOwner {
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

// contract can buy or sell tokens for ETH
// prices are in amount of wei per batch of token units

contract TokenTrader is Owned {

    address public asset;       // address of token
    uint256 public buyPrice;    // contract buys lots of token at this price
    uint256 public sellPrice;   // contract sells lots at this price
    uint256 public units;       // lot size (token-wei)

    bool public buysTokens;     // is contract buying
    bool public sellsTokens;    // is contract selling

    event ActivatedEvent(bool buys, bool sells);
    event MakerDepositedEther(uint256 amount);
    event MakerWithdrewAsset(uint256 tokens);
    event MakerWithdrewERC20Token(address tokenAddress, uint256 tokens);
    event MakerWithdrewEther(uint256 ethers);
    event TakerBoughtAsset(address indexed buyer, uint256 ethersSent,
        uint256 ethersReturned, uint256 tokensBought);
    event TakerSoldAsset(address indexed seller, uint256 tokensToSell,
        uint256 tokensSold, uint256 ethers);

    // Constructor - only to be called by the TokenTraderFactory contract
    function TokenTrader (
        address _asset,
        uint256 _buyPrice,
        uint256 _sellPrice,
        uint256 _units,
        bool    _buysTokens,
        bool    _sellsTokens
    ) internal {
        asset       = _asset;
        buyPrice    = _buyPrice;
        sellPrice   = _sellPrice;
        units       = _units;
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can activate or deactivate this contract's buying and
    // selling status
    //
    // The ActivatedEvent() event is logged with the following
    // parameter:
    //   buysTokens   this contract can buy asset tokens
    //   sellsTokens  this contract can sell asset tokens
    //
    function activate (
        bool _buysTokens,
        bool _sellsTokens
    ) onlyOwner {
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can deposit ethers to this contract so this contract
    // can buy asset tokens.
    //
    // Maker deposits asset tokens to this contract by calling the
    // asset's transfer() method with the following parameters
    //   _to     is the address of THIS contract
    //   _value  is the number of asset tokens to be transferred
    //
    // Taker MUST NOT send tokens directly to this contract. Takers
    // MUST use the takerSellAsset() method to sell asset tokens
    // to this contract
    //
    // The MakerDepositedEther() event is logged with the following
    // parameter:
    //   ethers  is the number of ethers deposited by the maker
    //
    // This method was called deposit() in the old version
    //
    function makerDepositEther() payable onlyOwner {
        MakerDepositedEther(msg.value);
    }

    // Maker can withdraw asset tokens from this contract, with the
    // following parameter:
    //   tokens  is the number of asset tokens to be withdrawn
    //
    // The MakerWithdrewAsset() event is logged with the following
    // parameter:
    //   tokens  is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawAsset() in the old version
    //
    function makerWithdrawAsset(uint256 tokens) onlyOwner returns (bool ok) {
        MakerWithdrewAsset(tokens);
        return ERC20(asset).transfer(owner, tokens);
    }

    // Maker can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The MakerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawToken() in the old version
    //
    function makerWithdrawERC20Token(
        address tokenAddress,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        MakerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

    // Maker can withdraw ethers from this contract
    //
    // The MakerWithdrewEther() event is logged with the following parameter
    //   ethers  is the number of ethers withdrawn by the maker
    //
    // This method was called withdraw() in the old version
    //
    function makerWithdrawEther(uint256 ethers) onlyOwner returns (bool ok) {
        if (this.balance >= ethers) {
            MakerWithdrewEther(ethers);
            return owner.send(ethers);
        }
    }

    // Taker buys asset tokens by sending ethers
    //
    // The TakerBoughtAsset() event is logged with the following parameters
    //   buyer           is the buyer's address
    //   ethersSent      is the number of ethers sent by the buyer
    //   ethersReturned  is the number of ethers sent back to the buyer as
    //                   change
    //   tokensBought    is the number of asset tokens sent to the buyer
    //
    // This method was called buy() in the old version
    //
    function takerBuyAsset() payable {
        if (sellsTokens || msg.sender == owner) {
            uint order    = msg.value / sellPrice;
            uint can_sell = ERC20(asset).balanceOf(address(this)) / units;
            uint256 change = 0;
            if (order > can_sell) {
                change = msg.value - (can_sell * sellPrice);
                order = can_sell;
                if (!msg.sender.send(change)) throw;
            }
            if (order > 0) {
                if(!ERC20(asset).transfer(msg.sender, order * units)) throw;
            }
            TakerBoughtAsset(msg.sender, msg.value, change, order * units);
        }
        // Return user funds if the contract is not selling
        else if (!msg.sender.send(msg.value)) throw;
    }

    // Taker sells asset tokens for ethers by:
    // 1. Calling the asset's approve() method with the following parameters
    //    _spender  is the address of this contract
    //    _value    is the number of tokens to be sold
    // 2. Calling this takerSellAsset() method with the following parameter
    //    tokens    is the number of asset tokens to be sold
    //
    // The TakerSoldAsset() event is logged with the following parameters
    //   seller        is the seller's address
    //   tokensToSell  is the number of asset tokens offered by the seller
    //   tokensSold    is the number of asset tokens sold
    //   ethers        is the number of ethers sent to the seller
    //
    // This method was called sell() in the old version
    //
    function takerSellAsset(uint256 tokens) {
        if (buysTokens || msg.sender == owner) {
            // Maximum number of token the contract can buy
            uint256 can_buy = this.balance / buyPrice;
            // Token lots available
            uint256 order = tokens / units;
            // Adjust order for funds available
            if (order > can_buy) order = can_buy;
            if (order > 0) {
                // Extract user tokens
                if(!ERC20(asset).transferFrom(msg.sender, address(this), order * units)) throw;
                // Pay user
                if(!msg.sender.send(order * buyPrice)) throw;
            }
            TakerSoldAsset(msg.sender, tokens, order * units, order * buyPrice);
        }
    }

    // Taker buys tokens by sending ethers
    function () payable {
        takerBuyAsset();
    }
}

// This contract deploys TokenTrader contracts and logs the event
contract TokenTraderFactory is Owned {

    event TradeListing(address ownerAddress, address tokenTraderAddress, address asset,
        uint256 buyPrice, uint256 sellPrice, uint256 units,
        bool buysTokens, bool sellsTokens);
    event OwnerWithdrewERC20Token(address tokenAddress, uint256 tokens);

    mapping(address => bool) _verify;

    // Anyone can call this method to verify the settings of a
    // TokenTrader contract. The parameters are:
    //   tradeContract  is the address of a TokenTrader contract
    //
    // Return values:
    //   valid        did this TokenTraderFactory create the TokenTrader contract?
    //   owner        is the owner of the TokenTrader contract
    //   asset        is the ERC20 asset address
    //   buyPrice     is the buy price in ethers per `units` of asset tokens
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   buysTokens   is the TokenTrader contract buying tokens?
    //   sellsTokens  is the TokenTrader contract selling tokens?
    //
    function verify(address tradeContract) constant returns (
        bool    valid,
        address owner,
        address asset,
        uint256 buyPrice,
        uint256 sellPrice,
        uint256 units,
        bool    buysTokens,
        bool    sellsTokens
    ) {
        valid = _verify[tradeContract];
        if (valid) {
            TokenTrader t = TokenTrader(tradeContract);
            owner         = t.owner();
            asset         = t.asset();
            buyPrice      = t.buyPrice();
            sellPrice     = t.sellPrice();
            units         = t.units();
            buysTokens    = t.buysTokens();
            sellsTokens   = t.sellsTokens();
        }
    }

    // Maker can call this method to create a new TokenTrader contract
    // with the maker being the owner of this new contract
    //
    // Parameters:
    //   asset        is the ERC20 asset address
    //   buyPrice     is the buy price in ethers per `units` of asset tokens
    //   sellPrice    is the sell price in ethers per `units` of asset tokens
    //   units        is the number of units of asset tokens
    //   buysTokens   is the TokenTrader contract buying tokens?
    //   sellsTokens  is the TokenTrader contract selling tokens?
    //
    // For example, listing a TokenTrader contract on the REP Augur token where
    // the contract will buy REP tokens at a rate of 39000/100000 = 0.39 ETH
    // per REP token and sell REP tokens at a rate of 41000/100000 = 0.41 ETH
    // per REP token:
    //   asset        0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5
    //   buyPrice     39000
    //   sellPrice    41000
    //   units        100000
    //   buysTokens   true
    //   sellsTokens  true
    //
    // The TradeListing() event is logged with the following parameters
    //   ownerAddress        is the Maker's address
    //   tokenTraderAddress  is the address of the newly created TokenTrader contract
    //   asset               is the ERC20 asset address
    //   buyPrice            is the buy price in ethers per `units` of asset tokens
    //   sellPrice           is the sell price in ethers per `units` of asset tokens
    //   unit                is the number of units of asset tokens
    //   buysTokens          is the TokenTrader contract buying tokens?
    //   sellsTokens         is the TokenTrader contract selling tokens?
    //
    function createTradeContract(
        address asset,
        uint256 buyPrice,
        uint256 sellPrice,
        uint256 units,
        bool    buysTokens,
        bool    sellsTokens
    ) returns (address trader) {
        // Cannot set negative price
        if (buyPrice < 0 || sellPrice < 0) throw;
        // Must make profit on spread
        if (buyPrice > sellPrice) throw;
        // Cannot buy or sell zero or negative units
        if (units <= 0) throw;
        trader = new TokenTrader(
            asset,
            buyPrice,
            sellPrice,
            units,
            buysTokens,
            sellsTokens);
        // Record that this factory created the trader
        _verify[trader] = true;
        // Set the owner to whoever called the function
        TokenTrader(trader).transferOwnership(msg.sender);
        TradeListing(msg.sender, trader, asset, buyPrice, sellPrice, units, buysTokens, sellsTokens);
    }

    // Factory owner can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The OwnerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    function ownerWithdrawERC20Token(address tokenAddress, uint256 tokens) onlyOwner returns (bool ok) {
        OwnerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

    // Prevents accidental sending of ether to the factory
    function () {
        throw;
    }
}


Updated Transfer event

Updated Transfer event

import "ConvertLib.sol";

// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!

contract MetaCoin {
	event Transfer(address indexed _from, address indexed _to, uint256 _value);

	mapping (address => uint) balances;

	function MetaCoin() {
		balances[tx.origin] = 10000;
	}

	function sendCoin(address receiver, uint amount) returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		Transfer(msg.sender, receiver, amount);
		return true;
	}
	function getBalanceInEth(address addr) returns(uint){
		return ConvertLib.convert(getBalance(addr),2);
	}
	function getBalance(address addr) returns(uint) {
		return balances[addr];
	}
}

import "ConvertLib.sol";

// This is just a simple example of a coin-like contract.
// It is not standards compatible and cannot be expected to talk to other
// coin/token contracts. If you want to create a standards-compliant
// token, see: https://github.com/ConsenSys/Tokens. Cheers!

contract MetaCoin {
	mapping (address => uint) balances;

	function MetaCoin() {
		balances[tx.origin] = 10000;
	}

	function sendCoin(address receiver, uint amount) returns(bool sufficient) {
		if (balances[msg.sender] < amount) return false;
		balances[msg.sender] -= amount;
		balances[receiver] += amount;
		return true;
	}
	function getBalanceInEth(address addr) returns(uint){
		return ConvertLib.convert(getBalance(addr),2);
	}
	function getBalance(address addr) returns(uint) {
		return balances[addr];
	}
	event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}


removed redundant returns

removed redundant returns

pragma solidity ^0.4.8;

import "./ERC20.sol";

/**
 * @title LimitedTransferToken
 * @dev LimitedTransferToken defines the generic interface and the implementation to limit token
 * transferability for different events. It is intended to be used as a base class for other token
 * contracts.
 * LimitedTransferToken has been designed to allow for different limiting factors,
 * this can be achieved by recursively calling super.transferableTokens() until the base class is
 * hit. For example:
 *     function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
 *       return min256(unlockedTokens, super.transferableTokens(holder, time));
 *     }
 * A working example is VestedToken.sol:
 * https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol
 */

contract LimitedTransferToken is ERC20 {

  /**
   * @dev Checks whether it can transfer or otherwise throws.
   */
  modifier canTransfer(address _sender, uint _value) {
   if (_value > transferableTokens(_sender, uint64(now))) throw;
   _;
  }

  /**
   * @dev Checks modifier and allows transfer if tokens are not locked.
   * @param _to The address that will recieve the tokens.
   * @param _value The amount of tokens to be transferred.
   */
  function transfer(address _to, uint _value) canTransfer(msg.sender, _value) {
    super.transfer(_to, _value);
  }

  /**
  * @dev Checks modifier and allows transfer if tokens are not locked.
  * @param _from The address that will send the tokens.
  * @param _to The address that will recieve the tokens.
  * @param _value The amount of tokens to be transferred.
  */
  function transferFrom(address _from, address _to, uint _value) canTransfer(_from, _value) {
    super.transferFrom(_from, _to, _value);
  }

  /**
   * @dev Default transferable tokens function returns all tokens for a holder (no limit).
   * @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the
   * specific logic for limiting token transferability for a holder over time.
   */
  function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
    return balanceOf(holder);
  }
}

pragma solidity ^0.4.8;

import "./ERC20.sol";

/**
 * @title LimitedTransferToken
 * @dev LimitedTransferToken defines the generic interface and the implementation to limit token
 * transferability for different events. It is intended to be used as a base class for other token
 * contracts.
 * LimitedTransferToken has been designed to allow for different limiting factors,
 * this can be achieved by recursively calling super.transferableTokens() until the base class is
 * hit. For example:
 *     function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
 *       return min256(unlockedTokens, super.transferableTokens(holder, time));
 *     }
 * A working example is VestedToken.sol:
 * https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol
 */

contract LimitedTransferToken is ERC20 {

  /**
   * @dev Checks whether it can transfer or otherwise throws.
   */
  modifier canTransfer(address _sender, uint _value) {
   if (_value > transferableTokens(_sender, uint64(now))) throw;
   _;
  }

  /**
   * @dev Checks modifier and allows transfer if tokens are not locked.
   * @param _to The address that will recieve the tokens.
   * @param _value The amount of tokens to be transferred.
   */
  function transfer(address _to, uint _value) canTransfer(msg.sender, _value) {
   return super.transfer(_to, _value);
  }

  /**
  * @dev Checks modifier and allows transfer if tokens are not locked.
  * @param _from The address that will send the tokens.
  * @param _to The address that will recieve the tokens.
  * @param _value The amount of tokens to be transferred.
  */
  function transferFrom(address _from, address _to, uint _value) canTransfer(_from, _value) {
   return super.transferFrom(_from, _to, _value);
  }

  /**
   * @dev Default transferable tokens function returns all tokens for a holder (no limit).
   * @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the
   * specific logic for limiting token transferability for a holder over time.
   */
  function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
    return balanceOf(holder);
  }
}


New WeiAccounts Factory

New WeiAccounts Factory

/// @title The core WeiFund crowdfunding interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFund {
    /// @notice creates a new crowdfunding campaign
    /// @dev This method starts a new crowdfunding campaign and calles the campaigns configuration contract if stated
    /// @param _name (campaign name) The campaign name
    /// @param _beneficiary (beneficiary) The address of the beneficiary for this campaign
    /// @param _fundingGoal (funding goal) The funding goal of the campaign. If this goal is not met by the timelimit, all ether will be refunded to the respective contributers
    /// @param _expiry (expiry) When the campaign will expire and contributions can no longer be made
    /// @param _config (configuration address) The configuration address
    /// @return _campaignID (campaign ID) The newly created campaign ID number
    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) returns (uint campaignID) {}

    /// @notice contributes ether to a WeiFund campaign
    /// @dev This method will contribute an amount of ether to the campaign at ID _cid. All contribution data will be stored so that the issuance of digital assets can be made out to the contributor address
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign
    /// @param _beneficiary (contribute As Address) This allows a user to contribute on behalf of another address, if left empty, the from sender address is used as the primary Funder address
    /// @return _contributionID (contributor ID) The newly created contributor ID
    function contribute(uint _campaignID, address _beneficiary) returns (uint _contributionID) {}

    /// @notice refunds your accounts contribution of a failed crowdfunding campaign
    /// @dev This method will refund the amount you contributed to a WeiFund campaign, if that campaign has failed to meet it's funding goal or has expired.
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign to be refunded
    function refund(uint _campaignID, uint _contribution) {}

    /// @notice this will payout a successful crowdfunding campaign to the beneficiary address
    /// @dev This method will payout a successful WeiFund crowdfunding campaign to the beneficiary address specified. Any person can trigger the payout by calling this method.
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign
    function payout(uint _campaignID) {}

    /// @notice user Campaign ID (the address of the user, the user campaign ID); get the campaign ID of one of the users crowdfunding campaigns.
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _operator (campaign creator) The address of the campaign operator
    /// @param _campaignIndex The user campaign ID
    /// @return _campaignID (campaign ID) The campaign ID
    function operatorCampaignID(address _operator, uint _campaignIndex) constant returns (uint campaignID) {}

    /// @notice total number of campaigns created by a specific user
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _operator (campaign creator) The user's address
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalCampaignsBy(address _operator) constant returns (uint numCampaigns) {}

    /// @notice total number of contributions made by a contributor account to a campaign
    /// @dev This method will retunr an unsigned integer of the total number of campaigns started by a single account
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (campaign contributor address) The user's address
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalContributionsBy(uint _campaignID, address _contributor) constant returns (uint) {}

    /// @notice the total number of campaigns on WeiFund
    /// @dev This method returns the total number of campaigns on WeiFund as an unsigned integer
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalCampaigns() constant returns (uint numCampaigns) {}

    /// @notice retrieve campaign contribution data at specified contributor ID
    /// @dev Retrieve contributor data (tuple) of a specific contributor
    /// @param _campaignID (campaign id) The address of the campaign operator.
    /// @param _contributionID (contributor id) The user campaign ID
    /// @return contributor, beneficiary, amountContributed, refunded, created
    function contributionAt(uint _campaignID, uint _contributionID) constant returns (address contributor,
                                                                                address beneficiary,
                                                                                uint amountContributed,
                                                                                bool refunded,
                                                                                uint created) {}
    /// @notice When the campaign was created
    /// @dev For retrieving the campaign created UNIX timestamp integer
    /// @param _campaignID (campaign id) The campaign id
    /// @return unix timestamp when the campaign was created
    function createdAt(uint _campaignID) public constant returns (uint) {}

    /// @notice Retreive a contributor ID of a campaign contributor
    /// @dev The contributor ID can be used to get contributor information such as how much they contributed to a campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (contributor address)
    /// @param _contributionIndex (contribution index)
    /// @return the contributor ID
    function contributionID(uint _campaignID, address _contributor, uint _contributionIndex) constant returns (uint) {}

    /// @notice Retreive the owner of a specific campaign
    /// @dev The campaign owner is the 20 byte address of the account that created the campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign owner's account
    function ownerOf(uint _campaignID) constant returns (address) {}

    /// @notice The beneficiary address of the campaign
    /// @dev The campaign beneficiary is the account that will receive the funds raised by the camapign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign beneficiary address
    function beneficiaryOf(uint _campaignID) constant returns (address) {}

    /// @notice The configuration contract address of the campaign
    /// @dev The configuration contract address allows campaigns to extend their functionality beyond the WeiFund contract
    /// @param _campaignID (campaign id) The campaign id
    /// @return The confirguation contract address
    function configOf(uint _campaignID) constant returns (address) {}

    /// @notice The amount raised by the campaign
    /// @dev The amount of ether raised by a specific campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign beneficiary address
    function amountRaisedBy(uint _campaignID) constant returns (uint) {}

    /// @notice The funding goal of a campaign
    /// @dev The amount of ether that needs to be raised in order for campaign funds to be released to the beneficiary, fund value is in wei
    /// @param _campaignID (campaign id) The campaign id
    /// @return The funding goal in wei
    function fundingGoalOf(uint _campaignID) constant returns (uint) {}

    /// @notice The campaign expiry
    /// @dev The unix timestamp at which the campaign funding goal must be reached in order for funds to be paid out
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign expiry
    function expiryOf(uint _campaignID) constant returns (uint) {}

    /// @notice The total number of campaign contributors
    /// @dev The total number of campaign contributors returned as an integer
    /// @param _campaignID (campaign id) The campaign id
    /// @return The total number of campaign contributors
    function totalContributors(uint _campaignID) constant returns (uint) {}

    /// @notice Check to see if account is a campaign contributor
    /// @dev Determine whether a specified account address is a campaign contributor
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (contributor address) The contributors account address
    /// @return is address contributor or not (boolean)
    function isContributor(uint _campaignID, address _contributor) constant returns (bool) {}

    /// @notice Check to see if account is a campaign owner
    /// @dev Determine whether a specified account address is the campaign owner
    /// @param _campaignID (campaign id) The campaign id
    /// @param _owner (owner) The owner address
    /// @return is address the campaign owner or not (boolean)
    function isOwner(uint _campaignID, address _owner) constant returns (bool) {}

    /// @notice Has the campaign failed to reach its goals
    /// @dev Determine whether a campaign has failed to reach its goals
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign failed or not (boolean)
    function hasFailed(uint _campaignID) constant returns (bool) {}

    /// @notice Has the campaign succeeded in reaching its funding goals
    /// @dev Determine whether a campaign has succeeded to reach its goals by the campaign expiry
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign succeeded or not (boolean)
    function isSuccess(uint _campaignID) constant returns (bool) {}

    /// @notice Has the campaign been paid out
    /// @dev Has the funds raised by the campaign been paid out (returns a boolean)
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign been paid out
    function isPaidOut(uint _campaignID) constant returns (bool) {}

    /// @notice The total amount of funds that have been refunded for a specified campaign
    /// @dev The total amount of ether that has been refunded for a specified campaign (funds are integers in the wei denomination)
    /// @param _campaignID (campaign id) The campaign id
    /// @return the total amount of funds refunded (an integer representing wei)
    function totalRefunded(uint _campaignID) constant returns (uint) {}

    /// @notice Has the campaign been completly refunded
    /// @dev Are all funds refunded from this campaign (returns a boolean)
    /// @param _campaignID (campaign id) The campaign id
    /// @return have the funds been refunded or not
    function isRefunded(uint _campaignID) constant returns (bool) {}

    event CampaignCreated(uint indexed _campaignID, address indexed _owner);
    event Contributed(uint indexed _campaignID, address indexed _contributor, uint _amountContributed);
    event Refunded(uint indexed _campaignID, address indexed _contributor, uint _amountRefunded);
    event PaidOut(uint indexed _campaignID, address indexed _beneficiary, uint _amountPaid);
}

/// @title A campaign account contribution forwarding contract
/// @author Nick Dodson <thenickdodson@gmail.com>
contract CampaignAccount {
    address public weifund;
    uint public campaignID;

    function CampaignAccount (address _weifund, uint _campaignID) {
        weifund = _weifund;
        campaignID = _campaignID;
    }

    function () {
        if(WeiFund(weifund).isSuccess(campaignID)
            || WeiFund(weifund).isPaidOut(campaignID)
            || WeiFund(weifund).hasFailed(campaignID)
            || msg.value <= 0)
            throw;

        WeiFund(weifund).contribute.value(msg.value)(campaignID, msg.sender);
    }
}

contract CampaignAccountRegistry {
    address public weifund;
    mapping(uint => address) public accounts;
    mapping(address => uint) public toCampaign;

    modifier validCampaign (uint _campaignID) {
        if(!WeiFund(weifund).isOwner(_campaignID, msg.sender)
            || accounts[_campaignID] != address(0)
            || WeiFund(weifund).isSuccess(_campaignID)
            || WeiFund(weifund).isPaidOut(_campaignID)
            || WeiFund(weifund).hasFailed(_campaignID))
            throw;
        else
            _
    }

    function register(uint _campaignID, address _contractAddress) validCampaign(_campaignID) {
        accounts[_campaignID] = _contractAddress;
        toCampaign[_contractAddress] = _campaignID;
    }

    function accountOf(uint _campaignID) constant returns (address) {
        return accounts[_campaignID];
    }

    function campaignOf(address _account) constant returns (uint) {
        return toCampaign[_account];
    }
}

/// @title Enables WeiFund campaigns to have their own contribution account
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiAccounts is CampaignAccountRegistry {
	uint public version = 1;

    event AccountCreated(uint _campaignID, address _account, address _owner);

    function CampaignAccountFactory (address _weifund) {
        weifund = _weifund;
    }

    function newCampaignAccount(uint _campaignID) validCampaign(_campaignID) returns (address contractAddress) {
        contractAddress = new CampaignAccount(weifund, _campaignID);
        register(_campaignID, contractAddress);
        AccountCreated(_campaignID, contractAddress, msg.sender);
    }
}

/// @title The core WeiFund crowdfunding interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFund {
    /// @notice creates a new crowdfunding campaign
    /// @dev This method starts a new crowdfunding campaign and calles the campaigns configuration contract if stated
    /// @param _name (campaign name) The campaign name
    /// @param _beneficiary (beneficiary) The address of the beneficiary for this campaign
    /// @param _fundingGoal (funding goal) The funding goal of the campaign. If this goal is not met by the timelimit, all ether will be refunded to the respective contributers
    /// @param _expiry (expiry) When the campaign will expire and contributions can no longer be made
    /// @param _config (configuration address) The configuration address
    /// @return _campaignID (campaign ID) The newly created campaign ID number
    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) returns (uint campaignID) {}

    /// @notice contributes ether to a WeiFund campaign
    /// @dev This method will contribute an amount of ether to the campaign at ID _cid. All contribution data will be stored so that the issuance of digital assets can be made out to the contributor address
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign
    /// @param _beneficiary (contribute As Address) This allows a user to contribute on behalf of another address, if left empty, the from sender address is used as the primary Funder address
    /// @return _contributionID (contributor ID) The newly created contributor ID
    function contribute(uint _campaignID, address _beneficiary) returns (uint _contributionID) {}

    /// @notice refunds your accounts contribution of a failed crowdfunding campaign
    /// @dev This method will refund the amount you contributed to a WeiFund campaign, if that campaign has failed to meet it's funding goal or has expired.
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign to be refunded
    function refund(uint _campaignID, uint _contribution) {}

    /// @notice this will payout a successful crowdfunding campaign to the beneficiary address
    /// @dev This method will payout a successful WeiFund crowdfunding campaign to the beneficiary address specified. Any person can trigger the payout by calling this method.
    /// @param _campaignID (campaign ID) The ID number of the crowdfunding campaign
    function payout(uint _campaignID) {}

    /// @notice user Campaign ID (the address of the user, the user campaign ID); get the campaign ID of one of the users crowdfunding campaigns.
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _operator (campaign creator) The address of the campaign operator
    /// @param _campaignIndex The user campaign ID
    /// @return _campaignID (campaign ID) The campaign ID
    function operatorCampaignID(address _operator, uint _campaignIndex) constant returns (uint campaignID) {}

    /// @notice total number of campaigns created by a specific user
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _operator (campaign creator) The user's address
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalCampaignsBy(address _operator) constant returns (uint numCampaigns) {}

    /// @notice total number of contributions made by a contributor account to a campaign
    /// @dev This method will retunr an unsigned integer of the total number of campaigns started by a single account
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (campaign contributor address) The user's address
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalContributionsBy(uint _campaignID, address _contributor) constant returns (uint) {}

    /// @notice the total number of campaigns on WeiFund
    /// @dev This method returns the total number of campaigns on WeiFund as an unsigned integer
    /// @return _numCampaigns (number of campaigns) The number of campaigns
    function totalCampaigns() constant returns (uint numCampaigns) {}

    /// @notice retrieve campaign contribution data at specified contributor ID
    /// @dev Retrieve contributor data (tuple) of a specific contributor
    /// @param _campaignID (campaign id) The address of the campaign operator.
    /// @param _contributionID (contributor id) The user campaign ID
    /// @return contributor, beneficiary, amountContributed, refunded, created
    function contributionAt(uint _campaignID, uint _contributionID) constant returns (address contributor,
                                                                                address beneficiary,
                                                                                uint amountContributed,
                                                                                bool refunded,
                                                                                uint created) {}
    /// @notice When the campaign was created
    /// @dev For retrieving the campaign created UNIX timestamp integer
    /// @param _campaignID (campaign id) The campaign id
    /// @return unix timestamp when the campaign was created
    function createdAt(uint _campaignID) public constant returns (uint) {}

    /// @notice Retreive a contributor ID of a campaign contributor
    /// @dev The contributor ID can be used to get contributor information such as how much they contributed to a campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (contributor address)
    /// @param _contributionIndex (contribution index)
    /// @return the contributor ID
    function contributionID(uint _campaignID, address _contributor, uint _contributionIndex) constant returns (uint) {}

    /// @notice Retreive the owner of a specific campaign
    /// @dev The campaign owner is the 20 byte address of the account that created the campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign owner's account
    function ownerOf(uint _campaignID) constant returns (address) {}

    /// @notice The beneficiary address of the campaign
    /// @dev The campaign beneficiary is the account that will receive the funds raised by the camapign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign beneficiary address
    function beneficiaryOf(uint _campaignID) constant returns (address) {}

    /// @notice The configuration contract address of the campaign
    /// @dev The configuration contract address allows campaigns to extend their functionality beyond the WeiFund contract
    /// @param _campaignID (campaign id) The campaign id
    /// @return The confirguation contract address
    function configOf(uint _campaignID) constant returns (address) {}

    /// @notice The amount raised by the campaign
    /// @dev The amount of ether raised by a specific campaign
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign beneficiary address
    function amountRaisedBy(uint _campaignID) constant returns (uint) {}

    /// @notice The funding goal of a campaign
    /// @dev The amount of ether that needs to be raised in order for campaign funds to be released to the beneficiary, fund value is in wei
    /// @param _campaignID (campaign id) The campaign id
    /// @return The funding goal in wei
    function fundingGoalOf(uint _campaignID) constant returns (uint) {}

    /// @notice The campaign expiry
    /// @dev The unix timestamp at which the campaign funding goal must be reached in order for funds to be paid out
    /// @param _campaignID (campaign id) The campaign id
    /// @return The campaign expiry
    function expiryOf(uint _campaignID) constant returns (uint) {}

    /// @notice The total number of campaign contributors
    /// @dev The total number of campaign contributors returned as an integer
    /// @param _campaignID (campaign id) The campaign id
    /// @return The total number of campaign contributors
    function totalContributors(uint _campaignID) constant returns (uint) {}

    /// @notice Check to see if account is a campaign contributor
    /// @dev Determine whether a specified account address is a campaign contributor
    /// @param _campaignID (campaign id) The campaign id
    /// @param _contributor (contributor address) The contributors account address
    /// @return is address contributor or not (boolean)
    function isContributor(uint _campaignID, address _contributor) constant returns (bool) {}

    /// @notice Check to see if account is a campaign owner
    /// @dev Determine whether a specified account address is the campaign owner
    /// @param _campaignID (campaign id) The campaign id
    /// @param _owner (owner) The owner address
    /// @return is address the campaign owner or not (boolean)
    function isOwner(uint _campaignID, address _owner) constant returns (bool) {}

    /// @notice Has the campaign failed to reach its goals
    /// @dev Determine whether a campaign has failed to reach its goals
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign failed or not (boolean)
    function hasFailed(uint _campaignID) constant returns (bool) {}

    /// @notice Has the campaign succeeded in reaching its funding goals
    /// @dev Determine whether a campaign has succeeded to reach its goals by the campaign expiry
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign succeeded or not (boolean)
    function isSuccess(uint _campaignID) constant returns (bool) {}

    /// @notice Has the campaign been paid out
    /// @dev Has the funds raised by the campaign been paid out (returns a boolean)
    /// @param _campaignID (campaign id) The campaign id
    /// @return has the campaign been paid out
    function isPaidOut(uint _campaignID) constant returns (bool) {}

    /// @notice The total amount of funds that have been refunded for a specified campaign
    /// @dev The total amount of ether that has been refunded for a specified campaign (funds are integers in the wei denomination)
    /// @param _campaignID (campaign id) The campaign id
    /// @return the total amount of funds refunded (an integer representing wei)
    function totalRefunded(uint _campaignID) constant returns (uint) {}

    /// @notice Has the campaign been completly refunded
    /// @dev Are all funds refunded from this campaign (returns a boolean)
    /// @param _campaignID (campaign id) The campaign id
    /// @return have the funds been refunded or not
    function isRefunded(uint _campaignID) constant returns (bool) {}

    event CampaignCreated(uint indexed _campaignID, address indexed _owner);
    event Contributed(uint indexed _campaignID, address indexed _contributor, uint _amountContributed);
    event Refunded(uint indexed _campaignID, address indexed _contributor, uint _amountRefunded);
    event PaidOut(uint indexed _campaignID, address indexed _beneficiary, uint _amountPaid);
}

/// @title A campaign account contribution forwarding contract
/// @author Nick Dodson <thenickdodson@gmail.com>
contract CampaignAccount {
    address public weifund;
    uint public campaignID;

    event Contributed(uint _campaignID, address _contributor, uint _amountContributed);

    mapping(address => uint) public contributions;

    function CampaignAccount (address _weifund, uint _campaignID) {
        weifund = _weifund;
        campaignID = _campaignID;
    }

    function () {
        if(WeiFund(weifund).isSuccess(campaignID)
            || WeiFund(weifund).isPaidOut(campaignID)
            || WeiFund(weifund).hasFailed(campaignID)
            || msg.value <= 0)
            throw;

        contributions[msg.sender] += msg.value;
        WeiFund(weifund).contribute.value(msg.value)(campaignID, msg.sender);
        Contributed(campaignID, msg.sender, msg.value);
    }

    function amountContributed(address _contributor) public constant returns (uint) {
        return contributions[_contributor];
    }
}

/// @title Enables WeiFund campaigns to have their own contribution account
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiAccounts {
    address public weifund;
	uint public version;
    mapping(uint => address) public accounts;
    mapping(address => uint) public toCampaign;

    event NewCampaignAccount(uint _campaignID, address _account, address _owner);

    function WeiAccounts (address _weifund) {
        weifund = _weifund;
		version = 1;
    }

    function newCampaignAccount(uint _campaignID) returns (address contractAddress) {
        if(!WeiFund(weifund).isOwner(_campaignID, msg.sender)
            || accounts[_campaignID] == address(0)
            || WeiFund(weifund).isSuccess(_campaignID)
            || WeiFund(weifund).isPaidOut(_campaignID)
            || WeiFund(weifund).hasFailed(_campaignID))
            throw;

        contractAddress = address(new CampaignAccount(weifund, _campaignID));
        accounts[_campaignID] = contractAddress;
        toCampaign[contractAddress] = _campaignID;
        NewCampaignAccount(_campaignID, contractAddress, msg.sender);
    }

    function accountOf(uint _campaignID) constant returns (address) {
        return accounts[_campaignID];
    }

    function campaignOf(address _account) constant returns (uint) {
        return toCampaign[_account];
    }
}


Made tags aware of their children and made assessor search bidirectional.

contract Creator
{
  address masterAddress;

  /*
  @type: event
  @name: TagCreation
  @occasion: When a tag is created
  @purpose: To help build a data store of tags
  @stores: string _tagName = the name of the tag that was created
  @stores: address _tagAddress = the address of the tag that was created
  @stores: address[] _parents = the addresses of the parents of the tag that as created
  */
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Creator()
  {
    Master master = new Master(address(this));
    masterAddress = address(master);
  }

  function addUser()
  {
    User newUser = new User(msg.sender, masterAddress);
    Tag(Master(masterAddress).getTagAddressFromName("account")).startAssessment(address(newUser),5, 600);
  }

  function addTag(string name, address[] parentList) constant returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] memory parents;
    if(Master(masterAddress).getTokenBalance(msg.sender) < 1)
    {
      response += 1;
    }
    if(Master(masterAddress).getTagAddressFromName(name) != 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i <= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents[i] = parentList[i];
        }
      }
      Tag newTag = new Tag(name, parents, masterAddress);
      address newTagAddress = address(newTag);
      Master(masterAddress).mapTagName(newTagAddress,name);
      Master(masterAddress).mapTagAddressFromName(name,newTagAddress);
      Master(masterAddress).mapTokenBalance(msg.sender,Master(masterAddress).getTokenBalance(msg.sender) - 1);
      for(uint j=0; j <= parents.length; j++)
      {
        Tag(parents[j]).addChild(newTagAddress);
      }
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

/*
@type: contract
@name: Master
@purpose: To store data for easy and secure access
*/
contract Master
{
  event Four(uint _four);
  address creatorAddress; //The address of the Creator contract
  mapping (address => uint)  tokenBalance; //Maps the addresses of users to their token balances
  mapping (address => string)  tagName; //Maps the address of tags to their names
  mapping (string => address) tagAddressFromName; //Maps the names of tags to their addresses
  mapping (address => address[])  achievements; //Maps the addresses of users to an array of addresses that contain the addresses of the tags that they have passed an assessment in
  mapping (address => bool)  availability; //Maps the addresses of users to their availability status for whether or not they can currently assess someone
  mapping (address => address)  users; //Maps the addresses of the users to their actual location of the blockchain

  /*
  @type: constructer function
  @purpose: To initialize the master contract and have it make the account tag
  @param: none
  @returns: nothing
  */
  function Master(address creator)
  {
    creatorAddress = creator; //Sets the address of the Creator contract
    tokenBalance[address(this)] = 1; //Gives the master contract a temporary tokenBalance so that it may make the tag
    address[] memory a; //Makes an empty array to serve as the parents of the tag
    uint useless = Creator(creatorAddress).addTag("account", a); //creates the account tag and gives the value of its error code to a relatively useless uint
  }

  function return4() constant returns(uint)
  {
      Four(4);
      return 4;
  }

  /*
  @type: function
  @purpose: To set a new token balance for a user
  @param: address user = the address of the user whose token balance is to be mapped
  @param: uint balance = the new token balance for the user
  @returns: nothing
  */
  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0) //makes sure this function is not called by a tag
    {
      tokenBalance[user] = balance; //sets the token balance of the user
    }
  }

  /*
  @type: function
  @purpose: To map the name of a tag to its address
  @param: address tagAddress = the address of the tag being mapped
  @param: string name = the name of the tag being mapped
  @return: nothing
  */
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name; //maps the name of the tag to its address
  }

  /*
  @type: function
  @purpose: To map the address of a tag to its name
  @param: string name = the name of the tag being mapped
  @param: address tagAddress = the address of the tag being mapped
  @returns: nothing
  */
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress; //maps the address of the tag to its name
  }

  /*
  @type: function
  @purpose: To map the address of a user to the address of the tag that the user has just passed
  @param: address user = the address of the user
  @param: address acheivment = the address of the tag just passed
  @returns: nothing
  */
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment); //adds the address of the tag to the end of the array that is mapped to the user
  }

  /*
  @type: function
  @purpose: To map the user's availability to assess to the user's address
  @param: address user = the address of the user
  @param: bool available = the availa status of the user to assess
  @returns: nothing
  */
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  /*
  @type: function
  @purpose: To get the user's token balance
  @param: address user = the address of the user
  @returns: The token balance in the form of a uint
  */
  function getTokenBalance(address user) constant returns(uint)
  {
    return tokenBalance[user];
  }

  /*
  @type: function
  @purpose: To get the name of a tag from its address
  @param: address tagAddress = the address of the tag
  @returns: The name of the tag in the form of a string
  */
  function getTagName(address tagAddress) constant returns(string)
  {
    return tagName[tagAddress];
  }

  /*
  @type: function
  @purpose: To get the address of a tag from its name
  @param: string name = the name of the tag
  @returns: The address of the tag in the form of an address
  */
  function getTagAddressFromName(string name) constant returns(address)
  {
    return tagAddressFromName[name];
  }

  /*
  @type: function
  @purpose: To get the addresses of the tags that the user has achieved
  @param: address user = the address of the user
  @returns: The addresses of the tags that the user has achieved in the form of an array of addresses
  */
  function getAchievement(address user) constant returns(address[])
  {
    return achievements[user];
  }

  /*
  @type: function
  @purpose: To get the number of achievments of a user
  @param: address user = the address of the user
  @returns: The number of achievments of a user in the form of a uint
  */
  function getNumberOfachievments(address user) constant returns(uint)
  {
    return achievements[user].length;
  }

  /*
  @type: function
  @purpose: To get the availab of a user to assess
  @param: address user = the address of the user
  @returns: The availability of a user to assess in the form of a bool
  */
  function getAvailability(address user) constant returns(bool)
  {
    return availability[user];
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address[] childTags;
  address master;
  string name;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => int) scores; //All assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    int _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
  }

  function getOwners() constant returns(address[])
  {
    return owners;
  }

  function getOwnerLength() constant returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) constant returns(address)
  {
    return owners[index];
  }

  function getParents() constant returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() constant returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) constant returns(address)
  {
      return parentTags[index];
  }

  function addParent(address parentAddress)
  {
      parentTags.push(parentAddress);
  }

  function getChildren() constant returns(address[])
  {
    return childTags;
  }

  function getChildrenLength() constant returns(uint)
  {
    return childTags.length;
  }

  function getChild(uint index) constant returns(address)
  {
      return childTags[index];
  }

  function addChild(address childAddress)
  {
      childTags.push(childAddress);
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }

        for(uint k = 0; k < Tag(tagAddress).getChildrenLength(); k++)
        {
          setAssessorPool(Tag(tagAddress).getChild(k), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessor();
      }
    }
  }

  function startAssessment(address assessee, uint size, uint timeForSettingTask)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master, timeForSettingTask);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, int score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).remove(master);
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) constant returns(uint)
  {
    return 12;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentResponses; //Given by the assessee as IPFS hashes
  mapping(address => int) assessmentResults; //Pass/Fail and Score given by assessors
  mapping(int => address) addressFromScore;
  mapping(int => bool) inRewardCluster;
  address currentAssessor;
  int finalScore;
  bool finalResult;
  uint taskCreationTime;
  uint taskCompletionTime;
  uint taskGradingTime;
  uint referenceTime;
  uint numberCancelled;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress, uint timeForTaskCreation)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    referenceTime = block.timestamp;
    taskCreationTime = timeForTaskCreation;
  }

  function getTaskCreationTime() constant returns(uint)
  {
      return taskCreationTime;
  }

  function getTaskCompletionTime() constant returns(uint)
  {
      return taskCompletionTime;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() constant returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() constant returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessor()
  {
    bool potentialAssessorSet = false;
    while(potentialAssessorSet == false)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      if(assessors[randomAssessor] == 0)
      {
        assessors[randomAssessor] = 3;
        currentAssessor = randomAssessor;
        potentialAssessorSet = true;
        User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
      }
    }
    referenceTime = now;
  }

  function getAssessorPoolLength() constant returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) constant returns(uint)
  {
    return 12;
  }

  function setTask(string data, uint timeLimit)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] == 5 && now - referenceTime <= taskCreationTime)
    {
      assessmentTasks[currentAssessor] = data;
      taskCompletionTime = timeLimit;
      assessors[currentAssessor] = 6;
      User(currentAssessor).notification("Task Data Inputted", tag, 5);
      User(assessee).notification("New Task Available", tag, 7);
      referenceTime = now;
    }
    if(now - referenceTime > taskCreationTime && assessors[currentAssessor]!=5)
    {
      User(currentAssessor).notification("Did Not Submit Task On Time", tag, 6);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(currentAssessor) - 1);
      User(currentAssessor).setReputation(User(currentAssessor).getReputation() + 1);
      numberCancelled++;
      setPotentialAssessor();
    }
  }

  function cancelAssessment()
  {
    User(assessee).notification("Assessment Cancled", tag, 8);
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      User(finalAssessors[i]).notification("Assessment Cancled", tag, 8);
    }
    suicide(master);
  }

  function getTask(address assessorAddress) constant returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setResponse(string data)
  {
    if(assessors[currentAssessor] == 6 && now - referenceTime <= taskCompletionTime)
    {
      assessmentResponses[currentAssessor] = data;
      assessors[currentAssessor] = 7;
      User(currentAssessor).notification("Task Response Inputted", tag, 10);
      taskGradingTime = (3*(now - referenceTime))/2;
      referenceTime = now;
    }
    if(now - referenceTime > taskCompletionTime && assessors[currentAssessor]!=6)
    {
      User(assessee).notification("Did Not Submit Task Response On Time", tag, 11);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(assessee) - 1);
      cancelAssessment();
    }
  }

  function getResponse(address assesseeAddress) constant returns(string)
  {
    return assessmentResponses[assesseeAddress];
  }

  function setResult(int score)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] == 7 && now - referenceTime <= taskGradingTime)
    {
      assessmentResults[currentAssessor] = score;
      addressFromScore[score] = currentAssessor;
      User(assessee).notification("Task Result Inputted", tag, 12);
      finalAssessors.push(currentAssessor);
    }
    if(now - referenceTime <= taskGradingTime && assessors[currentAssessor] != 7)
    {
      User(currentAssessor).notification("Did Not Submit Task Results On Time", tag, 13);
      User(assessee).notification("Assessor Has Not Graded In Time", tag, 14);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(assessee) - 1);
      User(currentAssessor).setReputation(User(currentAssessor).getReputation() + 1);
      numberCancelled++;
      setPotentialAssessor();
    }
    if(finalAssessors.length == numberOfAssessors)
    {
      calculateResult();
    }
    if(finalAssessors.length < numberOfAssessors)
    {
      setPotentialAssessor;
    }
  }

  function calculateMAD(int[] scores, int n) constant returns(int)
  {
    int meanScore;
    int totalRelativeDistance;
    int meanAbsoluteDeviation;
    for(uint j = 0; j < scores.length; j++)
    {
      meanScore += scores[j];
      inRewardCluster[scores[j]] = false;
    }
    meanScore /= n;
    for(uint k = 0; k < scores.length; k++)
    {
      int distanceFromMean = scores[k] - meanScore;
      if(distanceFromMean < 0)
      {
        distanceFromMean *= -1;
      }
      totalRelativeDistance += distanceFromMean;
    }
    meanAbsoluteDeviation = totalRelativeDistance/n;
  }

  function calculateResult()
  {
    int[][] memory clusters;
    int[] memory scores;
    uint n = 0;
    uint largestClusterIndex = 0;
    int averageScore;
    for(uint i = 0; i < numberOfAssessors; i++)
    {
      scores[i] = assessmentResults[finalAssessors[i]];
    }
    int meanAbsoluteDeviation = calculateMAD(scores,int(numberOfAssessors));
    for(uint l = 0; l < scores.length; l++)
    {
      for(uint m = 0; m < scores.length; m++)
      {
        n = 0;
        if(scores[l] - scores[m] <= meanAbsoluteDeviation)
        {
          clusters[l][n] = (scores[m]);
          n++;
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length)
      {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; n++)
    {
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length);
    finalScore = averageScore;
    if(averageScore > 0)
    {
      finalResult = true;
    }
    if(averageScore <= 0)
    {
      finalResult = false;
    }
    payout();
  }

  function confirmAssessor(uint confirm, bool timeKnow)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] != 0 && now - referenceTime <= 180)
    {
      assessors[currentAssessor] = confirm;
      if(confirm == 1)
      {
        User(currentAssessor).notification("Confirmed As Potential Assessor", tag, 2);
        referenceTime = now;
      }
      if(confirm == 2 || confirm == 4)
      {
        User(currentAssessor).notification("Confirmed As Not Assessing", tag, 3);
        numberCancelled ++;
        setPotentialAssessor();
      }
      if(confirm == 5)
      {
        User(currentAssessor).notification("Confirmed As Assessing", tag, 9);
        referenceTime = now;
      }
    }
    if(now - referenceTime > 180)
    {
      if(timeKnow == false && assessors[currentAssessor]==3)
      {
      User(currentAssessor).notification("Did Not Respond In Time To Be Assessor", tag, 4);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(currentAssessor) - 1);
      setPotentialAssessor();
      }
      if(timeKnow == true && assessors[currentAssessor]==1)
      {
        User(currentAssessor).notification("Did Not Respond In Time To Be Assessor", tag, 4);
        setPotentialAssessor();
      }
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function payout()
  {
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      int score = assessmentResults[finalAssessors[i]];
      int scoreDistance = ((score - finalScore)*100)/finalScore;
      if(scoreDistance < 0)
      {
        scoreDistance *= -1;
      }
      uint distance = uint(scoreDistance);
      if(inRewardCluster[score] == true)
      {
        Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) + uint((500/(100 - scoreDistance))));
        User(finalAssessors[i]).notification("You Have Received Payment For Your Assessment", tag, 15);
      }
      if(inRewardCluster[score] == true)
      {
        Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) - uint((500/(100 - scoreDistance))));
        User(finalAssessors[i]).notification("You Have Received A Fine For Your Assessment", tag, 16);
      }
    }
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

contract User
{
  address user;
  address master;
  address[]  acheivements;
  string userData;
  uint reputation;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
    reputation = 1;
  }

  function getReputation() constant returns(uint)
  {
    return reputation;
  }

  function setReputation(uint newReputation)
  {
    reputation = newReputation;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() constant returns(string)
  {
    return userData;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

contract Creator
{
  address public masterAddress;

  /*
  @type: event
  @name: TagCreation
  @occasion: When a tag is created
  @purpose: To help build a data store of tags
  @stores: string _tagName = the name of the tag that was created
  @stores: address _tagAddress = the address of the tag that was created
  @stores: address[] _parents = the addresses of the parents of the tag that as created
  */
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Creator()
  {
    Master master = new Master(address(this));
    masterAddress = address(master);
  }

  function addUser()
  {
    User newUser = new User(msg.sender, masterAddress);
    Tag(Master(masterAddress).getTagAddressFromName("account")).startAssessment(address(newUser),5, 600);
  }

  function addTag(string name, address[] parentList) constant returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] memory parents;
    if(Master(masterAddress).getTokenBalance(msg.sender) < 1)
    {
      response += 1;
    }
    if(Master(masterAddress).getTagAddressFromName(name) != 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents[i] = parentList[i];
        }
      }
      Tag newTag = new Tag(name, parents, masterAddress);
      address newTagAddress = address(newTag);
      Master(masterAddress).mapTagName(newTagAddress,name);
      Master(masterAddress).mapTagAddressFromName(name,newTagAddress);
      Master(masterAddress).mapTokenBalance(msg.sender,Master(masterAddress).getTokenBalance(msg.sender) - 1);
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

/*
@type: contract
@name: Master
@purpose: To store data for easy and secure access
*/
contract Master
{
  event Four(uint _four);
  address public creatorAddress; //The address of the Creator contract
  mapping (address => uint) public tokenBalance; //Maps the addresses of users to their token balances
  mapping (address => string) public tagName; //Maps the address of tags to their names
  mapping (string => address) tagAddressFromName; //Maps the names of tags to their addresses
  mapping (address => address[]) public achievements; //Maps the addresses of users to an array of addresses that contain the addresses of the tags that they have passed an assessment in
  mapping (address => bool) public availability; //Maps the addresses of users to their availability status for whether or not they can currently assess someone
  mapping (address => address) public users; //Maps the addresses of the users to their actual location of the blockchain

  /*
  @type: constructer function
  @purpose: To initialize the master contract and have it make the account tag
  @param: none
  @returns: nothing
  */
  function Master(address creator)
  {
    creatorAddress = creator; //Sets the address of the Creator contract
    tokenBalance[address(this)] = 1; //Gives the master contract a temporary tokenBalance so that it may make the tag
    address[] memory a; //Makes an empty array to serve as the parents of the tag
    uint useless = Creator(creatorAddress).addTag("account", a); //creates the account tag and gives the value of its error code to a relatively useless uint
  }

  function return4() constant returns(uint)
  {
      Four(4);
      return 4;
  }

  /*
  @type: function
  @purpose: To set a new token balance for a user
  @param: address user = the address of the user whose token balance is to be mapped
  @param: uint balance = the new token balance for the user
  @returns: nothing
  */
  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0) //makes sure this function is not called by a tag
    {
      tokenBalance[user] = balance; //sets the token balance of the user
    }
  }

  /*
  @type: function
  @purpose: To map the name of a tag to its address
  @param: address tagAddress = the address of the tag being mapped
  @param: string name = the name of the tag being mapped
  @return: nothing
  */
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name; //maps the name of the tag to its address
  }

  /*
  @type: function
  @purpose: To map the address of a tag to its name
  @param: string name = the name of the tag being mapped
  @param: address tagAddress = the address of the tag being mapped
  @returns: nothing
  */
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress; //maps the address of the tag to its name
  }

  /*
  @type: function
  @purpose: To map the address of a user to the address of the tag that the user has just passed
  @param: address user = the address of the user
  @param: address acheivment = the address of the tag just passed
  @returns: nothing
  */
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment); //adds the address of the tag to the end of the array that is mapped to the user
  }

  /*
  @type: function
  @purpose: To map the user's availability to assess to the user's address
  @param: address user = the address of the user
  @param: bool available = the availa status of the user to assess
  @returns: nothing
  */
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  /*
  @type: function
  @purpose: To get the user's token balance
  @param: address user = the address of the user
  @returns: The token balance in the form of a uint
  */
  function getTokenBalance(address user) constant returns(uint)
  {
    return tokenBalance[user];
  }

  /*
  @type: function
  @purpose: To get the name of a tag from its address
  @param: address tagAddress = the address of the tag
  @returns: The name of the tag in the form of a string
  */
  function getTagName(address tagAddress) constant returns(string)
  {
    return tagName[tagAddress];
  }

  /*
  @type: function
  @purpose: To get the address of a tag from its name
  @param: string name = the name of the tag
  @returns: The address of the tag in the form of an address
  */
  function getTagAddressFromName(string name) constant returns(address)
  {
    return tagAddressFromName[name];
  }

  /*
  @type: function
  @purpose: To get the addresses of the tags that the user has achieved
  @param: address user = the address of the user
  @returns: The addresses of the tags that the user has achieved in the form of an array of addresses
  */
  function getAchievement(address user) constant returns(address[])
  {
    return achievements[user];
  }

  /*
  @type: function
  @purpose: To get the number of achievments of a user
  @param: address user = the address of the user
  @returns: The number of achievments of a user in the form of a uint
  */
  function getNumberOfachievments(address user) constant returns(uint)
  {
    return achievements[user].length;
  }

  /*
  @type: function
  @purpose: To get the availab of a user to assess
  @param: address user = the address of the user
  @returns: The availability of a user to assess in the form of a bool
  */
  function getAvailability(address user) constant returns(bool)
  {
    return availability[user];
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] public parentTags;
  address public master;
  string public name;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => int) scores; //All assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    int _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
  }

  function getOwners() constant returns(address[])
  {
    return owners;
  }

  function getOwnerLength() constant returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) constant returns(address)
  {
    return owners[index];
  }

  function getParents() constant returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() constant returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) constant returns(address)
  {
      return parentTags[index];
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessor();
      }
    }
  }

  function startAssessment(address assessee, uint size, uint timeForSettingTask)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master, timeForSettingTask);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, int score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).remove(master);
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) constant returns(uint)
  {
    return 12;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentResponses; //Given by the assessee as IPFS hashes
  mapping(address => int) assessmentResults; //Pass/Fail and Score given by assessors
  mapping(int => address) addressFromScore;
  mapping(int => bool) inRewardCluster;
  address currentAssessor;
  int finalScore;
  bool finalResult;
  uint taskCreationTime;
  uint taskCompletionTime;
  uint taskGradingTime;
  uint referenceTime;
  uint numberCancelled;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress, uint timeForTaskCreation)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    referenceTime = block.timestamp;
    taskCreationTime = timeForTaskCreation;
  }

  function getTaskCreationTime() constant returns(uint)
  {
      return taskCreationTime;
  }

  function getTaskCompletionTime() constant returns(uint)
  {
      return taskCompletionTime;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() constant returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() constant returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessor()
  {
    bool potentialAssessorSet = false;
    while(potentialAssessorSet == false)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      if(assessors[randomAssessor] == 0)
      {
        assessors[randomAssessor] = 3;
        currentAssessor = randomAssessor;
        potentialAssessorSet = true;
        User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
      }
    }
    referenceTime = now;
  }

  function getAssessorPoolLength() constant returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) constant returns(uint)
  {
    return 12;
  }

  function setTask(string data, uint timeLimit)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] == 5 && now - referenceTime <= taskCreationTime)
    {
      assessmentTasks[currentAssessor] = data;
      taskCompletionTime = timeLimit;
      assessors[currentAssessor] = 6;
      User(currentAssessor).notification("Task Data Inputted", tag, 5);
      User(assessee).notification("New Task Available", tag, 7);
      referenceTime = now;
    }
    if(now - referenceTime > taskCreationTime && assessors[currentAssessor]!=5)
    {
      User(currentAssessor).notification("Did Not Submit Task On Time", tag, 6);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(currentAssessor) - 1);
      User(currentAssessor).setReputation(User(currentAssessor).getReputation() + 1);
      numberCancelled++;
      setPotentialAssessor();
    }
  }

  function cancelAssessment()
  {
    User(assessee).notification("Assessment Cancled", tag, 8);
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      User(finalAssessors[i]).notification("Assessment Cancled", tag, 8);
    }
    suicide(master);
  }

  function getTask(address assessorAddress) constant returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setResponse(string data)
  {
    if(assessors[currentAssessor] == 6 && now - referenceTime <= taskCompletionTime)
    {
      assessmentResponses[currentAssessor] = data;
      assessors[currentAssessor] = 7;
      User(currentAssessor).notification("Task Response Inputted", tag, 10);
      taskGradingTime = (3*(now - referenceTime))/2;
      referenceTime = now;
    }
    if(now - referenceTime > taskCompletionTime && assessors[currentAssessor]!=6)
    {
      User(assessee).notification("Did Not Submit Task Response On Time", tag, 11);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(assessee) - 1);
      cancelAssessment();
    }
  }

  function getResponse(address assesseeAddress) constant returns(string)
  {
    return assessmentResponses[assesseeAddress];
  }

  function setResult(int score)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] == 7 && now - referenceTime <= taskGradingTime)
    {
      assessmentResults[currentAssessor] = score;
      addressFromScore[score] = currentAssessor;
      User(assessee).notification("Task Result Inputted", tag, 12);
      finalAssessors.push(currentAssessor);
    }
    if(now - referenceTime <= taskGradingTime && assessors[currentAssessor] != 7)
    {
      User(currentAssessor).notification("Did Not Submit Task Results On Time", tag, 13);
      User(assessee).notification("Assessor Has Not Graded In Time", tag, 14);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(assessee) - 1);
      User(currentAssessor).setReputation(User(currentAssessor).getReputation() + 1);
      numberCancelled++;
      setPotentialAssessor();
    }
    if(finalAssessors.length == numberOfAssessors)
    {
      calculateResult();
    }
    if(finalAssessors.length < numberOfAssessors)
    {
      setPotentialAssessor;
    }
  }

  function calculateMAD(int[] scores, int n) constant returns(int)
  {
    int meanScore;
    int totalRelativeDistance;
    int meanAbsoluteDeviation;
    for(uint j = 0; j < scores.length; j++)
    {
      meanScore += scores[j];
      inRewardCluster[scores[j]] = false;
    }
    meanScore /= n;
    for(uint k = 0; k < scores.length; k++)
    {
      int distanceFromMean = scores[k] - meanScore;
      if(distanceFromMean < 0)
      {
        distanceFromMean *= -1;
      }
      totalRelativeDistance += distanceFromMean;
    }
    meanAbsoluteDeviation = totalRelativeDistance/n;
  }

  function calculateResult()
  {
    int[][] memory clusters;
    int[] memory scores;
    uint n = 0;
    uint largestClusterIndex = 0;
    int averageScore;
    for(uint i = 0; i < numberOfAssessors; i++)
    {
      scores[i] = assessmentResults[finalAssessors[i]];
    }
    int meanAbsoluteDeviation = calculateMAD(scores,int(numberOfAssessors));
    for(uint l = 0; l < scores.length; l++)
    {
      for(uint m = 0; m < scores.length; m++)
      {
        n = 0;
        if(scores[l] - scores[m] <= meanAbsoluteDeviation)
        {
          clusters[l][n] = (scores[m]);
          n++;
        }
      }
      if(clusters[l].length > clusters[largestClusterIndex].length)
      {
        largestClusterIndex = l;
      }
    }
    for(uint o = 0; o < clusters[largestClusterIndex].length; n++)
    {
      averageScore += clusters[largestClusterIndex][o];
      inRewardCluster[clusters[largestClusterIndex][o]] = true;
    }
    averageScore /= int(clusters[largestClusterIndex].length);
    finalScore = averageScore;
    if(averageScore > 0)
    {
      finalResult = true;
    }
    if(averageScore <= 0)
    {
      finalResult = false;
    }
    payout();
  }

  function confirmAssessor(uint confirm, bool timeKnow)
  {
    if(numberCancelled >= numberOfAssessors)
    {
      cancelAssessment();
    }
    if(assessors[currentAssessor] != 0 && now - referenceTime <= 180)
    {
      assessors[currentAssessor] = confirm;
      if(confirm == 1)
      {
        User(currentAssessor).notification("Confirmed As Potential Assessor", tag, 2);
        referenceTime = now;
      }
      if(confirm == 2 || confirm == 4)
      {
        User(currentAssessor).notification("Confirmed As Not Assessing", tag, 3);
        numberCancelled ++;
        setPotentialAssessor();
      }
      if(confirm == 5)
      {
        User(currentAssessor).notification("Confirmed As Assessing", tag, 9);
        referenceTime = now;
      }
    }
    if(now - referenceTime > 180)
    {
      if(timeKnow == false && assessors[currentAssessor]==3)
      {
      User(currentAssessor).notification("Did Not Respond In Time To Be Assessor", tag, 4);
      Master(master).mapTokenBalance(currentAssessor, Master(master).getTokenBalance(currentAssessor) - 1);
      setPotentialAssessor();
      }
      if(timeKnow == true && assessors[currentAssessor]==1)
      {
        User(currentAssessor).notification("Did Not Respond In Time To Be Assessor", tag, 4);
        setPotentialAssessor();
      }
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function payout()
  {
    for(uint i = 0; i < finalAssessors.length; i++)
    {
      int score = assessmentResults[finalAssessors[i]];
      int scoreDistance = ((score - finalScore)*100)/finalScore;
      if(scoreDistance < 0)
      {
        scoreDistance *= -1;
      }
      uint distance = uint(scoreDistance);
      if(inRewardCluster[score] == true)
      {
        Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) + uint((500/(100 - scoreDistance))));
        User(finalAssessors[i]).notification("You Have Received Payment For Your Assessment", tag, 15);
      }
      if(inRewardCluster[score] == true)
      {
        Master(master).mapTokenBalance(finalAssessors[i], Master(master).getTokenBalance(finalAssessors[i]) - uint((500/(100 - scoreDistance))));
        User(finalAssessors[i]).notification("You Have Received A Fine For Your Assessment", tag, 16);
      }
    }
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}

contract User
{
  address user;
  address public master;
  address[] public acheivements;
  string userData;
  uint reputation;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
    reputation = 1;
  }

  function getReputation() constant returns(uint)
  {
    return reputation;
  }

  function setReputation(uint newReputation)
  {
    reputation = newReputation;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() constant returns(string)
  {
    return userData;
  }

  function remove(address reciever)
  {
    suicide(reciever);
  }
}