1  
     2  
     3  
     4  
     5  package trace
     6  
     7  import (
     8  	"math"
     9  	"sort"
    10  )
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  type mud struct {
    27  	sorted, unsorted []edge
    28  
    29  	
    30  	
    31  	trackMass float64
    32  	
    33  	
    34  	
    35  	trackBucket int
    36  	
    37  	
    38  	trackSum float64
    39  
    40  	
    41  	hist [mudDegree]float64
    42  }
    43  
    44  const (
    45  	
    46  	
    47  	mudDegree = 1024
    48  )
    49  
    50  type edge struct {
    51  	
    52  	x, delta float64
    53  	
    54  	dirac float64
    55  }
    56  
    57  
    58  
    59  func (d *mud) add(l, r, area float64) {
    60  	if area == 0 {
    61  		return
    62  	}
    63  
    64  	if r < l {
    65  		l, r = r, l
    66  	}
    67  
    68  	
    69  	if l == r {
    70  		d.unsorted = append(d.unsorted, edge{l, 0, area})
    71  	} else {
    72  		delta := area / (r - l)
    73  		d.unsorted = append(d.unsorted, edge{l, delta, 0}, edge{r, -delta, 0})
    74  	}
    75  
    76  	
    77  	h := &d.hist
    78  	lbFloat, lf := math.Modf(l * mudDegree)
    79  	lb := int(lbFloat)
    80  	if lb >= mudDegree {
    81  		lb, lf = mudDegree-1, 1
    82  	}
    83  	if l == r {
    84  		h[lb] += area
    85  	} else {
    86  		rbFloat, rf := math.Modf(r * mudDegree)
    87  		rb := int(rbFloat)
    88  		if rb >= mudDegree {
    89  			rb, rf = mudDegree-1, 1
    90  		}
    91  		if lb == rb {
    92  			h[lb] += area
    93  		} else {
    94  			perBucket := area / (r - l) / mudDegree
    95  			h[lb] += perBucket * (1 - lf)
    96  			h[rb] += perBucket * rf
    97  			for i := lb + 1; i < rb; i++ {
    98  				h[i] += perBucket
    99  			}
   100  		}
   101  	}
   102  
   103  	
   104  	if thresh := float64(d.trackBucket) / mudDegree; l < thresh {
   105  		if r < thresh {
   106  			d.trackSum += area
   107  		} else {
   108  			d.trackSum += area * (thresh - l) / (r - l)
   109  		}
   110  		if d.trackSum >= d.trackMass {
   111  			
   112  			
   113  			d.setTrackMass(d.trackMass)
   114  		}
   115  	}
   116  }
   117  
   118  
   119  
   120  
   121  
   122  
   123  func (d *mud) setTrackMass(mass float64) {
   124  	d.trackMass = mass
   125  
   126  	
   127  	
   128  	sum := 0.0
   129  	for i, val := range d.hist[:] {
   130  		newSum := sum + val
   131  		if newSum > mass {
   132  			
   133  			d.trackBucket = i
   134  			d.trackSum = sum
   135  			return
   136  		}
   137  		sum = newSum
   138  	}
   139  	d.trackBucket = len(d.hist)
   140  	d.trackSum = sum
   141  }
   142  
   143  
   144  
   145  
   146  
   147  
   148  func (d *mud) approxInvCumulativeSum() (float64, float64, bool) {
   149  	if d.trackBucket == len(d.hist) {
   150  		return math.NaN(), math.NaN(), false
   151  	}
   152  	return float64(d.trackBucket) / mudDegree, float64(d.trackBucket+1) / mudDegree, true
   153  }
   154  
   155  
   156  
   157  
   158  
   159  
   160  
   161  
   162  func (d *mud) invCumulativeSum(y float64) (float64, bool) {
   163  	if len(d.sorted) == 0 && len(d.unsorted) == 0 {
   164  		return math.NaN(), false
   165  	}
   166  
   167  	
   168  	edges := d.unsorted
   169  	sort.Slice(edges, func(i, j int) bool {
   170  		return edges[i].x < edges[j].x
   171  	})
   172  	
   173  	d.unsorted = nil
   174  	if d.sorted == nil {
   175  		d.sorted = edges
   176  	} else {
   177  		oldSorted := d.sorted
   178  		newSorted := make([]edge, len(oldSorted)+len(edges))
   179  		i, j := 0, 0
   180  		for o := range newSorted {
   181  			if i >= len(oldSorted) {
   182  				copy(newSorted[o:], edges[j:])
   183  				break
   184  			} else if j >= len(edges) {
   185  				copy(newSorted[o:], oldSorted[i:])
   186  				break
   187  			} else if oldSorted[i].x < edges[j].x {
   188  				newSorted[o] = oldSorted[i]
   189  				i++
   190  			} else {
   191  				newSorted[o] = edges[j]
   192  				j++
   193  			}
   194  		}
   195  		d.sorted = newSorted
   196  	}
   197  
   198  	
   199  	csum, rate, prevX := 0.0, 0.0, 0.0
   200  	for _, e := range d.sorted {
   201  		newCsum := csum + (e.x-prevX)*rate
   202  		if newCsum >= y {
   203  			
   204  			
   205  			if rate == 0 {
   206  				
   207  				
   208  				
   209  				
   210  				return e.x, true
   211  			}
   212  			return (y-csum)/rate + prevX, true
   213  		}
   214  		newCsum += e.dirac
   215  		if newCsum >= y {
   216  			
   217  			return e.x, true
   218  		}
   219  		csum, prevX = newCsum, e.x
   220  		rate += e.delta
   221  	}
   222  	return prevX, false
   223  }
   224  
View as plain text