PerParticleFeeEntry implements FeeEntry { @Override public UInt256 feeFor(Atom a, int feeSize, Set<Particle> outputs) { long particleCount = outputs.stream() .filter(this.particleType::isInstance) .count(); if (particleCount <= this.threshold) { return UInt256.ZERO; } long overThresholdParticles = particleCount - this.threshold; UInt384 totalFee = UInt384.from(this.fee).multiply(UInt256.from(overThresholdParticles)); if (!totalFee.getHigh().isZero()) { throw new ArithmeticException("Fee overflow"); } return totalFee.getLow(); } private  PerParticleFeeEntry(long threshold, Class<? extends Particle> particleType, UInt256 fee); static PerParticleFeeEntry of(Class<? extends Particle> particleType, long threshold, UInt256 fee); long threshold(); Class<? extends Particle> particleType(); UInt256 fee(); @Override UInt256 feeFor(Atom a, int feeSize, Set<Particle> outputs); @Override int hashCode(); @Override boolean equals(Object obj); @Override String toString();  }
@Test public void testFeeForAtom() { PerParticleFeeEntry f = get(); Set<Particle> outputs = Sets.newHashSet(); RadixAddress addr = new RadixAddress((byte) 0, ECKeyPair.generateNew().getPublicKey()); assertEquals(UInt256.ZERO, f.feeFor(null, 0, outputs)); outputs.add(MessageParticle.builder().from(addr).to(addr).payload("Particle 1".getBytes()).build()); assertEquals(UInt256.ZERO, f.feeFor(null, 0, outputs)); outputs.add(MessageParticle.builder().from(addr).to(addr).payload("Particle 2".getBytes()).build()); assertEquals(FEE, f.feeFor(null, 0, outputs)); outputs.add(MessageParticle.builder().from(addr).to(addr).payload("Particle 3".getBytes()).build()); assertEquals(FEE.multiply(UInt256.TWO), f.feeFor(null, 0, outputs)); }