barometer: update DMA's vendoring packages
[barometer.git] / src / dma / vendor / golang.org / x / text / unicode / bidi / core.go
diff --git a/src/dma/vendor/golang.org/x/text/unicode/bidi/core.go b/src/dma/vendor/golang.org/x/text/unicode/bidi/core.go
new file mode 100644 (file)
index 0000000..48d1440
--- /dev/null
@@ -0,0 +1,1058 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bidi
+
+import "log"
+
+// This implementation is a port based on the reference implementation found at:
+// https://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/
+//
+// described in Unicode Bidirectional Algorithm (UAX #9).
+//
+// Input:
+// There are two levels of input to the algorithm, since clients may prefer to
+// supply some information from out-of-band sources rather than relying on the
+// default behavior.
+//
+// - Bidi class array
+// - Bidi class array, with externally supplied base line direction
+//
+// Output:
+// Output is separated into several stages:
+//
+//  - levels array over entire paragraph
+//  - reordering array over entire paragraph
+//  - levels array over line
+//  - reordering array over line
+//
+// Note that for conformance to the Unicode Bidirectional Algorithm,
+// implementations are only required to generate correct reordering and
+// character directionality (odd or even levels) over a line. Generating
+// identical level arrays over a line is not required. Bidi explicit format
+// codes (LRE, RLE, LRO, RLO, PDF) and BN can be assigned arbitrary levels and
+// positions as long as the rest of the input is properly reordered.
+//
+// As the algorithm is defined to operate on a single paragraph at a time, this
+// implementation is written to handle single paragraphs. Thus rule P1 is
+// presumed by this implementation-- the data provided to the implementation is
+// assumed to be a single paragraph, and either contains no 'B' codes, or a
+// single 'B' code at the end of the input. 'B' is allowed as input to
+// illustrate how the algorithm assigns it a level.
+//
+// Also note that rules L3 and L4 depend on the rendering engine that uses the
+// result of the bidi algorithm. This implementation assumes that the rendering
+// engine expects combining marks in visual order (e.g. to the left of their
+// base character in RTL runs) and that it adjusts the glyphs used to render
+// mirrored characters that are in RTL runs so that they render appropriately.
+
+// level is the embedding level of a character. Even embedding levels indicate
+// left-to-right order and odd levels indicate right-to-left order. The special
+// level of -1 is reserved for undefined order.
+type level int8
+
+const implicitLevel level = -1
+
+// in returns if x is equal to any of the values in set.
+func (c Class) in(set ...Class) bool {
+       for _, s := range set {
+               if c == s {
+                       return true
+               }
+       }
+       return false
+}
+
+// A paragraph contains the state of a paragraph.
+type paragraph struct {
+       initialTypes []Class
+
+       // Arrays of properties needed for paired bracket evaluation in N0
+       pairTypes  []bracketType // paired Bracket types for paragraph
+       pairValues []rune        // rune for opening bracket or pbOpen and pbClose; 0 for pbNone
+
+       embeddingLevel level // default: = implicitLevel;
+
+       // at the paragraph levels
+       resultTypes  []Class
+       resultLevels []level
+
+       // Index of matching PDI for isolate initiator characters. For other
+       // characters, the value of matchingPDI will be set to -1. For isolate
+       // initiators with no matching PDI, matchingPDI will be set to the length of
+       // the input string.
+       matchingPDI []int
+
+       // Index of matching isolate initiator for PDI characters. For other
+       // characters, and for PDIs with no matching isolate initiator, the value of
+       // matchingIsolateInitiator will be set to -1.
+       matchingIsolateInitiator []int
+}
+
+// newParagraph initializes a paragraph. The user needs to supply a few arrays
+// corresponding to the preprocessed text input. The types correspond to the
+// Unicode BiDi classes for each rune. pairTypes indicates the bracket type for
+// each rune. pairValues provides a unique bracket class identifier for each
+// rune (suggested is the rune of the open bracket for opening and matching
+// close brackets, after normalization). The embedding levels are optional, but
+// may be supplied to encode embedding levels of styled text.
+//
+// TODO: return an error.
+func newParagraph(types []Class, pairTypes []bracketType, pairValues []rune, levels level) *paragraph {
+       validateTypes(types)
+       validatePbTypes(pairTypes)
+       validatePbValues(pairValues, pairTypes)
+       validateParagraphEmbeddingLevel(levels)
+
+       p := &paragraph{
+               initialTypes:   append([]Class(nil), types...),
+               embeddingLevel: levels,
+
+               pairTypes:  pairTypes,
+               pairValues: pairValues,
+
+               resultTypes: append([]Class(nil), types...),
+       }
+       p.run()
+       return p
+}
+
+func (p *paragraph) Len() int { return len(p.initialTypes) }
+
+// The algorithm. Does not include line-based processing (Rules L1, L2).
+// These are applied later in the line-based phase of the algorithm.
+func (p *paragraph) run() {
+       p.determineMatchingIsolates()
+
+       // 1) determining the paragraph level
+       // Rule P1 is the requirement for entering this algorithm.
+       // Rules P2, P3.
+       // If no externally supplied paragraph embedding level, use default.
+       if p.embeddingLevel == implicitLevel {
+               p.embeddingLevel = p.determineParagraphEmbeddingLevel(0, p.Len())
+       }
+
+       // Initialize result levels to paragraph embedding level.
+       p.resultLevels = make([]level, p.Len())
+       setLevels(p.resultLevels, p.embeddingLevel)
+
+       // 2) Explicit levels and directions
+       // Rules X1-X8.
+       p.determineExplicitEmbeddingLevels()
+
+       // Rule X9.
+       // We do not remove the embeddings, the overrides, the PDFs, and the BNs
+       // from the string explicitly. But they are not copied into isolating run
+       // sequences when they are created, so they are removed for all
+       // practical purposes.
+
+       // Rule X10.
+       // Run remainder of algorithm one isolating run sequence at a time
+       for _, seq := range p.determineIsolatingRunSequences() {
+               // 3) resolving weak types
+               // Rules W1-W7.
+               seq.resolveWeakTypes()
+
+               // 4a) resolving paired brackets
+               // Rule N0
+               resolvePairedBrackets(seq)
+
+               // 4b) resolving neutral types
+               // Rules N1-N3.
+               seq.resolveNeutralTypes()
+
+               // 5) resolving implicit embedding levels
+               // Rules I1, I2.
+               seq.resolveImplicitLevels()
+
+               // Apply the computed levels and types
+               seq.applyLevelsAndTypes()
+       }
+
+       // Assign appropriate levels to 'hide' LREs, RLEs, LROs, RLOs, PDFs, and
+       // BNs. This is for convenience, so the resulting level array will have
+       // a value for every character.
+       p.assignLevelsToCharactersRemovedByX9()
+}
+
+// determineMatchingIsolates determines the matching PDI for each isolate
+// initiator and vice versa.
+//
+// Definition BD9.
+//
+// At the end of this function:
+//
+//  - The member variable matchingPDI is set to point to the index of the
+//    matching PDI character for each isolate initiator character. If there is
+//    no matching PDI, it is set to the length of the input text. For other
+//    characters, it is set to -1.
+//  - The member variable matchingIsolateInitiator is set to point to the
+//    index of the matching isolate initiator character for each PDI character.
+//    If there is no matching isolate initiator, or the character is not a PDI,
+//    it is set to -1.
+func (p *paragraph) determineMatchingIsolates() {
+       p.matchingPDI = make([]int, p.Len())
+       p.matchingIsolateInitiator = make([]int, p.Len())
+
+       for i := range p.matchingIsolateInitiator {
+               p.matchingIsolateInitiator[i] = -1
+       }
+
+       for i := range p.matchingPDI {
+               p.matchingPDI[i] = -1
+
+               if t := p.resultTypes[i]; t.in(LRI, RLI, FSI) {
+                       depthCounter := 1
+                       for j := i + 1; j < p.Len(); j++ {
+                               if u := p.resultTypes[j]; u.in(LRI, RLI, FSI) {
+                                       depthCounter++
+                               } else if u == PDI {
+                                       if depthCounter--; depthCounter == 0 {
+                                               p.matchingPDI[i] = j
+                                               p.matchingIsolateInitiator[j] = i
+                                               break
+                                       }
+                               }
+                       }
+                       if p.matchingPDI[i] == -1 {
+                               p.matchingPDI[i] = p.Len()
+                       }
+               }
+       }
+}
+
+// determineParagraphEmbeddingLevel reports the resolved paragraph direction of
+// the substring limited by the given range [start, end).
+//
+// Determines the paragraph level based on rules P2, P3. This is also used
+// in rule X5c to find if an FSI should resolve to LRI or RLI.
+func (p *paragraph) determineParagraphEmbeddingLevel(start, end int) level {
+       var strongType Class = unknownClass
+
+       // Rule P2.
+       for i := start; i < end; i++ {
+               if t := p.resultTypes[i]; t.in(L, AL, R) {
+                       strongType = t
+                       break
+               } else if t.in(FSI, LRI, RLI) {
+                       i = p.matchingPDI[i] // skip over to the matching PDI
+                       if i > end {
+                               log.Panic("assert (i <= end)")
+                       }
+               }
+       }
+       // Rule P3.
+       switch strongType {
+       case unknownClass: // none found
+               // default embedding level when no strong types found is 0.
+               return 0
+       case L:
+               return 0
+       default: // AL, R
+               return 1
+       }
+}
+
+const maxDepth = 125
+
+// This stack will store the embedding levels and override and isolated
+// statuses
+type directionalStatusStack struct {
+       stackCounter        int
+       embeddingLevelStack [maxDepth + 1]level
+       overrideStatusStack [maxDepth + 1]Class
+       isolateStatusStack  [maxDepth + 1]bool
+}
+
+func (s *directionalStatusStack) empty()     { s.stackCounter = 0 }
+func (s *directionalStatusStack) pop()       { s.stackCounter-- }
+func (s *directionalStatusStack) depth() int { return s.stackCounter }
+
+func (s *directionalStatusStack) push(level level, overrideStatus Class, isolateStatus bool) {
+       s.embeddingLevelStack[s.stackCounter] = level
+       s.overrideStatusStack[s.stackCounter] = overrideStatus
+       s.isolateStatusStack[s.stackCounter] = isolateStatus
+       s.stackCounter++
+}
+
+func (s *directionalStatusStack) lastEmbeddingLevel() level {
+       return s.embeddingLevelStack[s.stackCounter-1]
+}
+
+func (s *directionalStatusStack) lastDirectionalOverrideStatus() Class {
+       return s.overrideStatusStack[s.stackCounter-1]
+}
+
+func (s *directionalStatusStack) lastDirectionalIsolateStatus() bool {
+       return s.isolateStatusStack[s.stackCounter-1]
+}
+
+// Determine explicit levels using rules X1 - X8
+func (p *paragraph) determineExplicitEmbeddingLevels() {
+       var stack directionalStatusStack
+       var overflowIsolateCount, overflowEmbeddingCount, validIsolateCount int
+
+       // Rule X1.
+       stack.push(p.embeddingLevel, ON, false)
+
+       for i, t := range p.resultTypes {
+               // Rules X2, X3, X4, X5, X5a, X5b, X5c
+               switch t {
+               case RLE, LRE, RLO, LRO, RLI, LRI, FSI:
+                       isIsolate := t.in(RLI, LRI, FSI)
+                       isRTL := t.in(RLE, RLO, RLI)
+
+                       // override if this is an FSI that resolves to RLI
+                       if t == FSI {
+                               isRTL = (p.determineParagraphEmbeddingLevel(i+1, p.matchingPDI[i]) == 1)
+                       }
+                       if isIsolate {
+                               p.resultLevels[i] = stack.lastEmbeddingLevel()
+                               if stack.lastDirectionalOverrideStatus() != ON {
+                                       p.resultTypes[i] = stack.lastDirectionalOverrideStatus()
+                               }
+                       }
+
+                       var newLevel level
+                       if isRTL {
+                               // least greater odd
+                               newLevel = (stack.lastEmbeddingLevel() + 1) | 1
+                       } else {
+                               // least greater even
+                               newLevel = (stack.lastEmbeddingLevel() + 2) &^ 1
+                       }
+
+                       if newLevel <= maxDepth && overflowIsolateCount == 0 && overflowEmbeddingCount == 0 {
+                               if isIsolate {
+                                       validIsolateCount++
+                               }
+                               // Push new embedding level, override status, and isolated
+                               // status.
+                               // No check for valid stack counter, since the level check
+                               // suffices.
+                               switch t {
+                               case LRO:
+                                       stack.push(newLevel, L, isIsolate)
+                               case RLO:
+                                       stack.push(newLevel, R, isIsolate)
+                               default:
+                                       stack.push(newLevel, ON, isIsolate)
+                               }
+                               // Not really part of the spec
+                               if !isIsolate {
+                                       p.resultLevels[i] = newLevel
+                               }
+                       } else {
+                               // This is an invalid explicit formatting character,
+                               // so apply the "Otherwise" part of rules X2-X5b.
+                               if isIsolate {
+                                       overflowIsolateCount++
+                               } else { // !isIsolate
+                                       if overflowIsolateCount == 0 {
+                                               overflowEmbeddingCount++
+                                       }
+                               }
+                       }
+
+               // Rule X6a
+               case PDI:
+                       if overflowIsolateCount > 0 {
+                               overflowIsolateCount--
+                       } else if validIsolateCount == 0 {
+                               // do nothing
+                       } else {
+                               overflowEmbeddingCount = 0
+                               for !stack.lastDirectionalIsolateStatus() {
+                                       stack.pop()
+                               }
+                               stack.pop()
+                               validIsolateCount--
+                       }
+                       p.resultLevels[i] = stack.lastEmbeddingLevel()
+
+               // Rule X7
+               case PDF:
+                       // Not really part of the spec
+                       p.resultLevels[i] = stack.lastEmbeddingLevel()
+
+                       if overflowIsolateCount > 0 {
+                               // do nothing
+                       } else if overflowEmbeddingCount > 0 {
+                               overflowEmbeddingCount--
+                       } else if !stack.lastDirectionalIsolateStatus() && stack.depth() >= 2 {
+                               stack.pop()
+                       }
+
+               case B: // paragraph separator.
+                       // Rule X8.
+
+                       // These values are reset for clarity, in this implementation B
+                       // can only occur as the last code in the array.
+                       stack.empty()
+                       overflowIsolateCount = 0
+                       overflowEmbeddingCount = 0
+                       validIsolateCount = 0
+                       p.resultLevels[i] = p.embeddingLevel
+
+               default:
+                       p.resultLevels[i] = stack.lastEmbeddingLevel()
+                       if stack.lastDirectionalOverrideStatus() != ON {
+                               p.resultTypes[i] = stack.lastDirectionalOverrideStatus()
+                       }
+               }
+       }
+}
+
+type isolatingRunSequence struct {
+       p *paragraph
+
+       indexes []int // indexes to the original string
+
+       types          []Class // type of each character using the index
+       resolvedLevels []level // resolved levels after application of rules
+       level          level
+       sos, eos       Class
+}
+
+func (i *isolatingRunSequence) Len() int { return len(i.indexes) }
+
+func maxLevel(a, b level) level {
+       if a > b {
+               return a
+       }
+       return b
+}
+
+// Rule X10, second bullet: Determine the start-of-sequence (sos) and end-of-sequence (eos) types,
+//                      either L or R, for each isolating run sequence.
+func (p *paragraph) isolatingRunSequence(indexes []int) *isolatingRunSequence {
+       length := len(indexes)
+       types := make([]Class, length)
+       for i, x := range indexes {
+               types[i] = p.resultTypes[x]
+       }
+
+       // assign level, sos and eos
+       prevChar := indexes[0] - 1
+       for prevChar >= 0 && isRemovedByX9(p.initialTypes[prevChar]) {
+               prevChar--
+       }
+       prevLevel := p.embeddingLevel
+       if prevChar >= 0 {
+               prevLevel = p.resultLevels[prevChar]
+       }
+
+       var succLevel level
+       lastType := types[length-1]
+       if lastType.in(LRI, RLI, FSI) {
+               succLevel = p.embeddingLevel
+       } else {
+               // the first character after the end of run sequence
+               limit := indexes[length-1] + 1
+               for ; limit < p.Len() && isRemovedByX9(p.initialTypes[limit]); limit++ {
+
+               }
+               succLevel = p.embeddingLevel
+               if limit < p.Len() {
+                       succLevel = p.resultLevels[limit]
+               }
+       }
+       level := p.resultLevels[indexes[0]]
+       return &isolatingRunSequence{
+               p:       p,
+               indexes: indexes,
+               types:   types,
+               level:   level,
+               sos:     typeForLevel(maxLevel(prevLevel, level)),
+               eos:     typeForLevel(maxLevel(succLevel, level)),
+       }
+}
+
+// Resolving weak types Rules W1-W7.
+//
+// Note that some weak types (EN, AN) remain after this processing is
+// complete.
+func (s *isolatingRunSequence) resolveWeakTypes() {
+
+       // on entry, only these types remain
+       s.assertOnly(L, R, AL, EN, ES, ET, AN, CS, B, S, WS, ON, NSM, LRI, RLI, FSI, PDI)
+
+       // Rule W1.
+       // Changes all NSMs.
+       preceedingCharacterType := s.sos
+       for i, t := range s.types {
+               if t == NSM {
+                       s.types[i] = preceedingCharacterType
+               } else {
+                       if t.in(LRI, RLI, FSI, PDI) {
+                               preceedingCharacterType = ON
+                       }
+                       preceedingCharacterType = t
+               }
+       }
+
+       // Rule W2.
+       // EN does not change at the start of the run, because sos != AL.
+       for i, t := range s.types {
+               if t == EN {
+                       for j := i - 1; j >= 0; j-- {
+                               if t := s.types[j]; t.in(L, R, AL) {
+                                       if t == AL {
+                                               s.types[i] = AN
+                                       }
+                                       break
+                               }
+                       }
+               }
+       }
+
+       // Rule W3.
+       for i, t := range s.types {
+               if t == AL {
+                       s.types[i] = R
+               }
+       }
+
+       // Rule W4.
+       // Since there must be values on both sides for this rule to have an
+       // effect, the scan skips the first and last value.
+       //
+       // Although the scan proceeds left to right, and changes the type
+       // values in a way that would appear to affect the computations
+       // later in the scan, there is actually no problem. A change in the
+       // current value can only affect the value to its immediate right,
+       // and only affect it if it is ES or CS. But the current value can
+       // only change if the value to its right is not ES or CS. Thus
+       // either the current value will not change, or its change will have
+       // no effect on the remainder of the analysis.
+
+       for i := 1; i < s.Len()-1; i++ {
+               t := s.types[i]
+               if t == ES || t == CS {
+                       prevSepType := s.types[i-1]
+                       succSepType := s.types[i+1]
+                       if prevSepType == EN && succSepType == EN {
+                               s.types[i] = EN
+                       } else if s.types[i] == CS && prevSepType == AN && succSepType == AN {
+                               s.types[i] = AN
+                       }
+               }
+       }
+
+       // Rule W5.
+       for i, t := range s.types {
+               if t == ET {
+                       // locate end of sequence
+                       runStart := i
+                       runEnd := s.findRunLimit(runStart, ET)
+
+                       // check values at ends of sequence
+                       t := s.sos
+                       if runStart > 0 {
+                               t = s.types[runStart-1]
+                       }
+                       if t != EN {
+                               t = s.eos
+                               if runEnd < len(s.types) {
+                                       t = s.types[runEnd]
+                               }
+                       }
+                       if t == EN {
+                               setTypes(s.types[runStart:runEnd], EN)
+                       }
+                       // continue at end of sequence
+                       i = runEnd
+               }
+       }
+
+       // Rule W6.
+       for i, t := range s.types {
+               if t.in(ES, ET, CS) {
+                       s.types[i] = ON
+               }
+       }
+
+       // Rule W7.
+       for i, t := range s.types {
+               if t == EN {
+                       // set default if we reach start of run
+                       prevStrongType := s.sos
+                       for j := i - 1; j >= 0; j-- {
+                               t = s.types[j]
+                               if t == L || t == R { // AL's have been changed to R
+                                       prevStrongType = t
+                                       break
+                               }
+                       }
+                       if prevStrongType == L {
+                               s.types[i] = L
+                       }
+               }
+       }
+}
+
+// 6) resolving neutral types Rules N1-N2.
+func (s *isolatingRunSequence) resolveNeutralTypes() {
+
+       // on entry, only these types can be in resultTypes
+       s.assertOnly(L, R, EN, AN, B, S, WS, ON, RLI, LRI, FSI, PDI)
+
+       for i, t := range s.types {
+               switch t {
+               case WS, ON, B, S, RLI, LRI, FSI, PDI:
+                       // find bounds of run of neutrals
+                       runStart := i
+                       runEnd := s.findRunLimit(runStart, B, S, WS, ON, RLI, LRI, FSI, PDI)
+
+                       // determine effective types at ends of run
+                       var leadType, trailType Class
+
+                       // Note that the character found can only be L, R, AN, or
+                       // EN.
+                       if runStart == 0 {
+                               leadType = s.sos
+                       } else {
+                               leadType = s.types[runStart-1]
+                               if leadType.in(AN, EN) {
+                                       leadType = R
+                               }
+                       }
+                       if runEnd == len(s.types) {
+                               trailType = s.eos
+                       } else {
+                               trailType = s.types[runEnd]
+                               if trailType.in(AN, EN) {
+                                       trailType = R
+                               }
+                       }
+
+                       var resolvedType Class
+                       if leadType == trailType {
+                               // Rule N1.
+                               resolvedType = leadType
+                       } else {
+                               // Rule N2.
+                               // Notice the embedding level of the run is used, not
+                               // the paragraph embedding level.
+                               resolvedType = typeForLevel(s.level)
+                       }
+
+                       setTypes(s.types[runStart:runEnd], resolvedType)
+
+                       // skip over run of (former) neutrals
+                       i = runEnd
+               }
+       }
+}
+
+func setLevels(levels []level, newLevel level) {
+       for i := range levels {
+               levels[i] = newLevel
+       }
+}
+
+func setTypes(types []Class, newType Class) {
+       for i := range types {
+               types[i] = newType
+       }
+}
+
+// 7) resolving implicit embedding levels Rules I1, I2.
+func (s *isolatingRunSequence) resolveImplicitLevels() {
+
+       // on entry, only these types can be in resultTypes
+       s.assertOnly(L, R, EN, AN)
+
+       s.resolvedLevels = make([]level, len(s.types))
+       setLevels(s.resolvedLevels, s.level)
+
+       if (s.level & 1) == 0 { // even level
+               for i, t := range s.types {
+                       // Rule I1.
+                       if t == L {
+                               // no change
+                       } else if t == R {
+                               s.resolvedLevels[i] += 1
+                       } else { // t == AN || t == EN
+                               s.resolvedLevels[i] += 2
+                       }
+               }
+       } else { // odd level
+               for i, t := range s.types {
+                       // Rule I2.
+                       if t == R {
+                               // no change
+                       } else { // t == L || t == AN || t == EN
+                               s.resolvedLevels[i] += 1
+                       }
+               }
+       }
+}
+
+// Applies the levels and types resolved in rules W1-I2 to the
+// resultLevels array.
+func (s *isolatingRunSequence) applyLevelsAndTypes() {
+       for i, x := range s.indexes {
+               s.p.resultTypes[x] = s.types[i]
+               s.p.resultLevels[x] = s.resolvedLevels[i]
+       }
+}
+
+// Return the limit of the run consisting only of the types in validSet
+// starting at index. This checks the value at index, and will return
+// index if that value is not in validSet.
+func (s *isolatingRunSequence) findRunLimit(index int, validSet ...Class) int {
+loop:
+       for ; index < len(s.types); index++ {
+               t := s.types[index]
+               for _, valid := range validSet {
+                       if t == valid {
+                               continue loop
+                       }
+               }
+               return index // didn't find a match in validSet
+       }
+       return len(s.types)
+}
+
+// Algorithm validation. Assert that all values in types are in the
+// provided set.
+func (s *isolatingRunSequence) assertOnly(codes ...Class) {
+loop:
+       for i, t := range s.types {
+               for _, c := range codes {
+                       if t == c {
+                               continue loop
+                       }
+               }
+               log.Panicf("invalid bidi code %v present in assertOnly at position %d", t, s.indexes[i])
+       }
+}
+
+// determineLevelRuns returns an array of level runs. Each level run is
+// described as an array of indexes into the input string.
+//
+// Determines the level runs. Rule X9 will be applied in determining the
+// runs, in the way that makes sure the characters that are supposed to be
+// removed are not included in the runs.
+func (p *paragraph) determineLevelRuns() [][]int {
+       run := []int{}
+       allRuns := [][]int{}
+       currentLevel := implicitLevel
+
+       for i := range p.initialTypes {
+               if !isRemovedByX9(p.initialTypes[i]) {
+                       if p.resultLevels[i] != currentLevel {
+                               // we just encountered a new run; wrap up last run
+                               if currentLevel >= 0 { // only wrap it up if there was a run
+                                       allRuns = append(allRuns, run)
+                                       run = nil
+                               }
+                               // Start new run
+                               currentLevel = p.resultLevels[i]
+                       }
+                       run = append(run, i)
+               }
+       }
+       // Wrap up the final run, if any
+       if len(run) > 0 {
+               allRuns = append(allRuns, run)
+       }
+       return allRuns
+}
+
+// Definition BD13. Determine isolating run sequences.
+func (p *paragraph) determineIsolatingRunSequences() []*isolatingRunSequence {
+       levelRuns := p.determineLevelRuns()
+
+       // Compute the run that each character belongs to
+       runForCharacter := make([]int, p.Len())
+       for i, run := range levelRuns {
+               for _, index := range run {
+                       runForCharacter[index] = i
+               }
+       }
+
+       sequences := []*isolatingRunSequence{}
+
+       var currentRunSequence []int
+
+       for _, run := range levelRuns {
+               first := run[0]
+               if p.initialTypes[first] != PDI || p.matchingIsolateInitiator[first] == -1 {
+                       currentRunSequence = nil
+                       // int run = i;
+                       for {
+                               // Copy this level run into currentRunSequence
+                               currentRunSequence = append(currentRunSequence, run...)
+
+                               last := currentRunSequence[len(currentRunSequence)-1]
+                               lastT := p.initialTypes[last]
+                               if lastT.in(LRI, RLI, FSI) && p.matchingPDI[last] != p.Len() {
+                                       run = levelRuns[runForCharacter[p.matchingPDI[last]]]
+                               } else {
+                                       break
+                               }
+                       }
+                       sequences = append(sequences, p.isolatingRunSequence(currentRunSequence))
+               }
+       }
+       return sequences
+}
+
+// Assign level information to characters removed by rule X9. This is for
+// ease of relating the level information to the original input data. Note
+// that the levels assigned to these codes are arbitrary, they're chosen so
+// as to avoid breaking level runs.
+func (p *paragraph) assignLevelsToCharactersRemovedByX9() {
+       for i, t := range p.initialTypes {
+               if t.in(LRE, RLE, LRO, RLO, PDF, BN) {
+                       p.resultTypes[i] = t
+                       p.resultLevels[i] = -1
+               }
+       }
+       // now propagate forward the levels information (could have
+       // propagated backward, the main thing is not to introduce a level
+       // break where one doesn't already exist).
+
+       if p.resultLevels[0] == -1 {
+               p.resultLevels[0] = p.embeddingLevel
+       }
+       for i := 1; i < len(p.initialTypes); i++ {
+               if p.resultLevels[i] == -1 {
+                       p.resultLevels[i] = p.resultLevels[i-1]
+               }
+       }
+       // Embedding information is for informational purposes only so need not be
+       // adjusted.
+}
+
+//
+// Output
+//
+
+// getLevels computes levels array breaking lines at offsets in linebreaks.
+// Rule L1.
+//
+// The linebreaks array must include at least one value. The values must be
+// in strictly increasing order (no duplicates) between 1 and the length of
+// the text, inclusive. The last value must be the length of the text.
+func (p *paragraph) getLevels(linebreaks []int) []level {
+       // Note that since the previous processing has removed all
+       // P, S, and WS values from resultTypes, the values referred to
+       // in these rules are the initial types, before any processing
+       // has been applied (including processing of overrides).
+       //
+       // This example implementation has reinserted explicit format codes
+       // and BN, in order that the levels array correspond to the
+       // initial text. Their final placement is not normative.
+       // These codes are treated like WS in this implementation,
+       // so they don't interrupt sequences of WS.
+
+       validateLineBreaks(linebreaks, p.Len())
+
+       result := append([]level(nil), p.resultLevels...)
+
+       // don't worry about linebreaks since if there is a break within
+       // a series of WS values preceding S, the linebreak itself
+       // causes the reset.
+       for i, t := range p.initialTypes {
+               if t.in(B, S) {
+                       // Rule L1, clauses one and two.
+                       result[i] = p.embeddingLevel
+
+                       // Rule L1, clause three.
+                       for j := i - 1; j >= 0; j-- {
+                               if isWhitespace(p.initialTypes[j]) { // including format codes
+                                       result[j] = p.embeddingLevel
+                               } else {
+                                       break
+                               }
+                       }
+               }
+       }
+
+       // Rule L1, clause four.
+       start := 0
+       for _, limit := range linebreaks {
+               for j := limit - 1; j >= start; j-- {
+                       if isWhitespace(p.initialTypes[j]) { // including format codes
+                               result[j] = p.embeddingLevel
+                       } else {
+                               break
+                       }
+               }
+               start = limit
+       }
+
+       return result
+}
+
+// getReordering returns the reordering of lines from a visual index to a
+// logical index for line breaks at the given offsets.
+//
+// Lines are concatenated from left to right. So for example, the fifth
+// character from the left on the third line is
+//
+//             getReordering(linebreaks)[linebreaks[1] + 4]
+//
+// (linebreaks[1] is the position after the last character of the second
+// line, which is also the index of the first character on the third line,
+// and adding four gets the fifth character from the left).
+//
+// The linebreaks array must include at least one value. The values must be
+// in strictly increasing order (no duplicates) between 1 and the length of
+// the text, inclusive. The last value must be the length of the text.
+func (p *paragraph) getReordering(linebreaks []int) []int {
+       validateLineBreaks(linebreaks, p.Len())
+
+       return computeMultilineReordering(p.getLevels(linebreaks), linebreaks)
+}
+
+// Return multiline reordering array for a given level array. Reordering
+// does not occur across a line break.
+func computeMultilineReordering(levels []level, linebreaks []int) []int {
+       result := make([]int, len(levels))
+
+       start := 0
+       for _, limit := range linebreaks {
+               tempLevels := make([]level, limit-start)
+               copy(tempLevels, levels[start:])
+
+               for j, order := range computeReordering(tempLevels) {
+                       result[start+j] = order + start
+               }
+               start = limit
+       }
+       return result
+}
+
+// Return reordering array for a given level array. This reorders a single
+// line. The reordering is a visual to logical map. For example, the
+// leftmost char is string.charAt(order[0]). Rule L2.
+func computeReordering(levels []level) []int {
+       result := make([]int, len(levels))
+       // initialize order
+       for i := range result {
+               result[i] = i
+       }
+
+       // locate highest level found on line.
+       // Note the rules say text, but no reordering across line bounds is
+       // performed, so this is sufficient.
+       highestLevel := level(0)
+       lowestOddLevel := level(maxDepth + 2)
+       for _, level := range levels {
+               if level > highestLevel {
+                       highestLevel = level
+               }
+               if level&1 != 0 && level < lowestOddLevel {
+                       lowestOddLevel = level
+               }
+       }
+
+       for level := highestLevel; level >= lowestOddLevel; level-- {
+               for i := 0; i < len(levels); i++ {
+                       if levels[i] >= level {
+                               // find range of text at or above this level
+                               start := i
+                               limit := i + 1
+                               for limit < len(levels) && levels[limit] >= level {
+                                       limit++
+                               }
+
+                               for j, k := start, limit-1; j < k; j, k = j+1, k-1 {
+                                       result[j], result[k] = result[k], result[j]
+                               }
+                               // skip to end of level run
+                               i = limit
+                       }
+               }
+       }
+
+       return result
+}
+
+// isWhitespace reports whether the type is considered a whitespace type for the
+// line break rules.
+func isWhitespace(c Class) bool {
+       switch c {
+       case LRE, RLE, LRO, RLO, PDF, LRI, RLI, FSI, PDI, BN, WS:
+               return true
+       }
+       return false
+}
+
+// isRemovedByX9 reports whether the type is one of the types removed in X9.
+func isRemovedByX9(c Class) bool {
+       switch c {
+       case LRE, RLE, LRO, RLO, PDF, BN:
+               return true
+       }
+       return false
+}
+
+// typeForLevel reports the strong type (L or R) corresponding to the level.
+func typeForLevel(level level) Class {
+       if (level & 0x1) == 0 {
+               return L
+       }
+       return R
+}
+
+// TODO: change validation to not panic
+
+func validateTypes(types []Class) {
+       if len(types) == 0 {
+               log.Panic("types is null")
+       }
+       for i, t := range types[:len(types)-1] {
+               if t == B {
+                       log.Panicf("B type before end of paragraph at index: %d", i)
+               }
+       }
+}
+
+func validateParagraphEmbeddingLevel(embeddingLevel level) {
+       if embeddingLevel != implicitLevel &&
+               embeddingLevel != 0 &&
+               embeddingLevel != 1 {
+               log.Panicf("illegal paragraph embedding level: %d", embeddingLevel)
+       }
+}
+
+func validateLineBreaks(linebreaks []int, textLength int) {
+       prev := 0
+       for i, next := range linebreaks {
+               if next <= prev {
+                       log.Panicf("bad linebreak: %d at index: %d", next, i)
+               }
+               prev = next
+       }
+       if prev != textLength {
+               log.Panicf("last linebreak was %d, want %d", prev, textLength)
+       }
+}
+
+func validatePbTypes(pairTypes []bracketType) {
+       if len(pairTypes) == 0 {
+               log.Panic("pairTypes is null")
+       }
+       for i, pt := range pairTypes {
+               switch pt {
+               case bpNone, bpOpen, bpClose:
+               default:
+                       log.Panicf("illegal pairType value at %d: %v", i, pairTypes[i])
+               }
+       }
+}
+
+func validatePbValues(pairValues []rune, pairTypes []bracketType) {
+       if pairValues == nil {
+               log.Panic("pairValues is null")
+       }
+       if len(pairTypes) != len(pairValues) {
+               log.Panic("pairTypes is different length from pairValues")
+       }
+}