library AssetTokenL {
    using SafeMath for uint256;

///////////////////
// Struct Parameters (passed as parameter to library)
///////////////////

    struct Supply {
        // `balances` is the map that tracks the balance of each address, in this
        // contract when the balance changes. TransfersAndMints-index when the change
        // occurred is also included
        mapping (address => Checkpoint[]) balances;

        // Tracks the history of the `totalSupply` of the token
        Checkpoint[] totalSupplyHistory;

        // `allowed` tracks any extra transfer rights as in all ERC20 tokens
        mapping (address => mapping (address => uint256)) allowed;

        // Minting cap max amount of tokens
        uint256 cap;

        // When successfully funded
        uint256 goal;

        //crowdsale start
        uint256 startTime;

        //crowdsale end
        uint256 endTime;

        //crowdsale dividends
        Dividend[] dividends;

        //counter per address how much was claimed in continuous way
        //note: counter also increases when recycled and tried to claim in continous way
        mapping (address => uint256) dividendsClaimed;

        uint256 tokenActionIndex; //only modify within library
    }

    struct Availability {
        // Flag that determines if the token is yet alive.
        // Meta data cannot be changed anymore (except capitalControl)
        bool tokenAlive;

        // Flag that determines if the token is transferable or not.
        bool transfersEnabled;

        // Flag that minting is finished
        bool mintingPhaseFinished;

        // Flag that minting is paused
        bool mintingPaused;
    }

    struct Roles {
        // role that can pause/resume
        address pauseControl;

        // role that can rescue accidentally sent tokens
        address tokenRescueControl;

        // role that can mint during crowdsale (usually controller)
        address mintControl;
    }

///////////////////
// Structs (saved to blockchain)
///////////////////

    /// @dev `Dividend` is the structure that saves the status of a dividend distribution
    struct Dividend {
        uint256 currentTokenActionIndex;
        uint256 timestamp;
        DividendType dividendType;
        address dividendToken;
        uint256 amount;
        uint256 claimedAmount;
        uint256 totalSupply;
        bool recycled;
        mapping (address => bool) claimed;
    }

    /// @dev Dividends can be of those types.
    enum DividendType { Ether, ERC20 }

    /** @dev Checkpoint` is the structure that attaches a history index to a given value
      * @notice That uint128 is used instead of uint/uint256. That's to save space. Should be big enough (feedback from audit)
      */
    struct Checkpoint {

        // `currentTokenActionIndex` is the index when the value was generated. It's uint128 to save storage space
        uint128 currentTokenActionIndex;

        // `value` is the amount of tokens at a specific index. It's uint128 to save storage space
        uint128 value;
    }

///////////////////
// Functions
///////////////////

    /// @dev This is the actual transfer function in the token contract, it can
    ///  only be called by other functions in this contract. Check for availability must be done before.
    /// @param _from The address holding the tokens being transferred
    /// @param _to The address of the recipient
    /// @param _amount The amount of tokens to be transferred
    /// @return True if the transfer was successful
    function doTransfer(Supply storage _self, Availability storage /*_availability*/, address _from, address _to, uint256 _amount) public {
        // Do not allow transfer to 0x0 or the token contract itself
        require(_to != address(0), "addr0");
        require(_to != address(this), "target self");

        // If the amount being transfered is more than the balance of the
        //  account the transfer throws
        uint256 previousBalanceFrom = balanceOfNow(_self, _from);
        require(previousBalanceFrom >= _amount, "not enough");

        // First update the balance array with the new value for the address
        //  sending the tokens
        updateValueAtNow(_self, _self.balances[_from], previousBalanceFrom.sub(_amount));

        // Then update the balance array with the new value for the address
        //  receiving the tokens
        uint256 previousBalanceTo = balanceOfNow(_self, _to);
        
        updateValueAtNow(_self, _self.balances[_to], previousBalanceTo.add(_amount));

        //info: don't move this line inside updateValueAtNow (because transfer is 2 actions)
        increaseTokenActionIndex(_self);

        // An event to make the transfer easy to find on the blockchain
        emit Transfer(_from, _to, _amount);
    }

    function increaseTokenActionIndex(Supply storage _self) private {
        _self.tokenActionIndex = _self.tokenActionIndex.add(1);

        emit TokenActionIndexIncreased(_self.tokenActionIndex, block.number);
    }

    /// @notice `msg.sender` approves `_spender` to spend `_amount` of his tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @param _amount The amount of tokens to be approved for transfer
    /// @return True if the approval was successful
    function approve(Supply storage _self, address _spender, uint256 _amount) public returns (bool success) {
        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender,0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require((_amount == 0) || (_self.allowed[msg.sender][_spender] == 0), "amount");

        _self.allowed[msg.sender][_spender] = _amount;
        emit Approval(msg.sender, _spender, _amount);
        return true;
    }

    /// @notice Increase the amount of tokens that an owner allowed to a spender.
    /// @dev approve should be called when allowed[_spender] == 0. To increment
    ///  allowed value is better to use this function to avoid 2 calls (and wait until
    ///  the first transaction is mined)
    ///  From MonolithDAO Token.sol
    /// @param _spender The address which will spend the funds.
    /// @param _addedValue The amount of tokens to increase the allowance by.
    /// @return True if the approval was successful
    function increaseApproval(Supply storage _self, address _spender, uint256 _addedValue) public returns (bool) {
        _self.allowed[msg.sender][_spender] = _self.allowed[msg.sender][_spender].add(_addedValue);
        emit Approval(msg.sender, _spender, _self.allowed[msg.sender][_spender]);
        return true;
    }

    /// @notice Decrease the amount of tokens that an owner allowed to a spender.
    /// @dev approve should be called when allowed[_spender] == 0. To decrement
    ///  allowed value is better to use this function to avoid 2 calls (and wait until
    ///  the first transaction is mined)
    ///  From MonolithDAO Token.sol
    /// @param _spender The address which will spend the funds.
    /// @param _subtractedValue The amount of tokens to decrease the allowance by.
    /// @return True if the approval was successful
    function decreaseApproval(Supply storage _self, address _spender, uint256 _subtractedValue) public returns (bool) {
        uint256 oldValue = _self.allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
            _self.allowed[msg.sender][_spender] = 0;
        } else {
            _self.allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, _self.allowed[msg.sender][_spender]);
        return true;
    }

    /// @notice Send `_amount` tokens to `_to` from `_from` if it is approved by `_from`
    /// @param _from The address holding the tokens being transferred
    /// @param _to The address of the recipient
    /// @param _amount The amount of tokens to be transferred
    /// @return True if the transfer was successful
    function transferFrom(Supply storage _supply, Availability storage _availability, address _from, address _to, uint256 _amount) 
    public 
    returns (bool success) 
    {
        // The standard ERC 20 transferFrom functionality
        require(_supply.allowed[_from][msg.sender] >= _amount, "allowance");
        _supply.allowed[_from][msg.sender] = _supply.allowed[_from][msg.sender].sub(_amount);

        doTransfer(_supply, _availability, _from, _to, _amount);
        return true;
    }

    /// @notice Send `_amount` tokens to `_to` from `_from` WITHOUT approval. UseCase: notar transfers from lost wallet
    /// @param _from The address holding the tokens being transferred
    /// @param _to The address of the recipient
    /// @param _amount The amount of tokens to be transferred
    /// @param _fullAmountRequired Full amount required (causes revert if not).
    /// @return True if the transfer was successful
    function enforcedTransferFrom(
        Supply storage _self, 
        Availability storage _availability, 
        address _from, 
        address _to, 
        uint256 _amount, 
        bool _fullAmountRequired) 
    public 
    returns (bool success) 
    {
        if(_fullAmountRequired && _amount != balanceOfNow(_self, _from))
        {
            revert("Only full amount in case of lost wallet is allowed");
        }

        doTransfer(_self, _availability, _from, _to, _amount);

        emit SelfApprovedTransfer(msg.sender, _from, _to, _amount);

        return true;
    }

