require 'set'; require 'prime'; require 'pp'
INF=Float::INFINITY; MOD=10**9+7

h,w = gets.chomp.split.map(&:to_i)
ch, cw = gets.chomp.split.map(&:to_i).map{|e| e-1}
dh, dw = gets.chomp.split.map(&:to_i).map{|e| e-1}
grid = Array.new(h, :a)
# h = w= 100
# dh, dw = 99, 99
h.times do |i|
  grid[i] = gets.chomp.chars.map(&:to_sym)
  # grid.push(('.' * w).chars)
end

q = []
q.push [0, ch, cw]
grid[ch][cw] = 0

ans = 10000000
until q.empty?
  count, y, x = q.shift
  break if count >= ans
  gridyx = grid[y][x]
  # p ['main', count, [y, x], gridyx]
  next if gridyx != :'.' && gridyx < count

  [y-2, 0].max.upto([y+2,h-1].min) do |ny|
    [x-2, 0].max.upto([x+2,w-1].min) do |nx|
      next if ny == y && nx == x
      ncount = count
      is_near = (ny-y).abs + (nx-x).abs == 1
      ncount += 1 unless is_near

      val = grid[ny][nx]
      if ny == dh && nx == dw
        ans = [ans, ncount].min
      elsif val == :'#'
        # skip
      elsif val == :'.' || val > ncount
        grid[ny][nx] = ncount
        if is_near
          q.unshift [ncount, ny, nx]
        else
          q.push [ncount, ny, nx]
        end
      end
    end
  end
end

# grid.each{|l| p l }
if ans == 10000000
  puts -1
else
  puts ans
end


use std::collections::VecDeque;

pub fn main() {
  fn read_line() -> String {
    let mut buf = String::new();
    std::io::stdin().read_line(&mut buf);
    buf
  }
  fn read_tuple() -> (i32, i32) {
    let a: Vec<usize> = read_line()
      .split_whitespace()
      .map(|e| e.parse().unwrap())
      .collect();
    (a[0] as i32, a[1] as i32)
  }
  let (h, w) = read_tuple();
  let (ch, cw) = read_tuple();
  let (dh, dw) = read_tuple();

  let mut map: Vec<Vec<i32>> = Vec::new();
  for _ in 0..h {
    let mut row: Vec<i32> = Vec::new();
    let s: Vec<char> = read_line().trim().chars().collect();
    for j in 0..w {
      if s[j as usize] == '.' {
        row.push(-1);
      } else {
        row.push(-2);
      }
    }
    map.push(row);
  }

  let mv: Vec<(i32, i32)> = {
    let mut v = Vec::new();
    for i in -2..3 {
      for j in -2..3 {
        if !(i == 0 && j == 0) {
          v.push((i, j));
        }
      }
    }
    v
  };

  let mut deque: VecDeque<(i32, i32, i32)> = VecDeque::new();
  deque.push_front((ch, cw, 0));

  while !deque.is_empty() {
    let (cur_h, cur_w, point) = deque.pop_front().unwrap();
    if map[(cur_h as usize) - 1][(cur_w as usize) - 1] != -1 {
      continue;
    }
    map[(cur_h as usize) - 1][(cur_w as usize) - 1] = point;
    if cur_h == dh && cur_w == dw {
      println!("{}", point);
      return;
    }

    for next in &mv {
      let next_h = cur_h + next.0;
      let next_w = cur_w + next.1;
      if (next_h >= 1)
        && (next_w >= 1)
        && next_h <= h
        && next_w <= w
        && map[next_h as usize - 1][next_w as usize - 1] == -1
      {
        if next.0.abs() + next.1.abs() == 1 {
          deque.push_front((next_h, next_w, point));
        } else {
          deque.push_back((next_h, next_w, point + 1));
        }
      }
    }
  }
  println!("-1");
  return;
}


use std::io::*;
use std::str::*;
use std::collections::VecDeque;

fn read<T: FromStr>(s: &mut StdinLock) -> T {
    let s = s.by_ref().bytes().map(|c| c.unwrap() as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect::<String>();
    s.parse::<T>().ok().unwrap()
}

const  STEP0: &[(i32, i32);4] = &[ (-1, 0), (1, 0), (0, -1), (0, 1),];

const  STEP1: &[(i32, i32); 20] =
				&[ (-2, -2), (-1, -2), (0, -2), (1, -2), (2, -2),
				   (-2, -1), (-1, -1),          (1, -1), (2, -1),
				   (-2,  0),                             (2,  0),
				   (-2,  1), (-1,  1),          (1,  1), (2,  1),
				   (-2,  2), (-1,  2), (0,  2), (1,  2), (2,  2),];

fn main() {

	let s = stdin();
	let mut s  = s.lock();
	let s =  &mut s;

	struct Param {
		h :i32,
		w :i32,
		starty :i32,
		startx :i32,
		endy: i32,
		endx: i32,
		arr: Vec<u8>,
		steps: Vec<i32>,
	}

	let mut work = Param {
		h:  read::<i32>(s),
		w:  read::<i32>(s),
		starty: read::<i32>(s) - 1,
		startx: read::<i32>(s) - 1,
		endy: read::<i32>(s) - 1,
		endx: read::<i32>(s) - 1,
		arr:  Vec::<u8>::new(),
	    steps: Vec::<i32>::new(),
	};

	for _y in 0 .. work.h {
		work.arr.extend(read::<String>(s).bytes());
	}

	fn getindex(x: i32, y: i32, w: i32) -> usize {
 		 (y * w + x) as usize
	}

	fn isempty(ctx: &Param, x :i32 , y: i32) -> bool  {
		if x < 0 || x >= ctx.w || y < 0 || y >= ctx.h {
			return false;
		}
		let idx = getindex(x, y, ctx.w);
		let r = ctx.arr[idx] != b'#' && ctx.steps[idx] < 0;
		 return r;
	}

	fn check0(ctx :&mut Param) -> Vec< (i32, i32) >   {
		let mut queue = VecDeque::<(i32, i32)>::new();
		let mut vec0 = Vec::<(i32, i32)>::new();
		if isempty(ctx, ctx.startx, ctx.starty) {
			ctx.steps[getindex(ctx.startx, ctx.starty, ctx.w)] = 0;
	    	queue.push_back((ctx.startx, ctx.starty));
			vec0.push((ctx.startx, ctx.starty));
		}
		loop {
			match queue.pop_front() {
				None => { break; } ,
				Some(p) => {
					ctx.steps[getindex(p.0, p.1, ctx.w)] = 0;
					for ri in 0 .. STEP0.len() {
						let px = p.0  + STEP0[ri].0;
						let py = p.1  + STEP0[ri].1;
						if isempty(ctx, px, py) {
							ctx.steps[getindex(px, py,ctx.w)] = 0;
					 		queue.push_back( (px, py) );
							vec0.push( (px, py) );
						}
					}
				},
			}
		}
		vec0
	};

	fn check1<'a>(ctx : &mut Param, vec0: &mut Vec<(i32, i32)> )  -> Vec<(i32, i32)> {
		let mut vec1 = Vec::<(i32, i32)>::new();
		for tp in vec0.into_iter() {
			ctx.steps[getindex(tp.0, tp.1, ctx.w)] = 0;
			for ri in 0 .. STEP1.len() {
				let px = tp.0  + STEP1[ri].0;
				let py = tp.1  + STEP1[ri].1;
				if isempty(ctx, px, py) {
					ctx.steps[getindex(px, py, ctx.w)] = 0;
					vec1.push( (px, py) );
				}
			}
		}
		vec1
	};

	fn check2<'a>(ctx : &mut Param, vec0: &mut Vec<(i32, i32)> )  -> Vec<(i32, i32)> {
		let mut queue = VecDeque::<(i32, i32)>::new();
		let mut vec2 = Vec::<(i32, i32)>::new();
		for tp in vec0.into_iter() {
			ctx.steps[getindex(tp.0, tp.1, ctx.w)] = 0;
			vec2.push(*tp);
	    	queue.push_back(*tp);
			loop {
				match queue.pop_front() {
					None => { break; } ,
					Some(p) => {
						ctx.steps[getindex(p.0, p.1, ctx.w)] = 0;
						for ri in 0 .. STEP0.len() {
							let px = p.0  + STEP0[ri].0;
							let py = p.1  + STEP0[ri].1;
							if isempty(ctx, px, py) {
								ctx.steps[getindex(px, py,ctx.w)] = 0;
						 		queue.push_back( (px, py) );
								vec2.push( (px, py) );
							}
						}
					},
				}
			}
		}
		vec2
	};

	work.steps.resize((work.h * work.w) as usize, -1);

    let mut v = check0(&mut work);

	let mut step = 0;

	loop {
		if v.len() == 0 {
			step = -1;
			break;
		}
	 	if v.contains( &(work.endx, work.endy) ) {
			break;
		}
	    v = check1(&mut work,  &mut v) ;
		v = check2(&mut work, &mut v) ;
		//dump1(&v);
		step += 1;
	}
 	println!("{}", step);

}


use proconio::*;
use proconio::marker::*;

fn run() {
    input! {
        h: usize,
        w: usize,
        src: (Usize1, Usize1),
        dst: (Usize1, Usize1),
        mut s: [Chars; h],
    }
    for s in s.iter_mut() {
        for _ in 0..2 {
            s.insert(0, '#');
            s.push('#');
        }
    }
    for _ in 0..2 {
        s.insert(0, vec!['#'; w + 4]);
        s.push(vec!['#'; w + 4]);
    }
    let h = h + 4;
    let w = w + 4;
    let src = (src.0 + 2, src.1 + 2);
    let dst = (dst.0 + 2, dst.1 + 2);
    let mut used = vec![vec![false; w]; h];
    used[src.0][src.1] = true;
    let mut que = std::collections::VecDeque::new();
    que.push_back(src);
    for i in 0.. {
        if que.is_empty() {
            println!("-1");
            return;
        }
        let mut a = vec![];
        while let Some((x, y)) = que.pop_front() {
            a.push((x, y));
            for d in [!0, 0, 1, 0, !0].windows(2) {
                let x = x + d[0];
                let y = y + d[1];
                if !used[x][y] && s[x][y] != '#' {
                    used[x][y] = true;
                    que.push_back((x, y));
                }
            }
        }
        if used[dst.0][dst.1] {
            println!("{}", i);
            return;
        }
        for (x, y) in a {
            for p in [!1, !0, 0, 1, 2].iter() {
                for q in [!1, !0, 0, 1, 2].iter() {
                    let x = x + *p;
                    let y = y + *q;
                    if !used[x][y] && s[x][y] != '#' {
                        used[x][y] = true;
                        que.push_back((x, y));
                    }
                }
            }
        }
    }
}

fn main() {
    run();
}


use proconio::input;
use proconio::marker::{Chars};
use std::collections::VecDeque;

fn main() {
    input!{
        h: usize,
        w: usize,
        c: [usize; 2],
        d: [usize; 2],
        sdash: [Chars; h],
    }
    let inf: usize = 999999999999;
    let mut v = vec![vec![inf; w+4]; h+4];
    let mut s = vec![vec!['#'; w+4]; h+4];
    for i in 0..h {
        for j in 0..w {
            s[i+2][j+2] = sdash[i][j];
        }
    }
    let mut q = VecDeque::new();

    q.push_back((c[0]+1, c[1]+1, 0));

    while q.len() > 0 {
        let (x, y, cost) = q.pop_front().unwrap();
        v[x][y] = cost;
        if v[x+1][y] == inf && s[x+1][y] == '.' {
            q.push_front((x+1, y, cost));
//            v[x+1][y] = cost;
        }
        else if v[x][y+1] == inf && s[x][y+1] == '.' {
            q.push_front((x, y+1, cost));
//            v[x][y+1] = cost;
        }
        else if v[x-1][y] == inf && s[x-1][y] == '.' {
            q.push_front((x-1, y, cost));
//            v[x-1][y] = cost;
        }
        else if v[x][y-1] == inf && s[x][y-1] == '.' {
            q.push_front((x, y-1, cost));
            v[x][y-1] = cost;
        }
        else {
            for i in x-2..x+3 {
                for j in x-2..x+3 {
                    if v[i][j] == inf && s[i][j] == '.' {
                        q.push_back((i, j, cost+1));
//                        v[i][j] = cost+1;
                    }
                }
            }
        }
    }
    if v[d[0]+1][d[1]+1] == inf {
        println!("-1");
    }
    else {
        println!("{}", v[d[0]+1][d[1]+1]);
    }
}


