package d2grid

import 

// Row measurements preserve the original left-to-right floating-point results.
// Prefix sums are used only when every intermediate value is an exact integer.
type gridRowMeasurement struct {
	start, end    int
	size, withGap float64
}

type gridRowMeasurements struct {
	sizes          []float64
	gap            float64
	cache          []gridRowMeasurement
	prefixChecked  bool
	integralPrefix []float64
}

func newGridRowMeasurements( []float64,  float64) *gridRowMeasurements {
	return &gridRowMeasurements{sizes: , gap: }
}

func ( *gridRowMeasurements) (,  int) gridRowMeasurement {
	// Empty and singleton rows take constant time to measure. In particular,
	// a grid with one row per object should not allocate a large row cache.
	if - <= 1 {
		return .measure(, )
	}
	if !.prefixChecked {
		.initIntegralPrefix()
	}
	if .integralPrefix != nil {
		 := .integralPrefix[] - .integralPrefix[]
		return gridRowMeasurement{start: , end: , size: , withGap:  + .gap*float64(--1)}
	}
	if .cache == nil {
		// Bound the cache independently of diagram size; collisions only cause
		// recomputation. Small diagrams fit without collisions.
		const  = 32 * 1024
		 := 1
		for  <  && /len(.sizes) < len(.sizes) {
			 *= 2
		}
		.cache = make([]gridRowMeasurement, )
	}
	 := (uint()*uint(len(.sizes)) + uint()) & uint(len(.cache)-1)
	 := &.cache[]
	if .start ==  && .end ==  {
		return *
	}
	* = .measure(, )
	return *
}

func ( *gridRowMeasurements) () {
	.prefixChecked = true
	// All integers through 2^53 are represented exactly by float64. Requiring
	// nonnegative integral sizes and gaps, and bounding their complete sum,
	// also bounds every intermediate in the original row sums (including the
	// trailing gap before it is subtracted). Prefix subtraction and adding the
	// internal gaps therefore reproduce the original results exactly.
	const  = uint64(1 << 53)
	 := func( float64) bool {
		return  >= 0 &&  <= float64() && math.Trunc() == 
	}
	if !(.gap) {
		return
	}
	 := uint64(.gap)
	var  uint64
	for ,  := range .sizes {
		if !() {
			return
		}
		 := uint64()
		if  > - ||  > -- {
			return
		}
		 +=  + 
	}
	.integralPrefix = make([]float64, len(.sizes)+1)
	for ,  := range .sizes {
		.integralPrefix[+1] = .integralPrefix[] + 
	}
}

func ( *gridRowMeasurements) (,  int) gridRowMeasurement {
	,  := 0., 0.
	for ,  := range .sizes[:] {
		 += 
		 +=  + .gap
	}
	if  <  {
		 -= .gap
	}
	return gridRowMeasurement{start: , end: , size: , withGap: }
}

// The division and row checks run in their original order, including repeated
// checks: their attempt/skip counts affect when the bounded search terminates.
// The callback borrows cuts only until it returns.
type iterDivision func(division []int) (done bool)
type checkCut func(start, end int, starting bool) (ok bool)

func iterDivisions(,  int,  iterDivision,  checkCut) {
	if  < 2 ||  == 0 {
		return
	}
	 := make([]int, )
	var  func(,  int) bool
	 = func(,  int) bool {
		for  :=  - 1;  >= ; -- {
			if !(, , false) {
				continue
			}
			[-1] =  - 1
			if  > 1 {
				if (, -1) {
					return true
				}
			} else if (0, , true) && () {
				return true
			}
		}
		return false
	}
	(, )
}