~jpl8/text-to-piet

8f1525894c25408d02130c78a0f05eb24c2c2895 — jpl 1 year, 11 months ago f49d756
Refactor text encoding into instructions in Encoder trait.
3 files changed, 95 insertions(+), 108 deletions(-)

A raven-test.png
M src/instructions.rs
M src/main.rs
A raven-test.png => raven-test.png +0 -0
M src/instructions.rs => src/instructions.rs +93 -106
@@ 21,138 21,125 @@ pub enum Instructions {
    OUTCHAR,
}

pub fn expected_size(instructions: &Vec<Instructions>) -> u32 {
    // Num of extra blocks for corner_turn = 2 * num_corners
    // num_corners(side) = side - 3, for side > 2, 0 otherwise
    // num_blocks(instructions, side) = instructions.len() + corner_turn.len() * num_corners(side)
    //            = n + 2 * (side - 3)

    // len_spiral(side) = floor(sideĀ²/2)

    // num_blocks <= len_spiral
    let num_blocks = instructions.iter().fold(0, |acc, x| {
        acc + match x {
            PUSH(n) => *n,
            _ => 1,
pub trait PietEncoder {
    fn gen_num(&self, n: i64) -> Vec<Instructions>;
    fn gen_num_prev(&self, prev: i64, n: i64) -> Vec<Instructions> {
        if prev == n {
            return vec![DUP];
        }
    });

    // solving for side should work out to
        let mut instructions = self.gen_num(n - prev);
        instructions.push(ADD);
        instructions.push(DUP);

    //4 + (((2 * n - 8) as f64).sqrt() as u32) + 1
    //2 + ((2 * n - 8) as f64).sqrt() as u32
    //3 + ((2 * n - 9) as f64).sqrt() as u32
    //10 + ((2.5 * (num_blocks as f64)) as f64).sqrt() as u32
    //2 + ((2 * num_blocks - 8) as f64).sqrt() as u32
    4 + (((2 * num_blocks - 8) as f64).sqrt() as u32) + 1
}
        instructions
    }

pub fn text_to_instructions(s: String) -> Vec<Instructions> {
    let bytes: Vec<u8> = s.chars().map(|c| c as u8).collect();
    bytes
        .iter()
        .map(|c| {
            let mut instrs = get_num_bfs_ron(*c as i64);
            //eprintln!("GENERATING");
            instrs.push(OUTCHAR);
            instrs
        })
        .flatten()
        .collect()
}
    fn encode_text(&self, src: &str) -> Vec<Instructions> {
        let bytes: Vec<u8> = src.chars().map(|c| c as u8).collect();
        let mut instructions = self.gen_num(bytes[0] as i64);
        instructions.push(DUP);
        instructions.push(OUTCHAR);

pub fn text_to_instructions_prev(s: String) -> Vec<Instructions> {
    let bytes: Vec<u8> = s.chars().map(|c| c as u8).collect();
    let mut instructions = gen_num(bytes[0] as i64);
    instructions.push(DUP);
    instructions.push(OUTCHAR);
        for (&prev, &n) in bytes.iter().zip(bytes[1..].iter()) {
            instructions.append(&mut self.gen_num_prev(prev as i64, n as i64));
            instructions.push(OUTCHAR);
        }

    for (&prev, &n) in bytes.iter().zip(bytes[1..].iter()) {
        instructions.append(&mut get_num_bfs_ron_with_prev(prev as i64, n as i64));
        //instructions.push(DUP);
        instructions.push(OUTCHAR);
        instructions
    }

    instructions
}

fn gen_num(n: i64) -> Vec<Instructions> {
    let mut instructions = Vec::new();
    if n < 0 {
        instructions.push(PUSH(1));
        instructions.append(&mut gen_num(-n + 1));
        instructions.push(SUB);
    } else if n <= 5 {
        instructions = vec![PUSH(n as usize)];
    } else {
        let floor_pow_2_exp = floor_pow_2(n as u64);
        let floor_pow_2 = 1 << floor_pow_2_exp;

        instructions.push(PUSH(2));
        instructions.append(&mut vec![DUP; (floor_pow_2_exp - 1) as usize]);
        instructions.append(&mut vec![MUL; (floor_pow_2_exp - 1) as usize]);

        if (n as u64).is_power_of_two() {
            return instructions;
struct EncoderByDoubling {}

impl PietEncoder for EncoderByDoubling {
    fn gen_num(&self, n: i64) -> Vec<Instructions> {
        let mut instructions = Vec::new();
        if n < 0 {
            instructions.push(PUSH(1));
            instructions.append(&mut self.gen_num(-n + 1));
            instructions.push(SUB);
        } else if n <= 5 {
            instructions = vec![PUSH(n as usize)];
        } else {
            let floor_pow_2_exp = floor_pow_2(n as u64);
            let floor_pow_2 = 1 << floor_pow_2_exp;

            instructions.push(PUSH(2));
            instructions.append(&mut vec![DUP; (floor_pow_2_exp - 1) as usize]);
            instructions.append(&mut vec![MUL; (floor_pow_2_exp - 1) as usize]);

            if (n as u64).is_power_of_two() {
                return instructions;
            }

            instructions.append(&mut self.gen_num(n - floor_pow_2));
            instructions.push(ADD)
        }

        instructions.append(&mut gen_num(n - floor_pow_2));
        instructions.push(ADD)
        instructions
    }
}

    instructions
pub struct EncoderBFS {
    // .ron file
    cache_file: String,
}

fn gen_num_with_prev(prev: i64, n: i64) -> Vec<Instructions> {
    if prev == n {
        return vec![DUP];
impl EncoderBFS {
    pub fn new(cache_file: &str) -> Self {
        Self {
            cache_file: cache_file.to_string(),
        }
    }

    let mut instructions = gen_num(n - prev);
    instructions.push(ADD);
    instructions.push(DUP);

    instructions
}

pub fn get_num_bfs_ron(n: i64) -> Vec<Instructions> {
    //eprintln!("n: {n} {}", (n as u8) as char);
    if n < 0 {
        let mut instructions = vec![];
        instructions.push(PUSH(1));
        instructions.append(&mut get_num_bfs_ron(-n + 1));
        instructions.push(SUB);
        return instructions;
    }
impl PietEncoder for EncoderBFS {
    fn gen_num(&self, n: i64) -> Vec<Instructions> {
        //eprintln!("n: {n} {}", (n as u8) as char);
        if n < 0 {
            let mut instructions = vec![];
            instructions.push(PUSH(1));
            instructions.append(&mut self.gen_num(-n + 1));
            instructions.push(SUB);
            return instructions;
        }

    let line = BufReader::new(File::open("bfs_instructions.ron").unwrap())
        .lines()
        .nth(n as usize)
        .expect(format!("There is no {}th line!", n).as_str())
        .unwrap();
        let line = BufReader::new(File::open(&self.cache_file).unwrap())
            .lines()
            .nth(n as usize)
            .expect(format!("There is no {}th line!", n).as_str())
            .unwrap();

    let instructions = ron::from_str(&line).unwrap();
    instructions
        let instructions = ron::from_str(&line).unwrap();
        instructions
    }
}

pub fn get_num_bfs_ron_with_prev(prev: i64, n: i64) -> Vec<Instructions> {
    //let line = BufReader::new(File::open("bfs_instructions.ron").unwrap())
    //    .lines()
    //    .nth((prev * 256 + n) as usize)
    //    .expect(format!("There is no {}th line!", n).as_str())
    //    .unwrap();
pub fn expected_size(instructions: &Vec<Instructions>) -> u32 {
    // Num of extra blocks for corner_turn = 2 * num_corners
    // num_corners(side) = side - 3, for side > 2, 0 otherwise
    // num_blocks(instructions, side) = instructions.len() + corner_turn.len() * num_corners(side)
    //            = n + 2 * (side - 3)

    //let (_initial_stack, instructions): (Vec<Instructions>, _) = ron::from_str(&line).unwrap();
    // len_spiral(side) = floor(sideĀ²/2)

    if prev == n {
        return vec![DUP];
    }
    // num_blocks <= len_spiral
    let num_blocks = instructions.iter().fold(0, |acc, x| {
        acc + match x {
            PUSH(n) => *n,
            _ => 1,
        }
    });

    let mut instructions = get_num_bfs_ron(n - prev);
    instructions.push(ADD);
    instructions.push(DUP);
    // solving for side should work out to

    instructions
    //4 + (((2 * n - 8) as f64).sqrt() as u32) + 1
    //2 + ((2 * n - 8) as f64).sqrt() as u32
    //3 + ((2 * n - 9) as f64).sqrt() as u32
    //10 + ((2.5 * (num_blocks as f64)) as f64).sqrt() as u32
    //2 + ((2 * num_blocks - 8) as f64).sqrt() as u32
    4 + (((2 * num_blocks - 8) as f64).sqrt() as u32) + 1
}

pub fn gen_num_bfs(n: i64, initial_stack: Vec<i64>) -> Vec<Instructions> {

M src/main.rs => src/main.rs +2 -2
@@ 34,9 34,9 @@ fn main() {
    let args = Args::parse();

    let input = fs::read_to_string(args.input_file).expect("Failed to read input file");
    let instrs = text_to_instructions_prev(input);

    eprintln!("num_instructions: {:?}", instrs.len());
    let encoder = instructions::EncoderBFS::new("bfs_instructions.ron");
    let instrs = encoder.encode_text(&input);

    let img_size = args.img_size.unwrap_or(expected_size(&instrs));
    eprintln!("img_size: {:?}", img_size);