use proconio::input;
use proconio::marker::{Chars};
use std::collections::VecDeque;

fn main() {
    input!{
        h: usize,
        w: usize,
        c: [usize; 2],
        d: [usize; 2],
        sdash: [Chars; h],
    }
    let inf: usize = 999999999999;
    let mut v = vec![vec![inf; w+4]; h+4];
    let mut s = vec![vec!['#'; w+4]; h+4];
    for i in 0..h {
        for j in 0..w {
            s[i+2][j+2] = sdash[i][j];
        }
    }
    let mut q = VecDeque::new();

    q.push_back((c[0]+1, c[1]+1, 0));

    while q.len() > 0 {
        let (x, y, cost) = q.pop_front().unwrap();
        if v[x+1][y] == inf && s[x+1][y] == '.' {
            q.push_front((x+1, y, cost));
            v[x+1][y] = cost;
        }
        else if v[x][y+1] == inf && s[x][y+1] == '.' {
            q.push_front((x, y+1, cost));
            v[x][y+1] = cost;
        }
        else if v[x-1][y] == inf && s[x-1][y] == '.' {
            q.push_front((x-1, y, cost));
            v[x-1][y] = cost;
        }
        else if v[x][y-1] == inf && s[x][y-1] == '.' {
            q.push_front((x, y-1, cost));
            v[x][y-1] = cost;
        }
        else {
            for i in x-2..x+3 {
                for j in x-2..x+3 {
                    if v[i][j] == inf && s[i][j] == '.' {
                        q.push_back((i, j, cost+1));
                        v[i][j] = cost+1;
                    }
                }
            }
        }
    }
    if v[d[0]+1][d[1]+1] == inf {
        println!("-1");
    }
    else {
        println!("{}", v[d[0]+1][d[1]+1]);
    }
}


use proconio::input;
use proconio::marker::{Chars};
use std::collections::VecDeque;

fn main() {
    input!{
        h: usize,
        w: usize,
        c: [usize; 2],
        d: [usize; 2],
        sdash: [Chars; h],
    }
    let inf = 1000000000;
    let mut v = vec![vec![inf; w+4]; h+4];
    let mut s = vec![vec!['#'; w+4]; h+4];
    for i in 0..h {
        for j in 0..w {
            s[i+2][j+2] = sdash[i][j];
        }
    }
    let mut q = VecDeque::new();

    q.push_back((c[0]+1, c[1]+1, 0));

    while q.len() > 0 {
        let (x, y, cost) = q.pop_front().unwrap();
        if v[x+1][y] == inf && s[x+1][y] == '.' {
            q.push_front((x+1, y, cost));
            v[x+1][y] = cost;
        }
        else if v[x][y+1] == inf && s[x][y+1] == '.' {
            q.push_front((x, y+1, cost));
            v[x][y+1] = cost;
        }
        else if v[x-1][y] == inf && s[x-1][y] == '.' {
            q.push_front((x-1, y, cost));
            v[x-1][y] = cost;
        }
        else if v[x][y-1] == inf && s[x][y-1] == '.' {
            q.push_front((x, y-1, cost));
            v[x][y-1] = cost;
        }
        else {
            for i in x-2..x+3 {
                for j in x-2..x+3 {
                    if v[i][j] == inf && s[i][j] == '.' {
                        q.push_back((i, j, cost+1));
                        v[i][j] = cost+1;
                    }
                }
            }
        }
    }
    if v[d[0]+1][d[1]+1] == inf {
        println!("-1");
    }
    else {
        println!("{}", v[d[0]+1][d[1]+1]);
    }
}


let HW = readLine()!.split(separator: " ").compactMap { Int($0) }
let h = HW[0]
let w = HW[1]

let C = readLine()!.split(separator: " ").compactMap { Int($0) }
let ch = C[0]-1
let cw = C[1]-1

let D = readLine()!.split(separator: " ").compactMap { Int($0) }
let dh = D[0]-1
let dw = D[1]-1

var shw = (1...h).map { _ in Array(readLine()!) }

var queue: [[(Int, Int)]] = Array(repeating: [], count: 1000000)
var currentResult: Int = 0
var isExist = false

func isDot(i: Int, j: Int) -> Bool {
    if i < 0 || shw.count <= i { return false }
    if j < 0 || shw[i].count <= j { return false }
    return shw[i][j] == "."
}

func search(i: Int, j: Int) {
    for idot in i-2...i+2 {
        for jdot in j-2...j+2 {
            if idot == i && jdot == j { continue }

            if isDot(i: idot, j: jdot) {
                if ((idot == i - 1 || idot == i + 1) && jdot == j) || ((jdot == j - 1 || jdot == j + 1) && idot == i) {
                    queue[currentResult].append((idot, jdot))
                } else {
                    queue[currentResult+1].append((idot, jdot))
                }
            }
        }
    }
    shw[i][j] = "#"
}

search(i: ch, j: cw)
if queue[currentResult].isEmpty {
    currentResult += 1
}
while !queue[currentResult].isEmpty {
    let element = queue[currentResult].popLast()!

    if element.0 == dh && element.1 == dw {
        isExist = true
        break
    }

    if isDot(i: element.0, j: element.1) {
        search(i: element.0, j: element.1)
    }

    if queue[currentResult].isEmpty {
        currentResult += 1
    }
}
print(isExist ? currentResult : -1)


import Foundation

let main: () = {
    let dh = [0, 1, 0, -1]
    let dw = [1, 0, -1, 0]
    let INF = 1001002003

    let (H, W) = *listOfInt()
    let (Ch, Cw) = *listOfInt(-1)
    let (Dh, Dw) = *listOfInt(-1)
    var G = [[Character]](repeating: [Character](), count: H)
    repeate(H) { h in
        G[h] = next().map { $0 }
    }
    var D = Array<[Int]>(H) { IntArray(W) { INF } }
    D[Ch][Cw] = 0
    var Q = Deque<(Int, Int)>()
    Q.insert((Ch, Cw), at: 0)
    while !Q.isEmpty {
        let (h, w) = Q.removeFirst()
        let d = D[h][w]
        for n in 0 ..< 4 {
            let nh = h + dh[n], nw = w + dw[n]
            if !(0 ..< H ~= nh && 0 ..< W ~= nw) { continue }
            if (G[nh][nw] == "#") { continue }
            if (D[nh][nw] <= d) { continue }
            D[nh][nw] = d
            Q.insert((nh, nw), at: 0)
        }
        for dh in -2 ... 2 {
            for dw in -2 ... 2 {
                let nh = h + dh, nw = w + dw
                if !(0 ..< H ~= nh && 0 ..< W ~= nw) { continue }
                if (G[nh][nw] == "#") { continue }
                if (D[nh][nw] <= d + 1) { continue }
                D[nh][nw] = d + 1
                Q.append((nh, nw))
            }
        }
    }
    var ans = D[Dh][Dw]
    if ans == INF { ans = -1 }
    print(ans)
}()

//----------------------------------

let dx = [1, 0, -1, 0] //[1, 0, -1, 0, -1,  1, -1, 1]
let dy = [0, 1, 0, -1] //[0, 1, 0, -1, -1, -1,  1, 1]

@inlinable func gcd(_ a: Int, _ b: Int) -> Int { (b > 0) ? gcd(b, a % b) : a }
@inlinable func lcm(_ a: Int, _ b: Int) -> Int { a / gcd(a, b) * b }

//----------------------------------

infix operator  =~ : ComparisonPrecedence
infix operator !=~ : ComparisonPrecedence
func =~ <T: FixedWidthInteger>(left: T, right: Range<T>) -> Bool { right ~= left }
func !=~ <T: FixedWidthInteger>(left: T, right: Range<T>) -> Bool { !(right ~= left) }
func =~ <T: FixedWidthInteger>(left: T, right: ClosedRange<T>) -> Bool { right ~= left }
func !=~ <T: FixedWidthInteger>(left: T, right: ClosedRange<T>) -> Bool { !(right ~= left) }

func =~ <T: Collection > (left: T.Element, right: T) -> Bool where T.Element : Equatable { right.contains(left) }
func !=~ <T: Collection > (left: T.Element, right: T) -> Bool where T.Element : Equatable { !right.contains(left) }

infix operator /^ : MultiplicationPrecedence
infix operator /^= : AssignmentPrecedence
func /^ <T: FixedWidthInteger>(left: T, right: T) -> T { (left + (right - 1)) / right }
func /^= <T: FixedWidthInteger>(left: inout T, right: T) { left = (left + (right - 1)) / right }

@inlinable func chmax<T: Comparable>(_ m: inout T, _ q: T) { if (m < q) { m = q } }
@inlinable func chmin<T: Comparable>(_ m: inout T, _ q: T) { if (m > q) { m = q } }
@inlinable func chRmax<T: Comparable>(_ m: inout T, _ q: T) -> Bool { if (m < q) { m = q; return true } else { return false } }
@inlinable func chRmin<T: Comparable>(_ m: inout T, _ q: T) -> Bool { if (m > q) { m = q; return true } else { return false } }

prefix operator *
prefix func * <T> (a: [T]) -> (T, T) { (a[0], a[1]) }
prefix func * <T> (a: [T]) -> (T, T, T) { (a[0], a[1], a[2]) }
prefix func * <T> (a: [T]) -> (T, T, T, T) { (a[0], a[1], a[2], a[3]) }
prefix func * <T> (a: [T]) -> (T, T, T, T, T) { (a[0], a[1], a[2], a[3], a[4]) }
prefix func * <T> (a: [T]) -> (T, T, T, T, T, T) { (a[0], a[1], a[2], a[3], a[4], a[5]) }

func printErr(_ str: String, terminator: String = "\n") {
    let s = str + terminator
    FileHandle.standardError.write(s.data(using: .utf8)!)
    FileHandle.standardError.synchronizeFile()
}

@inlinable func next() -> String { readLine()! }
@inlinable func nextInt() -> Int { Int(next())! }
@inlinable func nextDouble() -> Double { Double(next())! }
@inlinable func listOfString() -> [String] { next().split(separator: " ").map(String.init) }
@inlinable func listOfInt() -> [Int] { listOfString().map { Int($0)! } }
@inlinable func listOfInt(_ offset: Int = 0) -> [Int] { listOfString().map { Int($0)! + offset } }
@inlinable func listOfInt(_ offsets: Int ...) -> [Int] { listOfString().enumerated().map { Int($0.element)! + offsets[$0.offset % offsets.count] } }
@inlinable func listOfDouble(_ offset: Double = 0.0) -> [Double] { listOfString().map { Double($0)! + offset } }
@inlinable func listOfDouble(_ offsets: Double ...) -> [Double] { listOfString().enumerated().map { Double($0.element)! + offsets[$0.offset % offsets.count] } }

extension Int {
    init(s: String) { self.init(s)! }
    var isEven: Bool { self.isMultiple(of: 2) }
    var isOdd: Bool { !self.isEven }
    var isTrue: Bool { self != 0 }
    var isFalse: Bool { !self.isTrue }
    var toString: String { String(self) }
    var toChar: Character { Character(UnicodeScalar(UInt8(self))) }
}
func + (left: Int, right: Bool) -> Int { left + (right ? 1 : 0) }
extension Character {
    var asciiCode: Int { Int(self.asciiValue!) }
    var string: String { String(self) }
}
extension String {
    @inlinable subscript (position: Int) -> Character { self[self.index(self.startIndex, offsetBy: position)] }
    @inlinable func repeate(_ times: Int) -> String { String(repeating: self, count: times) }
    //  @inlinable subscript (n: Int) -> String { String(self[self.index(self.startIndex, offsetBy: n)]) }
    //  @inlinable subscript (n: Int) -> Int { Int(self[self.index(self.startIndex, offsetBy: n)].asciiValue!) }
    var toInt: Int { Int(self)! }
}
extension String { // VB like
    @inlinable func _index(_ position: Int) -> String.Index! {
        if !(0 ..< self.count ~= position) { return nil }
        return self.index(self.startIndex, offsetBy: position)
    }
    @inlinable func mid(from index: Int, length: Int) -> String! {
        if let startPosition = self._index(index) {
            if let endPosition = self._index(index + length - 1) {
                return String(self[startPosition...endPosition])
            } else {
                let endPosition = self.index(before: self.endIndex)
                return String(self[startPosition...endPosition])
            }
        }
        return ""
    }
    @inlinable func left(_ length: Int) -> String { String(self.prefix(length)) }
    @inlinable func right(_ length: Int) -> String { String(self.suffix(length)) }
}

