<commit_before>namespace CSharpMath.Editor {
  using System;
  using System.Drawing;
  using System.Linq;

  using Display;
  using Display.Text;
  using FrontEnd;
  using Color = Structures.Color;

  public partial class DisplayEditingExtensions {
    public static int? GlyphIndexForXOffset<TFont, TGlyph>(this AttributedGlyphRun<TFont, TGlyph> line, TypesettingContext<TFont, TGlyph> context, float offset) where TFont : IFont<TGlyph> {
      if (offset < 0) return 0; // Move cursor to index 0
      if (line.Placeholder) return 0;
      int i = 0;
      float x = 0;
      var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs.AsForEach(), line.Length).Advances;
      foreach (var (advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create))
        if (x <= offset && offset < advance + x) {
          if (Math.Abs(offset - x) < Math.Abs(advance + x - offset))
            return i;
          return i + 1;
        } else {
          x += advance + kernAfter;
          i++;
          if (offset < x) // If the point is in the kern after this, then the index is the one after this
            return i;
        }
      return i;
    }

    public static float XOffsetForGlyphIndex<TFont, TGlyph>(this AttributedGlyphRun<TFont, TGlyph> line, TypesettingContext<TFont, TGlyph> context, int index) where TFont : IFont<TGlyph> {
      if (index < 0)
        throw ArgOutOfRange("The index is negative.", index, nameof(index));
      int i = 0;
      float x = 0;
      var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs.AsForEach(), line.Length).Advances;
      foreach (var (advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create))
        if (i++ >= index)
          return x;
        else
          x += advance + kernAfter;
      throw ArgOutOfRange("The index is beyond the end of the string.", index, nameof(index));
    }

    // Convert the index into the current string to an index into the mathlist. These may not be the same since a single
    // math atom may have multiple characters.
    public static int StringIndexToMathListIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int strIndex) where TFont : IFont<TGlyph> {
      int strLenCovered = 0;
      for (int mlIndex = 0; mlIndex < self.Atoms.Length; mlIndex++) {
        if (strLenCovered >= strIndex)
          return mlIndex;
        strLenCovered += self.Atoms[mlIndex].Nucleus.Length;
      }
      if (strLenCovered < strIndex)
        throw new InvalidCodePathException("StrIndex should not be more than the len covered");
      return self.Atoms.Length;
    }

    public static int MathListIndexToStringIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int mlIndex) where TFont : IFont<TGlyph> {
      if (mlIndex >= self.Atoms.Length)
        throw ArgOutOfRange($"The index is not in the range {self.Atoms.Length}", mlIndex, nameof(mlIndex));
      int strIndex = 0;
      for (int i = 0; i < mlIndex; i++) {
        strIndex += self.Atoms[i].Nucleus.Length;
      }
      return strIndex;
    }

    public static MathListIndex IndexForPoint<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, TypesettingContext<TFont, TGlyph> context, PointF point) where TFont : IFont<TGlyph> {
      // Convert the point to the reference of the CTLine
      var relativePoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);
      var indices =
        self.Runs
        .Select(run => ValueTuple.Create(run, run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X)))
        .Where(x => x.Item2.HasValue)
        .ToArray();
      if (indices.Length == 0)
        return null;
      var (r, nindex) = indices.Single();
      var index = nindex.GetValueOrDefault();
      var diffLng = r.Run.Length != r.Range.Length;
      if (index < 0 || (!diffLng && index > self.Range.Length) || (diffLng && index > r.Run.Length))
        throw new InvalidCodePathException($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})");
      if (!diffLng)
        return MathListIndex.Level0Index(self.Range.Location + index);
      if (index > r.Run.Length / 2)
        return MathListIndex.Level0Index(self.Range.End);

      return MathListIndex.Level0Index(self.Range.Location);
    }

    public static (TextRunDisplay<TFont, TGlyph> run, int charIndex) GetRunAndCharIndexFromStringIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int lineCharIndex) where TFont : IFont<TGlyph> {
      var currentRun = self.Runs.First(s => (lineCharIndex -= s.Run.Text.Length) < 0);
      return (currentRun, currentRun.Run.Text.Length + lineCharIndex); //offset for target char in its containing string
      /* //Quick test in C# Interactive
int strIndex = 6; //offset for target char if all strings in array are fused together
var ss = new[] { "abcde", "fgh", "i", "f", "g" };
var c = ss.First(s => (strIndex -= s.Length) < 0);
return c.Length + strIndex; //offset for target char in its containing string
*/
    }

    public static (TextRunDisplay<TFont, TGlyph> run, int charIndex) GetRunAndCharIndexFromCodepointIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int lineCharIndex) where TFont : IFont<TGlyph> {
      var currentRun = self.Runs.First(s => (lineCharIndex -= CountCodepoints(s.Run.Text)) < 0);
      return (currentRun, CountCodepoints(currentRun.Run.Text) + lineCharIndex); //offset for target char in its containing string
    }

    public static PointF? PointForIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, TypesettingContext<TFont, TGlyph> context, MathListIndex index) where TFont : IFont<TGlyph> {
      float offset;
      if (!(index.SubIndexType is MathListSubIndexType.None))<commit_msg>renaming<commit_after>namespace CSharpMath.Editor {
  using System;
  using System.Drawing;
  using System.Linq;

  using Display;
  using Display.Text;
  using FrontEnd;
  using Color = Structures.Color;

  public partial class DisplayEditingExtensions {
    public static int? GlyphIndexForXOffset<TFont, TGlyph>(this AttributedGlyphRun<TFont, TGlyph> line, TypesettingContext<TFont, TGlyph> context, float offset) where TFont : IFont<TGlyph> {
      if (offset < 0) return 0; // Move cursor to index 0
      if (line.Placeholder) return 0;
      int i = 0;
      float x = 0;
      var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs.AsForEach(), line.Length).Advances;
      foreach (var (advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create))
        if (x <= offset && offset < advance + x) {
          if (Math.Abs(offset - x) < Math.Abs(advance + x - offset))
            return i;
          return i + 1;
        } else {
          x += advance + kernAfter;
          i++;
          if (offset < x) // If the point is in the kern after this, then the index is the one after this
            return i;
        }
      return i;
    }

    public static float XOffsetForGlyphIndex<TFont, TGlyph>(this AttributedGlyphRun<TFont, TGlyph> line, TypesettingContext<TFont, TGlyph> context, int index) where TFont : IFont<TGlyph> {
      if (index < 0)
        throw ArgOutOfRange("The index is negative.", index, nameof(index));
      int i = 0;
      float x = 0;
      var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs.AsForEach(), line.Length).Advances;
      foreach (var (advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create))
        if (i++ >= index)
          return x;
        else
          x += advance + kernAfter;
      throw ArgOutOfRange("The index is beyond the end of the string.", index, nameof(index));
    }

    // Convert the index into the current string to an index into the mathlist. These may not be the same since a single
    // math atom may have multiple characters.
    public static int StringIndexToMathListIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int strIndex) where TFont : IFont<TGlyph> {
      int strLenCovered = 0;
      for (int mlIndex = 0; mlIndex < self.Atoms.Length; mlIndex++) {
        if (strLenCovered >= strIndex)
          return mlIndex;
        strLenCovered += self.Atoms[mlIndex].Nucleus.Length;
      }
      if (strLenCovered < strIndex)
        throw new InvalidCodePathException("StrIndex should not be more than the len covered");
      return self.Atoms.Length;
    }

    public static int MathListIndexToStringIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int mlIndex) where TFont : IFont<TGlyph> {
      if (mlIndex >= self.Atoms.Length)
        throw ArgOutOfRange($"The index is not in the range {self.Atoms.Length}", mlIndex, nameof(mlIndex));
      int strIndex = 0;
      for (int i = 0; i < mlIndex; i++) {
        strIndex += self.Atoms[i].Nucleus.Length;
      }
      return strIndex;
    }

    public static MathListIndex IndexForPoint<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, TypesettingContext<TFont, TGlyph> context, PointF point) where TFont : IFont<TGlyph> {
      // Convert the point to the reference of the CTLine
      var relativePoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y);
      var runsAndIndicies =
        self.Runs
        .Select(run => ValueTuple.Create(run, run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X)))
        .Where(x => x.Item2.HasValue)
        .ToArray();
      if (runsAndIndicies.Length == 0)
        return null;
      var (r, nindex) = runsAndIndicies.Single();
      var index = nindex.GetValueOrDefault();
      var diffLng = r.Run.Length != r.Range.Length;
      if (index < 0 || (!diffLng && index > self.Range.Length) || (diffLng && index > r.Run.Length))
        throw new InvalidCodePathException($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})");
      if (!diffLng)
        return MathListIndex.Level0Index(self.Range.Location + index);
      if (index > r.Run.Length / 2)
        return MathListIndex.Level0Index(self.Range.End);

      return MathListIndex.Level0Index(self.Range.Location);
    }

    public static (TextRunDisplay<TFont, TGlyph> run, int charIndex) GetRunAndCharIndexFromStringIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int lineCharIndex) where TFont : IFont<TGlyph> {
      var currentRun = self.Runs.First(s => (lineCharIndex -= s.Run.Text.Length) < 0);
      return (currentRun, currentRun.Run.Text.Length + lineCharIndex); //offset for target char in its containing string
      /* //Quick test in C# Interactive
int strIndex = 6; //offset for target char if all strings in array are fused together
var ss = new[] { "abcde", "fgh", "i", "f", "g" };
var c = ss.First(s => (strIndex -= s.Length) < 0);
return c.Length + strIndex; //offset for target char in its containing string
*/
    }

    public static (TextRunDisplay<TFont, TGlyph> run, int charIndex) GetRunAndCharIndexFromCodepointIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, int lineCharIndex) where TFont : IFont<TGlyph> {
      var currentRun = self.Runs.First(s => (lineCharIndex -= CountCodepoints(s.Run.Text)) < 0);
      return (currentRun, CountCodepoints(currentRun.Run.Text) + lineCharIndex); //offset for target char in its containing string
    }

    public static PointF? PointForIndex<TFont, TGlyph>(this TextLineDisplay<TFont, TGlyph> self, TypesettingContext<TFont, TGlyph> context, MathListIndex index) where TFont : IFont<TGlyph> {
      float offset;
      if (!(index.SubIndexType is MathListSubIndexType.None))
{'fraction_non_alphanumeric': 0.11191427842503614, 'fraction_numerical': 0.0042520622501913425, 'mean_word_length': 3.2593263310394787, 'pattern_counts': {'":': 0, '<': 89, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3750248', 'n_tokens_mistral': 3730, 'n_tokens_neox': 3642, 'n_words': 1049}