#include <bits/stdc++.h>

using namespace std;
inline void prep ()
{
  cin.tie (0);
  cin.sync_with_stdio (0);
};
//printf("%.12lf\n",mid);
// __builtin_popcount(int) count 1's in binary
// get decimal pert of number
/*
double intpart;
double thiss = modf((double)(n*m)/(double)k, &intpart);
*/
// round = (int)(num+.5)
int mod = 1000000007;

long long mymod(long long num){
  if (num< 0){
    if (mod == 1){
      return 0;
    }
    return mod-abs(num)%mod;
  }else{
    return num%mod;
  }
}

double eps = .000000001; 
long long lcm(long long a, long long b){
    return a*b/__gcd(a, b);
}
int main ()
{
  prep ();
  long long a, b, c, d;
  cin >> a >> b>>c >> d;
  if (c == 1 || d == 1){
      cout << 0;
      return 0;
  }
  if (b < a){
      long long temp = b;
      b = a;
      a = temp;
  }
  
  long long multc = (a-1)/c;
  long long multd = (a-1)/d;
  long long multcd = (a-1)/lcm(c,d);
  long long multall = multc+multd-multcd; // 
  long long multc2 = b/c;
  long long multd2 = b/d;
  long long multcd2 = b/lcm(c,d);
  long long multall2 = multc2+multd2-multcd2;
 
  cout << (b-a+1)-(multall2-multall);
  return 0;
}
// in dp problems think: up until here, intervals, or "what if there's 1, 2, 3..."
// in math problems write out equations
// also in math problems to change multiplication/division to addition/subtraction use logs, add/subtract something to make the multiplication easier (like if it's 3*f(x)+2, change it to 3*(f(x)+1)-1)
// if checked the logic check for bugs (like sorting wrong way or not putting a break in a loop)
// if n is 10^7 try brute force if the operations are simple
// if can't do dp do brute force/greedy
// to save time don't copy maps/vectors into variables if it already exists
// if wrong on decimal problem, check if program rounds or if you can round to a certain amt of decimals
// if decimals too, if can subtract instead of divide
// in recursion looking math problems look at things from the point of view of both sides (the start and the end sections of the recursion)
// think about how many possible answers there are to the problem
// try to set the answer to the problem and see if it's possible (like w/ binary search)
// if it makes it easier use +1 indexing
// think twice when writing things down that you're going to use later
// for conversions think if you can compute them w/ only one conversion 
// if the problem involves two things moving at the same time, see if you can generalise it to just one thing moving (590/B) (1117/C)
// if nt think primes
// see if you can do a pattern of steps to achieve something (1148/C) (1119/C)
// if sub-interval requirements, try to reduce problem into prefix array problem (pairwise numbers) (1174/D)
// think from the end of the problem (if this is the result, what is an attribute of it?)
// if repeating action, if good for debugging use function
// read problems twice, think about problem, even if have solution keep thinking about a more optimal one
// for uva see constraints, check if have to use getline
// if permutations, see if you can just iterate over numbers (like if it's a perm of 10 numbers)
// for geom check if it could be on inside/outside if intersections
// stop just assuming things (like on the problem where you just thought that there couldn't be 3 2's because 3 is odd)
// obviously, check problems solved by users occasionally to see if you should switch problems
// if # of sols are small precalculate
// if question is asking to find an optimal place, don't focus on finding the place, use what other things they give you to precalculate it (593/B) (Uva rat attack)
// if a constraint looks purposely small try to use it
// if can, change vector<bool> to bitset
// if brute force, try meet in middle
// if using dfs and using visited, after visit node, mark as not visited and keep vis global
// if a problem says to do something a number of times optimally, try to do the limits and see whats possible/impossible (like if k = 5, push n to 6, not 10)
// what are we forced to do because of the problem's constraints? (like if it's graph and 1s time lim, is probably only 1 traverse of dfs and not n)
// if greedy, look at an observation like "they have to be as far out as they can" for example
// if you think about binary search, don't just drop the idea quickly
// if it's a minutes/seconds question, convert everything to seconds
// try to avoid decimals
// for bisection don't use a last good variable
// if it's one of those problems where there's a lot of cases, go through the code again and see if there's a scenario you didn't think of
// if trying to maximize something, before categorizing/matching positives/negatives or evens/odds try only sorting

Measured memory_bytes: 256