extension Dictionary {
    @inlinable mutating func getOrSet(_ key: Key, _ defaultValue: () -> Value) -> Value {
        if let stored = self[key] { return stored }
        else {
            let newValue = defaultValue()
            self[key] = newValue
            return newValue
        }
    }
    @inlinable mutating func getOrElse(_ key: Key, _ defaultValue: () -> Value) -> Value {
        if let stored = self[key] { return stored } else { return defaultValue() }
    }
    @inlinable func containsKey(_ key: Key) -> Bool { self.keys.contains(key) }
}

extension Set {
    static func += (left: inout Set<Element>, right: Element) { left.insert(right) }
    static func -= (left: inout Set<Element>, right: Element) { left.remove(right) }
}

extension Array {
    static func += (left: inout Array<Element>, right: Element) { left.append(right) }
    init(_ size: Int, _ initValue: ((Int) -> Element)) {
        self = [Element]()
        for index in 0 ..< size {
            let value = initValue(index)
            self.append(value)
        }
    }
    init(_ size: Int, _ initValue: (() -> Element)) {
        self = [Element]()
        for _ in 0 ..< size {
            let value = initValue()
            self.append(value)
        }
    }
}
typealias IntArray = Array<Int>
typealias DoubleArray = Array<Double>
typealias BoolArray = Array<Bool>
typealias StringArray = Array<String>

extension Array where Element == Int {
    init(_ size: Int) {
        self = [Element](repeating: 0, count: size)
    }
}
extension Array where Element == Double {
    init(_ size: Int) {
        self = [Element](repeating: 0.0, count: size)
    }
}
extension Array where Element == Bool {
    init(_ size: Int) {
        self = [Element](repeating: false, count: size)
    }
}
extension Array where Element == String {
    init(_ size: Int) {
        self = [Element](repeating: "", count: size)
    }
}

extension Array {
    @inlinable func count(_ predicate: ((Element) -> Bool)) -> Int {
        var cnt = 0; for e in self { if (predicate(e)) { cnt += 1 } }
        return cnt
    }
    @inlinable func groupBy<K>(_ keySelector: (Element) -> K) -> [K : [Element]] {
        var destination = [K : [Element]]()
        for element in self {
            let key = keySelector(element)
            var list = destination.getOrSet(key) { [Element]() }
            list.append(element)
            destination[key] = list
        }
        return destination
    }
    @inlinable func groupByIndexed<K>(_ keySelector: (Element) -> K) -> [K : [Int]] {
        var destination = [K : [Int]]()
        for element in self.enumerated() {
            let key = keySelector(element.element)
            var list = destination.getOrSet(key) { [Int]() }
            list.append(element.offset)
            destination[key] = list
        }
        return destination
    }
}

extension Array {
    func binarySearch(_ element: Element, by compare: (Element, Element) -> Int, fromIndex: Int = 0, toIndex: Int = -1) -> Int {
        var low = fromIndex
        var high = (toIndex == -1) ? self.count - 1 : toIndex - 1
        while (low <= high) {
            let mid = (low + high) >> 1 // safe from overflows
            let midVal = self[mid]
            let cmp = compare(midVal, element)
            if (cmp < 0) { low = mid + 1 }          //-1
            else if (0 < cmp) { high = mid - 1 }    // 1
            else { return mid } // key found        // 0
        }
        return -(low + 1)  // key not found
    }
}
extension Array where Element : Comparable {
    func binarySearch(_ element: Element, fromIndex: Int = 0, toIndex: Int = -1) -> Int {
        var low = fromIndex
        var high = (toIndex == -1) ? self.count - 1 : toIndex - 1
        while (low <= high) {
            let mid = (low + high) >> 1 // safe from overflows
            let midVal = self[mid]
            if (midVal < element) { low = mid + 1 }
            else if (midVal > element) { high = mid - 1 }
            else { return mid } // key found
        }
        return -(low + 1)  // key not found
    }
}

extension Array where Element == Int {
    @inlinable func distinct() -> [Element] {
        var set = Set<Element>(), ary = [Element]()
        for n in self.indices { if !set.contains(self[n]) { ary.append(self[n]); set.insert(self[n]) } }
        return ary
    }
    @inlinable func any(_ predicate: (Element) -> Bool) -> Bool {
        var any = false; for element in self { if predicate(element) { any = true } }
        return any
    }
    @inlinable func all(_ predicate: (Element) -> Bool) -> Bool {
        self.allSatisfy(predicate)
    }
    @inlinable func sum() -> Element { self.reduce(0, +) }
    @inlinable func sum(by predicate: (Element) -> Element) -> Element {
        var sum = 0; for e in self { sum += predicate(e) }
        return sum
    }
    @inlinable func joinToString(_ sepatator: String = " ") -> String { self.map { String($0) }.joined(separator: sepatator) }
}

extension Array where Element == Double {
    @inlinable func sum() -> Element { self.reduce(0.0, +) }
    @inlinable func sum(by predicate: (Element) -> Element) -> Element {
        var sum = 0.0; for e in self { sum += predicate(e) }
        return sum
    }
    @inlinable func joinToString(_ sepatator: String = " ") -> String { self.map { String($0) }.joined(separator: sepatator) }
}
extension Array where Element == String {
    @inlinable func toIntDouble() -> (Int, Double) { (Int(self[0])!, Double(self[1])!) }
    @inlinable func toIntString() -> (Int, String) { (Int(self[0])!, self[1]) }
    @inlinable func toIntIntString() -> (Int, Int, String) { (Int(self[0])!, Int(self[1])!, self[2]) }
}

@inlinable func block( _ action: () -> Void) {
    action()
}
@inlinable func repeate(_ times1: Int, _ action: () -> Void) {
    for _ in 0 ..< times1 { action() }
}
@inlinable func repeate(_ times1: Int, _ action: (Int) -> Void) {
    for index1 in 0 ..< times1 {
        action(index1)
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ action: (Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            action(index1, index2)
        }
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ times3: Int, _ action: (Int, Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            for index3 in 0 ..< times3 {
                action(index1, index2, index3)
            }
        }
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ times3: Int, _ times4: Int, _ action: (Int, Int, Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            for index3 in 0 ..< times3 {
                for index4 in 0 ..< times4 {
                    action(index1, index2, index3, index4)
                }
            }
        }
    }
}

func measureTimeSeconds(block: (() -> Void)) -> Double {
    let startTime = Date()
    block()
    let duration = -startTime.timeIntervalSinceNow
    return duration
}

//
//  CwlDeque.swift
//  CwlUtils
//
//  Created by Matt Gallagher on 2016/09/13.
//  Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
//
//  Permission to use, copy, modify, and/or distribute this software for any
//  purpose with or without fee is hereby granted, provided that the above
//  copyright notice and this permission notice appear in all copies.
//
//  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
//  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
//  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
//  SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
//  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
//  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
//  IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//

import Foundation

/// This is a basic "circular-buffer" style Double-Ended Queue.
public struct Deque<T>: RandomAccessCollection, MutableCollection, RangeReplaceableCollection, ExpressibleByArrayLiteral, CustomDebugStringConvertible {
    public typealias Index = Int
    public typealias Indices = CountableRange<Int>
    public typealias Element = T

    private let overAllocateFactor = 2
    private let downsizeTriggerFactor = 16
    private let defaultMinimumCapacity = 5

    private var buffer: DequeBuffer<T>? = nil
    private var minCapacity: Int

    /// Implementation of RangeReplaceableCollection function
    public init() {
        self.minCapacity = defaultMinimumCapacity
    }

    /// Allocate with a minimum capacity
    public init(minCapacity: Int) {
        self.minCapacity = minCapacity
    }

    /// Implementation of ExpressibleByArrayLiteral function
    public init(arrayLiteral: T...) {
        self.minCapacity = defaultMinimumCapacity
        replaceSubrange(0..<0, with: arrayLiteral)
    }

    /// Implementation of CustomDebugStringConvertible function
    public var debugDescription: String {
        var result = "\(type(of: self))(["
        var iterator = makeIterator()
        if let next = iterator.next() {
            debugPrint(next, terminator: "", to: &result)
            while let n = iterator.next() {
                result += ", "
                debugPrint(n, terminator: "", to: &result)
            }
        }
        result += "])"
        return result
    }

    #if swift(>=4.1)
    public subscript(bounds: Range<Index>) -> Slice<Deque<T>> {
        return Slice<Deque<T>>(base: self, bounds: bounds)
    }
    #else
    public subscript(bounds: Range<Index>) -> RangeReplaceableRandomAccessSlice<Deque<T>> {
        return RangeReplaceableRandomAccessSlice<Deque<T>>(base: self, bounds: bounds)
    }
    #endif

    /// Implementation of RandomAccessCollection function
    public subscript(_ at: Index) -> T {
        get {
            return buffer!.withUnsafeMutablePointers { headerPtr, bodyPtr -> T in
                precondition(at >= 0 && at < headerPtr.pointee.count)
                var offset = headerPtr.pointee.offset + at
                if offset >= headerPtr.pointee.capacity {
                    offset -= headerPtr.pointee.capacity
                }
                return bodyPtr[offset]
            }
        }
        set {
            buffer!.withUnsafeMutablePointers { headerPtr, bodyPtr in
                precondition(at >= 0 && at < headerPtr.pointee.count)
                var offset = headerPtr.pointee.offset + at
                if offset >= headerPtr.pointee.capacity {
                    offset -= headerPtr.pointee.capacity
                }
                bodyPtr[offset] = newValue
            }
        }
    }

    /// Implementation of Collection function
    public var startIndex: Index {
        return 0
    }

    /// Implementation of Collection function
    public var endIndex: Index {
        return buffer?.withUnsafeMutablePointerToHeader { headerPtr in headerPtr.pointee.count } ?? 0
    }

    /// Implementation of Collection function
    public var isEmpty: Bool {
        return buffer?.withUnsafeMutablePointerToHeader { headerPtr in headerPtr.pointee.count == 0 } ?? true
    }

    /// Implementation of Collection function
    public var count: Int {
        return endIndex
    }

    /// Optimized implementation of RangeReplaceableCollection function
    public mutating func append(_ newElement: T) {
        let done = buffer?.withUnsafeMutablePointers { headerPtr, bodyPtr -> Bool in
            guard headerPtr.pointee.capacity >= headerPtr.pointee.count + 1 else { return false }
            var index = headerPtr.pointee.offset + headerPtr.pointee.count
            if index >= headerPtr.pointee.capacity {
                index -= headerPtr.pointee.capacity
            }
            bodyPtr.advanced(by: index).initialize(to: newElement)
            headerPtr.pointee.count += 1
            return true
            } ?? false

        if done {
            return
        }

        let index = endIndex
        return replaceSubrange(index..<index, with: CollectionOfOne(newElement))
    }

    /// Optimized implementation of RangeReplaceableCollection function
    public mutating func insert(_ newElement: T, at: Int) {
        let done = buffer?.withUnsafeMutablePointers { headerPtr, bodyPtr -> Bool in
            guard at == 0, headerPtr.pointee.capacity >= headerPtr.pointee.count + 1 else { return false }
            var index = headerPtr.pointee.offset - 1
            if index < 0 {
                index += headerPtr.pointee.capacity
            }
            bodyPtr.advanced(by: index).initialize(to: newElement)
            headerPtr.pointee.count += 1
            headerPtr.pointee.offset = index
            return true
            } ?? false

        if done {
            return
        }

        return replaceSubrange(at..<at, with: CollectionOfOne(newElement))
    }

    /// Optimized implementation of RangeReplaceableCollection function
    public mutating func remove(at: Int) {
        let done = buffer?.withUnsafeMutablePointers { headerPtr, bodyPtr -> Bool in
            if at == headerPtr.pointee.count - 1 {
                headerPtr.pointee.count -= 1
                return true
            } else if at == 0, headerPtr.pointee.count > 0 {
                headerPtr.pointee.offset += 1
                if headerPtr.pointee.offset >= headerPtr.pointee.capacity {
                    headerPtr.pointee.offset -= headerPtr.pointee.capacity
                }
                headerPtr.pointee.count -= 1
                return true
            }
            return false
            } ?? false

        if done {
            return
        }

        return replaceSubrange(at...at, with: EmptyCollection())
    }

