code: public static double median(AssociativeArray2D survivalFunction) {
          Double ApointTi = null;
          Double BpointTi = null;
          
          int n = survivalFunction.size();
          if(n==0) {
              throw new IllegalArgumentException("The provided collection can't be empty.");
          } 
          
          for(Map.Entry<Object, AssociativeArray> entry : survivalFunction.entrySet()) {
              Object ti = entry.getKey();
              AssociativeArray row = entry.getValue();
              
              Double Sti = row.getDouble("Sti");
              
              if(Sti==null) {
                  continue; //skip censored
              }
              
              Double point = Double.valueOf(ti.toString());
              if(Math.abs(Sti-0.5) < 0.0000001) {
                  return point; //we found extactly the point
              }
              else if(Sti>0.5) {
                  ApointTi=point; //keep the point just before the 0.5 probability
              }
              else {
                  BpointTi=point; //keep the first point after the 0.5 probability and exit loop
                  break;
              }
          }
          
          if(n==1) {
              return (ApointTi!=null)?ApointTi:BpointTi;
          }
          else if(ApointTi == null || BpointTi == null) {
              throw new IllegalArgumentException("Invalid A and B points."); //we should never get here
          }
          
          double ApointTiValue = TypeInference.toDouble(survivalFunction.get2d(ApointTi.toString(), "Sti"));
          double BpointTiValue = TypeInference.toDouble(survivalFunction.get2d(BpointTi.toString(), "Sti"));
          double median=BpointTi-(BpointTiValue-0.5)*(BpointTi-ApointTi)/(BpointTiValue-ApointTiValue);
  
          return median;
      }
docs: Calculates median.
  
  @param survivalFunction
  @return