code: package org.sunflow.core;
  
  import org.sunflow.image.Color;
  
  public class ShadingCache {
      private Sample first;
      private int depth;
      // stats
      long hits;
      long misses;
      long sumDepth;
      long numCaches;
  
      private static class Sample {
          Instance i;
          Shader s;
          float nx, ny, nz;
          float dx, dy, dz;
          Color c;
          Sample next; // linked list
      }
  
      public ShadingCache() {
          reset();
          hits = 0;
          misses = 0;
      }
  
      public void reset() {
          sumDepth += depth;
          if (depth > 0)
              numCaches++;
          first = null;
          depth = 0;
      }
  
      public Color lookup(ShadingState state, Shader shader) {
          if (state.getNormal() == null)
              return null;
          // search further
          for (Sample s = first; s != null; s = s.next) {
              if (s.i != state.getInstance())
                  continue;
              if (s.s != shader)
                  continue;
              if (state.getRay().dot(s.dx, s.dy, s.dz) < 0.999f)
                  continue;
              if (state.getNormal().dot(s.nx, s.ny, s.nz) < 0.99f)
                  continue;
              // we have a match
              hits++;
              return s.c;
          }
          misses++;
          return null;
      }
  
      public void add(ShadingState state, Shader shader, Color c) {
          if (state.getNormal() == null)
              return;
          depth++;
          Sample s = new Sample();
          s.i = state.getInstance();
          s.s = shader;
          s.c = c;
          s.dx = state.getRay().dx;
          s.dy = state.getRay().dy;
          s.dz = state.getRay().dz;
          s.nx = state.getNormal().x;
          s.ny = state.getNormal().y;
          s.nz = state.getNormal().z;
          s.next = first;
          first = s;
      }
  }