    /// Optimized implementation of RangeReplaceableCollection function
    public mutating func removeFirst() -> T {
        return buffer!.withUnsafeMutablePointers { headerPtr, bodyPtr -> T in
            precondition(headerPtr.pointee.count > 0, "Index beyond bounds")
            let result = bodyPtr[headerPtr.pointee.offset]
            #if swift(>=4.1)
            bodyPtr.advanced(by: headerPtr.pointee.offset).deinitialize(count: 1)
            #else
            bodyPtr.advanced(by: headerPtr.pointee.offset).deinitialize()
            #endif
            headerPtr.pointee.offset += 1
            if headerPtr.pointee.offset >= headerPtr.pointee.capacity {
                headerPtr.pointee.offset -= headerPtr.pointee.capacity
            }
            headerPtr.pointee.count -= 1
            return result
        }
    }

    // Used when removing a range from the collection or deiniting self.
    private static func deinitialize(range: CountableRange<Int>, header: UnsafeMutablePointer<DequeHeader>, body: UnsafeMutablePointer<T>) {
        let splitRange = header.pointee.splitRangeIndices(inRange: range)
        body.advanced(by: splitRange.low.startIndex).deinitialize(count: splitRange.low.count)
        body.advanced(by: splitRange.high.startIndex).deinitialize(count: splitRange.high.count)
    }

    // Move from an initialized to an uninitialized location, deinitializing the source.
    //
    // NOTE: the terms "preMapped" and "postMapped" are used. "preMapped" refer to the public indices exposed by this type (zero based, contiguous), and "postMapped" refers to internal offsets within the buffer (not necessarily zero based and may wrap around). This function will only handle a single, contiguous block of "postMapped" indices so the caller must ensure that this function is invoked separately for each contiguous block.
    private static func moveInitialize(preMappedSourceRange: CountableRange<Int>, postMappedDestinationRange: CountableRange<Int>, sourceHeader: UnsafeMutablePointer<DequeHeader>, sourceBody: UnsafeMutablePointer<T>, destinationBody: UnsafeMutablePointer<T>) {
        let sourceSplitRange = sourceHeader.pointee.splitRangeIndices(inRange: preMappedSourceRange)

        assert(sourceSplitRange.low.startIndex >= 0 && (sourceSplitRange.low.startIndex < sourceHeader.pointee.capacity || sourceSplitRange.low.startIndex == sourceSplitRange.low.endIndex))
        assert(sourceSplitRange.low.endIndex >= 0 && sourceSplitRange.low.endIndex <= sourceHeader.pointee.capacity)

        assert(sourceSplitRange.high.startIndex >= 0 && (sourceSplitRange.high.startIndex < sourceHeader.pointee.capacity || sourceSplitRange.high.startIndex == sourceSplitRange.high.endIndex))
        assert(sourceSplitRange.high.endIndex >= 0 && sourceSplitRange.high.endIndex <= sourceHeader.pointee.capacity)

        destinationBody.advanced(by: postMappedDestinationRange.startIndex).moveInitialize(from: sourceBody.advanced(by: sourceSplitRange.low.startIndex), count: sourceSplitRange.low.count)
        destinationBody.advanced(by: postMappedDestinationRange.startIndex + sourceSplitRange.low.count).moveInitialize(from: sourceBody.advanced(by: sourceSplitRange.high.startIndex), count: sourceSplitRange.high.count)
    }

    // Copy from an initialized to an uninitialized location, leaving the source initialized.
    //
    // NOTE: the terms "preMapped" and "postMapped" are used. "preMapped" refer to the public indices exposed by this type (zero based, contiguous), and "postMapped" refers to internal offsets within the buffer (not necessarily zero based and may wrap around). This function will only handle a single, contiguous block of "postMapped" indices so the caller must ensure that this function is invoked separately for each contiguous block.
    private static func copyInitialize(preMappedSourceRange: CountableRange<Int>, postMappedDestinationRange: CountableRange<Int>, sourceHeader: UnsafeMutablePointer<DequeHeader>, sourceBody: UnsafeMutablePointer<T>, destinationBody: UnsafeMutablePointer<T>) {
        let sourceSplitRange = sourceHeader.pointee.splitRangeIndices(inRange: preMappedSourceRange)

        assert(sourceSplitRange.low.startIndex >= 0 && (sourceSplitRange.low.startIndex < sourceHeader.pointee.capacity || sourceSplitRange.low.startIndex == sourceSplitRange.low.endIndex))
        assert(sourceSplitRange.low.endIndex >= 0 && sourceSplitRange.low.endIndex <= sourceHeader.pointee.capacity)

        assert(sourceSplitRange.high.startIndex >= 0 && (sourceSplitRange.high.startIndex < sourceHeader.pointee.capacity || sourceSplitRange.high.startIndex == sourceSplitRange.high.endIndex))
        assert(sourceSplitRange.high.endIndex >= 0 && sourceSplitRange.high.endIndex <= sourceHeader.pointee.capacity)

        destinationBody.advanced(by: postMappedDestinationRange.startIndex).initialize(from: sourceBody.advanced(by: sourceSplitRange.low.startIndex), count: sourceSplitRange.low.count)
        destinationBody.advanced(by: postMappedDestinationRange.startIndex + sourceSplitRange.low.count).initialize(from: sourceBody.advanced(by: sourceSplitRange.high.startIndex), count: sourceSplitRange.high.count)
    }

    // Internal implementation of replaceSubrange<C>(_:with:) when no reallocation
    // of the underlying buffer is required
    private static func mutateWithoutReallocate<C>(info: DequeMutationInfo2, elements newElements: C, header: UnsafeMutablePointer<DequeHeader>, body: UnsafeMutablePointer<T>) where C: Collection, C.Iterator.Element == T {
        if info.removed > 0 {
            Deque.deinitialize(range: info.start..<(info.start + info.removed), header: header, body: body)
        }

        if info.removed != info.inserted {
            if info.start < header.pointee.count - (info.start + info.removed) {
                let oldOffset = header.pointee.offset
                header.pointee.offset -= info.inserted - info.removed
                if header.pointee.offset < 0 {
                    header.pointee.offset += header.pointee.capacity
                } else if header.pointee.offset >= header.pointee.capacity {
                    header.pointee.offset -= header.pointee.capacity
                }
                let delta = oldOffset - header.pointee.offset
                if info.start != 0 {
                    let destinationSplitIndices = header.pointee.splitRangeIndices(inRange: 0..<info.start)
                    let lowCount = destinationSplitIndices.low.count
                    Deque.moveInitialize(preMappedSourceRange: delta..<(delta + lowCount), postMappedDestinationRange: destinationSplitIndices.low, sourceHeader: header, sourceBody: body, destinationBody: body)
                    if lowCount != info.start {
                        Deque.moveInitialize(preMappedSourceRange: (delta + lowCount)..<(info.start + delta), postMappedDestinationRange: destinationSplitIndices.high, sourceHeader: header, sourceBody: body, destinationBody: body)
                    }
                }
            } else {
                if (info.start + info.removed) != header.pointee.count {
                    let start = info.start + info.removed
                    let end = header.pointee.count
                    let destinationSplitIndices = header.pointee.splitRangeIndices(inRange: (info.start + info.inserted)..<(end - info.removed + info.inserted))
                    let lowCount = destinationSplitIndices.low.count

                    Deque.moveInitialize(preMappedSourceRange: start..<end, postMappedDestinationRange: destinationSplitIndices.low, sourceHeader: header, sourceBody: body, destinationBody: body)
                    if lowCount != end - start {
                        Deque.moveInitialize(preMappedSourceRange: (start + lowCount)..<end, postMappedDestinationRange: destinationSplitIndices.high, sourceHeader: header, sourceBody: body, destinationBody: body)
                    }
                }
            }
            header.pointee.count = header.pointee.count - info.removed + info.inserted
        }

        if info.inserted == 1, let e = newElements.first {
            if info.start >= header.pointee.capacity - header.pointee.offset {
                body.advanced(by: info.start - header.pointee.capacity + header.pointee.offset).initialize(to: e)
            } else {
                body.advanced(by: header.pointee.offset + info.start).initialize(to: e)
            }
        } else if info.inserted > 0 {
            let inserted = header.pointee.splitRangeIndices(inRange: info.start..<(info.start + info.inserted))
            var iterator = newElements.makeIterator()
            for i in inserted.low {
                if let n = iterator.next() {
                    body.advanced(by: i).initialize(to: n)
                }
            }
            for i in inserted.high {
                if let n = iterator.next() {
                    body.advanced(by: i).initialize(to: n)
                }
            }
        }
    }

    // Internal implementation of replaceSubrange<C>(_:with:) when reallocation
    // of the underlying buffer is required. Can handle no previous buffer or
    // previous buffer too small or previous buffer too big or previous buffer
    // non-unique.
    private mutating func reallocateAndMutate<C>(info: DequeMutationInfo2, elements newElements: C, header: UnsafeMutablePointer<DequeHeader>?, body: UnsafeMutablePointer<T>?, deletePrevious: Bool) where C: Collection, C.Iterator.Element == T {
        if info.newCount == 0 {
            // Let the regular deallocation handle the deinitialize
            buffer = nil
        } else {
            let newCapacity: Int
            let oldCapacity = header?.pointee.capacity ?? 0
            if info.newCount > oldCapacity || info.newCount <= oldCapacity / downsizeTriggerFactor {
                newCapacity = Swift.max(minCapacity, info.newCount * overAllocateFactor)
            } else {
                newCapacity = oldCapacity
            }

            let newBuffer = DequeBuffer<T>.create(minimumCapacity: newCapacity) { (buffer: ManagedBuffer<DequeHeader, T>) in
                return DequeHeader(offset: 0, count: info.newCount, capacity: newCapacity)
                } as! DequeBuffer<T>
            if let headerPtr = header, let bodyPtr = body {
                if deletePrevious, info.removed > 0 {
                    Deque.deinitialize(range: info.start..<(info.start + info.removed), header: headerPtr, body: bodyPtr)
                }

                newBuffer.withUnsafeMutablePointerToElements { newBody in
                    if info.start != 0 {
                        if deletePrevious {
                            Deque.moveInitialize(preMappedSourceRange: 0..<info.start, postMappedDestinationRange: 0..<info.start, sourceHeader: headerPtr, sourceBody: bodyPtr, destinationBody: newBody)
                        } else {
                            Deque.copyInitialize(preMappedSourceRange: 0..<info.start, postMappedDestinationRange: 0..<info.start, sourceHeader: headerPtr, sourceBody: bodyPtr, destinationBody: newBody)
                        }
                    }

                    let oldCount = header?.pointee.count ?? 0
                    if info.start + info.removed != oldCount {
                        if deletePrevious {
                            Deque.moveInitialize(preMappedSourceRange: (info.start + info.removed)..<oldCount, postMappedDestinationRange: (info.start + info.inserted)..<info.newCount, sourceHeader: headerPtr, sourceBody: bodyPtr, destinationBody: newBody)
                        } else {
                            Deque.copyInitialize(preMappedSourceRange: (info.start + info.removed)..<oldCount, postMappedDestinationRange: (info.start + info.inserted)..<info.newCount, sourceHeader: headerPtr, sourceBody: bodyPtr, destinationBody: newBody)
                        }
                    }
                }

                // Make sure the old buffer doesn't deinitialize when it deallocates.
                if deletePrevious {
                    headerPtr.pointee.count = 0
                }
            }

            if info.inserted > 0 {
                newBuffer.withUnsafeMutablePointerToElements { newBody in
                    #if swift(>=3.1)
                    let umbp = UnsafeMutableBufferPointer(start: newBody.advanced(by: info.start), count: info.inserted)
                    _ = umbp.initialize(from: newElements)
                    #else
                    // Insert the new subrange
                    newBody.advanced(by: info.start).initialize(from: newElements)
                    #endif
                }
            }

            buffer = newBuffer
        }
    }