////////////////
// Miniting 
////////////////

    /// @notice Function to mint tokens
    /// @param _to The address that will receive the minted tokens.
    /// @param _amount The amount of tokens to mint.
    /// @return A boolean that indicates if the operation was successful.
    function mint(Supply storage _self, address _to, uint256 _amount) public returns (bool) {
        uint256 curTotalSupply = totalSupplyNow(_self);
        uint256 previousBalanceTo = balanceOfNow(_self, _to);

        // Check cap
        require(curTotalSupply.add(_amount) <= _self.cap, "cap"); //leave inside library to never go over cap

        // Check timeframe
        require(_self.startTime <= now, "too soon");
        require(_self.endTime >= now, "too late");

        updateValueAtNow(_self, _self.totalSupplyHistory, curTotalSupply.add(_amount));
        updateValueAtNow(_self, _self.balances[_to], previousBalanceTo.add(_amount));

        //info: don't move this line inside updateValueAtNow (because transfer is 2 actions)
        increaseTokenActionIndex(_self);

        emit MintDetailed(msg.sender, _to, _amount);
        emit Transfer(address(0), _to, _amount);

        return true;
    }

////////////////
// Query balance and totalSupply in History
////////////////

    /// @dev Queries the balance of `_owner` at `_specificTransfersAndMintsIndex`
    /// @param _owner The address from which the balance will be retrieved
    /// @param _specificTransfersAndMintsIndex The balance at index
    /// @return The balance at `_specificTransfersAndMintsIndex`
    function balanceOfAt(Supply storage _self, address _owner, uint256 _specificTransfersAndMintsIndex) public view returns (uint256) {
        return getValueAt(_self.balances[_owner], _specificTransfersAndMintsIndex);
    }

    function balanceOfNow(Supply storage _self, address _owner) public view returns (uint256) {
        return getValueAt(_self.balances[_owner], _self.tokenActionIndex);
    }

    /// @dev Total amount of tokens at `_specificTransfersAndMintsIndex`.
    /// @param _specificTransfersAndMintsIndex The totalSupply at index
    /// @return The total amount of tokens at `_specificTransfersAndMintsIndex`
    function totalSupplyAt(Supply storage _self, uint256 _specificTransfersAndMintsIndex) public view returns(uint256) {
        return getValueAt(_self.totalSupplyHistory, _specificTransfersAndMintsIndex);
    }

    function totalSupplyNow(Supply storage _self) public view returns(uint256) {
        return getValueAt(_self.totalSupplyHistory, _self.tokenActionIndex);
    }

