← Home
package main

import (
	"bufio"
	"container/heap"
	"fmt"
	"os"
)

type Point struct{ x, y int }

type Move struct {
	x1, y1, x2, y2 int
}

type Item struct {
	p Point
	d int
	i int
}

type PQ []*Item

func (pq PQ) Len() int           { return len(pq) }
func (pq PQ) Less(i, j int) bool { return pq[i].d < pq[j].d }
func (pq PQ) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].i = i
	pq[j].i = j
}
func (pq *PQ) Push(x interface{}) {
	n := len(*pq)
	item := x.(*Item)
	item.i = n
	*pq = append(*pq, item)
}
func (pq *PQ) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.i = -1
	*pq = old[0 : n-1]
	return item
}

func getPathP(start, target Point, fixed [][]bool, grid [][]int, n int) []Point {
	dist := make([][]int, n+1)
	parent := make([][]Point, n+1)
	for i := 1; i <= n; i++ {
		dist[i] = make([]int, n+1)
		parent[i] = make([]Point, n+1)
		for j := 1; j <= n; j++ {
			dist[i][j] = 1e9
		}
	}

	pq := make(PQ, 0)
	heap.Init(&pq)

	dist[start.x][start.y] = 0
	heap.Push(&pq, &Item{p: start, d: 0})

	dx := []int{-1, 1, 0, 0}
	dy := []int{0, 0, -1, 1}

	for pq.Len() > 0 {
		curr := heap.Pop(&pq).(*Item)
		u := curr.p
		if curr.d > dist[u.x][u.y] {
			continue
		}
		if u == target {
			break
		}

		for k := 0; k < 4; k++ {
			nx, ny := u.x+dx[k], u.y+dy[k]
			if nx >= 1 && nx <= n && ny >= 1 && ny <= n {
				if fixed[nx][ny] {
					continue
				}
				cost := 1
				if grid[nx][ny] != 0 {
					cost = 100
				}
				if dist[u.x][u.y]+cost < dist[nx][ny] {
					dist[nx][ny] = dist[u.x][u.y] + cost
					parent[nx][ny] = u
					heap.Push(&pq, &Item{p: Point{nx, ny}, d: dist[nx][ny]})
				}
			}
		}
	}

	var path []Point
	curr := target
	for curr != start {
		path = append(path, curr)
		curr = parent[curr.x][curr.y]
	}

	for i := 0; i < len(path)/2; i++ {
		j := len(path) - 1 - i
		path[i], path[j] = path[j], path[i]
	}
	return path
}

func getPathQ(start Point, fixed [][]bool, grid [][]int, pos_c Point, n int) []Point {
	visited := make([][]bool, n+1)
	for i := 1; i <= n; i++ {
		visited[i] = make([]bool, n+1)
	}
	parent := make([][]Point, n+1)

	queue := []Point{start}
	visited[start.x][start.y] = true

	dx := []int{-1, 1, 0, 0}
	dy := []int{0, 0, -1, 1}

	var emptyCell Point

	for len(queue) > 0 {
		u := queue[0]
		queue = queue[1:]

		if grid[u.x][u.y] == 0 {
			emptyCell = u
			break
		}

		for k := 0; k < 4; k++ {
			nx, ny := u.x+dx[k], u.y+dy[k]
			if nx >= 1 && nx <= n && ny >= 1 && ny <= n {
				if fixed[nx][ny] {
					continue
				}
				if nx == pos_c.x && ny == pos_c.y {
					continue
				}
				if !visited[nx][ny] {
					visited[nx][ny] = true
					parent[nx][ny] = u
					queue = append(queue, Point{nx, ny})
				}
			}
		}
	}

	var path []Point
	curr := emptyCell
	for {
		path = append(path, curr)
		if curr == start {
			break
		}
		curr = parent[curr.x][curr.y]
	}
	for i := 0; i < len(path)/2; i++ {
		j := len(path) - 1 - i
		path[i], path[j] = path[j], path[i]
	}
	return path
}

func solve(initialPos []Point, n int, m int) []Move {
	grid := make([][]int, n+1)
	fixed := make([][]bool, n+1)
	for i := 1; i <= n; i++ {
		grid[i] = make([]int, n+1)
		fixed[i] = make([]bool, n+1)
	}

	pos := make([]Point, m+1)
	for i := 1; i <= m; i++ {
		p := initialPos[i]
		grid[p.x][p.y] = i
		pos[i] = p
	}

	moves := []Move{}

	for c := 1; c <= m; c++ {
		target := Point{1, c}
		if pos[c] == target {
			fixed[target.x][target.y] = true
			continue
		}

		P := getPathP(pos[c], target, fixed, grid, n)
		for _, p_next := range P {
			if grid[p_next.x][p_next.y] == 0 {
				grid[pos[c].x][pos[c].y] = 0
				grid[p_next.x][p_next.y] = c
				moves = append(moves, Move{pos[c].x, pos[c].y, p_next.x, p_next.y})
				pos[c] = p_next
			} else {
				Q := getPathQ(p_next, fixed, grid, pos[c], n)
				for j := len(Q) - 2; j >= 0; j-- {
					q_curr := Q[j]
					q_next := Q[j+1]
					cube_id := grid[q_curr.x][q_curr.y]
					grid[q_curr.x][q_curr.y] = 0
					grid[q_next.x][q_next.y] = cube_id
					pos[cube_id] = q_next
					moves = append(moves, Move{q_curr.x, q_curr.y, q_next.x, q_next.y})
				}
				grid[pos[c].x][pos[c].y] = 0
				grid[p_next.x][p_next.y] = c
				moves = append(moves, Move{pos[c].x, pos[c].y, p_next.x, p_next.y})
				pos[c] = p_next
			}
		}
		fixed[target.x][target.y] = true
	}
	return moves
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	var n, m int
	if _, err := fmt.Fscan(reader, &n, &m); err != nil {
		return
	}

	initial := make([]Point, m+1)
	for i := 1; i <= m; i++ {
		fmt.Fscan(reader, &initial[i].x, &initial[i].y)
	}

	final := make([]Point, m+1)
	for i := 1; i <= m; i++ {
		fmt.Fscan(reader, &final[i].x, &final[i].y)
	}

	moves1 := solve(initial, n, m)
	moves2 := solve(final, n, m)

	for i := 0; i < len(moves2)/2; i++ {
		j := len(moves2) - 1 - i
		moves2[i], moves2[j] = moves2[j], moves2[i]
	}
	for i := range moves2 {
		moves2[i] = Move{moves2[i].x2, moves2[i].y2, moves2[i].x1, moves2[i].y1}
	}

	totalMoves := append(moves1, moves2...)

	fmt.Println(len(totalMoves))
	writer := bufio.NewWriter(os.Stdout)
	for _, mv := range totalMoves {
		fmt.Fprintf(writer, "%d %d %d %d\n", mv.x1, mv.y1, mv.x2, mv.y2)
	}
	writer.Flush()
}