    /// Implemetation of the RangeReplaceableCollection function. Internally
    /// implemented using either mutateWithoutReallocate or reallocateAndMutate.
    public mutating func replaceSubrange<C>(_ subrange: Range<Int>, with newElements: C) where C: Collection, C.Iterator.Element == T {
        precondition(subrange.lowerBound >= 0, "Subrange lowerBound is negative")

        if isKnownUniquelyReferenced(&buffer), let b = buffer {
            b.withUnsafeMutablePointers { headerPtr, bodyPtr in
                let info = DequeMutationInfo2(subrange: subrange, previousCount: headerPtr.pointee.count, insertedCount: numericCast(newElements.count))
                if info.newCount <= headerPtr.pointee.capacity && (info.newCount < minCapacity || info.newCount > headerPtr.pointee.capacity / downsizeTriggerFactor) {
                    Deque.mutateWithoutReallocate(info: info, elements: newElements, header: headerPtr, body: bodyPtr)
                } else {
                    reallocateAndMutate(info: info, elements: newElements, header: headerPtr, body: bodyPtr, deletePrevious: true)
                }
            }
        } else if let b = buffer {
            b.withUnsafeMutablePointers { headerPtr, bodyPtr in
                let info = DequeMutationInfo2(subrange: subrange, previousCount: headerPtr.pointee.count, insertedCount: numericCast(newElements.count))
                reallocateAndMutate(info: info, elements: newElements, header: headerPtr, body: bodyPtr, deletePrevious: false)
            }
        } else {
            let info = DequeMutationInfo2(subrange: subrange, previousCount: 0, insertedCount: numericCast(newElements.count))
            reallocateAndMutate(info: info, elements: newElements, header: nil, body: nil, deletePrevious: true)
        }
    }
}

// Internal state for the Deque
private struct DequeHeader {
    var offset: Int
    var count: Int
    var capacity: Int

    // Translate from preMapped to postMapped indices.
    //
    // "preMapped" refer to the public indices exposed by this type (zero based, contiguous), and "postMapped" refers to internal offsets within the buffer (not necessarily zero based and may wrap around).
    //
    // Since "postMapped" indices are not necessarily contiguous, two separate, contiguous ranges are returned. Both `startIndex` and `endIndex` in the `high` range will equal the `endIndex` in the `low` range if the range specified by `inRange` is continuous after mapping.
    func splitRangeIndices(inRange: CountableRange<Int>) -> (low: CountableRange<Int>, high: CountableRange<Int>) {
        let limit = capacity - offset
        if inRange.startIndex >= limit {
            return (low: (inRange.startIndex - limit)..<(inRange.endIndex - limit), high: (inRange.endIndex - limit)..<(inRange.endIndex - limit))
        } else if inRange.endIndex > limit {
            return (low: (inRange.startIndex + offset)..<capacity, high: 0..<(inRange.endIndex - limit))
        }
        return (low: (inRange.startIndex + offset)..<(inRange.endIndex + offset), high: (inRange.endIndex + offset)..<(inRange.endIndex + offset))
    }

}

// Private type used to communicate parameters between replaceSubrange<C>(_:with:)
// and reallocateAndMutate or mutateWithoutReallocate
private struct DequeMutationInfo2 {
    let start: Int
    let removed: Int
    let inserted: Int
    let newCount: Int

    init(subrange: Range<Int>, previousCount: Int, insertedCount: Int) {
        precondition(subrange.upperBound <= previousCount, "Subrange upperBound is out of range")

        self.start = subrange.lowerBound
        self.removed = subrange.count
        self.inserted = insertedCount
        self.newCount = previousCount - self.removed + self.inserted
    }
}

// Private reimplementation of function with same name from stdlib/public/core/BuiltIn.swift
private func roundUp(_ offset: UInt, toAlignment alignment: Int) -> UInt {
    let x = offset + UInt(bitPattern: alignment) &- 1
    return x & ~(UInt(bitPattern: alignment) &- 1)
}

// Private reimplementation of definition from stdlib/public/SwiftShims/HeapObject.h
private struct HeapObject {
    let metadata: Int = 0
    let strongRefCount: UInt32 = 0
    let weakRefCount: UInt32 = 0
}

// An implementation of DequeBuffer using ManagedBufferPointer to allocate the
// storage and then using raw pointer offsets into self to access contents
// (avoiding the ManagedBufferPointer accessors which are a performance problem
// in Swift 3).
private final class DequeBuffer<T>: ManagedBuffer<DequeHeader, T> {
    #if true
    private static var headerOffset: Int {
        return Int(roundUp(UInt(MemoryLayout<HeapObject>.size), toAlignment: MemoryLayout<DequeHeader>.alignment))
    }

    private static var elementOffset: Int {
        return Int(roundUp(UInt(headerOffset) + UInt(MemoryLayout<DequeHeader>.size), toAlignment: MemoryLayout<T>.alignment))
    }

    private var bodyPtr: UnsafeMutablePointer<T> {
        return Unmanaged<DequeBuffer<T>>.passUnretained(self).toOpaque().advanced(by: DequeBuffer<T>.elementOffset).assumingMemoryBound(to: T.self)
    }

    private var headerPtr: UnsafeMutablePointer<DequeHeader> {
        return Unmanaged<DequeBuffer<T>>.passUnretained(self).toOpaque().advanced(by: DequeBuffer<T>.headerOffset).assumingMemoryBound(to: DequeHeader.self)
    }
    #endif

    deinit {
        #if true
        // We need to assert this in case some of our dirty assumptions stop being true
        assert(ManagedBufferPointer<DequeHeader, T>(unsafeBufferObject: self).withUnsafeMutablePointers { (header, body) in
            self.headerPtr == header && self.bodyPtr == body
        })

        let splitRange = headerPtr.pointee.splitRangeIndices(inRange: 0..<headerPtr.pointee.count)
        bodyPtr.advanced(by: splitRange.low.startIndex).deinitialize(count: splitRange.low.count)
        bodyPtr.advanced(by: splitRange.high.startIndex).deinitialize(count: splitRange.high.count)
        #else
        withUnsafeMutablePointers { headerPtr, bodyPtr in
            let splitRange = headerPtr.pointee.splitRangeIndices(inRange: 0..<headerPtr.pointee.count)
            bodyPtr.advanced(by: splitRange.low.startIndex).deinitialize(count: splitRange.low.count)
            bodyPtr.advanced(by: splitRange.high.startIndex).deinitialize(count: splitRange.high.count)
        }
        #endif
    }
}


import Foundation

let main: () = {
    let dh = [0, 1, 0, -1]
    let dw = [1, 0, -1, 0]
    let INF = 1001002003

    let (H, W) = *listOfInt()
    let (Ch, Cw) = *listOfInt(-1)
    let (Dh, Dw) = *listOfInt(-1)
    var G = [[Character]](repeating: [Character](), count: H)
    repeate(H) { h in
        G[h] = next().map { $0 }
    }
    var D = Array<[Int]>(H) { IntArray(W) { INF } }
    D[Ch][Cw] = 0
    var Q = Deque<(Int, Int)>()
    Q.addFirst((Ch, Cw))
    while !Q.isEmpty {
        let (h, w) = Q.removeFirst()!
        let d = D[h][w]
        for n in 0 ..< 4 {
            let nh = h + dh[n], nw = w + dw[n]
            if !(0 ..< H ~= nh && 0 ..< W ~= nw) { continue }
            if (G[nh][nw] == "#") { continue }
            if (D[nh][nw] <= d) { continue }
            D[nh][nw] = d
            Q.addFirst((nh, nw))
        }
        for dh in -2 ... 2 {
            for dw in -2 ... 2 {
                let nh = h + dh, nw = w + dw
                if !(0 ..< H ~= nh && 0 ..< W ~= nw) { continue }
                if (G[nh][nw] == "#") { continue }
                if (D[nh][nw] <= d + 1) { continue }
                D[nh][nw] = d + 1
                Q.addLast((nh, nw))
            }
        }
    }
    var ans = D[Dh][Dw]
    if ans == INF { ans = -1 }
    print(ans)
}()

//----------------------------------

let dx = [1, 0, -1, 0] //[1, 0, -1, 0, -1,  1, -1, 1]
let dy = [0, 1, 0, -1] //[0, 1, 0, -1, -1, -1,  1, 1]

@inlinable func gcd(_ a: Int, _ b: Int) -> Int { (b > 0) ? gcd(b, a % b) : a }
@inlinable func lcm(_ a: Int, _ b: Int) -> Int { a / gcd(a, b) * b }

//----------------------------------

infix operator  =~ : ComparisonPrecedence
infix operator !=~ : ComparisonPrecedence
func =~ <T: FixedWidthInteger>(left: T, right: Range<T>) -> Bool { right ~= left }
func !=~ <T: FixedWidthInteger>(left: T, right: Range<T>) -> Bool { !(right ~= left) }
func =~ <T: FixedWidthInteger>(left: T, right: ClosedRange<T>) -> Bool { right ~= left }
func !=~ <T: FixedWidthInteger>(left: T, right: ClosedRange<T>) -> Bool { !(right ~= left) }

func =~ <T: Collection > (left: T.Element, right: T) -> Bool where T.Element : Equatable { right.contains(left) }
func !=~ <T: Collection > (left: T.Element, right: T) -> Bool where T.Element : Equatable { !right.contains(left) }

infix operator /^ : MultiplicationPrecedence
infix operator /^= : AssignmentPrecedence
func /^ <T: FixedWidthInteger>(left: T, right: T) -> T { (left + (right - 1)) / right }
func /^= <T: FixedWidthInteger>(left: inout T, right: T) { left = (left + (right - 1)) / right }

@inlinable func chmax<T: Comparable>(_ m: inout T, _ q: T) { if (m < q) { m = q } }
@inlinable func chmin<T: Comparable>(_ m: inout T, _ q: T) { if (m > q) { m = q } }
@inlinable func chRmax<T: Comparable>(_ m: inout T, _ q: T) -> Bool { if (m < q) { m = q; return true } else { return false } }
@inlinable func chRmin<T: Comparable>(_ m: inout T, _ q: T) -> Bool { if (m > q) { m = q; return true } else { return false } }

prefix operator *
prefix func * <T> (a: [T]) -> (T, T) { (a[0], a[1]) }
prefix func * <T> (a: [T]) -> (T, T, T) { (a[0], a[1], a[2]) }
prefix func * <T> (a: [T]) -> (T, T, T, T) { (a[0], a[1], a[2], a[3]) }
prefix func * <T> (a: [T]) -> (T, T, T, T, T) { (a[0], a[1], a[2], a[3], a[4]) }
prefix func * <T> (a: [T]) -> (T, T, T, T, T, T) { (a[0], a[1], a[2], a[3], a[4], a[5]) }

func printErr(_ str: String, terminator: String = "\n") {
    let s = str + terminator
    FileHandle.standardError.write(s.data(using: .utf8)!)
    FileHandle.standardError.synchronizeFile()
}

@inlinable func next() -> String { readLine()! }
@inlinable func nextInt() -> Int { Int(next())! }
@inlinable func nextDouble() -> Double { Double(next())! }
@inlinable func listOfString() -> [String] { next().split(separator: " ").map(String.init) }
@inlinable func listOfInt() -> [Int] { listOfString().map { Int($0)! } }
@inlinable func listOfInt(_ offset: Int = 0) -> [Int] { listOfString().map { Int($0)! + offset } }
@inlinable func listOfInt(_ offsets: Int ...) -> [Int] { listOfString().enumerated().map { Int($0.element)! + offsets[$0.offset % offsets.count] } }
@inlinable func listOfDouble(_ offset: Double = 0.0) -> [Double] { listOfString().map { Double($0)! + offset } }
@inlinable func listOfDouble(_ offsets: Double ...) -> [Double] { listOfString().enumerated().map { Double($0.element)! + offsets[$0.offset % offsets.count] } }