////////////////
// Internal helper functions to query and set a value in a snapshot array
////////////////

    /// @dev `getValueAt` retrieves the number of tokens at a given index
    /// @param checkpoints The history of values being queried
    /// @param _specificTransfersAndMintsIndex The index to retrieve the history checkpoint value at
    /// @return The number of tokens being queried
    function getValueAt(Checkpoint[] storage checkpoints, uint256 _specificTransfersAndMintsIndex) private view returns (uint256) { 
        
        //  requested before a check point was ever created for this token
        if (checkpoints.length == 0 || checkpoints[0].currentTokenActionIndex > _specificTransfersAndMintsIndex) {
            return 0;
        }

        // Shortcut for the actual value
        if (_specificTransfersAndMintsIndex >= checkpoints[checkpoints.length-1].currentTokenActionIndex) {
            return checkpoints[checkpoints.length-1].value;
        }

        // Binary search of the value in the array
        uint256 min = 0;
        uint256 max = checkpoints.length-1;
        while (max > min) {
            uint256 mid = (max + min + 1)/2;
            if (checkpoints[mid].currentTokenActionIndex<=_specificTransfersAndMintsIndex) {
                min = mid;
            } else {
                max = mid-1;
            }
        }
        return checkpoints[min].value;
    }

    /// @dev `updateValueAtNow` used to update the `balances` map and the `totalSupplyHistory`
    /// @param checkpoints The history of data being updated
    /// @param _value The new number of tokens
    function updateValueAtNow(Supply storage _self, Checkpoint[] storage checkpoints, uint256 _value) private {
        require(_value == uint128(_value), "invalid cast1");
        require(_self.tokenActionIndex == uint128(_self.tokenActionIndex), "invalid cast2");

        checkpoints.push(Checkpoint(
            uint128(_self.tokenActionIndex),
            uint128(_value)
        ));
    }

    /// @dev Function to stop minting new tokens.
    /// @return True if the operation was successful.
    function finishMinting(Availability storage _self) public returns (bool) {
        if(_self.mintingPhaseFinished) {
            return false;
        }

        _self.mintingPhaseFinished = true;
        emit MintFinished(msg.sender);
        return true;
    }

    /// @notice Reopening crowdsale means minting is again possible. UseCase: notary approves and does that.
    /// @return True if the operation was successful.
    function reopenCrowdsale(Availability storage _self) public returns (bool) {
        if(_self.mintingPhaseFinished == false) {
            return false;
        }

        _self.mintingPhaseFinished = false;
        emit Reopened(msg.sender);
        return true;
    }

    /// @notice Set roles/operators.
    /// @param _pauseControl pause control.
    /// @param _tokenRescueControl token rescue control (accidentally assigned tokens).
    function setRoles(Roles storage _self, address _pauseControl, address _tokenRescueControl) public {
        require(_pauseControl != address(0), "addr0");
        require(_tokenRescueControl != address(0), "addr0");
        
        _self.pauseControl = _pauseControl;
        _self.tokenRescueControl = _tokenRescueControl;

        emit RolesChanged(msg.sender, _pauseControl, _tokenRescueControl);
    }

    /// @notice Set mint control.
    function setMintControl(Roles storage _self, address _mintControl) public {
        require(_mintControl != address(0), "addr0");

        _self.mintControl = _mintControl;

        emit MintControlChanged(msg.sender, _mintControl);
    }

    /// @notice Set token alive which can be seen as not in draft state anymore.
    function setTokenAlive(Availability storage _self) public {
        _self.tokenAlive = true;
    }

