use std::{collections::BTreeMap, io::stdin, ops::{Mul, Range}};
use anyhow::{Result, bail};

mod parse;

#[derive(Debug)]
struct MapRange {
    base: usize,
    target: usize,
}

/// Stores a map as a series of contiguous offset ranges.
#[derive(Debug)]
struct Map {
    from: String,
    to: String,
    ranges: Vec<MapRange>,
}


impl Map {

    fn from_ranges(from: String, to: String, mut v: Vec<(usize,usize,usize)>) -> Self {
        v.sort_by_key(|r| r.1);

        let mut ranges = Vec::with_capacity(2*v.len() + 1);
        ranges.push(MapRange {base: 0, target: 0 } );

        for (target, base, len) in v {
            if ranges.last().unwrap().base == base {
                ranges.pop();
            }
            ranges.push(MapRange { base, target });
            ranges.push(MapRange { base: base+len, target: base+len });
        }

        Self { from, to, ranges }
    }

    fn range_idx(&self, target: usize) -> usize {
        self.ranges.binary_search_by_key(&target, |r| r.base)
            .unwrap_or_else(|i| i-1)
    }

    fn lookup(&self, src: usize) -> usize {
        let range = &self.ranges[self.range_idx(src)];
        (src + range.target) - range.base
    }
}

fn main() -> Result<()> {
    let (seeds, maps) = parse::input(stdin().lock())?;

    eprintln!("{seeds:?}, {maps:?}");

    let mut current= "seed";

    for map in &maps {
        if map.from != current { bail!("Wrong map order, expected {current}") }
        current = &map.to;
    }

    let best = seeds.iter()
                .map(|&s| {
                    let mut cur = s;
                    eprint!("{cur}");
                    for m in &maps {
                        cur = m.lookup(cur);
                        eprint!(" -> {cur}")
                    }
                    eprintln!();
                    cur
                }).min();
    println!("lowest location: {best:?}");
    Ok(())
}