extension Int {
    init(s: String) { self.init(s)! }
    var isEven: Bool { self.isMultiple(of: 2) }
    var isOdd: Bool { !self.isEven }
    var isTrue: Bool { self != 0 }
    var isFalse: Bool { !self.isTrue }
    var toString: String { String(self) }
    var toChar: Character { Character(UnicodeScalar(UInt8(self))) }
}
func + (left: Int, right: Bool) -> Int { left + (right ? 1 : 0) }
extension Character {
    var asciiCode: Int { Int(self.asciiValue!) }
    var string: String { String(self) }
}
extension String {
    @inlinable subscript (position: Int) -> Character { self[self.index(self.startIndex, offsetBy: position)] }
    @inlinable func repeate(_ times: Int) -> String { String(repeating: self, count: times) }
    //  @inlinable subscript (n: Int) -> String { String(self[self.index(self.startIndex, offsetBy: n)]) }
    //  @inlinable subscript (n: Int) -> Int { Int(self[self.index(self.startIndex, offsetBy: n)].asciiValue!) }
    var toInt: Int { Int(self)! }
}
extension String { // VB like
    @inlinable func _index(_ position: Int) -> String.Index! {
        if !(0 ..< self.count ~= position) { return nil }
        return self.index(self.startIndex, offsetBy: position)
    }
    @inlinable func mid(from index: Int, length: Int) -> String! {
        if let startPosition = self._index(index) {
            if let endPosition = self._index(index + length - 1) {
                return String(self[startPosition...endPosition])
            } else {
                let endPosition = self.index(before: self.endIndex)
                return String(self[startPosition...endPosition])
            }
        }
        return ""
    }
    @inlinable func left(_ length: Int) -> String { String(self.prefix(length)) }
    @inlinable func right(_ length: Int) -> String { String(self.suffix(length)) }
}

extension Dictionary {
    @inlinable mutating func getOrSet(_ key: Key, _ defaultValue: () -> Value) -> Value {
        if let stored = self[key] { return stored }
        else {
            let newValue = defaultValue()
            self[key] = newValue
            return newValue
        }
    }
    @inlinable mutating func getOrElse(_ key: Key, _ defaultValue: () -> Value) -> Value {
        if let stored = self[key] { return stored } else { return defaultValue() }
    }
    @inlinable func containsKey(_ key: Key) -> Bool { self.keys.contains(key) }
}

extension Set {
    static func += (left: inout Set<Element>, right: Element) { left.insert(right) }
    static func -= (left: inout Set<Element>, right: Element) { left.remove(right) }
}

extension Array {
    static func += (left: inout Array<Element>, right: Element) { left.append(right) }
    init(_ size: Int, _ initValue: ((Int) -> Element)) {
        self = [Element]()
        for index in 0 ..< size {
            let value = initValue(index)
            self.append(value)
        }
    }
    init(_ size: Int, _ initValue: (() -> Element)) {
        self = [Element]()
        for _ in 0 ..< size {
            let value = initValue()
            self.append(value)
        }
    }
}
typealias IntArray = Array<Int>
typealias DoubleArray = Array<Double>
typealias BoolArray = Array<Bool>
typealias StringArray = Array<String>

extension Array where Element == Int {
    init(_ size: Int) {
        self = [Element](repeating: 0, count: size)
    }
}
extension Array where Element == Double {
    init(_ size: Int) {
        self = [Element](repeating: 0.0, count: size)
    }
}
extension Array where Element == Bool {
    init(_ size: Int) {
        self = [Element](repeating: false, count: size)
    }
}
extension Array where Element == String {
    init(_ size: Int) {
        self = [Element](repeating: "", count: size)
    }
}

extension Array {
    @inlinable func count(_ predicate: ((Element) -> Bool)) -> Int {
        var cnt = 0; for e in self { if (predicate(e)) { cnt += 1 } }
        return cnt
    }
    @inlinable func groupBy<K>(_ keySelector: (Element) -> K) -> [K : [Element]] {
        var destination = [K : [Element]]()
        for element in self {
            let key = keySelector(element)
            var list = destination.getOrSet(key) { [Element]() }
            list.append(element)
            destination[key] = list
        }
        return destination
    }
    @inlinable func groupByIndexed<K>(_ keySelector: (Element) -> K) -> [K : [Int]] {
        var destination = [K : [Int]]()
        for element in self.enumerated() {
            let key = keySelector(element.element)
            var list = destination.getOrSet(key) { [Int]() }
            list.append(element.offset)
            destination[key] = list
        }
        return destination
    }
}

extension Array {
    func binarySearch(_ element: Element, by compare: (Element, Element) -> Int, fromIndex: Int = 0, toIndex: Int = -1) -> Int {
        var low = fromIndex
        var high = (toIndex == -1) ? self.count - 1 : toIndex - 1
        while (low <= high) {
            let mid = (low + high) >> 1 // safe from overflows
            let midVal = self[mid]
            let cmp = compare(midVal, element)
            if (cmp < 0) { low = mid + 1 }          //-1
            else if (0 < cmp) { high = mid - 1 }    // 1
            else { return mid } // key found        // 0
        }
        return -(low + 1)  // key not found
    }
}
extension Array where Element : Comparable {
    func binarySearch(_ element: Element, fromIndex: Int = 0, toIndex: Int = -1) -> Int {
        var low = fromIndex
        var high = (toIndex == -1) ? self.count - 1 : toIndex - 1
        while (low <= high) {
            let mid = (low + high) >> 1 // safe from overflows
            let midVal = self[mid]
            if (midVal < element) { low = mid + 1 }
            else if (midVal > element) { high = mid - 1 }
            else { return mid } // key found
        }
        return -(low + 1)  // key not found
    }
}

extension Array where Element == Int {
    @inlinable func distinct() -> [Element] {
        var set = Set<Element>(), ary = [Element]()
        for n in self.indices { if !set.contains(self[n]) { ary.append(self[n]); set.insert(self[n]) } }
        return ary
    }
    @inlinable func any(_ predicate: (Element) -> Bool) -> Bool {
        var any = false; for element in self { if predicate(element) { any = true } }
        return any
    }
    @inlinable func all(_ predicate: (Element) -> Bool) -> Bool {
        self.allSatisfy(predicate)
    }
    @inlinable func sum() -> Element { self.reduce(0, +) }
    @inlinable func sum(by predicate: (Element) -> Element) -> Element {
        var sum = 0; for e in self { sum += predicate(e) }
        return sum
    }
    @inlinable func joinToString(_ sepatator: String = " ") -> String { self.map { String($0) }.joined(separator: sepatator) }
}

extension Array where Element == Double {
    @inlinable func sum() -> Element { self.reduce(0.0, +) }
    @inlinable func sum(by predicate: (Element) -> Element) -> Element {
        var sum = 0.0; for e in self { sum += predicate(e) }
        return sum
    }
    @inlinable func joinToString(_ sepatator: String = " ") -> String { self.map { String($0) }.joined(separator: sepatator) }
}
extension Array where Element == String {
    @inlinable func toIntDouble() -> (Int, Double) { (Int(self[0])!, Double(self[1])!) }
    @inlinable func toIntString() -> (Int, String) { (Int(self[0])!, self[1]) }
    @inlinable func toIntIntString() -> (Int, Int, String) { (Int(self[0])!, Int(self[1])!, self[2]) }
}

@inlinable func block( _ action: () -> Void) {
    action()
}
@inlinable func repeate(_ times1: Int, _ action: () -> Void) {
    for _ in 0 ..< times1 { action() }
}
@inlinable func repeate(_ times1: Int, _ action: (Int) -> Void) {
    for index1 in 0 ..< times1 {
        action(index1)
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ action: (Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            action(index1, index2)
        }
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ times3: Int, _ action: (Int, Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            for index3 in 0 ..< times3 {
                action(index1, index2, index3)
            }
        }
    }
}
@inlinable func repeate(_ times1: Int, _ times2: Int, _ times3: Int, _ times4: Int, _ action: (Int, Int, Int, Int) -> Void) {
    for index1 in 0 ..< times1 {
        for index2 in 0 ..< times2 {
            for index3 in 0 ..< times3 {
                for index4 in 0 ..< times4 {
                    action(index1, index2, index3, index4)
                }
            }
        }
    }
}

func measureTimeSeconds(block: (() -> Void)) -> Double {
    let startTime = Date()
    block()
    let duration = -startTime.timeIntervalSinceNow
    return duration
}

struct Deque<T> {
    var firstQueue = [T]()
    var lastQueue = [T]()
    var isEmpty: Bool { firstQueue.isEmpty && lastQueue.isEmpty }
    var count: Int { firstQueue.count + lastQueue.count }
    mutating func addFirst(_ t: T) { firstQueue.append(t) }
    mutating func addLast(_ t: T) { lastQueue.append(t) }
    mutating func removeFirst() -> T? {
        if !firstQueue.isEmpty {
            return firstQueue.removeLast()
        } else if !lastQueue.isEmpty {
            return lastQueue.removeFirst()
        } else {
            return nil
        }

    }
}


let HW = readLine()!.split(separator: " ").compactMap { Int($0) }
let h = HW[0]
let w = HW[1]

let C = readLine()!.split(separator: " ").compactMap { Int($0) }
let ch = C[0]-1
let cw = C[1]-1

let D = readLine()!.split(separator: " ").compactMap { Int($0) }
let dh = D[0]-1
let dw = D[1]-1

var shw = (1...h).map { _ in Array(readLine()!) }

var queue: [[(Int, Int)]] = Array(repeating: [], count: 1000)
var currentResult: Int = 0
var isExist = false

func isDot(i: Int, j: Int) -> Bool {
    if i < 0 || shw.count <= i { return false }
    if j < 0 || shw[i].count <= j { return false }
    return shw[i][j] == "."
}

func search(i: Int, j: Int) {
    for idot in i-2...i+2 {
        for jdot in j-2...j+2 {
            if idot == i && jdot == j { continue }

            if isDot(i: idot, j: jdot) {
                if ((idot == i - 1 || idot == i + 1) && jdot == j) || ((jdot == j - 1 || jdot == j + 1) && idot == i) {
                    queue[currentResult].append((idot, jdot))
                } else {
                    queue[currentResult+1].append((idot, jdot))
                }
            }
        }
    }
    shw[i][j] = "#"
}

search(i: ch, j: cw)
if queue[currentResult].isEmpty {
    currentResult += 1
}
while !queue[currentResult].isEmpty {
    let element = queue[currentResult].popLast()!

    if element.0 == dh && element.1 == dw {
        isExist = true
        break
    }

    if isDot(i: element.0, j: element.1) {
        search(i: element.0, j: element.1)
    }

    if queue[currentResult].isEmpty {
        currentResult += 1
    }
}
print(isExist ? currentResult : -1)


let HW = readLine()!.split(separator: " ").compactMap { Int($0) }
let h = HW[0]
let w = HW[1]

let C = readLine()!.split(separator: " ").compactMap { Int($0) }
let ch = C[0]-1
let cw = C[1]-1

let D = readLine()!.split(separator: " ").compactMap { Int($0) }
let dh = D[0]-1
let dw = D[1]-1

var shw = (1...h).map { _ in Array(readLine()!) }
var shwResults = shw.map { $0.map { _ in -1 } }

var queue: [[(Int, Int)]] = Array(repeating: [], count: 1000)
var currentResult: Int = 0
var isExist = false

func isDot(i: Int, j: Int) -> Bool {
    if i < 0 || shw.count <= i { return false }
    if j < 0 || shw[i].count <= j { return false }
    return shw[i][j] == "."
}

func search(i: Int, j: Int) {
    for idot in i-2...i+2 {
        for jdot in j-2...j+2 {
            if idot == i && jdot == j { continue }

            if isDot(i: idot, j: jdot) {
                if ((idot == i - 1 || idot == i + 1) && jdot == j) || ((jdot == j - 1 || jdot == j + 1) && idot == i) {
                    queue[currentResult].append((idot, jdot))
                } else {
                    queue[currentResult+1].append((idot, jdot))
                }
            }
        }
    }
    shw[i][j] = "#"
}

search(i: ch, j: cw)
if queue[currentResult].isEmpty {
    currentResult += 1
}
while !queue[currentResult].isEmpty {
    let element = queue[currentResult].popLast()!

    if element.0 == dh && element.1 == dw {
        isExist = true
        break
    }

    if (shwResults[element.0][element.1] != -1) && (shwResults[element.0][element.1] <= currentResult) { return }

    search(i: element.0, j: element.1)
    if queue[currentResult].isEmpty {
        currentResult += 1
    }
}
print(isExist ? currentResult : -1)