////////////////
// Pausing token for unforeseen reasons
////////////////

    /// @notice pause transfer.
    /// @param _transfersEnabled True if transfers are allowed.
    function pauseTransfer(Availability storage _self, bool _transfersEnabled) public
    {
        _self.transfersEnabled = _transfersEnabled;

        if(_transfersEnabled) {
            emit TransferResumed(msg.sender);
        } else {
            emit TransferPaused(msg.sender);
        }
    }

    /// @notice calling this can enable/disable capital increase/decrease flag
    /// @param _mintingEnabled True if minting is allowed
    function pauseCapitalIncreaseOrDecrease(Availability storage _self, bool _mintingEnabled) public
    {
        _self.mintingPaused = (_mintingEnabled == false);

        if(_mintingEnabled) {
            emit MintingResumed(msg.sender);
        } else {
            emit MintingPaused(msg.sender);
        }
    }

    /// @notice Receives ether to be distriubted to all token owners
    function depositDividend(Supply storage _self, uint256 msgValue)
    public 
    {
        require(msgValue > 0, "amount0");

        // gets the current number of total token distributed
        uint256 currentSupply = totalSupplyNow(_self);

        // a deposit without investment would end up in unclaimable deposit for token holders
        require(currentSupply > 0, "0investors");

        // creates a new index for the dividends
        uint256 dividendIndex = _self.dividends.length;

        // Stores the current meta data for the dividend payout
        _self.dividends.push(
            Dividend(
                _self.tokenActionIndex, // current index used for claiming
                block.timestamp, // Timestamp of the distribution
                DividendType.Ether, // Type of dividends
                address(0),
                msgValue, // Total amount that has been distributed
                0, // amount that has been claimed
                currentSupply, // Total supply now
                false // Already recylced
            )
        );
        emit DividendDeposited(msg.sender, _self.tokenActionIndex, msgValue, currentSupply, dividendIndex);
    }

    /// @notice Receives ERC20 to be distriubted to all token owners
    function depositERC20Dividend(Supply storage _self, address _dividendToken, uint256 _amount, address baseCurrency)
    public
    {
        require(_amount > 0, "amount0");
        require(_dividendToken == baseCurrency, "not baseCurrency");

        // gets the current number of total token distributed
        uint256 currentSupply = totalSupplyNow(_self);

        // a deposit without investment would end up in unclaimable deposit for token holders
        require(currentSupply > 0, "0investors");

        // creates a new index for the dividends
        uint256 dividendIndex = _self.dividends.length;

        // Stores the current meta data for the dividend payout
        _self.dividends.push(
            Dividend(
                _self.tokenActionIndex, // index that counts up on transfers and mints
                block.timestamp, // Timestamp of the distribution
                DividendType.ERC20, 
                _dividendToken, 
                _amount, // Total amount that has been distributed
                0, // amount that has been claimed
                currentSupply, // Total supply now
                false // Already recylced
            )
        );

        // it shouldn't return anything but according to ERC20 standard it could if badly implemented
        // IMPORTANT: potentially a call with reentrance -> do at the end
        require(ERC20(_dividendToken).transferFrom(msg.sender, address(this), _amount), "transferFrom");

        emit DividendDeposited(msg.sender, _self.tokenActionIndex, _amount, currentSupply, dividendIndex);
    }

    /// @notice Function to claim dividends for msg.sender
    /// @dev dividendsClaimed should not be handled here.
    function claimDividend(Supply storage _self, uint256 _dividendIndex) public {
        // Loads the details for the specific Dividend payment
        Dividend storage dividend = _self.dividends[_dividendIndex];

        // Devidends should not have been claimed already
        require(dividend.claimed[msg.sender] == false, "claimed");

         // Devidends should not have been recycled already
        require(dividend.recycled == false, "recycled");

        // get the token balance at the time of the dividend distribution
        uint256 balance = balanceOfAt(_self, msg.sender, dividend.currentTokenActionIndex.sub(1));

        // calculates the amount of dividends that can be claimed
        uint256 claim = balance.mul(dividend.amount).div(dividend.totalSupply);

        // flag that dividends have been claimed
        dividend.claimed[msg.sender] = true;
        dividend.claimedAmount = SafeMath.add(dividend.claimedAmount, claim);

        claimThis(dividend.dividendType, _dividendIndex, msg.sender, claim, dividend.dividendToken);
    }

    /// @notice Claim all dividends.
    /// @dev dividendsClaimed counter should only increase when claimed in hole-free way.
    function claimDividendAll(Supply storage _self) public {
        claimLoopInternal(_self, _self.dividendsClaimed[msg.sender], (_self.dividends.length-1));
    }

    /// @notice Claim dividends in batches. In case claimDividendAll runs out of gas.
    /// @dev dividendsClaimed counter should only increase when claimed in hole-free way.
    /// @param _startIndex start index (inclusive number).
    /// @param _endIndex end index (inclusive number).
    function claimInBatches(Supply storage _self, uint256 _startIndex, uint256 _endIndex) public {
        claimLoopInternal(_self, _startIndex, _endIndex);
    }

    /// @notice Claim loop of batch claim and claim all.
    /// @dev dividendsClaimed counter should only increase when claimed in hole-free way.
    /// @param _startIndex start index (inclusive number).
    /// @param _endIndex end index (inclusive number).
    function claimLoopInternal(Supply storage _self, uint256 _startIndex, uint256 _endIndex) private {
        require(_startIndex <= _endIndex, "start after end");

        //early exit if already claimed
        require(_self.dividendsClaimed[msg.sender] < _self.dividends.length, "all claimed");

        uint256 dividendsClaimedTemp = _self.dividendsClaimed[msg.sender];

        // Cycle through all dividend distributions and make the payout
        for (uint256 i = _startIndex; i <= _endIndex; i++) {
            if (_self.dividends[i].recycled == true) {
                //recycled and tried to claim in continuous way internally counts as claimed
                dividendsClaimedTemp = SafeMath.add(i, 1);
            }
            else if (_self.dividends[i].claimed[msg.sender] == false) {
                dividendsClaimedTemp = SafeMath.add(i, 1);
                claimDividend(_self, i);
            }
        }

        // This is done after the loop to reduce writes.
        // Remembers what has been claimed after hole-free claiming procedure.
        // IMPORTANT: do only if batch claim docks on previous claim to avoid unexpected claim all behaviour.
        if(_startIndex <= _self.dividendsClaimed[msg.sender]) {
            _self.dividendsClaimed[msg.sender] = dividendsClaimedTemp;
        }
    }

    /// @notice Dividends which have not been claimed can be claimed by owner after timelock (to avoid loss)
    /// @param _dividendIndex index of dividend to recycle.
    /// @param _recycleLockedTimespan timespan required until possible.
    /// @param _currentSupply current supply.
    function recycleDividend(Supply storage _self, uint256 _dividendIndex, uint256 _recycleLockedTimespan, uint256 _currentSupply) public {
        // Get the dividend distribution
        Dividend storage dividend = _self.dividends[_dividendIndex];

        // should not have been recycled already
        require(dividend.recycled == false, "recycled");

        // The recycle time has to be over
        require(dividend.timestamp < SafeMath.sub(block.timestamp, _recycleLockedTimespan), "timeUp");

        // Devidends should not have been claimed already
        require(dividend.claimed[msg.sender] == false, "claimed");

        //
        //refund
        //

        // The amount, which has not been claimed is distributed to token owner
        _self.dividends[_dividendIndex].recycled = true;

        // calculates the amount of dividends that can be claimed
        uint256 claim = SafeMath.sub(dividend.amount, dividend.claimedAmount);

        // flag that dividends have been claimed
        dividend.claimed[msg.sender] = true;
        dividend.claimedAmount = SafeMath.add(dividend.claimedAmount, claim);

        claimThis(dividend.dividendType, _dividendIndex, msg.sender, claim, dividend.dividendToken);

        emit DividendRecycled(msg.sender, _self.tokenActionIndex, claim, _currentSupply, _dividendIndex);
    }

    /// @dev the core claim function of single dividend.
    function claimThis(DividendType _dividendType, uint256 _dividendIndex, address payable _beneficiary, uint256 _claim, address _dividendToken) 
    private 
    {
        // transfer the dividends to the token holder
        if (_claim > 0) {
            if (_dividendType == DividendType.Ether) { 
                _beneficiary.transfer(_claim);
            } 
            else if (_dividendType == DividendType.ERC20) { 
                require(ERC20(_dividendToken).transfer(_beneficiary, _claim), "transfer");
            }
            else {
                revert("unknown type");
            }

            emit DividendClaimed(_beneficiary, _dividendIndex, _claim);
        }
    }

    /// @notice If this contract gets a balance in some other ERC20 contract - or even iself - then we can rescue it.
    /// @param _foreignTokenAddress token where contract has balance.
    /// @param _to the beneficiary.
    function rescueToken(Availability storage _self, address _foreignTokenAddress, address _to) public
    {
        require(_self.mintingPhaseFinished, "unfinished");
        ERC20(_foreignTokenAddress).transfer(_to, ERC20(_foreignTokenAddress).balanceOf(address(this)));
    }

