<commit_before>"use strict";

module.exports = trace;

var I = require("ancient-oak");

function arr2d (h) {
  var arr = I([]);
  for (var i = 0; i < h; ++i) {
    arr = arr.set(i, []);
  }

  return arr;
}

function trace (orientmap, w, h) {
  var coverage = arr2d(h);
  var paths = I([]);

  do {
    var start = starting_point(orientmap, coverage, w, h);
    if (!start) break;

    var r = trace_one(start, orientmap, coverage);

    paths = paths.push(r("moves"));
    coverage = r("coverage");
  } while (true);

  // flatten
  return paths.reduce(function (agg, p) {
    return p.reduce(function (agg, el) {
      return agg.push(el);
    }, agg);
  }, I([]));
}

function starting_point (orientmap, coverage, w, h) {
  // find starting point that hasn't been covered yet.
  outer_loop:
  for (var y = 0; y < h; ++y) {
    for (var x = 0; x < w; ++x) {
      if (orientmap(y)(x) && !coverage(y)(x)) break outer_loop;
    }
  }

  return (x !== w || y !== h
          ? I({x: x, y: y})
          : null);
}

function trace_one (start, orientmap, coverage) {
  var x = start("x");
  var y = start("y");

  var start_x = x;
  var start_y = y;

  var moves = I([{ x: x, y: y }]);

  do {
    coverage = coverage.update(y, function (row) {
      return row.set(x, true);
    });

    var move = orientmap(y)(x);

    // special case where we have to turn left from current
    // direction.
    if (move("next")) {
      move = next_move_ccw(moves(moves.size - 1));
    }

    x += move("h") || 0;
    y += move("v") || 0;

    if (x === start_x && y === start_y) break;

    moves = moves.push(move);
  } while (true);

  return I({
    moves:    moves,
    coverage: coverage
  })
}

function next_move_ccw (move, dir) {
  return I(move("v") === -1
           ? { h: -1 }
           : move("h") === -1
           ? { v: 1 }
           : move("v") === 1
           ? { h: 1 }
           : { v: -1 });
}<commit_msg>Functional trace function.<commit_after>"use strict";

module.exports = trace;

var I = require("ancient-oak");

function arr2d (h) {
  var arr = I([]);
  for (var i = 0; i < h; ++i) {
    arr = arr.set(i, []);
  }

  return arr;
}

function trace (orientmap, w, h) {
  var paths = lazy(function (state) {
    var start = starting_point(orientmap, state("coverage"), points(w, h, state("start")));
    return (start
            ? trace_one(start, orientmap, state("coverage")).set("start", start)
            : null);
  }, {
    start: { x: 0, y: 0 },
    coverage: arr2d(h)
  }).all().slice(1).map(function (state) {
    return state("moves");
  });

  return flatten(paths);
}

function flatten(paths) {
  return paths.reduce(function (agg, p) {
    return p.reduce(function (agg, el) {
      return agg.push(el);
    }, agg);
  }, I([]));
}

function inc (v) {
  return v + 1;
}

function points (w, h, start) {
  return lazy(function (point) {
    return (point("x") < w - 1
            ? point.update("x", inc)
            : point("y") < h - 1
            ? point.update("y", inc).set("x", 0)
            : null);
  }, start || {
    x: 0, y: 0
  });
}

function lazy (generator, value) {
  value = I(value);

  var list = Object.freeze({
    value: value,
    next:  next,
    find:  find,
    all:   all
  });

  return list;

  function next () {
    return lazy(generator, generator(value));
  }

  function find (condition) {
    return lazy_find(list, condition);
  }

  function all () {
    return lazy_all(list);
  }
}

function lazy_all (list) {
  var result = I([]);
  while (list.value !== null) {
    result = result.push(list.value);
    list = list.next();
  }
  return result;
}

function lazy_find (list, condition) {
  var head = list.value;

  while (head !== null && condition(head)) {
    list = list.next();
    head = list.value;
  }

  return list;
}

function starting_point (orientmap, coverage, points) {
  // find starting point that hasn't been covered yet.
  return points.find(function (value) {
    var x = value("x");
    var y = value("y");
    return !orientmap(y)(x) || coverage(y)(x);
  }).value;
}

function trace_one (start, orientmap, coverage) {
  var x = start("x");
  var y = start("y");

  var start_x = x;
  var start_y = y;

  var moves = I([{ x: x, y: y }]);

  do {
    coverage = coverage.update(y, function (row) {
      return row.set(x, true);
    });

    var move = orientmap(y)(x);

    // special case where we have to turn left from current
    // direction.
    if (move("next")) {
      move = next_move_ccw(moves(moves.size - 1));
    }

    x += move("h") || 0;
    y += move("v") || 0;

    if (x === start_x && y === start_y) break;

    moves = moves.push(move);
  } while (true);

  return I({
    moves:    moves,
    coverage: coverage
  })
}

function next_move_ccw (move) {
  return I(move("v") === -1
           ? { h: -1 }
           : move("h") === -1
           ? { v: 1 }
           : move("v") === 1
           ? { h: 1 }
           : { v: -1 });
}
{'fraction_non_alphanumeric': 0.18483316481294237, 'fraction_numerical': 0.0074823053589484325, 'mean_word_length': 2.295136575616256, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 3, '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': '2299164', 'n_tokens_mistral': 1837, 'n_tokens_neox': 1785, 'n_words': 496}