func main() {
    let hw = readLine()!.split(separator: " ").map{Int($0)!}
    let (h, w) = (hw[0], hw[1])
    let cArray = readLine()!.split(separator: " ").map{Int($0)!}
    let ch = cArray[0]
    let cw = cArray[1]
    let dArray = readLine()!.split(separator: " ").map{Int($0)!}
    let dh = dArray[0]
    let dw = dArray[1]
    var maze = [Int]()
    for _ in 0..<h {
        let line = readLine()!.map{$0 == "#" ? 1 : 0}
        maze += line
    }
    let walk = [(01,00), (-1,00), (00,01), (00,-1)]

    let warp = [(-2,02), (-2,01), (-2,00), (-2,-1), (-2,-2),
                (-1,02), (-1,01),          (-1,-1), (-1,-2),
                (00,02),                            (00,-2),
                (01,02), (01,01),          (01,-1), (02,-2),
                (02,02), (02,01), (02,00), (02,-1), (02,-2)
                ]

    var dist = 0
    var set1 = Set<Int>()
    func indexing(a: Int, b: Int) -> Int {
        return w*(a-1) + (b-1)
    }
    let start = indexing(a: ch, b: cw)
    let goal = indexing(a: dh, b: dw)
    set1.insert(start)
    var set2 = Set<Int>() // walked
    var set3 = Set<Int>() // warped
    while true {
        let one = set1.popFirst()!
        set2.insert(one)
        if one == goal {
            break
        }
        for each in walk {
            let x = one / w + each.0
            let y = one % w + each.1
            if x >= 0 && x < h && y >= 0 && y < w {
                if !set2.contains(one + w*each.0 + each.1) && maze[one + w*each.0 + each.1] == 0 {
                    set1.insert(one + w*each.0 + each.1)
                }
            }
        }
        if set1.isEmpty {
            dist += 1
            set1 = set2
            for item in set2 {
                for each in warp {
                    let x = item / w + each.0
                    let y = item % w + each.1
                    if x >= 0 && x < h && y >= 0 && y < w {
                        if !set1.contains(item + w*each.0 + each.1) && maze[item + w*each.0 + each.1] == 0 {
                            set3.insert(item + w*each.0 + each.1)
                        }
                    }
                }
            }
            if set3.isEmpty {
                dist = -1
                break
            }
            set1 = set3
            set2 = Set<Int>()
            set3 = Set<Int>()
        }
    }
    print(dist)
}
main()


import * as fs from 'fs';