///////////////////
// Events (must be redundant in calling contract to work!)
///////////////////

    event Transfer(address indexed from, address indexed to, uint256 value);
    event SelfApprovedTransfer(address indexed initiator, address indexed from, address indexed to, uint256 value);
    event MintDetailed(address indexed initiator, address indexed to, uint256 amount);
    event MintFinished(address indexed initiator);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event TransferPaused(address indexed initiator);
    event TransferResumed(address indexed initiator);
    event MintingPaused(address indexed initiator);
    event MintingResumed(address indexed initiator);
    event Reopened(address indexed initiator);
    event DividendDeposited(address indexed depositor, uint256 transferAndMintIndex, uint256 amount, uint256 totalSupply, uint256 dividendIndex);
    event DividendClaimed(address indexed claimer, uint256 dividendIndex, uint256 claim);
    event DividendRecycled(address indexed recycler, uint256 transferAndMintIndex, uint256 amount, uint256 totalSupply, uint256 dividendIndex);
    event RolesChanged(address indexed initiator, address pauseControl, address tokenRescueControl);
    event MintControlChanged(address indexed initiator, address mintControl);
    event TokenActionIndexIncreased(uint256 tokenActionIndex, uint256 blocknumber);
}
/** @title AssetTokenL library. */
function getValueAt(Checkpoint[] storage checkpoints, uint256 _specificTransfersAndMintsIndex) private view returns (uint256) { 
        
        //  requested before a check point was ever created for this token
        if (checkpoints.length == 0 || checkpoints[0].currentTokenActionIndex > _specificTransfersAndMintsIndex) {
            return 0;
        }

        // Shortcut for the actual value
        if (_specificTransfersAndMintsIndex >= checkpoints[checkpoints.length-1].currentTokenActionIndex) {
            return checkpoints[checkpoints.length-1].value;
        }

        // Binary search of the value in the array
        uint256 min = 0;
        uint256 max = checkpoints.length-1;
        while (max > min) {
            uint256 mid = (max + min + 1)/2;
            if (checkpoints[mid].currentTokenActionIndex<=_specificTransfersAndMintsIndex) {
                min = mid;
            } else {
                max = mid-1;
            }
        }
        return checkpoints[min].value;
    }
////////////////
/// @dev `getValueAt` retrieves the number of tokens at a given index
/// @param checkpoints The history of values being queried
/// @param _specificTransfersAndMintsIndex The index to retrieve the history checkpoint value at
/// @return The number of tokens being queried