# SchellingCoin implementation
#
# Epoch length: 100 blocks
# Target savings depletion rate: 0.1% per epoch

data epoch
data hashes_submitted
data output
data quicksort_pairs
data accounts[2^160]
data submissions[2^80](hash, deposit, address, value)
extern any: [call]


def init():
    self.epoch = block.number / 100
    self.quicksort_pairs = create('quicksort_pairs.se')

def any():
    if block.number / 100 > epoch:
        # Sort all values submitted
        N = self.hashes_submitted
        o = array(N * 2)
        i = 0
        j = 0
        while i < N:
            v = self.submissions[i].value
            if v:
                o[j] = v
                o[j + 1] = i
                j += 2
            i += 1
        values = self.quicksort_pairs.call(data=o, datasz=j, outsz=j)

        # Calculate total deposit, refund non-submitters and
        # cleanup

        deposits = array(j / 2)
        addresses = array(j / 2)
        
        i = 0
        total_deposit = 0
        while i < j / 2:
            base_index = HASHES + values[i * 2 + 1] * 3
            deposits[i] = self.submissions[i].deposit
            addresses[i] = self.submissions[i].address
            if self.submissions[values[i * 2 + 1]].value:
                total_deposit += deposits[i]
            else:
                send(addresses[i], deposits[i] * 999 / 1000)
            i += 1

        inverse_profit_ratio = total_deposit / (contract.balance / 1000) + 1

        # Reward everyone
        i = 0
        running_deposit_sum = 0
        halfway_passed = 0
        while i < j / 2:
            new_deposit_sum = running_deposit_sum + deposits[i]
            if new_deposit_sum > total_deposit / 4 and running_deposit_sum < total_deposit * 3 / 4:
                send(addresses[i], deposits[i] + deposits[i] / inverse_profit_ratio * 2)
            else:
                send(addresses[i], deposits[i] - deposits[i] / inverse_profit_ratio)

            if not halfway_passed and new_deposit_sum > total_deposit / 2:
                self.output = self.submissions[i].value
                halfway_passed = 1
            self.submissions[i].value = 0
            running_deposit_sum = new_deposit_sum
            i += 1
        self.epoch = block.number / 100
        self.hashes_submitted = 0
        
def submit_hash(h):
    if block.number % 100 < 50:
        cur = self.hashes_submitted
        pos = HASHES + cur * 3
        self.submissions[cur].hash = h
        self.submissions[cur].deposit = msg.value
        self.submissions[cur].address = msg.sender
        self.hashes_submitted = cur + 1
        return(cur)

def submit_value(index, v):
    if sha3([msg.sender, v], 2) == self.submissions[index].hash:
            self.submissions[index].value = v
            return(1)

def request_balance():
    return(contract.balance)

def request_output():
    return(self.output)