// main
function main(input: string[]) {
    // param
    var h: number;
    var w: number;
    var ch: number;
    var cw: number;
    var dh: number;
    var dw: number;
    var shw: string[][];
    var ans;
    // init
    [h, w] = input.shift().split(" ").map(x => Number(x));
    [ch, cw] = input.shift().split(" ").map(x => Number(x) - 1);
    [dh, dw] = input.shift().split(" ").map(x => Number(x) - 1);
    shw = [];
    for (var hx = 0; hx < h; hx++) {
        shw[hx] = ("##" + input[hx] + "##").split("");
    }
    h += 4;
    w += 4;
    ch += 2;
    cw += 2;
    dh += 2;
    dw += 2;
    shw.unshift(new Array(w).fill("#"));
    shw.unshift(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // solve
    var patn = [
        [-1, +0],
        [+0, -1],
        [+1, +0],
        [+0, +1]
    ];
    var patw = [
        [-2, -2],
        [-2, -1],
        [-2, +0],
        [-2, +1],
        [-2, +2],
        [-1, -2],
        [-1, -1],
        [-1, +1],
        [-1, +2],
        [+0, -2],
        [+0, +2],
        [+1, -2],
        [+1, -1],
        [+1, +1],
        [+1, +2],
        [+2, -2],
        [+2, -1],
        [+2, +0],
        [+2, +1],
        [+2, +2]
    ];
    var que = [[ch, cw]];
    var warp = 0;
    loop: {
        while (true) {
            var que0 = [];
            while (0 < que.length) {
                var cur = que.pop();
                que0.push(cur)
                shw[cur[0]][cur[1]] = warp.toString();
                if (cur == [dh, dw]) break loop;
                for (var patnx = 0; patnx < patn.length; patnx++) {
                    var s = [cur[0] + patn[patnx][0], cur[1] + patn[patnx][1]];
                    var c = shw[s[0]][s[1]];
                    if (c == ".") que.push(s);
                }
            }
            while (0 < que0.length) {
                var cur = que0.pop();
                for (var patwx = 0; patwx < patw.length; patwx++) {
                    var s = [cur[0] + patw[patwx][0], cur[1] + patw[patwx][1]];
                    var c = shw[s[0]][s[1]];
                    if (c == ".") que.push(s), shw[s[0]][s[1]] = "_";
                }
            }
            if (que.length == 0) break loop;
            warp++;
        }
    }
    var s = [dh, dw];
    var c = shw[s[0]][s[1]];
    ans = (c == ".") ? -1 : c;
    console.debug(h, w, ch, cw, dh, dw);
    console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // answer
    console.log(ans);
}

// entrypoint
function entrypoint() {
    try {
        var input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        console.debug = function () { };
        main(input);
    }
    catch (ex) {
        console.log("file='%s'", __filename.split("\\").slice(-1)[0]);
        const lines: string[] = [];
        // @ts-ignore
        const reader = require('readline').createInterface({
            // @ts-ignore
            input: process.stdin,
            // @ts-ignore
            output: process.stdout
        });
        reader.on('line', function (line: string) {
            lines.push(line);
        });
        reader.on('close', function () {
            input = lines;
            console.log("input=%o", input);
            console.time();
            main(input);
            console.timeEnd();
        });
    }
}
entrypoint();


import * as fs from 'fs';

// main
function main(input: string[]) {
    // param
    var h: number;
    var w: number;
    var ch: number;
    var cw: number;
    var dh: number;
    var dw: number;
    var shw: string[][];
    var ans;
    // init
    [h, w] = input.shift().split(" ").map(x => Number(x));
    [ch, cw] = input.shift().split(" ").map(x => Number(x) - 1);
    [dh, dw] = input.shift().split(" ").map(x => Number(x) - 1);
    shw = [];
    for (var hx = 0; hx < h; hx++) {
        shw[hx] = ("##" + input[hx] + "##").split("");
    }
    h += 4;
    w += 4;
    ch += 2;
    cw += 2;
    dh += 2;
    dw += 2;
    shw.unshift(new Array(w).fill("#"));
    shw.unshift(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // solve
    var patn = [
        [-1, +0],
        [+0, -1],
        [+1, +0],
        [+0, +1]
    ];
    var patw = [
        [-2, -2],
        [-2, -1],
        [-2, +0],
        [-2, +1],
        [-2, +2],
        [-1, -2],
        [-1, -1],
        [-1, +1],
        [-1, +2],
        [+0, -2],
        [+0, +2],
        [+1, -2],
        [+1, -1],
        [+1, +1],
        [+1, +2],
        [+2, -2],
        [+2, -1],
        [+2, +0],
        [+2, +1],
        [+2, +2]
    ];
    var quen = [[ch, cw]];
    var quep = [];
    var warp = 0;
    loop: {
        while (true) {
            // normal
            while (0 < quen.length) {
                var cur = quen.pop();
                shw[cur[0]][cur[1]] = warp.toString();
                if (cur == [dh, dw]) break loop;
                quep.push(cur)
                for (var patnx = 0; patnx < patn.length; patnx++) {
                    var s = [cur[0] + patn[patnx][0], cur[1] + patn[patnx][1]];
                    var c = shw[s[0]][s[1]];
                    if (c == ".") quen.push(s);
                }
            }
            // warp
            while (0 < quep.length) {
                var cur = quep.pop();
                for (var patwx = 0; patwx < patw.length; patwx++) {
                    var s = [cur[0] + patw[patwx][0], cur[1] + patw[patwx][1]];
                    var c = shw[s[0]][s[1]];
                    if (c == ".") quen.push(s), shw[s[0]][s[1]] = "_";
                }
            }
            if (quen.length == 0) break loop;
            warp++;
        }
    }
    var s = [dh, dw];
    var c = shw[s[0]][s[1]];
    ans = (c == ".") ? -1 : c;
    console.debug(h, w, ch, cw, dh, dw);
    console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // answer
    console.log(ans);
}

// entrypoint
function entrypoint() {
    try {
        var input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        console.debug = function () { };
        main(input);
    }
    catch (ex) {
        console.log("file='%s'", __filename.split("\\").slice(-1)[0]);
        const lines: string[] = [];
        // @ts-ignore
        const reader = require('readline').createInterface({
            // @ts-ignore
            input: process.stdin,
            // @ts-ignore
            output: process.stdout
        });
        reader.on('line', function (line: string) {
            lines.push(line);
        });
        reader.on('close', function () {
            input = lines;
            console.log("input=%o", input);
            console.time();
            main(input);
            console.timeEnd();
        });
    }
}
entrypoint();


import * as fs from 'fs';

// main
function main(input: string[]) {
    // param
    var h: number;
    var w: number;
    var ch: number;
    var cw: number;
    var dh: number;
    var dw: number;
    var shw: string[][];
    var ans;
    // init
    [h, w] = input.shift().split(" ").map(x => Number(x));
    [ch, cw] = input.shift().split(" ").map(x => Number(x) - 1);
    [dh, dw] = input.shift().split(" ").map(x => Number(x) - 1);
    shw = [];
    for (var hx = 0; hx < h; hx++) {
        shw[hx] = ("##" + input[hx] + "##").split("");
    }
    h += 4;
    w += 4;
    ch += 2;
    cw += 2;
    dh += 2;
    dw += 2;
    shw.unshift(new Array(w).fill("#"));
    shw.unshift(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // solve
    var patn = [
        [-1, +0],
        [+0, -1],
        [+1, +0],
        [+0, +1]
    ];
    var patw = [
        [-2, -2],
        [-2, -1],
        [-2, +0],
        [-2, +1],
        [-2, +2],
        [-1, -2],
        [-1, -1],
        [-1, +1],
        [-1, +2],
        [+0, -2],
        [+0, +2],
        [+1, -2],
        [+1, -1],
        [+1, +1],
        [+1, +2],
        [+2, -2],
        [+2, -1],
        [+2, +0],
        [+2, +1],
        [+2, +2]
    ];
    var quen = [[ch, cw]];
    var quep = [];
    var warp = 0;
    loop: {
        while (true) {
            // normal
            while (0 < quen.length) {
                var cur = quen.pop();
                shw[cur[0]][cur[1]] = warp.toString();
                if (cur == [dh, dw]) break loop;
                quep.push(cur)
                GRAPH_enumPattern(cur, patn).forEach(function(pos) {
                    var chr = shw[pos[0]][pos[1]];
                    if (chr == ".") quen.push(pos);
                });
            }
            // warp
            while (0 < quep.length) {
                var cur = quep.pop();
                GRAPH_enumPattern(cur, patw).forEach(function(pos) {
                    var chr = shw[pos[0]][pos[1]];
                    if (chr == ".") quen.push(pos), shw[pos[0]][pos[1]] = "_";
                }
            }
            if (quen.length == 0) break loop;
            warp++;
        }
    }
    var pos = [dh, dw];
    var chr = shw[pos[0]][pos[1]];
    ans = (chr == ".") ? -1 : chr;
    console.debug(h, w, ch, cw, dh, dw);
    console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // answer
    console.log(ans);
}

// graph library
function GRAPH_enumPattern(cur: number[], pat: number[][]): number[][] {
    var arr = [];
    for (var patx = 0; patx < pat.length; patx++) {
        var pos = [cur[0] + pat[patx][0], cur[1] + pat[patx][1]];
        arr.push(pos);
    }
    return arr;
}

// entrypoint
function entrypoint() {
    try {
        var input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        console.debug = function () { };
        main(input);
    }
    catch (ex) {
        console.log("file='%s'", __filename.split("\\").slice(-1)[0]);
        const lines: string[] = [];
        // @ts-ignore
        const reader = require('readline').createInterface({
            // @ts-ignore
            input: process.stdin,
            // @ts-ignore
            output: process.stdout
        });
        reader.on('line', function (line: string) {
            lines.push(line);
        });
        reader.on('close', function () {
            input = lines;
            console.log("input=%o", input);
            console.time();
            main(input);
            console.timeEnd();
        });
    }
}
entrypoint();


import * as fs from 'fs';

// main
function main(input: string[]) {
    // param
    var h: number;
    var w: number;
    var ch: number;
    var cw: number;
    var dh: number;
    var dw: number;
    var shw: string[][];
    var ans;
    // init
    [h, w] = input.shift().split(" ").map(x => Number(x));
    [ch, cw] = input.shift().split(" ").map(x => Number(x) - 1);
    [dh, dw] = input.shift().split(" ").map(x => Number(x) - 1);
    shw = [];
    for (var hx = 0; hx < h; hx++) {
        shw[hx] = ("##" + input[hx] + "##").split("");
    }
    h += 4;
    w += 4;
    ch += 2;
    cw += 2;
    dh += 2;
    dw += 2;
    shw.unshift(new Array(w).fill("#"));
    shw.unshift(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // solve
    var patn = [
        [-1, +0],
        [+0, +1],
        [+1, +0],
        [+0, -1]
    ];
    var patw = [
        [-2, -2],
        [-2, -1],
        [-2, +0],
        [-2, +1],
        [-2, +2],
        [-1, -2],
        [-1, -1],
        [-1, +1],
        [-1, +2],
        [+0, -2],
        [+0, +2],
        [+1, -2],
        [+1, -1],
        [+1, +1],
        [+1, +2],
        [+2, -2],
        [+2, -1],
        [+2, +0],
        [+2, +1],
        [+2, +2]
    ];
    var que = [[ch, cw]];
    var warp = 0;
    while (true) {
        var que0 = [];
        while (0 < que.length) {
            var cur = que.pop();
            que0.push(cur)
            shw[cur[0]][cur[1]] = warp.toString();
            for (var patnx = 0; patnx < patn.length; patnx++) {
                var s = [cur[0] + patn[patnx][0], cur[1] + patn[patnx][1]];
                var c = shw[s[0]][s[1]];
                if (c == ".") que.push(s);
            }
        }
        // console.debug(que, que0);
        while (0 < que0.length) {
            var cur = que0.pop();
            // console.debug("----");
            for (var patwx = 0; patwx < patw.length; patwx++) {
                var s = [cur[0] + patw[patwx][0], cur[1] + patw[patwx][1]];
                var c = shw[s[0]][s[1]];
                // console.debug(s[0], s[1], c);
                if (c == ".") que.push(s), shw[s[0]][s[1]] = "_";
            }
        }
        if (que.length == 0) break;
        warp++;
    }
    console.debug(h, w, ch, cw, dh, dw);
    console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    var s = [dh, dw];
    var c = shw[s[0]][s[1]];
    ans = (c == ".") ? -1 : warp;
    // answer
    console.log(ans);
}

// math library
function Math_max(arr: number[]): number { return arr.reduce((x, y) => Math.max(x, y)); }
function Math_min(arr: number[]): number { return arr.reduce((x, y) => Math.min(x, y)); }
function Math_gcd(): number {
    var ans = arguments[0];
    var f = (a: number, b: number): number => b ? f(b, a % b) : a
    for (var i = 1; i < arguments.length; i++) {
        ans = f(ans, arguments[i]);
    }
    return ans
};
function Math_gcdArray(values: number[]): number {
    var ans = values[0];
    var f = (a: number, b: number): number => b ? f(b, a % b) : a
    for (var i = 1; i < values.length; i++) {
        ans = f(ans, values[i]);
    }
    return ans
};
function Math_lcm(): number {
    var a = arguments
    var g = (n: number, m: number): number => m ? g(m, n % m) : n
    var l = (n: number, m: number): number => n * m / g(n, m)
    var ans = a[0]
    for (var i = 1; i < a.length; i++) {
        ans = l(ans, a[i])
    }
    return ans
}
function Math_lcmArray(values: number[]): number {
    var a = values
    var g = (n: number, m: number): number => m ? g(m, n % m) : n
    var l = (n: number, m: number): number => n * m / g(n, m)
    var ans = a[0]
    for (var i = 1; i < a.length; i++) {
        ans = l(ans, a[i])
    }
    return ans
}
function Math_primeList(value: number): number[] {
    var ans = [];
    var arr = new Array(value).fill(0);
    for (var i = 2; i < value; i++) {
        if (arr[i] == 0) {
            for (var j = i; j < value; j += i) {
                if (arr[j] == 0) arr[j] = i;
            }
        }
    }
    for (var i = 2; i < value; i++) {
        if (arr[i] == i) ans.push(i);
    }
    return ans;
}
function Math_primeFactor(p: number[], value: number): number[][] {
    var ans: number[][] = [];
    var valmax = value / 2;;
    for (var i = 0; i < p.length; i++) {
        var prime = p[i];
        if (valmax < prime) break;
        var count = 0;
        while (value % prime == 0) {
            value /= prime;
            count++;
        }
        if (count != 0) {
            ans.push([prime, count]);
        }
        if (value == 1) break;
    }
    return ans;
}

// union-find library
var g_uf_root: number[];
var g_uf_count: number[];
function UF_initRoot(num: number) {
    g_uf_root = new Array(num).fill(-1);
    g_uf_count = new Array(num).fill(1);
}
function UF_getRoot(index: number) {
    if (g_uf_root[index] < 0) {
        return index;
    } else {
        var work0: number = UF_getRoot(g_uf_root[index]);
        g_uf_root[index] = work0;
        return work0
    }
}
function UF_mergeRoot(a: number, b: number) {
    var root0: number = UF_getRoot(a);
    var root1: number = UF_getRoot(b);
    if (root0 == root1) return;
    if (root0 > root1) [root0, root1] = [root1, root0];
    g_uf_count[root0] += g_uf_count[root1];
    g_uf_count[root1] = 0;
    g_uf_root[root1] = root0;
}

// entrypoint
function entrypoint() {
    try {
        var input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        console.debug = function () { };
        main(input);
    }
    catch (ex) {
        console.log("file='%s'", __filename.split("\\").slice(-1)[0]);
        const lines: string[] = [];
        // @ts-ignore
        const reader = require('readline').createInterface({
            // @ts-ignore
            input: process.stdin,
            // @ts-ignore
            output: process.stdout
        });
        reader.on('line', function (line: string) {
            lines.push(line);
        });
        reader.on('close', function () {
            input = lines;
            console.log("input=%o", input);
            console.time();
            main(input);
            console.timeEnd();
        });
    }
}
entrypoint();


import * as fs from 'fs';

// main
function main(input: string[]) {
    // param
    var h: number;
    var w: number;
    var ch: number;
    var cw: number;
    var dh: number;
    var dw: number;
    var shw: string[][];
    var ans;
    // init
    [h, w] = input.shift().split(" ").map(x => Number(x));
    [ch, cw] = input.shift().split(" ").map(x => Number(x) - 1);
    [dh, dw] = input.shift().split(" ").map(x => Number(x) - 1);
    shw = [];
    for (var hx = 0; hx < h; hx++) {
        shw[hx] = ("##" + input[hx] + "##").split("");
    }
    h += 4;
    w += 4;
    ch += 2;
    cw += 2;
    dh += 2;
    dw += 2;
    shw.unshift(new Array(w).fill("#"));
    shw.unshift(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    shw.push(new Array(w).fill("#"));
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    // solve
    var patn = [
        [-1, +0],
        [+0, +1],
        [+1, +0],
        [+0, -1]
    ];
    var patw = [
        [-2, -2],
        [-2, -1],
        [-2, +0],
        [-2, +1],
        [-2, +2],
        [-1, -2],
        [-1, -1],
        [-1, +1],
        [-1, +2],
        [+0, -2],
        [+0, +2],
        [+1, -2],
        [+1, -1],
        [+1, +1],
        [+1, +2],
        [+2, -2],
        [+2, -1],
        [+2, +0],
        [+2, +1],
        [+2, +2]
    ];
    var que = [[ch, cw]];
    var warp = 0;
    while (true) {
        var que0 = [];
        while (0 < que.length) {
            var cur = que.pop();
            que0.push(cur)
            shw[cur[0]][cur[1]] = warp.toString();
            for (var patnx = 0; patnx < patn.length; patnx++) {
                var s = [cur[0] + patn[patnx][0], cur[1] + patn[patnx][1]];
                var c = shw[s[0]][s[1]];
                if (c == ".") que.push(s);
            }
        }
        // console.debug(que, que0);
        while (0 < que0.length) {
            var cur = que0.pop();
            // console.debug("----");
            shw[cur[0]][cur[1]] = "_";
            for (var patwx = 0; patwx < patw.length; patwx++) {
                var s = [cur[0] + patw[patwx][0], cur[1] + patw[patwx][1]];
                var c = shw[s[0]][s[1]];
                // console.debug(s[0], s[1], c);
                if (c == ".") que.push(s);
            }
        }
        if (que.length == 0) break;
        warp++;
    }
    // console.debug(h, w, ch, cw, dh, dw);
    // console.debug(shw.map(x => x.join("")).reduce(((x, y) => x + "\n" + y)));
    var s = [dh, dw];
    var c = shw[s[0]][s[1]];
    ans = (c == ".") ? -1 : warp;
    // answer
    console.log(ans);
}

// math library
function Math_max(arr: number[]): number { return arr.reduce((x, y) => Math.max(x, y)); }
function Math_min(arr: number[]): number { return arr.reduce((x, y) => Math.min(x, y)); }
function Math_gcd(): number {
    var ans = arguments[0];
    var f = (a: number, b: number): number => b ? f(b, a % b) : a
    for (var i = 1; i < arguments.length; i++) {
        ans = f(ans, arguments[i]);
    }
    return ans
};
function Math_gcdArray(values: number[]): number {
    var ans = values[0];
    var f = (a: number, b: number): number => b ? f(b, a % b) : a
    for (var i = 1; i < values.length; i++) {
        ans = f(ans, values[i]);
    }
    return ans
};
function Math_lcm(): number {
    var a = arguments
    var g = (n: number, m: number): number => m ? g(m, n % m) : n
    var l = (n: number, m: number): number => n * m / g(n, m)
    var ans = a[0]
    for (var i = 1; i < a.length; i++) {
        ans = l(ans, a[i])
    }
    return ans
}
function Math_lcmArray(values: number[]): number {
    var a = values
    var g = (n: number, m: number): number => m ? g(m, n % m) : n
    var l = (n: number, m: number): number => n * m / g(n, m)
    var ans = a[0]
    for (var i = 1; i < a.length; i++) {
        ans = l(ans, a[i])
    }
    return ans
}
function Math_primeList(value: number): number[] {
    var ans = [];
    var arr = new Array(value).fill(0);
    for (var i = 2; i < value; i++) {
        if (arr[i] == 0) {
            for (var j = i; j < value; j += i) {
                if (arr[j] == 0) arr[j] = i;
            }
        }
    }
    for (var i = 2; i < value; i++) {
        if (arr[i] == i) ans.push(i);
    }
    return ans;
}
function Math_primeFactor(p: number[], value: number): number[][] {
    var ans: number[][] = [];
    var valmax = value / 2;;
    for (var i = 0; i < p.length; i++) {
        var prime = p[i];
        if (valmax < prime) break;
        var count = 0;
        while (value % prime == 0) {
            value /= prime;
            count++;
        }
        if (count != 0) {
            ans.push([prime, count]);
        }
        if (value == 1) break;
    }
    return ans;
}

// union-find library
var g_uf_root: number[];
var g_uf_count: number[];
function UF_initRoot(num: number) {
    g_uf_root = new Array(num).fill(-1);
    g_uf_count = new Array(num).fill(1);
}
function UF_getRoot(index: number) {
    if (g_uf_root[index] < 0) {
        return index;
    } else {
        var work0: number = UF_getRoot(g_uf_root[index]);
        g_uf_root[index] = work0;
        return work0
    }
}
function UF_mergeRoot(a: number, b: number) {
    var root0: number = UF_getRoot(a);
    var root1: number = UF_getRoot(b);
    if (root0 == root1) return;
    if (root0 > root1) [root0, root1] = [root1, root0];
    g_uf_count[root0] += g_uf_count[root1];
    g_uf_count[root1] = 0;
    g_uf_root[root1] = root0;
}

// entrypoint
function entrypoint() {
    try {
        var input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        console.debug = function () { };
        main(input);
    }
    catch (ex) {
        console.log("file='%s'", __filename.split("\\").slice(-1)[0]);
        const lines: string[] = [];
        // @ts-ignore
        const reader = require('readline').createInterface({
            // @ts-ignore
            input: process.stdin,
            // @ts-ignore
            output: process.stdout
        });
        reader.on('line', function (line: string) {
            lines.push(line);
        });
        reader.on('close', function () {
            input = lines;
            console.log("input=%o", input);
            console.time();
            main(input);
            console.timeEnd();
        });
    }
}
entrypoint();