import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.Collections;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;

class Account {
    int id; int balance; KeyPair key;
    Account(int id, int balance, KeyPair key){
	this.id = id;
	this.balance = balance;
	this.key = key;
    }
    // begin provided
    byte[] sign(byte[] value){
	byte[] ret = null;
	try{
	    Signature sign = Signature.getInstance("SHA256withRSA");
	    sign.initSign(key.getPrivate());
	    sign.update(value,0,value.length);
	    ret = sign.sign();
	}catch(Exception e){} // ignore
	return ret;
    }
    boolean verify(byte[] value, byte[] signature){
	boolean ret = false;
	try{
	    Signature sign = Signature.getInstance("SHA256withRSA");
	    sign.initVerify(key.getPublic());
	    sign.update(value,0,value.length);
	    ret = sign.verify(signature);
	}catch(Exception e){} // ingore
	return ret;
    }
    @Override public String toString(){return "Account#"+id+"("+balance+")";};
    // end provided
}

class Database {
    Collection<Account> accounts;
    Database(Collection<Account> accounts){
	this.accounts = accounts;
    }
    // begin provided
    @Override public String toString(){return accounts.toString();};
    public int sum(){return accounts.stream().map(x->x.balance).collect(Collectors.summingInt(Integer::intValue));};
    // end provided
}

class Transaction{
    String id;
    Account from;
    Account to;
    int amount;
    byte[] signature;
    Transaction(String id, Account from, Account to, int amount){
	this.id  = id;
	this.from = from;
	this.to = to;
	this.amount = amount;
	this.signature = from.sign(id.getBytes());
    }
    // begin provided
    @Override public String toString(){return "Tranasction#"+id+"("+from.id+"-"+to.id+")";};
    // end provided
}

class Ledger{
    Database db;
    List<Transaction> blockchain;
    Ledger(Database db){
	this.db = db;
	blockchain = new ArrayList<>();
    }
    boolean isLegit(Transaction t){
	return db.accounts.contains(t.from)
	    && db.accounts.contains(t.to)
	    && t.from.verify(t.id.getBytes(),t.signature);
    }
    void execute(Transaction t) throws IllegalArgumentException{
	if(!isLegit(t))
	    throw new IllegalArgumentException("Invalid transaction!");
	t.from.balance -= t.amount;
	t.to.balance += t.amount;
	blockchain.add(t);
    }
    // begin provided
    @Override public String toString(){return db+" "+blockchain;};
    // end provided
}

class CCoin extends Ledger{
    // begin provided
    List<Integer> primeFactors(int number){
	List<Integer> ret = new ArrayList<>();
	for(int i = 2; i< i; i++) {
	    while(number%i == 0) {
		number = number/i;
	    }
	    ret.add(i);
	}
	return ret;
    }
    // end provided
    CCoin(Database db){
	super(db);
    }
    @Override
    void execute(Transaction t) throws IllegalArgumentException{
	primeFactors(blockchain.size());
	super.execute(t);
    }    
}

// begin provided
class Main{
    static final int ACCOUNTS=10;
    static final int TRANSACTIONS=10;
    public static void main(String[] args){
	// 1 - create ledger
	Collection<Account> accounts = new ArrayList<Account>();
	for(int i = 0; i<ACCOUNTS; i++){
	    accounts.add(new Account(i,100,newKeyPair()));
	}
	Database db = new Database(accounts);
	Ledger ledger = new CCoin(db);
	
	// 2 - run transactions
	System.out.println(ledger);
	System.out.println(ledger.db.sum());
	for(int i = 0; i<TRANSACTIONS; i++){	    
	    List<Account> shuffled  = new ArrayList<>(db.accounts);
	    Collections.shuffle(shuffled);
	    Account from = shuffled.get(1);
	    Account to = shuffled.get(2);
	    Transaction t = new Transaction(Integer.toString(i),from,to,1);
	    ledger.execute(t);
	    System.out.println("Executing "+t);
	}

	// 3 - check its content
	System.out.println(ledger);
	System.out.println(ledger.db.sum());
    }
    static KeyPair newKeyPair(){
	KeyPair ret = null;
	try{
	    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
	    kpg.initialize(2048);
	    ret = kpg.generateKeyPair();
	}catch(Exception e){} // ignore	
	return ret; 
    }
}
// end provided
